text:
Home/_CULTURE SLIDER/Dubai Expo showcases African cuisine, unites world through food
The African dining hall, named Alkebulan, which is the original name for Africa, featured for the first time at the Expo. The Alkebulan concept was curated by chef Smalls who sees the Expo Dubai as a global platform to begin breaking down stereotypes and misconceptions about what African Cuisine is/Photo Credit: Afro street food
Dubai Expo showcases African cuisine, unites world through food
With more than 250 restaurants representing 191 countries all gathered in a place, the Expo 2020 Dubai has rightly been described as the largest gathering of the global culinary community.
Beyond showcasing delicacies from various parts of the world, a cardinal aim of the food hall at the Expo, according to Simon Wright, Founder and Chairman of TGP International, the global hospitality agency curating the culinary experiences at Expo 2020 Dubai*, is the appreciation for diverse culinary heritage and acceptance of meals from other cultures and countries.
Capturing the essence of the continent, African dining hall Alkebulan – a culinary concept curated by award-winning chef Alexander Smalls – will make its debut at Expo 2020 Dubai, showcasing Africa's cultural diversities and food scenes. Bringing together people, communities and nations, Alkebulan hosts 10 outlets that will give diners the chance to experience gourmet dishes, street bites and fusion fare, all in one destination/Photo: © Expo 2020 Dubai.
Speaking on the potential of the Expo to foster global cohesion through food, Simon said: Expo 2020 Dubai is the largest gathering of the global culinary community. There are more than 250 cultural food concepts representing 191 countries – all in one place. Our thought is that food is all of our common language. And if we can celebrate these nations, the pride of their heritage, and serve amazing food in a "wow" setting, we can tear down some of the divides that exist between us so that people may walk away with a deeper understanding of each other's culinary landscapes.
The meals prepared by the chefs at the African dining hall represent the culinary heritage of 54 African countries/hoto: © Expo 2020 Dubai.
The TGP International Founder is also urging visitors to take advantage of the eclectic culinary line-up, featuring chefs from virtually every part of the globe, to learn more about the foods of other cultures, while also disabusing their minds of previously held notions about meals from other nationalities.
"We're really hoping to serve as the culinary capital of the world to shine a light on rising stars and educate as many attendees as we can to look at food a bit differently than they have before – to try something new, break out of their comfort zones and flip biases about certain foods on their head. I can tell you that after just a few short weeks of Expo 2020 Dubai, it's happening exactly like this in real-time with attendees from all walks of life having their curiosity and senses awakened," he stated.
Chef Alexander Smalls- Curator of Alkebulan, African Dining Hall/Photo: © African Media Agency
Diversity and inclusion added to the uniqueness of the Expo and these factors helped attract renowned chefs such as Alexander Smalls, Scott Conant, Kwame Onwuachi, Geoffrey Zakarian, Mathew Kenney and David Myers amongst others to showcase their culinary prowess.
The African dining hall, named Alkebulan, which is the original name for Africa, featured for the first time at the Expo. The Alkebulan concept was curated by chef Smalls who sees the Expo Dubai as a global platform to begin breaking down stereotypes and misconceptions about what African Cuisine is.
The meals prepared by the chefs at the African dining hall represent the culinary heritage of 54 African countries just as the Alkebulan blends music, art and food to embody the culture of each special place
The success of the Alkebulan has resulted in plans to replicate the concept in international markets such as New York and London to further elevate the way people think about and enjoy African food and how it relates to fine dining.
Speaking on what he hopes will be the key takeaways for attendees at the Expo Dubai, Simon said: "We are using food as a tool to connect and educate people about cultures from all around the world. It is my hope that the first taste inspires someone to continue learning, educating and even visiting a new place."
Afro Street Food
Distributed by African Media Agency on behalf of TGP International.
About Expo 2020 Dubai
From 1 October 2021 to 31 March 2022, Expo 2020 Dubai will welcome visitors from every corner of the globe to join the making of a new world, as it brings together the planet in one place to reimagine tomorrow.
Expo 2020 will be the biggest cultural gathering in the world, presenting a visually striking and emotionally inspiring 182 days, as more than 200 participants – including nations, multilateral organisations, businesses, and educational institutions, as well as millions of visitors – create the largest and most diverse World Expo ever
Expo 2020's sub-themes of Opportunity, Mobility and Sustainability will inspire visitors to preserve and protect our planet, explore new frontiers and build a better future for everyone. For six months, Expo 2020 will be a must-visit family destination, with thousands of events, amazing exploratory experiences, and free entry for children up to age 18.
Expo 2020 is the first World Expo to take place in the Middle East, Africa and South Asia (MEASA) region, located on a 4.38 sqkm site adjacent to Al Maktoum International Airport in Dubai South.
About World Expos
In 1851 the Crystal Palace was the centrepiece of London's Great Exhibition – the first World Expo. It celebrated the man-made industrial wonders of a rapidly changing world. Architecture, contents and a theme, 'Industry of All Nations', were combined to create a big idea of nations meeting nations in shared technological and commercial progress.
In more recent years, participants at World Expos, including governments, international organisations and companies, have gathered to find solutions to universal challenges and to promote their achievements, products, ideas, innovations, their national brand, and their nations as destinations for tourism, trade and investment.
World Expos are held under the auspices of the Bureau International des Expositions (BIE), the intergovernmental organisation responsible for overseeing and regulating international exhibitions ('Expos') and for fostering their core values of Education, Innovation and Cooperation. Today, four types of Expos are organised under the BIE's auspices: World Expos, Specialised Expos, Horticultural Expos and the Triennale di Milano.
Previous Tech Start-ups Key to Africa's Digital Transformation but.. – New report
Next German-African Business Forum NRW holds in February – Register now!
Washington Readies for US-African Leaders Summit
UN agency releases horrendous casualty figures on migration routes
How an African refugee became a star chef in Germany
Afrikamera 2022: Film festival spotlights African diaspora life
Book: ? Why not embrace the one-of-a-kind,..? (Series)
Book: Who are you, if you are not YOU? (Series)
Mahsa Amini: Dead because of a lock of hair
Angela Merkel wins UN refugee agency award
Nigerian leader appeals to West on funds stolen from Africa
Wode Maya & Co: How YouTubers are transforming Africa's image
Berlin museum displays Benin Bronzes in elaborate exhibition
Gates Foundation optimistic about SDGs despite setbacks
"Africa has high Fever" — former UN Secretary General Ban Ki-moon
Potsdam celebrates Africa as Festival marks 11th anniversary!
Facebook, Instagram spotlight 10 leading content creators in Africa
Germany: Céphas Bansah crowned Golden Winemaker with glamour
First Nigerian-German Cultural Carnival holds in Frankfurt
Ghanaians in Germany celebrate annual cultural festival
Germany's leading African film festival celebrates 30th anniversary
Understanding Zimbabwe's Shona Stone Sculpture
Remembering Stephen Osita Osadebe, 15 years on
World's biggest Afrobeats festival moves to Ghana in December
Nigerian cultural festival holds in Germany on Saturday
Remembering Fela Anikulapo-Kuti, 25 years on
Buhari praises Nigerians living abroad, pledges support
Nigeria's Adire Carnival Vienna 2022 – Special report
Nigeria, Germany to sign agreement for return of Benin bronzes in Berlin
Nigeria's Adire festival returns with a bang in Vienna
Experiencing Angélique Kidjo live in Dortmund
Lagos hosts Adire cultural festival
Virgin Music launches African label
Turkey changes official name
London to Lagos biker arrives to rousing welcome in Nigeria
The Nigerian man who set out in London on 19 April for a road trip …


An outbreak of swine flu in Morocco has left nine people dead in the past week, the kingdom's health minister said Saturday.
The spread of the H1N1 virus was "normal" for the time of the year, said Anas Doukkali, quoted by Morocco's official MAP news agency.
The first death in Morocco from the influenza-type virus was reported by local media on Wednesday, since then, anxious rumors have spread on social media.
"Vigilant efforts continue to detect possible cases in different hospitals across the kingdom," said government spokesman Mustapha Khalfi.


Brits - No.1 Squadron (Hurricanes) in France, BBC2 tonight.
Will there be improved cockpits for the Me109?
Wilol there be any cockpit updates?
If you crashland and make it alive... run fast!!
Improved damage modeling for ships in expansion pack?
Ubi.com starts to hog CPU in online games dropping framerate alarmingly!
How to land a I-16?
POLL: Any other I-16 fans out there?
Multiple people flying in a multiseat plane?
Got the whole CH Products Setup!
Forgotten Battles Dedicated Server and Cooperative Mode?
Where Can I get the English Voice Pack? Ntesla? Extreme One?
How to instructions for IL-2 Manager???
Bug in multi engine aircraft mixture behaviour..
Poll: How do you look around in FB?
If I supply a photo, can someone do this for me?
Is Ram the only thing I need?
More RAM or something else???
An idea to satisfy a "special" group of customers!
190 cockpit on - need tips?
So how did the SoCal LAN party go?
VFC COOPs Tonight (Sunday) starting from 10pm eastern US time.
SkyChimp, I could use your help.
Joy2key, where do you get it?
Which is the best Sturmovik..?
How do you enjoy Virtual Cockpits, I mean enjoy?
FBD-stats for UK-Dedicated, can someone explain please?
ME 262 4 Volume book set, Any good?
Track IR and FULL REAL. question ?
Most successful air action of WWII?
Poll. What is the most embarrassing.....?
How to *remove* a HOTAS axis?
Russian aircraft in the Finnish June '44 offensive.
MS FFB2 + X45 Throttle is GOLDEN!!
What is the difference between the ki84s we are getting?
August of 1944 Russian book???
The Role of a Wingman in FB?
Aces Expansion Pack Warstory: "Shoot To Kill."
a couple of simple questions!
Need a new stick - Recommendations?
So, what server/s do you prefer ?
Ilyushin I12: was it really that effective?
Feature Request: Arrow indicators with cockpit on?
How far have you gotten in dynamic campaigns?
Martin B-26 Marauder in IL2FB ?
OT: US ARMY CANCELS RH-66 COMMANCHE PROGRAM!!!!
Famous Quote, who said it thou?
Bf109k4 bug? B&Z for accel speed?
P-38 War Story: Have you seen this?
Metal Storm 1.000.000 Rounds Per Min !
Dedicated Server, what spec would you recommend?
For Fw190 fans and haters!
'Technical support' has now become 'Community Help'?
Is our view of reality correct?
Great game. What is BOB?
is it worth selling my GeForce FX5200 for a 5700 or a Radeon 9600?
Do the Aircraft we Choose Represent our Character?
POLL:: Predict Gibbage's June Surprise ??
Changing skins in Campaign missions. Anyone advise?
Is this lag or what?
Great Idea for a sp Il-2 mission set.
How do you get AI pilots off your six?


