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(¶ms);
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