1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <stdint.h>
6 
7 #include "base/files/file_path.h"
8 #include "base/files/file_util.h"
9 #include "base/files/scoped_temp_dir.h"
10 #include "base/macros.h"
11 #include "base/run_loop.h"
12 #include "base/scoped_observer.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/test/metrics/histogram_tester.h"
15 #include "base/threading/thread_restrictions.h"
16 #include "build/build_config.h"
17 #include "chrome/browser/download/download_prefs.h"
18 #include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
19 #include "chrome/browser/extensions/api/extension_action/test_extension_action_api_observer.h"
20 #include "chrome/browser/extensions/api/extension_action/test_icon_image_observer.h"
21 #include "chrome/browser/extensions/extension_action.h"
22 #include "chrome/browser/extensions/extension_action_icon_factory.h"
23 #include "chrome/browser/extensions/extension_action_manager.h"
24 #include "chrome/browser/extensions/extension_action_runner.h"
25 #include "chrome/browser/extensions/extension_apitest.h"
26 #include "chrome/browser/extensions/extension_tab_util.h"
27 #include "chrome/browser/extensions/extension_util.h"
28 #include "chrome/browser/profiles/profile.h"
29 #include "chrome/browser/ui/browser.h"
30 #include "chrome/browser/ui/browser_commands.h"
31 #include "chrome/browser/ui/browser_finder.h"
32 #include "chrome/browser/ui/browser_navigator_params.h"
33 #include "chrome/browser/ui/browser_window.h"
34 #include "chrome/browser/ui/extensions/extension_action_test_helper.h"
35 #include "chrome/browser/ui/tabs/tab_strip_model.h"
36 #include "chrome/browser/ui/ui_features.h"
37 #include "chrome/common/pref_names.h"
38 #include "chrome/common/url_constants.h"
39 #include "chrome/test/base/ui_test_utils.h"
40 #include "components/prefs/pref_service.h"
41 #include "content/public/browser/browser_context.h"
42 #include "content/public/browser/notification_service.h"
43 #include "content/public/browser/overlay_window.h"
44 #include "content/public/browser/picture_in_picture_window_controller.h"
45 #include "content/public/browser/render_frame_host.h"
46 #include "content/public/browser/web_contents.h"
47 #include "content/public/test/browser_test_utils.h"
48 #include "content/public/test/content_browser_test_utils.h"
49 #include "content/public/test/download_test_observer.h"
50 #include "content/public/test/test_navigation_observer.h"
51 #include "content/public/test/test_utils.h"
52 #include "extensions/browser/extension_host.h"
53 #include "extensions/browser/extension_host_observer.h"
54 #include "extensions/browser/extension_registry.h"
55 #include "extensions/browser/extension_system.h"
56 #include "extensions/browser/process_manager.h"
57 #include "extensions/browser/test_extension_registry_observer.h"
58 #include "extensions/common/feature_switch.h"
59 #include "extensions/common/scoped_worker_based_extensions_channel.h"
60 #include "extensions/test/extension_test_message_listener.h"
61 #include "extensions/test/result_catcher.h"
62 #include "net/test/embedded_test_server/embedded_test_server.h"
63 #include "testing/gmock/include/gmock/gmock.h"
64 #include "ui/base/resource/resource_bundle.h"
65 #include "ui/gfx/geometry/rect.h"
66 #include "ui/gfx/geometry/size.h"
67 #include "ui/gfx/image/canvas_image_source.h"
68 #include "ui/gfx/image/image_skia.h"
69 #include "ui/gfx/image/image_skia_operations.h"
70 #include "ui/gfx/image/image_unittest_util.h"
71 #include "ui/gfx/skia_util.h"
72 
73 using content::WebContents;
74 
75 namespace extensions {
76 namespace {
77 
ExecuteExtensionAction(Browser * browser,const Extension * extension)78 void ExecuteExtensionAction(Browser* browser, const Extension* extension) {
79   ExtensionActionRunner::GetForWebContents(
80       browser->tab_strip_model()->GetActiveWebContents())
81       ->RunAction(extension, true);
82 }
83 
84 const char kEmptyImageDataError[] =
85     "The imageData property must contain an ImageData object or dictionary "
86     "of ImageData objects.";
87 const char kEmptyPathError[] = "The path property must not be empty.";
88 
89 // Makes sure |bar_rendering| has |model_icon| in the middle (there's additional
90 // padding that correlates to the rest of the button, and this is ignored).
VerifyIconsMatch(const gfx::Image & bar_rendering,const gfx::Image & model_icon)91 void VerifyIconsMatch(const gfx::Image& bar_rendering,
92                       const gfx::Image& model_icon) {
93   gfx::Rect icon_portion(gfx::Point(), bar_rendering.Size());
94   icon_portion.ClampToCenteredSize(model_icon.Size());
95 
96   EXPECT_TRUE(gfx::test::AreBitmapsEqual(
97       model_icon.AsImageSkia().GetRepresentation(1.0f).GetBitmap(),
98       gfx::ImageSkiaOperations::ExtractSubset(bar_rendering.AsImageSkia(),
99                                               icon_portion)
100           .GetRepresentation(1.0f)
101           .GetBitmap()));
102 }
103 
104 class BrowserActionApiTest : public ExtensionApiTest {
105  public:
BrowserActionApiTest()106   BrowserActionApiTest() {}
~BrowserActionApiTest()107   ~BrowserActionApiTest() override {}
108 
TearDownOnMainThread()109   void TearDownOnMainThread() override {
110     // Clean up the test util first, so that any created UI properly removes
111     // itself before profile destruction.
112     browser_action_test_util_.reset();
113     ExtensionApiTest::TearDownOnMainThread();
114   }
115 
116  protected:
GetBrowserActionsBar()117   ExtensionActionTestHelper* GetBrowserActionsBar() {
118     if (!browser_action_test_util_)
119       browser_action_test_util_ = ExtensionActionTestHelper::Create(browser());
120     return browser_action_test_util_.get();
121   }
122 
GetBrowserAction(Browser * browser,const Extension & extension)123   ExtensionAction* GetBrowserAction(Browser* browser,
124                                     const Extension& extension) {
125     ExtensionAction* extension_action =
126         ExtensionActionManager::Get(browser->profile())
127             ->GetExtensionAction(extension);
128     return extension_action->action_type() == ActionInfo::TYPE_BROWSER
129                ? extension_action
130                : nullptr;
131   }
132 
133  private:
134   std::unique_ptr<ExtensionActionTestHelper> browser_action_test_util_;
135 
136   DISALLOW_COPY_AND_ASSIGN(BrowserActionApiTest);
137 };
138 
139 // Canvas tests rely on the harness producing pixel output in order to read back
140 // pixels from a canvas element. So we have to override the setup function.
141 class BrowserActionApiCanvasTest : public BrowserActionApiTest {
142  public:
SetUp()143   void SetUp() override {
144     EnablePixelOutput();
145     ExtensionApiTest::SetUp();
146   }
147 };
148 
149 enum TestFlags {
150   kNone = 0,
151   kUseServiceWorker = 1,
152   kUseExtensionsMenuUi = 1 << 1,
153 };
154 
155 class BrowserActionApiLazyTest : public BrowserActionApiTest,
156                                  public testing::WithParamInterface<int> {
157  public:
SetUp()158   void SetUp() override {
159     BrowserActionApiTest::SetUp();
160     // Service Workers are currently only available on certain channels, so set
161     // the channel for those tests.
162     if ((GetParam() & kUseServiceWorker) != 0) {
163       current_channel_ =
164           std::make_unique<extensions::ScopedWorkerBasedExtensionsChannel>();
165     }
166 
167     if ((GetParam() & kUseExtensionsMenuUi) != 0) {
168       feature_list_.InitAndEnableFeature(features::kExtensionsToolbarMenu);
169     } else {
170       feature_list_.InitAndDisableFeature(features::kExtensionsToolbarMenu);
171     }
172   }
173 
LoadExtensionWithParamFlags(const base::FilePath & path)174   const extensions::Extension* LoadExtensionWithParamFlags(
175       const base::FilePath& path) {
176     int flags = kFlagEnableFileAccess;
177     if ((GetParam() & kUseServiceWorker) != 0)
178       flags |= ExtensionBrowserTest::kFlagRunAsServiceWorkerBasedExtension;
179     return LoadExtensionWithFlags(path, flags);
180   }
181 
182  private:
183   base::test::ScopedFeatureList feature_list_;
184   std::unique_ptr<extensions::ScopedWorkerBasedExtensionsChannel>
185       current_channel_;
186 };
187 
IN_PROC_BROWSER_TEST_P(BrowserActionApiLazyTest,Basic)188 IN_PROC_BROWSER_TEST_P(BrowserActionApiLazyTest, Basic) {
189   ExtensionTestMessageListener ready_listener("ready", false);
190   ASSERT_TRUE(embedded_test_server()->Start());
191   const Extension* extension = LoadExtensionWithParamFlags(
192       test_data_dir_.AppendASCII("browser_action/basics"));
193   ASSERT_TRUE(extension) << message_;
194 
195   // Test that there is a browser action in the toolbar.
196   ASSERT_EQ(1, GetBrowserActionsBar()->NumberOfBrowserActions());
197 
198   ASSERT_TRUE(ready_listener.WaitUntilSatisfied());
199 
200   // Open a URL in the tab, so the event handler can check the tab's
201   // "url" and "title" properties.
202   ui_test_utils::NavigateToURL(
203       browser(), embedded_test_server()->GetURL("/extensions/test_file.txt"));
204 
205   ResultCatcher catcher;
206   // Simulate the browser action being clicked.
207   ExecuteExtensionAction(browser(), extension);
208 
209   EXPECT_TRUE(catcher.GetNextResult());
210 }
211 
212 using BrowserActionApiUpdateLazyTest = BrowserActionApiLazyTest;
IN_PROC_BROWSER_TEST_P(BrowserActionApiUpdateLazyTest,Update)213 IN_PROC_BROWSER_TEST_P(BrowserActionApiUpdateLazyTest, Update) {
214   ExtensionTestMessageListener ready_listener("ready", true);
215   ASSERT_TRUE(embedded_test_server()->Start());
216   const Extension* extension = LoadExtensionWithParamFlags(
217       test_data_dir_.AppendASCII("browser_action/update"));
218   ASSERT_TRUE(extension) << message_;
219   // Test that there is a browser action in the toolbar.
220   ASSERT_EQ(1, GetBrowserActionsBar()->NumberOfBrowserActions());
221 
222   ASSERT_TRUE(ready_listener.WaitUntilSatisfied());
223   ExtensionAction* action = GetBrowserAction(browser(), *extension);
224   EXPECT_EQ("This is the default title.",
225             action->GetTitle(ExtensionAction::kDefaultTabId));
226   EXPECT_EQ("",
227             action->GetExplicitlySetBadgeText(ExtensionAction::kDefaultTabId));
228   EXPECT_EQ(SkColorSetARGB(0, 0, 0, 0),
229             action->GetBadgeBackgroundColor(ExtensionAction::kDefaultTabId));
230 
231   // Tell the extension to update the browser action state and then
232   // catch the result.
233   ResultCatcher catcher;
234   ready_listener.Reply("update");
235   ASSERT_TRUE(catcher.GetNextResult());
236 
237   // Test that we received the changes.
238   EXPECT_EQ("Modified", action->GetTitle(ExtensionAction::kDefaultTabId));
239   EXPECT_EQ("badge",
240             action->GetExplicitlySetBadgeText(ExtensionAction::kDefaultTabId));
241   EXPECT_EQ(SkColorSetARGB(255, 255, 255, 255),
242             action->GetBadgeBackgroundColor(ExtensionAction::kDefaultTabId));
243 }
244 
245 INSTANTIATE_TEST_SUITE_P(EventPageAndLegacyToolbar,
246                          BrowserActionApiLazyTest,
247                          ::testing::Values(kNone));
248 INSTANTIATE_TEST_SUITE_P(EventPageAndExtensionsMenu,
249                          BrowserActionApiLazyTest,
250                          ::testing::Values(kUseExtensionsMenuUi));
251 INSTANTIATE_TEST_SUITE_P(ServiceWorkerAndLegacyToolbar,
252                          BrowserActionApiLazyTest,
253                          ::testing::Values(kUseServiceWorker));
254 INSTANTIATE_TEST_SUITE_P(ServiceWorkerAndExtensionsMenu,
255                          BrowserActionApiLazyTest,
256                          ::testing::Values(kUseServiceWorker |
257                                            kUseExtensionsMenuUi));
258 
259 INSTANTIATE_TEST_SUITE_P(EventPageAndLegacyToolbar,
260                          BrowserActionApiUpdateLazyTest,
261                          ::testing::Values(kNone));
262 INSTANTIATE_TEST_SUITE_P(EventPageAndExtensionsMenu,
263                          BrowserActionApiUpdateLazyTest,
264                          ::testing::Values(kUseExtensionsMenuUi));
265 // TODO(crbug.com/1015136): Enable these once setIcon works in Service worker
266 // extensions. Also, combine this suite with BrowserActionApiLazyTest.
267 // INSTANTIATE_TEST_SUITE_P(ServiceWorkerAndLegacyToolbar,
268 //                          BrowserActionApiUpdateLazyTest,
269 //                          ::testing::Values(kUseServiceWorker));
270 // INSTANTIATE_TEST_SUITE_P(ServiceWorkerAndExtensionsMenu,
271 //                          BrowserActionApiUpdateLazyTest,
272 //                          ::testing::Values(kUseServiceWorker |
273 //                                            kUseExtensionsMenuUi));
274 
IN_PROC_BROWSER_TEST_F(BrowserActionApiCanvasTest,DynamicBrowserAction)275 IN_PROC_BROWSER_TEST_F(BrowserActionApiCanvasTest, DynamicBrowserAction) {
276   ASSERT_TRUE(RunExtensionTest("browser_action/no_icon")) << message_;
277   const Extension* extension = GetSingleLoadedExtension();
278   ASSERT_TRUE(extension) << message_;
279 
280 #if defined (OS_MACOSX)
281   // We need this on mac so we don't loose 2x representations from browser icon
282   // in transformations gfx::ImageSkia -> NSImage -> gfx::ImageSkia.
283   std::vector<ui::ScaleFactor> supported_scale_factors;
284   supported_scale_factors.push_back(ui::SCALE_FACTOR_100P);
285   supported_scale_factors.push_back(ui::SCALE_FACTOR_200P);
286   ui::SetSupportedScaleFactors(supported_scale_factors);
287 #endif
288 
289   // We should not be creating icons asynchronously, so we don't need an
290   // observer.
291   ExtensionActionIconFactory icon_factory(
292       profile(), extension, GetBrowserAction(browser(), *extension), nullptr);
293   // Test that there is a browser action in the toolbar.
294   ASSERT_EQ(1, GetBrowserActionsBar()->NumberOfBrowserActions());
295   EXPECT_TRUE(GetBrowserActionsBar()->HasIcon(0));
296 
297   gfx::Image action_icon = icon_factory.GetIcon(0);
298   uint32_t action_icon_last_id = action_icon.ToSkBitmap()->getGenerationID();
299 
300   // Let's check that |GetIcon| doesn't always return bitmap with new id.
301   ASSERT_EQ(action_icon_last_id,
302             icon_factory.GetIcon(0).ToSkBitmap()->getGenerationID());
303 
304   gfx::Image last_bar_icon = GetBrowserActionsBar()->GetIcon(0);
305   EXPECT_TRUE(gfx::test::AreImagesEqual(last_bar_icon,
306                                         GetBrowserActionsBar()->GetIcon(0)));
307 
308   // The reason we don't test more standard scales (like 1x, 2x, etc.) is that
309   // these may be generated from the provided scales.
310   float kSmallIconScale = 21.f / ExtensionAction::ActionIconSize();
311   float kLargeIconScale = 42.f / ExtensionAction::ActionIconSize();
312   ASSERT_FALSE(ui::IsSupportedScale(kSmallIconScale));
313   ASSERT_FALSE(ui::IsSupportedScale(kLargeIconScale));
314 
315   // Tell the extension to update the icon using ImageData object.
316   ResultCatcher catcher;
317   GetBrowserActionsBar()->Press(0);
318   ASSERT_TRUE(catcher.GetNextResult());
319 
320   EXPECT_FALSE(gfx::test::AreImagesEqual(last_bar_icon,
321                                          GetBrowserActionsBar()->GetIcon(0)));
322   last_bar_icon = GetBrowserActionsBar()->GetIcon(0);
323 
324   action_icon = icon_factory.GetIcon(0);
325   uint32_t action_icon_current_id = action_icon.ToSkBitmap()->getGenerationID();
326   EXPECT_GT(action_icon_current_id, action_icon_last_id);
327   action_icon_last_id = action_icon_current_id;
328   VerifyIconsMatch(last_bar_icon, action_icon);
329 
330   // Check that only the smaller size was set (only a 21px icon was provided).
331   EXPECT_TRUE(action_icon.ToImageSkia()->HasRepresentation(kSmallIconScale));
332   EXPECT_FALSE(action_icon.ToImageSkia()->HasRepresentation(kLargeIconScale));
333 
334   // Tell the extension to update the icon using path.
335   GetBrowserActionsBar()->Press(0);
336   ASSERT_TRUE(catcher.GetNextResult());
337 
338   // Make sure the browser action bar updated.
339   EXPECT_FALSE(gfx::test::AreImagesEqual(last_bar_icon,
340                                          GetBrowserActionsBar()->GetIcon(0)));
341   last_bar_icon = GetBrowserActionsBar()->GetIcon(0);
342 
343   action_icon = icon_factory.GetIcon(0);
344   action_icon_current_id = action_icon.ToSkBitmap()->getGenerationID();
345   EXPECT_GT(action_icon_current_id, action_icon_last_id);
346   action_icon_last_id = action_icon_current_id;
347   VerifyIconsMatch(last_bar_icon, action_icon);
348 
349   // Check that only the smaller size was set (only a 21px icon was provided).
350   EXPECT_TRUE(action_icon.ToImageSkia()->HasRepresentation(kSmallIconScale));
351   EXPECT_FALSE(action_icon.ToImageSkia()->HasRepresentation(kLargeIconScale));
352 
353   // Tell the extension to update the icon using dictionary of ImageData
354   // objects.
355   GetBrowserActionsBar()->Press(0);
356   ASSERT_TRUE(catcher.GetNextResult());
357 
358   EXPECT_FALSE(gfx::test::AreImagesEqual(last_bar_icon,
359                                          GetBrowserActionsBar()->GetIcon(0)));
360   last_bar_icon = GetBrowserActionsBar()->GetIcon(0);
361 
362   action_icon = icon_factory.GetIcon(0);
363   action_icon_current_id = action_icon.ToSkBitmap()->getGenerationID();
364   EXPECT_GT(action_icon_current_id, action_icon_last_id);
365   action_icon_last_id = action_icon_current_id;
366   VerifyIconsMatch(last_bar_icon, action_icon);
367 
368   // Check both sizes were set (as two icon sizes were provided).
369   EXPECT_TRUE(action_icon.ToImageSkia()->HasRepresentation(kSmallIconScale));
370   EXPECT_TRUE(action_icon.AsImageSkia().HasRepresentation(kLargeIconScale));
371 
372   // Tell the extension to update the icon using dictionary of paths.
373   GetBrowserActionsBar()->Press(0);
374   ASSERT_TRUE(catcher.GetNextResult());
375 
376   EXPECT_FALSE(gfx::test::AreImagesEqual(last_bar_icon,
377                                          GetBrowserActionsBar()->GetIcon(0)));
378   last_bar_icon = GetBrowserActionsBar()->GetIcon(0);
379 
380   action_icon = icon_factory.GetIcon(0);
381   action_icon_current_id = action_icon.ToSkBitmap()->getGenerationID();
382   EXPECT_GT(action_icon_current_id, action_icon_last_id);
383   action_icon_last_id = action_icon_current_id;
384   VerifyIconsMatch(last_bar_icon, action_icon);
385 
386   // Check both sizes were set (as two icon sizes were provided).
387   EXPECT_TRUE(action_icon.ToImageSkia()->HasRepresentation(kSmallIconScale));
388   EXPECT_TRUE(action_icon.AsImageSkia().HasRepresentation(kLargeIconScale));
389 
390   // Tell the extension to update the icon using dictionary of ImageData
391   // objects, but setting only one size.
392   GetBrowserActionsBar()->Press(0);
393   ASSERT_TRUE(catcher.GetNextResult());
394 
395   EXPECT_FALSE(gfx::test::AreImagesEqual(last_bar_icon,
396                                          GetBrowserActionsBar()->GetIcon(0)));
397   last_bar_icon = GetBrowserActionsBar()->GetIcon(0);
398 
399   action_icon = icon_factory.GetIcon(0);
400   action_icon_current_id = action_icon.ToSkBitmap()->getGenerationID();
401   EXPECT_GT(action_icon_current_id, action_icon_last_id);
402   action_icon_last_id = action_icon_current_id;
403   VerifyIconsMatch(last_bar_icon, action_icon);
404 
405   // Check that only the smaller size was set (only a 21px icon was provided).
406   EXPECT_TRUE(action_icon.ToImageSkia()->HasRepresentation(kSmallIconScale));
407   EXPECT_FALSE(action_icon.ToImageSkia()->HasRepresentation(kLargeIconScale));
408 
409   // Tell the extension to update the icon using dictionary of paths, but
410   // setting only one size.
411   GetBrowserActionsBar()->Press(0);
412   ASSERT_TRUE(catcher.GetNextResult());
413 
414   EXPECT_FALSE(gfx::test::AreImagesEqual(last_bar_icon,
415                                          GetBrowserActionsBar()->GetIcon(0)));
416   last_bar_icon = GetBrowserActionsBar()->GetIcon(0);
417 
418   action_icon = icon_factory.GetIcon(0);
419   action_icon_current_id = action_icon.ToSkBitmap()->getGenerationID();
420   EXPECT_GT(action_icon_current_id, action_icon_last_id);
421   action_icon_last_id = action_icon_current_id;
422   VerifyIconsMatch(last_bar_icon, action_icon);
423 
424   // Check that only the smaller size was set (only a 21px icon was provided).
425   EXPECT_TRUE(action_icon.ToImageSkia()->HasRepresentation(kSmallIconScale));
426   EXPECT_FALSE(action_icon.ToImageSkia()->HasRepresentation(kLargeIconScale));
427 
428   // Tell the extension to update the icon using dictionary of ImageData
429   // objects, but setting only size 42.
430   GetBrowserActionsBar()->Press(0);
431   ASSERT_TRUE(catcher.GetNextResult());
432 
433   EXPECT_FALSE(gfx::test::AreImagesEqual(last_bar_icon,
434                                          GetBrowserActionsBar()->GetIcon(0)));
435   last_bar_icon = GetBrowserActionsBar()->GetIcon(0);
436 
437   action_icon = icon_factory.GetIcon(0);
438   action_icon_current_id = action_icon.ToSkBitmap()->getGenerationID();
439   EXPECT_GT(action_icon_current_id, action_icon_last_id);
440   action_icon_last_id = action_icon_current_id;
441 
442   // Check that only the larger size was set (only a 42px icon was provided).
443   EXPECT_FALSE(action_icon.ToImageSkia()->HasRepresentation(kSmallIconScale));
444   EXPECT_TRUE(action_icon.AsImageSkia().HasRepresentation(kLargeIconScale));
445 
446   // Try setting icon with empty dictionary of ImageData objects.
447   GetBrowserActionsBar()->Press(0);
448   ASSERT_FALSE(catcher.GetNextResult());
449   EXPECT_EQ(kEmptyImageDataError, catcher.message());
450 
451   // Try setting icon with empty dictionary of path objects.
452   GetBrowserActionsBar()->Press(0);
453   ASSERT_FALSE(catcher.GetNextResult());
454   EXPECT_EQ(kEmptyPathError, catcher.message());
455 }
456 
IN_PROC_BROWSER_TEST_F(BrowserActionApiCanvasTest,InvisibleIconBrowserAction)457 IN_PROC_BROWSER_TEST_F(BrowserActionApiCanvasTest, InvisibleIconBrowserAction) {
458   // Turn this on so errors are reported.
459   ExtensionActionSetIconFunction::SetReportErrorForInvisibleIconForTesting(
460       true);
461   ASSERT_TRUE(RunExtensionTest("browser_action/invisible_icon")) << message_;
462   const Extension* extension = GetSingleLoadedExtension();
463   ASSERT_TRUE(extension) << message_;
464 
465   // Test there is a browser action in the toolbar.
466   ASSERT_EQ(1, GetBrowserActionsBar()->NumberOfBrowserActions());
467   EXPECT_TRUE(GetBrowserActionsBar()->HasIcon(0));
468   gfx::Image initial_bar_icon = GetBrowserActionsBar()->GetIcon(0);
469 
470   ExtensionHost* background_page =
471       ProcessManager::Get(profile())->GetBackgroundHostForExtension(
472           extension->id());
473   ASSERT_TRUE(background_page);
474 
475   static constexpr char kScript[] =
476       "setIcon(%s).then(function(arg) {"
477       "  domAutomationController.send(arg);"
478       "});";
479 
480   const std::string histogram_name =
481       "Extensions.DynamicExtensionActionIconWasVisible";
482   const std::string new_histogram_name =
483       "Extensions.DynamicExtensionActionIconWasVisibleRendered";
484   {
485     base::HistogramTester histogram_tester;
486     std::string result;
487     EXPECT_TRUE(ExecuteScriptAndExtractString(
488         background_page->host_contents(),
489         base::StringPrintf(kScript, "invisibleImageData"), &result));
490     EXPECT_EQ("Icon not sufficiently visible.", result);
491     // The icon should not have changed.
492     EXPECT_TRUE(gfx::test::AreImagesEqual(initial_bar_icon,
493                                           GetBrowserActionsBar()->GetIcon(0)));
494     EXPECT_THAT(histogram_tester.GetAllSamples(histogram_name),
495                 testing::ElementsAre(base::Bucket(0, 1)));
496     EXPECT_THAT(histogram_tester.GetAllSamples(new_histogram_name),
497                 testing::ElementsAre(base::Bucket(0, 1)));
498   }
499 
500   {
501     base::HistogramTester histogram_tester;
502     std::string result;
503     EXPECT_TRUE(ExecuteScriptAndExtractString(
504         background_page->host_contents(),
505         base::StringPrintf(kScript, "visibleImageData"), &result));
506     EXPECT_EQ("", result);
507     // The icon should have changed.
508     EXPECT_FALSE(gfx::test::AreImagesEqual(initial_bar_icon,
509                                            GetBrowserActionsBar()->GetIcon(0)));
510     EXPECT_THAT(histogram_tester.GetAllSamples(histogram_name),
511                 testing::ElementsAre(base::Bucket(1, 1)));
512     EXPECT_THAT(histogram_tester.GetAllSamples(new_histogram_name),
513                 testing::ElementsAre(base::Bucket(1, 1)));
514   }
515 }
516 
IN_PROC_BROWSER_TEST_F(BrowserActionApiTest,TabSpecificBrowserActionState)517 IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, TabSpecificBrowserActionState) {
518   ASSERT_TRUE(RunExtensionTest("browser_action/tab_specific_state")) <<
519       message_;
520   const Extension* extension = GetSingleLoadedExtension();
521   ASSERT_TRUE(extension) << message_;
522 
523   // Test that there is a browser action in the toolbar and that it has an icon.
524   ASSERT_EQ(1, GetBrowserActionsBar()->NumberOfBrowserActions());
525   EXPECT_TRUE(GetBrowserActionsBar()->HasIcon(0));
526 
527   // Execute the action, its title should change.
528   ResultCatcher catcher;
529   GetBrowserActionsBar()->Press(0);
530   ASSERT_TRUE(catcher.GetNextResult());
531   EXPECT_EQ("Showing icon 2", GetBrowserActionsBar()->GetTooltip(0));
532 
533   // Open a new tab, the title should go back.
534   chrome::NewTab(browser());
535   EXPECT_EQ("hi!", GetBrowserActionsBar()->GetTooltip(0));
536 
537   // Go back to first tab, changed title should reappear.
538   browser()->tab_strip_model()->ActivateTabAt(
539       0, {TabStripModel::GestureType::kOther});
540   EXPECT_EQ("Showing icon 2", GetBrowserActionsBar()->GetTooltip(0));
541 
542   // Reload that tab, default title should come back.
543   ui_test_utils::NavigateToURL(browser(), GURL("about:blank"));
544   EXPECT_EQ("hi!", GetBrowserActionsBar()->GetTooltip(0));
545 }
546 
547 // Test that calling chrome.browserAction.setPopup() can enable and change
548 // a popup.
IN_PROC_BROWSER_TEST_F(BrowserActionApiTest,BrowserActionAddPopup)549 IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, BrowserActionAddPopup) {
550   ASSERT_TRUE(RunExtensionTest("browser_action/add_popup")) << message_;
551   const Extension* extension = GetSingleLoadedExtension();
552   ASSERT_TRUE(extension) << message_;
553 
554   int tab_id = ExtensionTabUtil::GetTabId(
555       browser()->tab_strip_model()->GetActiveWebContents());
556 
557   ExtensionAction* browser_action = GetBrowserAction(browser(), *extension);
558   ASSERT_TRUE(browser_action)
559       << "Browser action test extension should have a browser action.";
560 
561   ASSERT_FALSE(browser_action->HasPopup(tab_id));
562   ASSERT_FALSE(browser_action->HasPopup(ExtensionAction::kDefaultTabId));
563 
564   // Simulate a click on the browser action icon.  The onClicked handler
565   // will add a popup.
566   {
567     ResultCatcher catcher;
568     GetBrowserActionsBar()->Press(0);
569     ASSERT_TRUE(catcher.GetNextResult());
570   }
571 
572   // The call to setPopup in background.html set a tab id, so the
573   // current tab's setting should have changed, but the default setting
574   // should not have changed.
575   ASSERT_TRUE(browser_action->HasPopup(tab_id))
576       << "Clicking on the browser action should have caused a popup to "
577       << "be added.";
578   ASSERT_FALSE(browser_action->HasPopup(ExtensionAction::kDefaultTabId))
579       << "Clicking on the browser action should not have set a default "
580       << "popup.";
581 
582   ASSERT_STREQ("/a_popup.html",
583                browser_action->GetPopupUrl(tab_id).path().c_str());
584 
585   // Now change the popup from a_popup.html to another_popup.html by loading
586   // a page which removes the popup using chrome.browserAction.setPopup().
587   {
588     ResultCatcher catcher;
589     ui_test_utils::NavigateToURL(
590         browser(),
591         GURL(extension->GetResourceURL("change_popup.html")));
592     ASSERT_TRUE(catcher.GetNextResult());
593   }
594 
595   // The call to setPopup in change_popup.html did not use a tab id,
596   // so the default setting should have changed as well as the current tab.
597   ASSERT_TRUE(browser_action->HasPopup(tab_id));
598   ASSERT_TRUE(browser_action->HasPopup(ExtensionAction::kDefaultTabId));
599   ASSERT_STREQ("/another_popup.html",
600                browser_action->GetPopupUrl(tab_id).path().c_str());
601 }
602 
603 // Test that calling chrome.browserAction.setPopup() can remove a popup.
IN_PROC_BROWSER_TEST_F(BrowserActionApiTest,BrowserActionRemovePopup)604 IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, BrowserActionRemovePopup) {
605   // Load the extension, which has a browser action with a default popup.
606   ASSERT_TRUE(RunExtensionTest("browser_action/remove_popup")) << message_;
607   const Extension* extension = GetSingleLoadedExtension();
608   ASSERT_TRUE(extension) << message_;
609 
610   int tab_id = ExtensionTabUtil::GetTabId(
611       browser()->tab_strip_model()->GetActiveWebContents());
612 
613   ExtensionAction* browser_action = GetBrowserAction(browser(), *extension);
614   ASSERT_TRUE(browser_action)
615       << "Browser action test extension should have a browser action.";
616 
617   ASSERT_TRUE(browser_action->HasPopup(tab_id))
618       << "Expect a browser action popup before the test removes it.";
619   ASSERT_TRUE(browser_action->HasPopup(ExtensionAction::kDefaultTabId))
620       << "Expect a browser action popup is the default for all tabs.";
621 
622   // Load a page which removes the popup using chrome.browserAction.setPopup().
623   {
624     ResultCatcher catcher;
625     ui_test_utils::NavigateToURL(
626         browser(),
627         GURL(extension->GetResourceURL("remove_popup.html")));
628     ASSERT_TRUE(catcher.GetNextResult());
629   }
630 
631   ASSERT_FALSE(browser_action->HasPopup(tab_id))
632       << "Browser action popup should have been removed.";
633   ASSERT_TRUE(browser_action->HasPopup(ExtensionAction::kDefaultTabId))
634       << "Browser action popup default should not be changed by setting "
635       << "a specific tab id.";
636 }
637 
IN_PROC_BROWSER_TEST_P(BrowserActionApiLazyTest,IncognitoBasic)638 IN_PROC_BROWSER_TEST_P(BrowserActionApiLazyTest, IncognitoBasic) {
639   ExtensionTestMessageListener ready_listener("ready", false);
640   ASSERT_TRUE(embedded_test_server()->Start());
641   const Extension* extension = LoadExtensionWithParamFlags(
642       test_data_dir_.AppendASCII("browser_action/basics"));
643   ASSERT_TRUE(extension) << message_;
644 
645   // Test that there is a browser action in the toolbar.
646   ASSERT_EQ(1, GetBrowserActionsBar()->NumberOfBrowserActions());
647 
648   // Open an incognito window and test that the browser action isn't there by
649   // default.
650   Browser* incognito_browser = CreateIncognitoBrowser(browser()->profile());
651 
652   ASSERT_EQ(0, ExtensionActionTestHelper::Create(incognito_browser)
653                    ->NumberOfBrowserActions());
654 
655   ASSERT_TRUE(ready_listener.WaitUntilSatisfied());
656 
657   // Now enable the extension in incognito mode, and test that the browser
658   // action shows up.
659   // SetIsIncognitoEnabled() requires a reload of the extension, so we have to
660   // wait for it.
661   ExtensionTestMessageListener incognito_ready_listener("ready", false);
662   TestExtensionRegistryObserver registry_observer(
663       ExtensionRegistry::Get(profile()), extension->id());
664   extensions::util::SetIsIncognitoEnabled(
665       extension->id(), browser()->profile(), true);
666   extension = registry_observer.WaitForExtensionLoaded();
667 
668   ASSERT_EQ(1, ExtensionActionTestHelper::Create(incognito_browser)
669                    ->NumberOfBrowserActions());
670 
671   ASSERT_TRUE(incognito_ready_listener.WaitUntilSatisfied());
672 
673   // Open a URL in the tab, so the event handler can check the tab's
674   // "url" and "title" properties.
675   ui_test_utils::NavigateToURL(
676       incognito_browser,
677       embedded_test_server()->GetURL("/extensions/test_file.txt"));
678 
679   ResultCatcher catcher;
680   // Simulate the browser action being clicked.
681   ExecuteExtensionAction(incognito_browser, extension);
682 
683   EXPECT_TRUE(catcher.GetNextResult());
684 }
685 
IN_PROC_BROWSER_TEST_P(BrowserActionApiLazyTest,IncognitoUpdate)686 IN_PROC_BROWSER_TEST_P(BrowserActionApiLazyTest, IncognitoUpdate) {
687   // TODO(crbug.com/1015136): Investigate flakiness WRT Service Workers and
688   // incognito mode.
689   if ((GetParam() & kUseServiceWorker) != 0)
690     return;
691   ASSERT_TRUE(embedded_test_server()->Start());
692   const Extension* extension = LoadExtensionWithParamFlags(
693       test_data_dir_.AppendASCII("browser_action/update"));
694   ASSERT_TRUE(extension) << message_;
695   // Test that there is a browser action in the toolbar.
696   ASSERT_EQ(1, GetBrowserActionsBar()->NumberOfBrowserActions());
697 
698   // Open an incognito window and test that the browser action isn't there by
699   // default.
700   Browser* incognito_browser = CreateIncognitoBrowser(browser()->profile());
701 
702   ASSERT_EQ(0, ExtensionActionTestHelper::Create(incognito_browser)
703                    ->NumberOfBrowserActions());
704 
705   // Set up a listener so we can reply for the extension to do the update.
706   ExtensionTestMessageListener incognito_ready_listener("incognito ready",
707                                                         true);
708   // Now enable the extension in incognito mode, and test that the browser
709   // action shows up.
710   // SetIsIncognitoEnabled() requires a reload of the extension, so we have to
711   // wait for it.
712   TestExtensionRegistryObserver registry_observer(
713       ExtensionRegistry::Get(profile()), extension->id());
714   extensions::util::SetIsIncognitoEnabled(extension->id(), browser()->profile(),
715                                           true);
716   extension = registry_observer.WaitForExtensionLoaded();
717   ASSERT_EQ(1, ExtensionActionTestHelper::Create(incognito_browser)
718                    ->NumberOfBrowserActions());
719 
720   ASSERT_TRUE(incognito_ready_listener.WaitUntilSatisfied());
721   ExtensionAction* action = GetBrowserAction(incognito_browser, *extension);
722   EXPECT_EQ("This is the default title.",
723             action->GetTitle(ExtensionAction::kDefaultTabId));
724   EXPECT_EQ("",
725             action->GetExplicitlySetBadgeText(ExtensionAction::kDefaultTabId));
726   EXPECT_EQ(SkColorSetARGB(0, 0, 0, 0),
727             action->GetBadgeBackgroundColor(ExtensionAction::kDefaultTabId));
728   // Tell the extension to update the browser action state and then
729   // catch the result.
730   ResultCatcher incognito_catcher;
731   incognito_ready_listener.Reply("incognito update");
732   ASSERT_TRUE(incognito_catcher.GetNextResult());
733 
734   // Test that we received the changes.
735   EXPECT_EQ("Modified", action->GetTitle(ExtensionAction::kDefaultTabId));
736   EXPECT_EQ("badge",
737             action->GetExplicitlySetBadgeText(ExtensionAction::kDefaultTabId));
738   EXPECT_EQ(SkColorSetARGB(255, 255, 255, 255),
739             action->GetBadgeBackgroundColor(ExtensionAction::kDefaultTabId));
740 }
741 
742 // Tests that events are dispatched to the correct profile for split mode
743 // extensions.
IN_PROC_BROWSER_TEST_F(BrowserActionApiTest,IncognitoSplit)744 IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, IncognitoSplit) {
745   ResultCatcher catcher;
746   const Extension* extension = LoadExtensionWithFlags(
747       test_data_dir_.AppendASCII("browser_action/split_mode"),
748       kFlagEnableIncognito);
749   ASSERT_TRUE(extension) << message_;
750 
751   // Open an incognito browser.
752   Browser* incognito_browser = CreateIncognitoBrowser(browser()->profile());
753   ASSERT_EQ(1, ExtensionActionTestHelper::Create(incognito_browser)
754                    ->NumberOfBrowserActions());
755 
756   // A click in the regular profile should open a tab in the regular profile.
757   ExecuteExtensionAction(browser(), extension);
758   ASSERT_TRUE(catcher.GetNextResult()) << catcher.message();
759 
760   // A click in the incognito profile should open a tab in the
761   // incognito profile.
762   ExecuteExtensionAction(incognito_browser, extension);
763   ASSERT_TRUE(catcher.GetNextResult()) << catcher.message();
764 }
765 
IN_PROC_BROWSER_TEST_F(BrowserActionApiTest,CloseBackgroundPage)766 IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, CloseBackgroundPage) {
767   ExtensionTestMessageListener listener("ready", /*will_reply=*/false);
768   ASSERT_TRUE(LoadExtension(
769       test_data_dir_.AppendASCII("browser_action/close_background")));
770   const Extension* extension = GetSingleLoadedExtension();
771   ASSERT_TRUE(listener.WaitUntilSatisfied());
772 
773   // There is a background page and a browser action with no badge text.
774   extensions::ProcessManager* manager =
775       extensions::ProcessManager::Get(browser()->profile());
776 
777   ExtensionHost* extension_host =
778       manager->GetBackgroundHostForExtension(extension->id());
779   ASSERT_TRUE(extension_host);
780 
781   ExtensionAction* action = GetBrowserAction(browser(), *extension);
782   ASSERT_EQ("",
783             action->GetExplicitlySetBadgeText(ExtensionAction::kDefaultTabId));
784 
785   // A helper class to wait for the ExtensionHost to shut down.
786   // TODO(devlin): Hoist this somewhere more common and track down other similar
787   // usages.
788   class ExtensionHostDestructionObserver : public ExtensionHostObserver {
789    public:
790     explicit ExtensionHostDestructionObserver(ExtensionHost* host) {
791       host_observer_.Add(host);
792     }
793     ExtensionHostDestructionObserver(
794         const ExtensionHostDestructionObserver& other) = delete;
795     ExtensionHostDestructionObserver& operator=(
796         const ExtensionHostDestructionObserver& other) = delete;
797     ~ExtensionHostDestructionObserver() override = default;
798 
799     void OnExtensionHostDestroyed(const ExtensionHost* host) override {
800       // TODO(devlin): It would be nice to
801       // ASSERT_TRUE(host_observer_.IsObserving(host));
802       // host_observer_.Remove(host);
803       // But we can't, because |host| is const. Work around it by just
804       // RemoveAll()ing.
805       host_observer_.RemoveAll();
806       run_loop_.QuitWhenIdle();
807     }
808 
809     void Wait() { run_loop_.Run(); }
810 
811    private:
812     base::RunLoop run_loop_;
813     ScopedObserver<ExtensionHost, ExtensionHostObserver> host_observer_{this};
814   };
815 
816   ExtensionHostDestructionObserver host_destroyed_observer(extension_host);
817 
818   // Click the browser action.
819   ExecuteExtensionAction(browser(), extension);
820 
821   host_destroyed_observer.Wait();
822 
823   EXPECT_FALSE(manager->GetBackgroundHostForExtension(extension->id()));
824   EXPECT_EQ("X",
825             action->GetExplicitlySetBadgeText(ExtensionAction::kDefaultTabId));
826 }
827 
IN_PROC_BROWSER_TEST_F(BrowserActionApiTest,BadgeBackgroundColor)828 IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, BadgeBackgroundColor) {
829   ASSERT_TRUE(embedded_test_server()->Start());
830   ASSERT_TRUE(RunExtensionTest("browser_action/color")) << message_;
831   const Extension* extension = GetSingleLoadedExtension();
832   ASSERT_TRUE(extension) << message_;
833 
834   // Test that there is a browser action in the toolbar.
835   ASSERT_EQ(1, GetBrowserActionsBar()->NumberOfBrowserActions());
836 
837   // Test that CSS values (#FF0000) set color correctly.
838   ExtensionAction* action = GetBrowserAction(browser(), *extension);
839   ASSERT_EQ(SkColorSetARGB(255, 255, 0, 0),
840             action->GetBadgeBackgroundColor(ExtensionAction::kDefaultTabId));
841 
842   // Tell the extension to update the browser action state.
843   ResultCatcher catcher;
844   ui_test_utils::NavigateToURL(browser(),
845       GURL(extension->GetResourceURL("update.html")));
846   ASSERT_TRUE(catcher.GetNextResult());
847 
848   // Test that CSS values (#0F0) set color correctly.
849   ASSERT_EQ(SkColorSetARGB(255, 0, 255, 0),
850             action->GetBadgeBackgroundColor(ExtensionAction::kDefaultTabId));
851 
852   ui_test_utils::NavigateToURL(browser(),
853       GURL(extension->GetResourceURL("update2.html")));
854   ASSERT_TRUE(catcher.GetNextResult());
855 
856   // Test that array values set color correctly.
857   ASSERT_EQ(SkColorSetARGB(255, 255, 255, 255),
858             action->GetBadgeBackgroundColor(ExtensionAction::kDefaultTabId));
859 
860   ui_test_utils::NavigateToURL(browser(),
861                                GURL(extension->GetResourceURL("update3.html")));
862   ASSERT_TRUE(catcher.GetNextResult());
863 
864   // Test that hsl() values 'hsl(120, 100%, 50%)' set color correctly.
865   ASSERT_EQ(SkColorSetARGB(255, 0, 255, 0),
866             action->GetBadgeBackgroundColor(ExtensionAction::kDefaultTabId));
867 
868   // Test basic color keyword set correctly.
869   ui_test_utils::NavigateToURL(browser(),
870                                GURL(extension->GetResourceURL("update4.html")));
871   ASSERT_TRUE(catcher.GetNextResult());
872 
873   ASSERT_EQ(SkColorSetARGB(255, 0, 0, 255),
874             action->GetBadgeBackgroundColor(ExtensionAction::kDefaultTabId));
875 }
876 
IN_PROC_BROWSER_TEST_F(BrowserActionApiTest,Getters)877 IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, Getters) {
878   ASSERT_TRUE(RunExtensionTest("browser_action/getters")) << message_;
879   const Extension* extension = GetSingleLoadedExtension();
880   ASSERT_TRUE(extension) << message_;
881 
882   // Test that there is a browser action in the toolbar.
883   ASSERT_EQ(1, GetBrowserActionsBar()->NumberOfBrowserActions());
884 
885   // Test the getters for defaults.
886   ResultCatcher catcher;
887   ui_test_utils::NavigateToURL(browser(),
888       GURL(extension->GetResourceURL("update.html")));
889   ASSERT_TRUE(catcher.GetNextResult());
890 
891   // Test the getters for a specific tab.
892   ui_test_utils::NavigateToURL(browser(),
893       GURL(extension->GetResourceURL("update2.html")));
894   ASSERT_TRUE(catcher.GetNextResult());
895 }
896 
897 // Verify triggering browser action.
IN_PROC_BROWSER_TEST_F(BrowserActionApiTest,TestTriggerBrowserAction)898 IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, TestTriggerBrowserAction) {
899   ASSERT_TRUE(embedded_test_server()->Start());
900 
901   ASSERT_TRUE(RunExtensionTest("trigger_actions/browser_action")) << message_;
902   const Extension* extension = GetSingleLoadedExtension();
903   ASSERT_TRUE(extension) << message_;
904 
905   // Test that there is a browser action in the toolbar.
906   ASSERT_EQ(1, GetBrowserActionsBar()->NumberOfBrowserActions());
907 
908   ui_test_utils::NavigateToURL(browser(),
909                                embedded_test_server()->GetURL("/simple.html"));
910 
911   ExtensionAction* browser_action = GetBrowserAction(browser(), *extension);
912   EXPECT_TRUE(browser_action);
913 
914   // Simulate a click on the browser action icon.
915   {
916     ResultCatcher catcher;
917     GetBrowserActionsBar()->Press(0);
918     EXPECT_TRUE(catcher.GetNextResult());
919   }
920 
921   WebContents* tab =
922       browser()->tab_strip_model()->GetActiveWebContents();
923   EXPECT_TRUE(tab);
924 
925   // Verify that the browser action turned the background color red.
926   const std::string script =
927       "window.domAutomationController.send(document.body.style."
928       "backgroundColor);";
929   std::string result;
930   EXPECT_TRUE(content::ExecuteScriptAndExtractString(tab, script, &result));
931   EXPECT_EQ(result, "red");
932 }
933 
IN_PROC_BROWSER_TEST_F(BrowserActionApiTest,BrowserActionWithRectangularIcon)934 IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, BrowserActionWithRectangularIcon) {
935   ExtensionTestMessageListener ready_listener("ready", true);
936 
937   const Extension* extension = LoadExtension(
938       test_data_dir_.AppendASCII("browser_action").AppendASCII("rect_icon"));
939   ASSERT_TRUE(extension);
940   EXPECT_TRUE(ready_listener.WaitUntilSatisfied());
941 
942   // Wait for the default icon to load before accessing the underlying
943   // gfx::Image.
944   TestIconImageObserver::WaitForExtensionActionIcon(extension, profile());
945 
946   gfx::Image first_icon = GetBrowserActionsBar()->GetIcon(0);
947   ASSERT_FALSE(first_icon.IsEmpty());
948 
949   TestExtensionActionAPIObserver observer(profile(), extension->id());
950   ResultCatcher catcher;
951   ready_listener.Reply(std::string());
952   EXPECT_TRUE(catcher.GetNextResult());
953   // Wait for extension action to be updated.
954   observer.Wait();
955 
956   gfx::Image next_icon = GetBrowserActionsBar()->GetIcon(0);
957   ASSERT_FALSE(next_icon.IsEmpty());
958   EXPECT_FALSE(gfx::test::AreImagesEqual(first_icon, next_icon));
959 }
960 
961 // Test that we don't try and show a browser action popup with
962 // browserAction.openPopup if there is no toolbar (e.g., for web popup windows).
963 // Regression test for crbug.com/584747.
IN_PROC_BROWSER_TEST_F(BrowserActionApiTest,BrowserActionOpenPopupOnPopup)964 IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, BrowserActionOpenPopupOnPopup) {
965   // Open a new web popup window.
966   NavigateParams params(browser(), GURL("http://www.google.com/"),
967                         ui::PAGE_TRANSITION_LINK);
968   params.disposition = WindowOpenDisposition::NEW_POPUP;
969   params.window_action = NavigateParams::SHOW_WINDOW;
970   ui_test_utils::NavigateToURL(&params);
971   Browser* popup_browser = params.browser;
972   // Verify it is a popup, and it is the active window.
973   ASSERT_TRUE(popup_browser);
974   // The window isn't considered "active" on MacOSX for odd reasons. The more
975   // important test is that it *is* considered the last active browser, since
976   // that's what we check when we try to open the popup.
977 #if !defined(OS_MACOSX)
978   EXPECT_TRUE(popup_browser->window()->IsActive());
979 #endif
980   EXPECT_FALSE(browser()->window()->IsActive());
981   EXPECT_FALSE(popup_browser->SupportsWindowFeature(Browser::FEATURE_TOOLBAR));
982   EXPECT_EQ(popup_browser,
983             chrome::FindLastActiveWithProfile(browser()->profile()));
984 
985   // Load up the extension, which will call chrome.browserAction.openPopup()
986   // when it is loaded and verify that the popup didn't open.
987   ExtensionTestMessageListener listener("ready", true);
988   EXPECT_TRUE(LoadExtension(
989       test_data_dir_.AppendASCII("browser_action/open_popup_on_reply")));
990   EXPECT_TRUE(listener.WaitUntilSatisfied());
991 
992   ResultCatcher catcher;
993   listener.Reply(std::string());
994   EXPECT_TRUE(catcher.GetNextResult()) << message_;
995 }
996 
997 // Verify video can enter and exit Picture-in_Picture when browser action icon
998 // is clicked.
IN_PROC_BROWSER_TEST_F(BrowserActionApiTest,TestPictureInPictureOnBrowserActionIconClick)999 IN_PROC_BROWSER_TEST_F(BrowserActionApiTest,
1000                        TestPictureInPictureOnBrowserActionIconClick) {
1001   ASSERT_TRUE(StartEmbeddedTestServer());
1002 
1003   ASSERT_TRUE(
1004       RunExtensionTest("trigger_actions/browser_action_picture_in_picture"))
1005       << message_;
1006   const Extension* extension = GetSingleLoadedExtension();
1007   ASSERT_TRUE(extension) << message_;
1008 
1009   // Test that there is a browser action in the toolbar.
1010   ASSERT_EQ(1, GetBrowserActionsBar()->NumberOfBrowserActions());
1011 
1012   ExtensionAction* browser_action = GetBrowserAction(browser(), *extension);
1013   EXPECT_TRUE(browser_action);
1014 
1015   // Find the background page.
1016   ProcessManager* process_manager =
1017       extensions::ProcessManager::Get(browser()->profile());
1018   content::WebContents* web_contents =
1019       process_manager->GetBackgroundHostForExtension(extension->id())
1020           ->web_contents();
1021   ASSERT_TRUE(web_contents);
1022   content::PictureInPictureWindowController* window_controller =
1023       content::PictureInPictureWindowController::GetOrCreateForWebContents(
1024           web_contents);
1025   ASSERT_TRUE(window_controller->GetWindowForTesting());
1026   EXPECT_FALSE(window_controller->GetWindowForTesting()->IsVisible());
1027 
1028   // Click on the browser action icon to enter Picture-in-Picture.
1029   ResultCatcher catcher;
1030   GetBrowserActionsBar()->Press(0);
1031   EXPECT_TRUE(catcher.GetNextResult());
1032   EXPECT_TRUE(window_controller->GetWindowForTesting()->IsVisible());
1033 
1034   // Click on the browser action icon to exit Picture-in-Picture.
1035   GetBrowserActionsBar()->Press(0);
1036   EXPECT_TRUE(catcher.GetNextResult());
1037   EXPECT_FALSE(window_controller->GetWindowForTesting()->IsVisible());
1038 }
1039 
1040 }  // namespace
1041 }  // namespace extensions
1042