Great things often come humbly disguised. While not the perfect metaphor, it comes pretty close to describing vocalist Alyssa Graham's new recording What Love Is. A self-produced effort, and I mean that literally as Miss Graham and her ensemble recorded these 11 selections in Miss Graham's one-bedroom apartment on New York City's Upper West Side, What Love Is proves possession of intimacy and empathy not possible in the sterile confines of the studio. In fact, What Love Is more closely approximates a quiet, well-behaved live date.
Her fourth recorded effort, What Love Is, is also Miss Graham's most fully-realized release to date. Concentrating specifically on standards, the singer and her musical director, Jordan Perlson, more sharply focus the instrumental and vocal muses. Reinterpreting the familiar is in an educational sense an excellent way to introduce jazz to the novice, particularly if done in a thoughtful and conservative way. It is in this respect that Miss Graham is most successful.
Perfectly capable of vocal pyrotechnics, Miss Graham remains close to the melody, accomplishing two important goals in the bargain: one, showing off her beautifully-controlled and sumptuous voice, unclouded by unnecessary experimentation and, two, by presenting straight melodies to standards that new listeners will eventually hear performed instrumentally and with careful listening will better understand.
Saxophonist Lester Young made it a point to know all of the lyrics to the standards he performed. That enabled him to produce music with the added dimension of cognizance, deepening the earthy sensuality of his playing. Conversely, jazz vocalists often help reveal the pathos behind instrumental interpretations of the songs they sing. In Miss Graham's case, listening to her weave through "Easy Living" enables the listener to understand Young's head and solos of the same composition.
Miss Graham assembles a crack, swinging ensemble for What Love Is. The singer augments her enduring guitar/piano quartet (guitarist Doug Graham, drummer Jordan Perlson, pianists Jonathan Anderson and Garry Dial, and bassist Evan Gregor) with Matt Shulman's trumpet and Zachary Colwell's tenor saxophone and flute. Together, the group executes with an easy, intelligent swing, never accelerating into solo or technical excess. This is appropriately and thankfully, just the facts, ma'am... music.
Trumpeter Shulman performs muted on most of his appearances, providing muscular and compelling accompaniment and solos, particularly on "Easy Living" and "You Don't Know What Love Is." On the latter piece, Graham and Shulman supported by an organically-grown harmonic underpinning thoroughly removes the ballad from the gravitational pull of Chet Baker and flings it to the edge of the musical galaxy. Miss Graham does the same with saxophonist Colwell on "One for My Baby," loosening Sinatra's tight grip on the Harold Arlen classic, revealing some of the song's other veiled treasures.
The greatest simpatico is between Miss Graham and her husband, guitarist Doug Graham. He plays cool on acoustic on "Easy Living," switching to hotter electric on "I'll Remember April." Mr. and Mrs. Graham meet perfectly on the surprise cover of Neil Young's "Don't Let It Bring You Down." Here, the Graham's take possession of the piece, making it their own as they transform Young's bleak ballad of revolution into the harmonically earthy jazz hybrid that put Cassandra Wilson on the map. Doug Graham plays a soulful acoustic slide guitar that rubs against Jonathan Anderson's understated piano and Alyssa Graham's resigned with hope lyric presentation.
It is a pleasure to receive and review a recording as well conceived and performed as What Love Is. Miss Graham and Company have turned in a first rate jazz vocal offering. I recommend this disc to the fussiest of my jazz friends and music colleagues.
Track listing: Easy Living; I'll Remember April; September In The Rain; Don't Let It Bring You Down; I Can't Get Started; Chega De Saudade; I Didn't Know What Time it Was; You Don't Know What Love Is; A Beautiful Friendship; Like Someone In Love; One For My Baby.
Personnel: Alyssa Graham: Vocals; Doug Graham: Guitar; Jordan Perlson: Drums & Percussion; Jonathan Anderson, Garry Dial: piano; Matt Shulman: Trumpet; Zachary Colwell: Tenor Saxophone & Flute.


New York City, January 24, 2005– Amidst all the talk about the risk of an influenza pandemic, little has been said about the difficult decisions that would have to be made in an overwhelming health care crisis. An article in the February issue of Academic Emergency Medicine, the Official Journal of the Society for Academic Emergency Medicine, outlines a sample set of guidelines for prioritizing the use of a piece of equipment likely to be in short supply in a pandemic: mechanical ventilators.
Drs. John L. Hick and Daniel T. O'Laughlin of University of Minnesota propose a concept of operations and a set of suggested guidelines for triage of mechanical ventilators in a pandemic or bioterrorist attack. "When a large-scale pandemic does occur, we're simply going to run out of resources," comments Dr. Hick. "We need to be talking now about how we are going to allocate scarce resources to the patients most likely to benefit from them."
Drs. Hick and O'Laughlin developed their proposal after a recent drill showing that an epidemic affecting ten percent of the Minneapolis area would lead to a "rapid and critical shortfall" in the supply of mechanical ventilators. "Despite a surge capacity of between 2,500 and 3,500 beds in the area, there were 16 ventilators available in our regional system," says Dr. Hick.
The proposal calls for a set of general guidelines to be developed in advance by a team of public health officials and medical experts. The guidelines would be subject to review by a separate group, including elected officials and members of the public.
The guidelines would be arranged in tiers—as resources are exhausted, another, more stringent set of criteria would come into effect. At each level, the criteria would be based on clinical indicators of the patient's chances or survival and likely use of health care resources, with the objective of directing scarce resources to the patients most likely to benefit.
In a crisis, the guidelines would rapidly be tailored to the specific situation, then issued as part of an emergency order. "The goal would be to promote consistency between hospitals and regions," says Dr Hick. "Across the board, resource allocation would be as equal as possible. The concept is distributive justice—doing the greatest good for the greatest number of patients."
In Minnesota, the process is underway, according to Dr. Hick. "A set of general guidelines has been developed by a science team and vetted by a guidelines review group. Reception has generally been very positive, including discussions of the ethical issues involved with community groups."
Even more important than the specific plan that emerges, Dr. Hick believes, is the establishment of a decision-making process. "We need to start talking about this now and develop a fair process so that, in a crisis, a mechanism is in place to apply the best science we have to make the best use of the available resources. Although our plan is not definitive, we hope it will serve as the starting point for a discussion that needs to be had."


text:
© 2023 by Tyler Reece. Proudly created with Wix.com
ALEX STURBAUM
musician, singer, songwriter, performer, teacher, and more
Alex Sturbaum is a one-of-a-kind performer. Steeped in musical traditions from both sides of the Atlantic, he is equally comfortable performing for dancers in a crowded grange hall, singing original songs at an intimate house concert, belting out sea shanties at a pub sing, or playing bluegrass music at a festival. Alex's love for the music and joy in playing it is evident in every note he plays.
Alex grew up in Cincinnati, Ohio, on the banks of the Ohio River. Born into a musical household, he soon fell in love with the jigs and reels of Irish and Scottish music, the sea songs of the Canadian maritimes, the fiddle tunes and ballads of Appalachia, and more. Alex studied at the Riley School of Irish Music in Cincinnati before attending Oberlin College, where he began playing music for contra dances and never looked back. He now lives in Seattle, Washington, where he performs solo and with various bands, teaches, and, when he is not playing music, does environmental restoration.

redpajama_set_name: RedPajamaCommonCrawl


There are some possible ways to watch Hindi Movies Online for free. You can certainly browse through it awesome and mind-boggling contents without paying a penny. Amazon Prime Video top our list of best websites to watch Hindi movies online free without downloading. It contains some premium movies for the cost of Rs. And if are a Hindi movie enthusiast and love to watch Bollywood movies online, you will certainly be taken aback while beholding its inventory.
Exquisite and astonishing work of Bollywood is well accessible for free of cost. Them 4500 that you can watch online. Now watching Hindi movies online is way easier than before. . Planning to watch a Bollywood movie online? These sites offer both free and premium stuff. It supports Linux, Windows, Mac and other browsers. Watch free latest movies online on Flenix! Indeed, one of the best sites to watch Hindi movies online.
The same possesses an arsenal of shows, movies. Content ranges from mind-boggling new releases to evergreen classics. Yupptv launched an app for online movie users to watch Bollywood, Hollywood and some of the Hollywood movies. On this site, users can stream online Hindi movies in high definition from their large catalog of Hollywood, Bollywood films, and others. So is such a preference. If you go through the website it looks less clarity but looks perfect.
Besides, you can enjoy all the latest and new releases that are still in theaters. Yes, you read it right. Some of the titles can be downloaded and watch it for offline content for free of cost. All of the free movies found on this website are hosted on third-party servers that are freely available to watch online for all internet users. If you need a user-friendly places to stream movies online, simply bookmark Sonyliv. The best thing about this website, it allows users to stream movies on full-length in Hindi, Tamil, Telugu, Punjabi, Malayalam and other Indian regional languages.
Channels and also a dedicated section for kids. If you spend a lot of time searching for a decent movie, searching tons of sites that are filled with advertising? Ozee movies have huge collection videos and music albums from different languages especially Tamil, Kannada, Malayalam, and Hindi from various states. And if you are a Hindi movie fanatic, then its a guarantee that it would cent percent be able to hold up your expectations. The site doesn't require registration and you can browse by genre, country, or date of movie release for easy access to a vast collection of Hindi Movies Online. They also provide the movie quality in a much more minimalistic way by pinning down the quality i.
We are pleased to inform you that you've come to the right place. YouTube Movies: YouTube meets the user expectations has become number one entertainment channel in the world. Opting for the same will undoubtedly provide you with some of the uber cool features like — ad-free content, movie download, accessing the same account from multiple devices at the same time etc. We have worked a lot on filters algorithms, and continue to improve them. Thank you for staying with us! Ozee Movies: Ozee movies owned by Zee Entertainment limited. Grab yourself packets of popcorn or other mouth-watering snacks and start enjoying your favorite movies.
The site may not work in some regions so google if the above domain is not working. Hotstar is a versatile streaming platform that lets you check out movies of various genre and culture , , ,. Sony Live offers best user interface, easy to use for normal users also. Almost all movies in YouTube Movies are free to watch online and download through the embed code. PopCorn Flix: PopCornFlix offers free video streaming offers Full-length Hindi movies and as well as Bollywood web series. Though most of the contents of the same are free, yet for full-fledged entertainment, you need to pay them a handful amount which certainly depends on your country or whereabouts.
It allows a number of ads on that pop up in between the movies. You can download the favourite one for free of cost as an offline content. Though most of the contents boasting in the same are free, yet few falls beneath the premium section which is unquestionably affordable. Though most of the contents of the same are not free, yet spending pennies over such an instance is certainly worthy. Watch Hindi Movies Online is the trend in the United Kingdom, Netherlands, United States, and some other parts of the world right now by Hindi movies lovers viewers.
Ending With Hope your primarily unquenchable thirst is well quenched. The site is way simplistic and the subheads are quite self-explainable. A good movie is a perfect time pass as it not only entertains you but also gives you the best exposure on your favorite generic. Voot is a part of Viacom 18 Digital ventures, it gives high streaming experience. Convenient filters that you can use through the movies page. If you are looking for legal places to watch the latest Hindi movies online, this Amazon prime is actually one of my favorites in terms of watching unlimited actions, heroic stories, inspirational movies, romantic movies, thrill movies, suspense movies and more.


text:
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/permissions/permission_request_manager.h"

#include <memory>

#include "base/bind.h"
#include "base/command_line.h"
#include "base/metrics/field_trial.h"
#include "base/run_loop.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h"
#include "chrome/browser/download/download_permission_request.h"
#include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
#include "chrome/browser/permissions/attestation_permission_request.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
#include "chrome/browser/ui/test/test_browser_dialog.h"
#include "chrome/common/chrome_features.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "chrome/test/permissions/permission_request_manager_test_api.h"
#include "components/back_forward_cache/back_forward_cache_disable.h"
#include "components/content_settings/core/common/content_settings_types.h"
#include "components/custom_handlers/register_protocol_handler_permission_request.h"
#include "components/permissions/features.h"
#include "components/permissions/permission_context_base.h"
#include "components/permissions/permission_request.h"
#include "components/permissions/permission_ui_selector.h"
#include "components/permissions/permission_util.h"
#include "components/permissions/request_type.h"
#include "components/permissions/test/mock_permission_prompt_factory.h"
#include "components/permissions/test/mock_permission_request.h"
#include "components/variations/variations_associated_data.h"
#include "content/public/browser/permission_controller_delegate.h"
#include "content/public/browser/permission_type.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/common/content_features.h"
#include "content/public/test/back_forward_cache_util.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/fenced_frame_test_util.h"
#include "content/public/test/prerender_test_util.h"
#include "content/public/test/test_navigation_observer.h"
#include "content/public/test/test_utils.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "services/device/public/cpp/test/scoped_geolocation_overrider.h"
#include "third_party/blink/public/mojom/permissions/permission_status.mojom-shared.h"
#include "url/gurl.h"
#include "url/origin.h"

namespace {

const char* kPermissionsKillSwitchFieldStudy =
    permissions::PermissionContextBase::kPermissionsKillSwitchFieldStudy;
const char* kPermissionsKillSwitchBlockedValue =
    permissions::PermissionContextBase::kPermissionsKillSwitchBlockedValue;
const char kPermissionsKillSwitchTestGroup[] = "TestGroup";

// Test implementation of PermissionUiSelector that always returns a canned
// decision.
class TestQuietNotificationPermissionUiSelector
    : public permissions::PermissionUiSelector {
 public:
  explicit TestQuietNotificationPermissionUiSelector(
      const Decision& canned_decision)
      : canned_decision_(canned_decision) {}

  TestQuietNotificationPermissionUiSelector(
      const TestQuietNotificationPermissionUiSelector&) = delete;
  TestQuietNotificationPermissionUiSelector& operator=(
      const TestQuietNotificationPermissionUiSelector&) = delete;

  ~TestQuietNotificationPermissionUiSelector() override = default;

 protected:
  // permissions::PermissionUiSelector:
  void SelectUiToUse(permissions::PermissionRequest* request,
                     DecisionMadeCallback callback) override {
    std::move(callback).Run(canned_decision_);
  }

  bool IsPermissionRequestSupported(
      permissions::RequestType request_type) override {
    return request_type == permissions::RequestType::kNotifications;
  }

 private:
  Decision canned_decision_;
};

class PermissionRequestManagerBrowserTest : public InProcessBrowserTest {
 public:
  PermissionRequestManagerBrowserTest() {
    scoped_feature_list_.InitAndEnableFeature(
        permissions::features::kBlockRepeatedNotificationPermissionPrompts);
  }

  PermissionRequestManagerBrowserTest(
      const PermissionRequestManagerBrowserTest&) = delete;
  PermissionRequestManagerBrowserTest& operator=(
      const PermissionRequestManagerBrowserTest&) = delete;

  ~PermissionRequestManagerBrowserTest() override = default;

  void SetUpOnMainThread() override {
    permissions::PermissionRequestManager* manager =
        GetPermissionRequestManager();
    mock_permission_prompt_factory_ =
        std::make_unique<permissions::MockPermissionPromptFactory>(manager);

    host_resolver()->AddRule("*", "127.0.0.1");
  }

  void TearDownOnMainThread() override {
    ShutDownFirstTabMockPermissionPromptFactory();
  }

  void ShutDownFirstTabMockPermissionPromptFactory() {
    mock_permission_prompt_factory_.reset();
  }

  permissions::PermissionRequestManager* GetPermissionRequestManager() {
    return permissions::PermissionRequestManager::FromWebContents(
        browser()->tab_strip_model()->GetActiveWebContents());
  }

  permissions::MockPermissionPromptFactory* bubble_factory() {
    return mock_permission_prompt_factory_.get();
  }

  void EnableKillSwitch(ContentSettingsType content_settings_type) {
    std::map<std::string, std::string> params;
    params[permissions::PermissionUtil::GetPermissionString(
        content_settings_type)] = kPermissionsKillSwitchBlockedValue;
    variations::AssociateVariationParams(kPermissionsKillSwitchFieldStudy,
                                         kPermissionsKillSwitchTestGroup,
                                         params);
    base::FieldTrialList::CreateFieldTrial(kPermissionsKillSwitchFieldStudy,
                                           kPermissionsKillSwitchTestGroup);
  }

  void TriggerAndExpectPromptCooldownToBeStillActiveAfterNavigationAction(
      void navigation_action(content::WebContents*, const GURL&),
      bool expect_cooldown) {
    ASSERT_TRUE(embedded_test_server()->Start());

    const GURL kInitialURL = embedded_test_server()->GetURL(
        "a.localhost", "/permissions/killswitch_tester.html");
    const GURL kSecondURL = embedded_test_server()->GetURL(
        "b.localhost", "/permissions/killswitch_tester.html");
    const GURL kThirdURL = embedded_test_server()->GetURL(
        "c.localhost", "/permissions/killswitch_tester.html");

    ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), kInitialURL));
    bubble_factory()->ResetCounts();
    bubble_factory()->set_response_type(
        permissions::PermissionRequestManager::AutoResponseType::DENY_ALL);

    // Simulate a notification permission request that is denied by the user.
    std::string result;
    content::WebContents* web_contents =
        browser()->tab_strip_model()->GetActiveWebContents();
    ASSERT_TRUE(content::ExecuteScriptWithoutUserGestureAndExtractString(
        web_contents, "requestNotification();", &result));
    ASSERT_EQ(1, bubble_factory()->show_count());
    ASSERT_EQ(1, bubble_factory()->TotalRequestCount());
    ASSERT_EQ("denied", result);

    // In response, simulate the website automatically triggering a
    // renderer-initiated cross-origin navigation without user gesture.
    content::TestNavigationObserver navigation_observer(web_contents);
    ASSERT_TRUE(content::ExecuteScriptWithoutUserGesture(
        web_contents, "window.location = \"" + kSecondURL.spec() + "\";"));
    navigation_observer.Wait();

    bubble_factory()->ResetCounts();
    bubble_factory()->set_response_type(
        permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ALL);

    // Request the notification permission again from a different origin.
    // Cross-origin permission prompt cool-down should be in effect.
    ASSERT_TRUE(content::ExecuteScriptWithoutUserGestureAndExtractString(
        web_contents, "requestNotification();", &result));
    ASSERT_EQ(0, bubble_factory()->show_count());
    ASSERT_EQ(0, bubble_factory()->TotalRequestCount());
    ASSERT_EQ("default", result);

    // Now try one of a number other kinds of navigations, and request the
    // notification permission again.
    navigation_action(web_contents, kThirdURL);
    ASSERT_TRUE(content::ExecuteScriptAndExtractString(
        web_contents, "requestNotification();", &result));

    // Cross-origin prompt cool-down may or may not be in effect anymore
    // depending on the type of navigation.
    if (expect_cooldown) {
      EXPECT_EQ(0, bubble_factory()->show_count());
      EXPECT_EQ(0, bubble_factory()->TotalRequestCount());
      EXPECT_EQ("default", result);
    } else {
      EXPECT_EQ(1, bubble_factory()->show_count());
      EXPECT_EQ(1, bubble_factory()->TotalRequestCount());
      EXPECT_EQ("granted", result);
    }
  }

  content::RenderFrameHost* GetActiveMainFrame() {
    return browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame();
  }

 private:
  base::test::ScopedFeatureList scoped_feature_list_;
  std::unique_ptr<permissions::MockPermissionPromptFactory>
      mock_permission_prompt_factory_;
};

class PermissionRequestManagerWithBackForwardCacheBrowserTest
    : public PermissionRequestManagerBrowserTest {
 public:
  void SetUpCommandLine(base::CommandLine* command_line) override {
    PermissionRequestManagerBrowserTest::SetUpCommandLine(command_line);
    feature_list_.InitWithFeaturesAndParameters(
        {{features::kBackForwardCache,
          {{"TimeToLiveInBackForwardCacheInSeconds", "3600"},
           {"ignore_outstanding_network_request_for_testing", "true"}}}},
        // Allow BackForwardCache for all devices regardless of their memory.
        {features::kBackForwardCacheMemoryControls});
  }

 private:
  base::test::ScopedFeatureList feature_list_;
};

class PermissionRequestManagerWithPrerenderingTest
    : public PermissionRequestManagerBrowserTest {
 public:
  PermissionRequestManagerWithPrerenderingTest()
      : prerender_test_helper_(base::BindRepeating(
            &PermissionRequestManagerWithPrerenderingTest::GetWebContents,
            base::Unretained(this))) {}

  void SetUp() override {
    prerender_test_helper_.SetUp(embedded_test_server());
    PermissionRequestManagerBrowserTest::SetUp();
  }

  void SetUpOnMainThread() override {
    PermissionRequestManagerBrowserTest::SetUpOnMainThread();
    ASSERT_TRUE(embedded_test_server()->Start());
  }

  content::test::PrerenderTestHelper& prerender_test_helper() {
    return prerender_test_helper_;
  }

 private:
  content::WebContents* GetWebContents() {
    return browser()->tab_strip_model()->GetActiveWebContents();
  }

  content::test::PrerenderTestHelper prerender_test_helper_;
};

// Requests before the load event should be bundled into one bubble.
// http://crbug.com/512849 flaky
IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
                       DISABLED_RequestsBeforeLoad) {
  ASSERT_TRUE(embedded_test_server()->Start());

  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
      browser(),
      embedded_test_server()->GetURL("/permissions/requests-before-load.html"),
      1);
  bubble_factory()->WaitForPermissionBubble();

  EXPECT_EQ(1, bubble_factory()->show_count());
  EXPECT_EQ(2, bubble_factory()->TotalRequestCount());
}

// Requests before the load should not be bundled with a request after the load.
IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
                       RequestsBeforeAfterLoad) {
  ASSERT_TRUE(embedded_test_server()->Start());

  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
      browser(),
      embedded_test_server()->GetURL(
          "/permissions/requests-before-after-load.html"),
      1);
  bubble_factory()->WaitForPermissionBubble();

  EXPECT_EQ(1, bubble_factory()->show_count());
  EXPECT_EQ(1, bubble_factory()->TotalRequestCount());
}

// Navigating twice to the same URL should be equivalent to refresh. This means
// showing the bubbles twice.
// http://crbug.com/512849 flaky
IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest, DISABLED_NavTwice) {
  ASSERT_TRUE(embedded_test_server()->Start());

  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
      browser(),
      embedded_test_server()->GetURL("/permissions/requests-before-load.html"),
      1);
  bubble_factory()->WaitForPermissionBubble();

  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
      browser(),
      embedded_test_server()->GetURL("/permissions/requests-before-load.html"),
      1);
  bubble_factory()->WaitForPermissionBubble();

  EXPECT_EQ(2, bubble_factory()->show_count());
  EXPECT_EQ(2, bubble_factory()->TotalRequestCount());
}

// Navigating twice to the same URL with a hash should be navigation within the
// page. This means the bubble is only shown once.
// http://crbug.com/512849 flaky
IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
                       DISABLED_NavTwiceWithHash) {
  ASSERT_TRUE(embedded_test_server()->Start());

  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
      browser(),
      embedded_test_server()->GetURL("/permissions/requests-before-load.html"),
      1);
  bubble_factory()->WaitForPermissionBubble();

  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
      browser(),
      embedded_test_server()->GetURL(
          "/permissions/requests-before-load.html#0"),
      1);
  bubble_factory()->WaitForPermissionBubble();

  EXPECT_EQ(1, bubble_factory()->show_count());
  EXPECT_EQ(1, bubble_factory()->TotalRequestCount());
}

// Bubble requests should be shown after same-document navigation.
IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
                       SameDocumentNavigation) {
  ASSERT_TRUE(embedded_test_server()->Start());

  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
      browser(), embedded_test_server()->GetURL("/empty.html"), 1);

  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
      browser(), embedded_test_server()->GetURL("/empty.html#0"), 1);

  // Request 'geolocation' permission.
  ExecuteScriptAndGetValue(
      GetActiveMainFrame(),
      "navigator.geolocation.getCurrentPosition(function(){});");
  bubble_factory()->WaitForPermissionBubble();

  EXPECT_EQ(1, bubble_factory()->show_count());
  EXPECT_EQ(1, bubble_factory()->TotalRequestCount());
}

// Prompts are only shown for active tabs and (on Desktop) hidden on tab
// switching
// Flaky on bots crbug.com/1003747.
IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
                       DISABLED_MultipleTabs) {
  ASSERT_TRUE(embedded_test_server()->Start());

  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
      browser(), embedded_test_server()->GetURL("/empty.html"), 1);

  ui_test_utils::NavigateToURLWithDisposition(
      browser(), embedded_test_server()->GetURL("/empty.html"),
      WindowOpenDisposition::NEW_FOREGROUND_TAB,
      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);

  // SetUp() only creates a mock prompt factory for the first tab.
  permissions::MockPermissionPromptFactory* bubble_factory_0 = bubble_factory();
  std::unique_ptr<permissions::MockPermissionPromptFactory> bubble_factory_1(
      std::make_unique<permissions::MockPermissionPromptFactory>(
          GetPermissionRequestManager()));

  TabStripModel* tab_strip_model = browser()->tab_strip_model();
  ASSERT_EQ(2, tab_strip_model->count());
  ASSERT_EQ(1, tab_strip_model->active_index());

  // Request geolocation in foreground tab, prompt should be shown.
  ExecuteScriptAndGetValue(
      tab_strip_model->GetWebContentsAt(1)->GetMainFrame(),
      "navigator.geolocation.getCurrentPosition(function(){});");
  EXPECT_EQ(1, bubble_factory_1->show_count());
  EXPECT_FALSE(bubble_factory_0->is_visible());
  EXPECT_TRUE(bubble_factory_1->is_visible());

  tab_strip_model->ActivateTabAt(0);
  EXPECT_FALSE(bubble_factory_0->is_visible());
  EXPECT_FALSE(bubble_factory_1->is_visible());

  tab_strip_model->ActivateTabAt(1);
  EXPECT_EQ(2, bubble_factory_1->show_count());
  EXPECT_FALSE(bubble_factory_0->is_visible());
  EXPECT_TRUE(bubble_factory_1->is_visible());

  // Request notification in background tab. No prompt is shown until the tab
  // itself is activated.
  ExecuteScriptAndGetValue(tab_strip_model->GetWebContentsAt(0)->GetMainFrame(),
                           "Notification.requestPermission()");
  EXPECT_FALSE(bubble_factory_0->is_visible());
  EXPECT_EQ(2, bubble_factory_1->show_count());

  tab_strip_model->ActivateTabAt(0);
  EXPECT_TRUE(bubble_factory_0->is_visible());
  EXPECT_EQ(1, bubble_factory()->show_count());
  EXPECT_EQ(2, bubble_factory_1->show_count());
}

// Regularly timing out in Windows, Linux and macOS Debug Builds.
// https://crbug.com/931657
IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
                       DISABLED_BackgroundTabNavigation) {
  ASSERT_TRUE(embedded_test_server()->Start());

  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
      browser(), embedded_test_server()->GetURL("/empty.html"), 1);

  // Request camera, prompt should be shown.
  ExecuteScriptAndGetValue(
      browser()->tab_strip_model()->GetWebContentsAt(0)->GetMainFrame(),
      "navigator.getUserMedia({video: true}, ()=>{}, ()=>{})");
  bubble_factory()->WaitForPermissionBubble();
  EXPECT_TRUE(bubble_factory()->is_visible());
  EXPECT_EQ(1, bubble_factory()->show_count());

  // SetUp() only creates a mock prompt factory for the first tab but this test
  // doesn't request any permissions in the second tab so it doesn't need one.
  ui_test_utils::NavigateToURLWithDisposition(
      browser(), embedded_test_server()->GetURL("/empty.html"),
      WindowOpenDisposition::NEW_FOREGROUND_TAB,
      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);

  // Navigate background tab, prompt should be removed.
  ExecuteScriptAndGetValue(
      browser()->tab_strip_model()->GetWebContentsAt(0)->GetMainFrame(),
      "window.location = 'simple.html'");
  content::TestNavigationObserver observer(
      browser()->tab_strip_model()->GetWebContentsAt(0));
  observer.Wait();
  EXPECT_FALSE(bubble_factory()->is_visible());

  browser()->tab_strip_model()->ActivateTabAt(0);
  EXPECT_FALSE(bubble_factory()->is_visible());
  EXPECT_EQ(1, bubble_factory()->show_count());
}

// Bubble requests should not be shown when the killswitch is on.
IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
                       KillSwitchGeolocation) {
  ASSERT_TRUE(embedded_test_server()->Start());

  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(),
      embedded_test_server()->GetURL("/permissions/killswitch_tester.html")));

  content::WebContents* web_contents =
      browser()->tab_strip_model()->GetActiveWebContents();
  EXPECT_TRUE(content::ExecuteScript(web_contents, "requestGeolocation();"));
  bubble_factory()->WaitForPermissionBubble();
  EXPECT_EQ(1, bubble_factory()->show_count());
  EXPECT_EQ(1, bubble_factory()->TotalRequestCount());

  // Now enable the geolocation killswitch.
  EnableKillSwitch(ContentSettingsType::GEOLOCATION);

  // Reload the page to get around blink layer caching for geolocation
  // requests.
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(),
      embedded_test_server()->GetURL("/permissions/killswitch_tester.html")));

  std::string result;
  EXPECT_TRUE(content::ExecuteScriptAndExtractString(
      web_contents, "requestGeolocation();", &result));
  EXPECT_EQ("denied", result);
  EXPECT_EQ(1, bubble_factory()->show_count());
  EXPECT_EQ(1, bubble_factory()->TotalRequestCount());
}

// Regression test for crbug.com/900997.
IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
                       CrossOriginPromptCooldown) {
  ASSERT_TRUE(embedded_test_server()->Start());

  const GURL kInitialURL = embedded_test_server()->GetURL(
      "a.localhost", "/permissions/killswitch_tester.html");
  const GURL kSecondURL = embedded_test_server()->GetURL(
      "b.localhost", "/permissions/killswitch_tester.html");

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), kInitialURL));
  bubble_factory()->set_response_type(
      permissions::PermissionRequestManager::AutoResponseType::DENY_ALL);

  // Simulate a notification permission request that is denied by the user.
  std::string result;
  content::WebContents* web_contents =
      browser()->tab_strip_model()->GetActiveWebContents();
  ASSERT_TRUE(content::ExecuteScriptWithoutUserGestureAndExtractString(
      web_contents, "requestNotification();", &result));
  ASSERT_EQ(1, bubble_factory()->show_count());
  ASSERT_EQ(1, bubble_factory()->TotalRequestCount());
  ASSERT_EQ("denied", result);

  // In response, simulate the website automatically triggering a
  // renderer-initiated cross-origin navigation without user gesture.
  content::TestNavigationObserver navigation_observer(web_contents);
  ASSERT_TRUE(content::ExecuteScriptWithoutUserGesture(
      web_contents, "window.location = \"" + kSecondURL.spec() + "\";"));
  navigation_observer.Wait();

  // Request the notification permission again from a different origin.
  // Cross-origin permission prompt cool-down should be in effect.
  bubble_factory()->ResetCounts();
  bubble_factory()->set_response_type(
      permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ALL);
  ASSERT_TRUE(content::ExecuteScriptWithoutUserGestureAndExtractString(
      web_contents, "requestNotification();", &result));
  EXPECT_EQ(0, bubble_factory()->show_count());
  EXPECT_EQ(0, bubble_factory()->TotalRequestCount());
  EXPECT_EQ("default", result);
}

// Regression test for crbug.com/900997.
IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
                       CooldownEndsOnUserInitiatedReload) {
  TriggerAndExpectPromptCooldownToBeStillActiveAfterNavigationAction(
      [](content::WebContents* web_contents, const GURL& unused_url) {
        content::NavigationController& controller =
            web_contents->GetController();
        controller.Reload(content::ReloadType::NORMAL, false);
        EXPECT_TRUE(content::WaitForLoadStop(web_contents));
      },
      false /* expect_cooldown */);
}

// Regression test for crbug.com/900997.
IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
                       CooldownEndsOnBrowserInitiateNavigation) {
  TriggerAndExpectPromptCooldownToBeStillActiveAfterNavigationAction(
      [](content::WebContents* web_contents, const GURL& url) {
        EXPECT_TRUE(content::NavigateToURL(web_contents, url));
      },
      false /* expect_cooldown */);
}

// Regression test for crbug.com/900997.
IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
                       CooldownEndsOnRendererInitiateNavigationWithGesture) {
  TriggerAndExpectPromptCooldownToBeStillActiveAfterNavigationAction(
      [](content::WebContents* web_contents, const GURL& url) {
        content::TestNavigationObserver navigation_observer(web_contents);
        EXPECT_TRUE(content::ExecuteScript(
            web_contents, "window.location = \"" + url.spec() + "\";"));
        navigation_observer.Wait();
      },
      false /* expect_cooldown */);
}

// Regression test for crbug.com/900997.
IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
                       CooldownOutlastsRendererInitiatedReload) {
  TriggerAndExpectPromptCooldownToBeStillActiveAfterNavigationAction(
      [](content::WebContents* web_contents, const GURL& unused_url) {
        content::TestNavigationObserver navigation_observer(web_contents);
        EXPECT_TRUE(content::ExecuteScriptWithoutUserGesture(
            web_contents, "window.location.reload();"));
        navigation_observer.Wait();
      },
      true /* expect_cooldown */);
}

// Regression test for crbug.com/900997.
IN_PROC_BROWSER_TEST_F(
    PermissionRequestManagerBrowserTest,
    CooldownOutlastsRendererInitiateNavigationWithoutGesture) {
  TriggerAndExpectPromptCooldownToBeStillActiveAfterNavigationAction(
      [](content::WebContents* web_contents, const GURL& url) {
        content::TestNavigationObserver navigation_observer(web_contents);
        EXPECT_TRUE(content::ExecuteScriptWithoutUserGesture(
            web_contents, "window.location = \"" + url.spec() + "\";"));
        navigation_observer.Wait();
      },
      true /* expect_cooldown */);
}

// Bubble requests should not be shown when the killswitch is on.
IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
                       KillSwitchNotifications) {
  ASSERT_TRUE(embedded_test_server()->Start());

  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(),
      embedded_test_server()->GetURL("/permissions/killswitch_tester.html")));

  content::WebContents* web_contents =
      browser()->tab_strip_model()->GetActiveWebContents();
  EXPECT_TRUE(content::ExecuteScript(web_contents, "requestNotification();"));
  bubble_factory()->WaitForPermissionBubble();
  EXPECT_EQ(1, bubble_factory()->show_count());
  EXPECT_EQ(1, bubble_factory()->TotalRequestCount());

  // Now enable the notifications killswitch.
  EnableKillSwitch(ContentSettingsType::NOTIFICATIONS);

  std::string result;
  EXPECT_TRUE(content::ExecuteScriptAndExtractString(
      web_contents, "requestNotification();", &result));
  EXPECT_EQ("denied", result);
  EXPECT_EQ(1, bubble_factory()->show_count());
  EXPECT_EQ(1, bubble_factory()->TotalRequestCount());
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
                       PendingRequestsDisableBackForwardCache) {
  content::BackForwardCacheDisabledTester back_forward_cache_tester;
  ASSERT_TRUE(embedded_test_server()->Start());

  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
      browser(),
      embedded_test_server()->GetURL(
          "/permissions/requests-before-after-load.html"),
      1);
  bubble_factory()->WaitForPermissionBubble();

  content::RenderFrameHost* main_frame = GetActiveMainFrame();
  int main_frame_process_id = main_frame->GetProcess()->GetID();
  int main_frame_routing_id = main_frame->GetRoutingID();

  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
      browser(), embedded_test_server()->GetURL("b.com", "/title1.html"), 1);
  EXPECT_TRUE(back_forward_cache_tester.IsDisabledForFrameWithReason(
      main_frame_process_id, main_frame_routing_id,
      back_forward_cache::DisabledReason(
          back_forward_cache::DisabledReasonId::kPermissionRequestManager)));
}

class PermissionRequestManagerQuietUiBrowserTest
    : public PermissionRequestManagerBrowserTest {
 public:
  PermissionRequestManagerQuietUiBrowserTest() {
    scoped_feature_list_.InitAndEnableFeature(
        features::kQuietNotificationPrompts);
  }

 protected:
  using UiDecision = permissions::PermissionUiSelector::Decision;
  using QuietUiReason = permissions::PermissionUiSelector::QuietUiReason;
  using WarningReason = permissions::PermissionUiSelector::WarningReason;

  void SetCannedUiDecision(absl::optional<QuietUiReason> quiet_ui_reason,
                           absl::optional<WarningReason> warning_reason) {
    GetPermissionRequestManager()->set_permission_ui_selector_for_testing(
        std::make_unique<TestQuietNotificationPermissionUiSelector>(
            UiDecision(quiet_ui_reason, warning_reason)));
  }

 private:
  base::test::ScopedFeatureList scoped_feature_list_;
};

// Re-enable when 1016233 is fixed.
// Quiet permission requests are cancelled when a new request is made.
IN_PROC_BROWSER_TEST_F(PermissionRequestManagerQuietUiBrowserTest,
                       DISABLED_QuietPendingRequestsKilledOnNewRequest) {
  content::RenderFrameHost* source_frame = GetActiveMainFrame();
  // First add a quiet permission request. Ensure that this request is decided
  // by the end of this test.
  permissions::MockPermissionRequest request_quiet(
      permissions::RequestType::kNotifications);
  GetPermissionRequestManager()->AddRequest(source_frame, &request_quiet);
  base::RunLoop().RunUntilIdle();

  // Add a second permission request. This ones should cause the initial
  // request to be cancelled.
  permissions::MockPermissionRequest request_loud(
      permissions::RequestType::kGeolocation);
  GetPermissionRequestManager()->AddRequest(source_frame, &request_loud);
  base::RunLoop().RunUntilIdle();

  // The first dialog should now have been decided.
  EXPECT_TRUE(request_quiet.finished());
  EXPECT_EQ(1u, GetPermissionRequestManager()->Requests().size());

  // Cleanup remaining request. And check that this was the last request.
  GetPermissionRequestManager()->Dismiss();
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(0u, GetPermissionRequestManager()->Requests().size());
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerQuietUiBrowserTest,
                       PermissionPromptDisposition) {
  SetCannedUiDecision(QuietUiReason::kTriggeredDueToAbusiveContent,
                      WarningReason::kAbusiveContent);

  auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
  permissions::MockPermissionRequest request_quiet(
      permissions::RequestType::kNotifications);
  GetPermissionRequestManager()->AddRequest(web_contents->GetMainFrame(),
                                            &request_quiet);

  bubble_factory()->WaitForPermissionBubble();
  auto* manager = GetPermissionRequestManager();

  absl::optional<permissions::PermissionPromptDisposition> disposition =
      manager->current_request_prompt_disposition_for_testing();
  auto disposition_from_prompt_bubble =
      manager->view_for_testing()->GetPromptDisposition();

  manager->Dismiss();
  base::RunLoop().RunUntilIdle();

  EXPECT_TRUE(disposition.has_value());
  EXPECT_EQ(disposition.value(), disposition_from_prompt_bubble);
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerQuietUiBrowserTest,
                       PermissionPromptDispositionHidden) {
  SetCannedUiDecision(QuietUiReason::kTriggeredDueToAbusiveContent,
                      WarningReason::kAbusiveContent);

  auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
  permissions::MockPermissionRequest request_quiet(
      permissions::RequestType::kNotifications);
  GetPermissionRequestManager()->AddRequest(web_contents->GetMainFrame(),
                                            &request_quiet);

  bubble_factory()->WaitForPermissionBubble();
  auto* manager = GetPermissionRequestManager();
  auto disposition_from_prompt_bubble =
      manager->view_for_testing()->GetPromptDisposition();

  // There will be no instance of PermissionPromptImpl after a tab marked as
  // HIDDEN.
  manager->OnVisibilityChanged(content::Visibility::HIDDEN);

  absl::optional<permissions::PermissionPromptDisposition> disposition =
      manager->current_request_prompt_disposition_for_testing();

  EXPECT_TRUE(disposition.has_value());
  EXPECT_EQ(disposition.value(), disposition_from_prompt_bubble);

  //  DCHECK failure if Closing executed on HIDDEN PermissionRequestManager.
  manager->OnVisibilityChanged(content::Visibility::VISIBLE);
  manager->Dismiss();
  base::RunLoop().RunUntilIdle();
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerQuietUiBrowserTest,
                       ConsoleMessages) {
  const struct {
    absl::optional<QuietUiReason> simulated_quiet_ui_reason;
    absl::optional<WarningReason> simulated_warning_reason;
    const char* expected_message;
  } kTestCases[] = {
      {UiDecision::UseNormalUi(), UiDecision::ShowNoWarning(), nullptr},
      {QuietUiReason::kEnabledInPrefs, UiDecision::ShowNoWarning(), nullptr},
      {QuietUiReason::kTriggeredByCrowdDeny, UiDecision::ShowNoWarning(),
       nullptr},
      {QuietUiReason::kTriggeredDueToAbusiveRequests,
       UiDecision::ShowNoWarning(),
       permissions::kAbusiveNotificationRequestsEnforcementMessage},
      {UiDecision::UseNormalUi(), WarningReason::kAbusiveRequests,
       permissions::kAbusiveNotificationRequestsWarningMessage},
      {QuietUiReason::kTriggeredDueToAbusiveContent,
       UiDecision::ShowNoWarning(),
       permissions::kAbusiveNotificationContentEnforcementMessage},
      {UiDecision::UseNormalUi(), WarningReason::kAbusiveContent,
       permissions::kAbusiveNotificationContentWarningMessage},
  };

  constexpr char kCounterVerificationPattern[] = "NOTHING";

  for (const auto& test : kTestCases) {
    SCOPED_TRACE(testing::Message() << "Test index: " << (&test - kTestCases));

    SetCannedUiDecision(test.simulated_quiet_ui_reason,
                        test.simulated_warning_reason);

    auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
    content::WebContentsConsoleObserver console_observer(web_contents);

    permissions::MockPermissionRequest request_quiet(
        permissions::RequestType::kNotifications);
    GetPermissionRequestManager()->AddRequest(web_contents->GetMainFrame(),
                                              &request_quiet);

    bubble_factory()->WaitForPermissionBubble();
    GetPermissionRequestManager()->Dismiss();
    base::RunLoop().RunUntilIdle();

    if (!test.expected_message) {
      web_contents->GetMainFrame()->AddMessageToConsole(
          blink::mojom::ConsoleMessageLevel::kInfo,
          kCounterVerificationPattern);
    }

    console_observer.Wait();

    ASSERT_EQ(1u, console_observer.messages().size());
    if (test.expected_message) {
      EXPECT_EQ(test.expected_message, console_observer.GetMessageAt(0));
      EXPECT_EQ(blink::mojom::ConsoleMessageLevel::kWarning,
                console_observer.messages()[0].log_level);
    } else {
      EXPECT_EQ(kCounterVerificationPattern, console_observer.GetMessageAt(0));
    }
  }
}

// Two loud requests are simply queued one after another.
IN_PROC_BROWSER_TEST_F(PermissionRequestManagerBrowserTest,
                       LoudPendingRequestsQueued) {
  content::RenderFrameHost* source_frame = GetActiveMainFrame();
  permissions::MockPermissionRequest request1(
      permissions::RequestType::kClipboard);
  GetPermissionRequestManager()->AddRequest(source_frame, &request1);
  base::RunLoop().RunUntilIdle();

  permissions::MockPermissionRequest request2(
      permissions::RequestType::kMicStream);
  GetPermissionRequestManager()->AddRequest(source_frame, &request2);
  base::RunLoop().RunUntilIdle();

  // Both requests are still pending (though only one is active).
  EXPECT_FALSE(request1.finished());
  EXPECT_FALSE(request2.finished());
  EXPECT_EQ(1u, GetPermissionRequestManager()->Requests().size());

  // Close first request.
  GetPermissionRequestManager()->Dismiss();
  base::RunLoop().RunUntilIdle();

  if (base::FeatureList::IsEnabled(permissions::features::kPermissionChip)) {
    EXPECT_FALSE(request1.finished());
    EXPECT_TRUE(request2.finished());
  } else {
    EXPECT_TRUE(request1.finished());
    EXPECT_FALSE(request2.finished());
  }
  EXPECT_EQ(1u, GetPermissionRequestManager()->Requests().size());

  // Close second request. No more requests pending
  GetPermissionRequestManager()->Dismiss();
  base::RunLoop().RunUntilIdle();

  EXPECT_TRUE(request1.finished());
  EXPECT_TRUE(request2.finished());
  EXPECT_EQ(0u, GetPermissionRequestManager()->Requests().size());
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerWithBackForwardCacheBrowserTest,
                       NoPermissionBubbleShownForPagesInCache) {
  ASSERT_TRUE(embedded_test_server()->Start());
  GURL url_a = embedded_test_server()->GetURL("a.com", "/title1.html");
  GURL url_b = embedded_test_server()->GetURL("b.com", "/title1.html");

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url_a));
  content::RenderFrameHostWrapper rfh_a(GetActiveMainFrame());

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url_b));
  EXPECT_EQ(rfh_a->GetLifecycleState(),
            content::RenderFrameHost::LifecycleState::kInBackForwardCache);

  permissions::MockPermissionRequest req(
      permissions::RequestType::kNotifications);
  GetPermissionRequestManager()->AddRequest(rfh_a.get(), &req);

  base::RunLoop().RunUntilIdle();

  EXPECT_EQ(0, bubble_factory()->show_count());
  EXPECT_EQ(0, bubble_factory()->TotalRequestCount());
  // Page gets evicted if bubble would have been shown.
  ASSERT_TRUE(rfh_a.WaitUntilRenderFrameDeleted());
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerWithBackForwardCacheBrowserTest,
                       RequestsForPagesInCacheNotGrouped) {
  ASSERT_TRUE(embedded_test_server()->Start());
  GURL url_a = embedded_test_server()->GetURL("a.com", "/title1.html");
  GURL url_b = embedded_test_server()->GetURL("b.com", "/title1.html");

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url_a));
  content::RenderFrameHostWrapper rfh_a(GetActiveMainFrame());

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url_b));
  EXPECT_EQ(rfh_a->GetLifecycleState(),
            content::RenderFrameHost::LifecycleState::kInBackForwardCache);
  content::RenderFrameHostWrapper rfh_b(GetActiveMainFrame());

  // Mic, camera, and pan/tilt/zoom requests are grouped if they come from the
  // same origin. Make sure this will not include requests from a cached frame.
  // Note pages will not be cached when navigating within the same origin, so we
  // have different urls in the navigations above but use the same (default) url
  // for the MockPermissionRequest here.
  permissions::MockPermissionRequest req_a_1(
      permissions::RequestType::kCameraPanTiltZoom,
      permissions::PermissionRequestGestureType::GESTURE);
  permissions::MockPermissionRequest req_b_1(
      permissions::RequestType::kCameraStream,
      permissions::PermissionRequestGestureType::GESTURE);
  permissions::MockPermissionRequest req_b_2(
      permissions::RequestType::kMicStream,
      permissions::PermissionRequestGestureType::GESTURE);
  GetPermissionRequestManager()->AddRequest(rfh_a.get(),
                                            &req_a_1);  // Should be skipped
  GetPermissionRequestManager()->AddRequest(rfh_b.get(), &req_b_1);
  GetPermissionRequestManager()->AddRequest(rfh_b.get(), &req_b_2);

  bubble_factory()->WaitForPermissionBubble();

  // One bubble with the two grouped requests and not the skipped one.
  EXPECT_EQ(1, bubble_factory()->show_count());
  EXPECT_EQ(2, bubble_factory()->TotalRequestCount());
  EXPECT_TRUE(req_a_1.cancelled());

  // Page gets evicted if bubble would have been shown.
  ASSERT_TRUE(rfh_a.WaitUntilRenderFrameDeleted());

  // Cleanup before we delete the requests.
  GetPermissionRequestManager()->Dismiss();
}

class PermissionRequestManagerOneTimeGeolocationPermissionBrowserTest
    : public PermissionRequestManagerBrowserTest {
 public:
  PermissionRequestManagerOneTimeGeolocationPermissionBrowserTest() {
    scoped_feature_list_.InitAndEnableFeature(
        permissions::features::kOneTimeGeolocationPermission);
    geolocation_overrider_ =
        std::make_unique<device::ScopedGeolocationOverrider>(0, 0);
  }

 private:
  base::test::ScopedFeatureList scoped_feature_list_;
  std::unique_ptr<device::ScopedGeolocationOverrider> geolocation_overrider_;
};

IN_PROC_BROWSER_TEST_F(
    PermissionRequestManagerOneTimeGeolocationPermissionBrowserTest,
    RequestForPermission) {
  const char kQueryCurrentPosition[] = R"(
        navigator.geolocation.getCurrentPosition(
          _ => domAutomationController.send('success'),
          _ => domAutomationController.send('failure'));
      )";
  ASSERT_TRUE(embedded_test_server()->Start());
  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
      browser(), embedded_test_server()->GetURL("/title1.html"), 1);
  bubble_factory()->set_response_type(
      permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ONCE);

  // Request 'geolocation' permission.
  std::string result =
      content::EvalJs(GetActiveMainFrame(), kQueryCurrentPosition,
                      content::EXECUTE_SCRIPT_USE_MANUAL_REPLY)
          .ExtractString();
  EXPECT_EQ("success", result);
  EXPECT_EQ(1, bubble_factory()->TotalRequestCount());

  // Request 'geolocation' permission. There should not be a 2nd prompt.
  result = content::EvalJs(GetActiveMainFrame(), kQueryCurrentPosition,
                           content::EXECUTE_SCRIPT_USE_MANUAL_REPLY)
               .ExtractString();
  EXPECT_EQ("success", result);
  EXPECT_EQ(1, bubble_factory()->TotalRequestCount());

  // Open a new tab with same domain.
  ui_test_utils::NavigateToURLWithDisposition(
      browser(), embedded_test_server()->GetURL("/title1.html"),
      WindowOpenDisposition::NEW_FOREGROUND_TAB,
      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);

  // Create a new mock permission prompt factory for the second tab.
  std::unique_ptr<permissions::MockPermissionPromptFactory>
      second_tab_bubble_factory(
          std::make_unique<permissions::MockPermissionPromptFactory>(
              GetPermissionRequestManager()));

  // Request 'geolocation' permission.
  result = content::EvalJs(GetActiveMainFrame(), kQueryCurrentPosition,
                           content::EXECUTE_SCRIPT_USE_MANUAL_REPLY)
               .ExtractString();
  EXPECT_EQ("success", result);
  // There should be no permission prompt.
  EXPECT_EQ(0, second_tab_bubble_factory.get()->TotalRequestCount());

  // Open a new empty tab before closing the first two tabs.
  ui_test_utils::NavigateToURLWithDisposition(
      browser(), GURL(url::kAboutBlankURL),
      WindowOpenDisposition::NEW_FOREGROUND_TAB,
      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);

  // Need to close the mock permission managers before closing the tabs.
  // Otherwise the tab instances can't be destroyed due to a DCHECK
  ShutDownFirstTabMockPermissionPromptFactory();
  second_tab_bubble_factory.reset();

  // Close the first two tabs.
  TabStripModel* tab_strip_model = browser()->tab_strip_model();
  tab_strip_model->CloseWebContentsAt(0, TabStripModel::CLOSE_USER_GESTURE);
  tab_strip_model->CloseWebContentsAt(0, TabStripModel::CLOSE_USER_GESTURE);

  ASSERT_EQ(1, tab_strip_model->count());

  // Create a new mock permission prompt factory for the third tab.
  std::unique_ptr<permissions::MockPermissionPromptFactory>
      third_tab_bubble_factory(
          std::make_unique<permissions::MockPermissionPromptFactory>(
              GetPermissionRequestManager()));

  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
      browser(), embedded_test_server()->GetURL("/title1.html"), 1);
  third_tab_bubble_factory.get()->set_response_type(
      permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ONCE);

  // Request 'geolocation' permission. We should get a prompt.
  result = content::EvalJs(GetActiveMainFrame(), kQueryCurrentPosition,
                           content::EXECUTE_SCRIPT_USE_MANUAL_REPLY)
               .ExtractString();
  EXPECT_EQ("success", result);

  EXPECT_EQ(1, third_tab_bubble_factory.get()->TotalRequestCount());
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerWithPrerenderingTest,
                       RequestForPermission) {
  GURL initial_url = embedded_test_server()->GetURL("a.test", "/empty.html");
  GURL prerender_url = embedded_test_server()->GetURL("a.test", "/title1.html");
  ASSERT_NE(ui_test_utils::NavigateToURL(browser(), initial_url), nullptr);
  ASSERT_EQ(GetActiveMainFrame()->GetLastCommittedURL(), initial_url);

  prerender_test_helper().AddPrerender(prerender_url);
  int host_id = prerender_test_helper().GetHostForUrl(prerender_url);
  content::RenderFrameHost* prerender_frame =
      prerender_test_helper().GetPrerenderedMainFrameHost(host_id);
  EXPECT_NE(prerender_frame, nullptr);

  content::RenderFrameDeletedObserver deleted_observer(prerender_frame);
  permissions::MockPermissionRequest request(
      permissions::RequestType::kNotifications);
  bubble_factory()->set_response_type(
      permissions::PermissionRequestManager::ACCEPT_ALL);
  GetPermissionRequestManager()->AddRequest(prerender_frame, &request);

  deleted_observer.WaitUntilDeleted();

  // Permission request should be denied and prerender that sent the request
  // should be discarded.
  EXPECT_TRUE(request.cancelled());
  EXPECT_TRUE(deleted_observer.deleted());
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerWithPrerenderingTest,
                       DuplicateRequestForPermission) {
  GURL initial_url = embedded_test_server()->GetURL("a.test", "/empty.html");
  GURL prerender_url = embedded_test_server()->GetURL("a.test", "/title1.html");
  ASSERT_NE(ui_test_utils::NavigateToURL(browser(), initial_url), nullptr);
  ASSERT_EQ(GetActiveMainFrame()->GetLastCommittedURL(), initial_url);

  prerender_test_helper().AddPrerender(prerender_url);
  int host_id = prerender_test_helper().GetHostForUrl(prerender_url);
  content::RenderFrameHost* prerender_frame =
      prerender_test_helper().GetPrerenderedMainFrameHost(host_id);
  EXPECT_NE(prerender_frame, nullptr);

  content::RenderFrameDeletedObserver deleted_observer(prerender_frame);
  permissions::MockPermissionRequest request_1(
      permissions::RequestType::kNotifications);
  auto request_2 = request_1.CreateDuplicateRequest();
  bubble_factory()->set_response_type(
      permissions::PermissionRequestManager::ACCEPT_ALL);
  GetPermissionRequestManager()->AddRequest(GetActiveMainFrame(), &request_1);
  GetPermissionRequestManager()->AddRequest(prerender_frame, request_2.get());

  base::RunLoop().RunUntilIdle();

  // Permission request from main frame should be granted, similar request from
  // prerender should be denied.
  EXPECT_TRUE(request_1.granted());
  EXPECT_TRUE(request_2->cancelled());
  EXPECT_TRUE(deleted_observer.deleted());
}

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerWithPrerenderingTest,
                       PrerenderLoadsWhileRequestsPending) {
  GURL initial_url = embedded_test_server()->GetURL("a.test", "/empty.html");
  GURL prerender_url = embedded_test_server()->GetURL("a.test", "/title1.html");
  GURL next_url = embedded_test_server()->GetURL("b.test", "/title1.html");
  ASSERT_NE(ui_test_utils::NavigateToURL(browser(), initial_url), nullptr);
  ASSERT_EQ(GetActiveMainFrame()->GetLastCommittedURL(), initial_url);

  permissions::MockPermissionRequest request_1(
      permissions::RequestType::kNotifications);
  permissions::MockPermissionRequest request_2(
      permissions::RequestType::kGeolocation);
  GetPermissionRequestManager()->AddRequest(GetActiveMainFrame(), &request_1);
  GetPermissionRequestManager()->AddRequest(GetActiveMainFrame(), &request_2);

  prerender_test_helper().AddPrerender(prerender_url);
  int host_id = prerender_test_helper().GetHostForUrl(prerender_url);
  content::RenderFrameHost* prerender_frame =
      prerender_test_helper().GetPrerenderedMainFrameHost(host_id);
  EXPECT_NE(prerender_frame, nullptr);

  // Prerender's navigation should not cancel pending primary main frame
  // permission requests.
  EXPECT_FALSE(request_1.cancelled());
  EXPECT_FALSE(request_2.cancelled());

  // Navigate primary main frame.
  ASSERT_NE(ui_test_utils::NavigateToURL(browser(), next_url), nullptr);

  // Primary main frame navigation should cancel pending permission requests.
  EXPECT_TRUE(request_1.cancelled());
  EXPECT_TRUE(request_2.cancelled());
}

class PermissionRequestManagerWithFencedFrameTest
    : public PermissionRequestManagerBrowserTest {
 public:
  PermissionRequestManagerWithFencedFrameTest() = default;
  ~PermissionRequestManagerWithFencedFrameTest() override = default;

  PermissionRequestManagerWithFencedFrameTest(
      const PermissionRequestManagerWithFencedFrameTest&) = delete;
  PermissionRequestManagerWithFencedFrameTest& operator=(
      const PermissionRequestManagerWithFencedFrameTest&) = delete;

 protected:
  content::test::FencedFrameTestHelper& fenced_frame_test_helper() {
    return fenced_frame_helper_;
  }

 private:
  content::test::FencedFrameTestHelper fenced_frame_helper_;
};

IN_PROC_BROWSER_TEST_F(PermissionRequestManagerWithFencedFrameTest,
                       GetCurrentPosition) {
  ASSERT_TRUE(embedded_test_server()->Start());

  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), embedded_test_server()->GetURL("/title1.html")));
  content::WebContents* web_contents =
      browser()->tab_strip_model()->GetActiveWebContents();

  // Load a fenced frame.
  GURL fenced_frame_url =
      embedded_test_server()->GetURL("/fenced_frames/title1.html");
  content::RenderFrameHost* fenced_frame_host =
      fenced_frame_test_helper().CreateFencedFrame(web_contents->GetMainFrame(),
                                                   fenced_frame_url);
  ASSERT_TRUE(fenced_frame_host);

  const char kQueryPermission[] = R"(
      (async () => {
        const status = await navigator.permissions.query({name: 'geolocation'});
        return status.state;
      })();
    )";

  // The result of query 'geolocation' permission in the fenced frame should be
  // 'denied'.
  EXPECT_EQ("denied", content::EvalJs(fenced_frame_host, kQueryPermission));

  const char kQueryCurrentPosition[] = R"(
      (async () => {
        return await new Promise(resolve => {
          navigator.geolocation.getCurrentPosition(
              () => resolve('granted'), () => resolve('denied'));
        });
      })();
    )";

  bubble_factory()->set_response_type(
      permissions::PermissionRequestManager::AutoResponseType::ACCEPT_ONCE);

  // The getCurrentPosition() call in the fenced frame should be denied.
  EXPECT_EQ("denied",
            content::EvalJs(fenced_frame_host, kQueryCurrentPosition));
  // The permission prompt should not be shown.
  EXPECT_EQ(0, bubble_factory()->TotalRequestCount());
}

// Tests that the permission request for a fenced frame is blocked
// when the permission is requested thru PermissionControllerDelegate.
IN_PROC_BROWSER_TEST_F(PermissionRequestManagerWithFencedFrameTest,
                       RequestPermissionThruDelegate) {
  net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
  https_server.AddDefaultHandlers(GetChromeTestDataDir());
  https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_OK);
  ASSERT_TRUE(https_server.Start());
  auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();

  GURL initial_url = https_server.GetURL("/title1.html");
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url));

  // Load a fenced frame.
  GURL fenced_frame_url = https_server.GetURL("/fenced_frames/title1.html");
  content::RenderFrameHost* fenced_frame_host =
      fenced_frame_test_helper().CreateFencedFrame(web_contents->GetMainFrame(),
                                                   fenced_frame_url);
  ASSERT_TRUE(fenced_frame_host);

  // The permission request is denied because it's from the fenced frame.
  const char kExpectedConsolePattern[] =
      "*blocked because it was requested inside a fenced frame*";
  content::WebContentsConsoleObserver console_observer(web_contents);
  console_observer.SetFilter(base::BindRepeating(
      [](content::RenderFrameHost* render_frame_host,
         const content::WebContentsConsoleObserver::Message& message) {
        return message.source_frame == render_frame_host;
      },
      fenced_frame_host->GetOutermostMainFrame()));
  console_observer.SetPattern(kExpectedConsolePattern);

  base::MockOnceCallback<void(blink::mojom::PermissionStatus)> callback;
  EXPECT_CALL(callback, Run(blink::mojom::PermissionStatus::DENIED));
  auto* delegate = browser()->profile()->GetPermissionControllerDelegate();
  delegate->RequestPermission(content::PermissionType::SENSORS,
                              fenced_frame_host, fenced_frame_url,
                              /* user_gesture = */ true, callback.Get());
  console_observer.Wait();
  ASSERT_EQ(1u, console_observer.messages().size());
}

}  // anonymous namespace

redpajama_set_name: RedPajamaGithub


Anthony, Jennifer, Sarah, and friends attend the largest anim� convention in the United States, Otakon, at the Baltimore Convention Center!
There are 13 files, last one added on Aug 12, 2006.
Jen from WRAJ Internet Radio, Jen's friend Jen are cosplaying their favorite anime character and visiting the I-Con 23 at Stony Brook University. Anthony joined the two.
There are 10 files, last one added on Nov 17, 2004.


Take off baby's arithmetic power!
Baby do math. Training baby math in a happy way (Addition and Subtraction less than 10/20). Baby do math and win the score, then go to stickers' wall to play. We include 108 stickers in this app, including animals, birds, fishes and even paper-cut paintings. Enjoy your happy time; search us with "tiqiaa" or "tianjia" in app store.


text:
News / Doubling Down
Can Brexit help Britain to finally get over its imperial past?
Thursday, 11 October 2018 2:32 AM [ Last Update: Thursday, 11 October 2018 8:22 AM ]
While some called it an expression of popular will against a remote political elite, others claimed it was a reassertion of national identity from a people tired of multiculturalism and mass European migration.
But three slogans that rang out during the referendum campaign reveal a much deeper reason, which has its roots in Britain's past. They are "Take Back Control", "Putting the Great Back into Britain", and "Global Britain".
According to several leading commentators including the Guardian's Gary Younge and Oxford University's Danny Dorling, these slogans come from a mentality that yearns for the days when Britain controlled a third of the globe, with an empire that Winston Churchill thought might last for a thousand years. That empire is long gone, but the mentality it fostered is still with us. It's an attitude that tells British people they alone won two world wars, that they were better off before they joined the EU, and they gave the world civilisation
MORE FROM Doubling Down
How would sports and humanitarian efforts mix?
Has Germany forgotten lessons of brutal past?
What now for International Criminal Court?


text:
1. [](#bugfix)
    * Only include `outerclasses` DIV if defined [#135](https://github.com/getgrav/grav-plugin-form/issues/135)

# v2.3.0
## 03/17/2017

1. [](#new)
    * Ability to process any form on any page via `action:`.  Super useful if you want to handle form processing on some other non-form page (or Ajax)
    * Added the ability for the form to set the `template:` to use to render the form processing response.
1. [](#bugfix)
    * Fix `number` field so it works with min value `0` [#130](https://github.com/getgrav/grav-plugin-form/issues/130)

# v2.2.0
## 03/13/2017

1. [](#new)
    * Added new `fieldset` form field [#125](https://github.com/getgrav/grav-plugin-form/issues/125)
    * Added new `conditional form field` to show fields only if some `condition` is set
1. [](#improved)
    * Added the option to have outer-classes on buttons [#124](https://github.com/getgrav/grav-plugin-form/issues/124)
    * Added the option to disable fields label if not defined [#126](https://github.com/getgrav/grav-plugin-form/issues/126)

# v2.1.1
## 02/17/2017

1. [](#improved)
    * Better default output for select, checkbox and checkboxes fields in the form destination page and in the emails sent via form submit [#121](https://github.com/getgrav/grav-plugin-form/issues/121)

# v2.1.0
## 02/10/2017

1. [](#improved)
    * Reworked logic so form caching is based on `Pages::getPagesCacheId()`
    * Added `url` option for button field
1. [](#bugfix)
    * Fixed issue with `honeypot` field not throwing exception properly

# v2.0.10
## 02/08/2017

1. [](#improved)
    * Optimistically set 'status' to `success` when requesting a form via Ajax. Form processing listeners should take care of setting status to something else
1. [](#bugfix)
    * File uploads are now adding a `__form-file-uploader__` POST field to better allow identifying them with Ajax
    * Require jQuery when using the File field, as it's needed by the form.min.js file required in the file upload functionality

# v2.0.9
## 01/24/2017

1. [](#bugfix)
    * Translate the labels in data.html.twig [https://github.com/getgrav/grav-plugin-comments/issues/38](https://github.com/getgrav/grav-plugin-comments/issues/38)
    * Fixed file input when `System` > `Twig` > `Autoescape` is set to `Yes`

# v2.0.8
## 12/13/2016

1. [](#new)
    * RC released as stable
    * Added a new `honeypot` field for form anti-spam protection

# v2.0.8-rc.1
## 11/26/2016

1. [](#bugfix)
    * Fixed Forms 2.0 changes for registration form [#101](https://github.com/getgrav/grav-plugin-form/issues/101)
    * Fixed errant reference to Grav DI container in Form#getPagePathFromToken [#105](https://github.com/getgrav/grav-plugin-form/issues/105)
    * Fixed issue with spacer fields being displayed first, not in order [#104](https://github.com/getgrav/grav-plugin-form/issues/104)

# v2.0.7
## 11/17/2016

1. [](#improved)
    * Added method to set all data in a form
    * Added params to form action URL
    * Added ability to add ids to buttons and to set them disabled
1. [](#bugfix)
    * Moved Files Upload GC logic to function in front-end only

# v2.0.6
## 10/19/2016

1. [](#bugfix)
    * Fixed translations for `display` field
    * Fixed [#95](https://github.com/getgrav/grav-plugin-form/issues/95) multilanguage forms submission
    * Fixed duplicate textarea class tag [#98](https://github.com/getgrav/grav-plugin-form/issues/98)

# v2.0.5
## 09/15/2016

1. [](#bugfix)
    * Fix passing updating the header through event, no need for return value

# v2.0.4
## 09/15/2016

1. [](#improved)
    * Allow filling the page header form dynamically (e.g. use case: Comments plugin)

# v2.0.3
## 09/12/2016

1. [](#improved)
    * Use `Page::slug()` for form name if not set in the form itself (better backwards compatibility)

# v2.0.2
## 09/08/2016

1. [](#improved)
    * Added support for Grav's autoescape twig setting
    * Allow to add additional markup fields in form and field twig overrides
    * Updated the french language translation

# v2.0.1
## 09/07/2016

1. [](#bugfix)
    * Fixed a backwards compatibility issue with Admin forms

# v2.0.0
## 09/07/2016

1. [](#new)
    * Forms now supports multiple forms per page!
    * Access forms from any other page within the current page
    * Instantiate forms directly in page content with Twig processing enabled
    * New Twig function to get forms data from any other page
    * Ability to use Twig in saved filename
    * Reworked the `file` field. All files get uploaded via Ajax and are stored upon Submit. Fully backward compatible, `file` field now includes also a `limit` and `filesize` option. The former determines how many files are allowed to be uploaded when in combination with `multiple: true` (default: 10), the latter determines the file size limit (in MB) allowed for each file (default: 5MB)
1. [](#improved)
    * Added several missing HTML5 form input field types [#87](https://github.com/getgrav/grav-plugin-form/issues/87)
    * Added Support for CSS id in form definition

# v1.3.2
## 08/10/2016

1. [](#improved)
    * Added Romanian translation
1. [](#bugfix)
    * Fixed an issue with Recaptcha secret throwing errors [#84](https://github.com/getgrav/grav-plugin-form/pull/84)

# v1.3.1
## 07/27/2016

1. [](#improved)
    * Added support for multiple emails in `email` field (add `multiple: true` to enable)
1. [](#bugfix)
    * Fixed backward incompatibility with forms submission and data retrieval [getgrav/grav#933](https://github.com/getgrav/grav/issues/933)

# v1.3.0
## 07/14/2016

1. [](#improved)
    * When uploading a file through a form, if the file is already existing prepend the current day and time to the filename instead of overwriting it.

# v1.3.0-rc.4
## 06/21/2016

1. [](#bugfix)
    * Fixed running on Grav 1.0.x

# v1.3.0-rc.3
## 06/17/2016

1. [](#new)
    * Set hints for checkboxes options and allow field descriptions

# v1.3.0-rc.2
## 06/08/2016

1. [](#new)
    * Allow to process Twig in a hidden field, by setting `evaluate: true`

# v1.3.0-rc.1
## 06/01/2016

1. [](#improved)
    * French updated

# v1.3.0-beta.6
## 05/23/2016

1. [](#new)
    * Added support for advanced blueprint functionality in forms
    * Added site-wide form options to set Google Captcha site + secret keys [#34](https://github.com/getgrav/grav-plugin-form/pull/34)
    * Session-based 'flash' storage of form for redirects [#48](https://github.com/getgrav/grav-plugin-form/issues/48)
    * Added ability to **append** to file if you include a `process: save: body:` template attribute [#65](https://github.com/getgrav/grav-plugin-form/issues/65)
1. [](#improved)
    * Support `keyname` form format like admin forms
    * Added backwards compatibility for Captcha field
    * Added 'markdown-notices' style output for better errors
    * Added `Forms::getValue()` method to retrieve values programatically
    * Changed `datetime` form field to simply extend `text` until implemented
    * Updated french language
1. [](#bugfix)
    * Refactored the files upload logic
    * Missing Language string
    * Fixed errors not getting output

# v1.3.0-beta.5
## 05/12/2016

1. [](#improved)
    * Moved form/field.html.twig file to the default folder, to be more easily extended in themes

# v1.3.0-beta.4
## 05/04/2016

1. [](#new)
    * Added support for `prepend` and `append` field attributes for Text input

# v1.3.0-beta.3
## 05/03/2016

1. [](#bugfix)
    * Fix for select field admin translation

# v1.3.0-beta.2
## 04/27/2016

1. [](#bugfix)
    * Fix for autoescape in spacer and display form fields
    * Fix issue with form reset action [#66](https://github.com/getgrav/grav-plugin-form/pull/66)

# v1.3.0-beta.1
## 04/20/2016

1. [](#new)
    * Added the HTML5 `range` input field with `min` and `max` parameters
1. [](#improved)
    * Allow to override classes in Form definition for the form element
    * Add more blocks in the Form twig template, so classes can be overridden more easily in themes
    * Reworked some fields to fit the new Admin
    * Use `scope` for form fields to allow fields to be excluded from the data by adding `input@: false` to their definition
    * Added german translation
    * Allow to add inline Twig to the form message definition
1. [](#bugfix)
    * Fixed the form action URL for home page forms
    * Fix stopping form events propagation, correctly stop when one event is stopped
    * Allow to translate the fields placeholders and the form message
    * Fix captcha javascript function ordering. Also, render it in the site active language
    * Support attribute `for="id"` on label for checkbox
    * Fix select fields with the multiple option enabled
    * Fixed select options escaping with autoescape on - [#502](https://github.com/getgrav/grav-plugin-admin/issues/502)

# v1.2.2
## 02/11/2016

1. [](#bugfix)
    * Fixed case issue when including form file.

# v1.2.1
## 02/11/2016

1. [](#new)
    * Allow placeholder for **select** field
1. [](#improved)
    * Use common language strings in blueprints
    * Use `for` attribute in labels
    * Improved `README.md`
    * Code lint
1. [](#bugfix)
    * Moved `nl2br` to correct place or will break for arrays

# v1.2.0
## 01/06/2016

1. [](#bugfix)
    * Correctly merge the file field configuration
    * restore full file information save

# v1.1.0
## 12/18/2015

1. [](#new)
    * Croatian translation
    * Added id, style, and disabled options to select fields
1. [](#improved)
    * Allow adding form labels and help text as lang strings
    * Allow translating field content
    * Allow translating button and checkbox labels
    * Allow adding classes to the form field container with `field.outerclasses`
    * Updated French translation
1. [](#bugfix)
    * Fixed error message on file upload
    * Fixed overriding defaults for the file type in forms

# v1.0.3
## 12/11/2015

1. [](#improved)
    * Updated languages
    * Allow an action to stop processing
1. [](#bugfix)
    * Fix captcha validation
    * Fix issue where Form was unsetting valid page

# v1.0.2
## 12/01/2015

1. [](#bugfix)
    * Fixed merge of defaults settings
    * Support for arrays in `data.txt.twig`
    * Fixed blueprint for admin

# v1.0.1
## 12/01/2015

1. [](#new)
    * New **file upload** field
    * Added modular form template
    * Spanish translation
    * Hungarian translation
    * Italian translation

# v1.0.0
## 11/21/2015

1. [](#new)
    * Server-side validation of forms #11
    * Added french translation
    * Added **nonce** form security
1. [](#improved)
    * Show a more meaningful error when the display page is not found
    * Added links to learn site for form examples
    * Label can be omitted
    * Allow user to set the CSS class for buttons
1. [](#bugfix)
    * Fixed multi-language forms
    * Checkbox is translatable
    * Minor fixes

# v0.6.0
## 10/21/2015

1. [](#bugfix)
    * Fixed for missing attributes in textarea field
    * Fixed checkbox inputs

# v0.5.0
## 10/15/2015

1. [](#new)
    * New `operation` param to allow different file saving strategies
    * Ability to add new file saving strategies
    * Now calls a `process()` method during form processing
1. [](#improved)
    * Added server-side captcha validation and removed front-end validation
    * Allow `filename` instead of `prefix`, `format` + `extension`
1. [](#bugfix)
    * Fixed radio inputs

# v0.4.0
## 9/16/2015

1. [](#new)
    * PHP server-side form validation
    * Added new Google Catpcha field with front-end validation
1. [](#improved)
    * Add defaults for forms, moved from the themes to the Form plugin
    * Store multi-line fields with line endings converted to HTML

# v0.3.0
## 9/11/2015

1. [](#improved)
    * Refactored all the forms fields

# v0.2.1
## 08/24/2015

1. [](#improved)
    * Translated tooltips

# v0.2.0
## 08/11/2015

1. [](#improved)
    * Disable `enable` in admin

# v0.1.0
## 08/04/2015

1. [](#new)
    * ChangeLog started...


After three months flicking through slide after slide of the same three hundred or so whale sharks, you wonder about the delicate dictation of genes that shape the subtle nuances between phenotypes. Some of their patterns are remarkably similar, different in only the breadth of a stripe here or the completeness of a circle there. Others are perhaps distorted reflections of another. One has its own easily discerned pattern but on closer inspection: the same taper to a V above the pectoral fin, or a familiar swirl adjacent to the posterior-most operculum.


*Prices reflect cost of the vehicle and on road costs. Offer available for specific in stock models only sold by April 22, 2019.
Models shown are examples and may have additional optional extras. Finance available.
2010kw Galaxy Blue and fitted with 22" alloys, platinum grey exterior trim, sunroof, adaptive cruise control.
160kw Sline Navarra Blue 20" alloys, sports seats, economical and smooth.
Two to choose from, Ara Blue and Floret Silver, rear camera, satellite navigation, Bluetooth.
Jet Black Metallic/Red Roof, 365HP Flat 4-Turbo, 0-100km/h in 4.3 sec's.
Quarzite Grey Metallic, 462HP Twin Engine V6T + Electric Motor, maximum torque 700Nm, 0-100km/h in 5.0 sec's.
Jaguar E-Pace D150 AWD S - Indus Silver.
V6 Convertible - Storm Grey.
2.0T Sport - Loire Blue.
2.0T Prestige - Glacier White.
Td6 HSE - Yulong White.
D5 AWD Momentum - Crystal White.
T6 AWD R-Design - Crystal White.
D5 AWD R-Design - Bright Silver.


GIG is the name behind world-class catering and hospitality experiences.
We are an award-winning specialist with an international reach but personal touch. We've been leading the way by working with some of the biggest brands for over 30 years, never forgetting that every single one is unique. Infinitely flexible and always approachable, we use knowledge, craft and culinary flair to create the perfect solution and the best impression, feeding your imagination along the way.
Eat to the Beat is a specialist division of GIG and a great support act in providing first rate, behind-the-scenes catering and hospitality to artists, production and crew.


Harness the power of online reviews and increase sales.
experience and enhance your reputation.
Harness the power of online reviews and increase sales. Monitor and promptly respond to customer reviews from across the web. Proactively solicit reviews from your customers. Use feedback to improve the customer experience and enhance your reputation.
ReviewIgnite is a powerful online reputation management platform that allows businesses to improve their reputation across the web and grow sales. Through a secure, easy-to-use dashboard, businesses can monitor what customers are saying on major review websites and social media — such as Google, Yelp and Facebook — from any computer or mobile device, anytime. ReviewIgnite saves time while giving your staff the tools to receive alerts for new reviews, quickly respond to these reviews and analyze their feedback. The results? Improved service delivery, brand reputation and bottom line.
Let's face it. More and more consumers read online reviews before making purchase decisions. Ignoring reviews about your business can negatively impact sales. By engaging with customers and actively generating additional reviews, businesses see an increase in revenue. Online reviews can impact your SEO efforts as search engines like Google are placing reviews higher in search results.
Experienced Online Reputation Management Professionals.
At ReviewIgnite, we understand improving your brand reputation is a journey. That's why our staff is made up of dedicated people that have decades of experience working with business owners in showing them how delivering exceptional customer experiences and leveraging online customer reviews can improve brand reputation and revenue.
The ReviewIgnite Online Reputation Management Platform is a mature software system more than capable of handling the monitoring and data needs of any size organization. It's easy-to-use, intuitive design means less of a learning curve for you and your staff to get up and running fast.
Never fear – help is just a click or phone call away! A key part of our services is providing friendly, knowledgeable support to make sure you get the most out of the ReviewIgnite system.
Step 1 – Signup for ReviewIgnite by providing a few pieces of information about your business.
Step 2 – Receive login credentials and instructions to access your own online dashboard.
Step 3 – Start monitoring and harnessing the power of online reviews for all your business locations!
Want to learn more? Request your free demo from ReviewIgnite today!