diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-03-11 11:32:04 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-03-18 13:40:17 +0000 |
commit | 31ccca0778db85c159634478b4ec7997f6704860 (patch) | |
tree | 3d33fc3afd9d5ec95541e1bbe074a9cf8da12a0e /chromium/chrome/browser/extensions/api/extension_action | |
parent | 248b70b82a40964d5594eb04feca0fa36716185d (diff) |
BASELINE: Update Chromium to 80.0.3987.136
Change-Id: I98e1649aafae85ba3a83e67af00bb27ef301db7b
Reviewed-by: Jüri Valdmann <juri.valdmann@qt.io>
Diffstat (limited to 'chromium/chrome/browser/extensions/api/extension_action')
5 files changed, 162 insertions, 80 deletions
diff --git a/chromium/chrome/browser/extensions/api/extension_action/browser_action_apitest.cc b/chromium/chrome/browser/extensions/api/extension_action/browser_action_apitest.cc index f3052240da4..def1cb767b5 100644 --- a/chromium/chrome/browser/extensions/api/extension_action/browser_action_apitest.cc +++ b/chromium/chrome/browser/extensions/api/extension_action/browser_action_apitest.cc @@ -78,21 +78,6 @@ void ExecuteExtensionAction(Browser* browser, const Extension* extension) { ->RunAction(extension, true); } -// An ImageSkia source that will do nothing (i.e., have a blank skia). We need -// this because we need a blank canvas at a certain size, and that can't be done -// by just using a null ImageSkia. -class BlankImageSource : public gfx::CanvasImageSource { - public: - explicit BlankImageSource(const gfx::Size& size) - : gfx::CanvasImageSource(size) {} - ~BlankImageSource() override {} - - void Draw(gfx::Canvas* canvas) override {} - - private: - DISALLOW_COPY_AND_ASSIGN(BlankImageSource); -}; - const char kEmptyImageDataError[] = "The imageData property must contain an ImageData object or dictionary " "of ImageData objects."; @@ -147,9 +132,10 @@ class BrowserActionApiTest : public ExtensionApiTest { return source.ptr(); } - ExtensionAction* GetBrowserAction(const Extension& extension) { + ExtensionAction* GetBrowserAction(Browser* browser, + const Extension& extension) { ExtensionAction* extension_action = - ExtensionActionManager::Get(browser()->profile()) + ExtensionActionManager::Get(browser->profile()) ->GetExtensionAction(extension); return extension_action->action_type() == ActionInfo::TYPE_BROWSER ? extension_action @@ -195,35 +181,59 @@ class RenderFrameChangedWatcher : public content::WebContentsObserver { }; IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, Basic) { + ExtensionTestMessageListener ready_listener("ready", false); ASSERT_TRUE(embedded_test_server()->Start()); - ASSERT_TRUE(RunExtensionTest("browser_action/basics")) << message_; - const Extension* extension = GetSingleLoadedExtension(); + const Extension* extension = + LoadExtension(test_data_dir_.AppendASCII("browser_action/basics")); ASSERT_TRUE(extension) << message_; // Test that there is a browser action in the toolbar. ASSERT_EQ(1, GetBrowserActionsBar()->NumberOfBrowserActions()); - // Tell the extension to update the browser action state. - ResultCatcher catcher; - ui_test_utils::NavigateToURL(browser(), - GURL(extension->GetResourceURL("update.html"))); - ASSERT_TRUE(catcher.GetNextResult()) << catcher.message(); - - // Test that we received the changes. - ExtensionAction* action = GetBrowserAction(*extension); - ASSERT_EQ("Modified", action->GetTitle(ExtensionAction::kDefaultTabId)); - ASSERT_EQ("badge", - action->GetExplicitlySetBadgeText(ExtensionAction::kDefaultTabId)); - ASSERT_EQ(SkColorSetARGB(255, 255, 255, 255), - action->GetBadgeBackgroundColor(ExtensionAction::kDefaultTabId)); + ASSERT_TRUE(ready_listener.WaitUntilSatisfied()); - // Simulate the browser action being clicked. + // Open a URL in the tab, so the event handler can check the tab's + // "url" and "title" properties. ui_test_utils::NavigateToURL( browser(), embedded_test_server()->GetURL("/extensions/test_file.txt")); + ResultCatcher catcher; + // Simulate the browser action being clicked. ExecuteExtensionAction(browser(), extension); - ASSERT_TRUE(catcher.GetNextResult()) << catcher.message(); + EXPECT_TRUE(catcher.GetNextResult()); +} + +IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, Update) { + ExtensionTestMessageListener ready_listener("ready", true); + ASSERT_TRUE(embedded_test_server()->Start()); + const Extension* extension = + LoadExtension(test_data_dir_.AppendASCII("browser_action/update")); + ASSERT_TRUE(extension) << message_; + // Test that there is a browser action in the toolbar. + ASSERT_EQ(1, GetBrowserActionsBar()->NumberOfBrowserActions()); + + ASSERT_TRUE(ready_listener.WaitUntilSatisfied()); + ExtensionAction* action = GetBrowserAction(browser(), *extension); + EXPECT_EQ("This is the default title.", + action->GetTitle(ExtensionAction::kDefaultTabId)); + EXPECT_EQ("", + action->GetExplicitlySetBadgeText(ExtensionAction::kDefaultTabId)); + EXPECT_EQ(SkColorSetARGB(0, 0, 0, 0), + action->GetBadgeBackgroundColor(ExtensionAction::kDefaultTabId)); + + // Tell the extension to update the browser action state and then + // catch the result. + ResultCatcher catcher; + ready_listener.Reply("update"); + ASSERT_TRUE(catcher.GetNextResult()); + + // Test that we received the changes. + EXPECT_EQ("Modified", action->GetTitle(ExtensionAction::kDefaultTabId)); + EXPECT_EQ("badge", + action->GetExplicitlySetBadgeText(ExtensionAction::kDefaultTabId)); + EXPECT_EQ(SkColorSetARGB(255, 255, 255, 255), + action->GetBadgeBackgroundColor(ExtensionAction::kDefaultTabId)); } IN_PROC_BROWSER_TEST_F(BrowserActionApiCanvasTest, DynamicBrowserAction) { @@ -243,10 +253,7 @@ IN_PROC_BROWSER_TEST_F(BrowserActionApiCanvasTest, DynamicBrowserAction) { // We should not be creating icons asynchronously, so we don't need an // observer. ExtensionActionIconFactory icon_factory( - profile(), - extension, - GetBrowserAction(*extension), - NULL); + profile(), extension, GetBrowserAction(browser(), *extension), nullptr); // Test that there is a browser action in the toolbar. ASSERT_EQ(1, GetBrowserActionsBar()->NumberOfBrowserActions()); EXPECT_TRUE(GetBrowserActionsBar()->HasIcon(0)); @@ -411,7 +418,14 @@ IN_PROC_BROWSER_TEST_F(BrowserActionApiCanvasTest, DynamicBrowserAction) { EXPECT_EQ(kEmptyPathError, catcher.message()); } -IN_PROC_BROWSER_TEST_F(BrowserActionApiCanvasTest, InvisibleIconBrowserAction) { +// https://crbug.com/1019669; flaky on ChromeOS. +#if defined(OS_CHROMEOS) +#define MAYBE_InvisibleIconBrowserAction DISABLED_InvisibleIconBrowserAction +#else +#define MAYBE_InvisibleIconBrowserAction InvisibleIconBrowserAction +#endif +IN_PROC_BROWSER_TEST_F(BrowserActionApiCanvasTest, + MAYBE_InvisibleIconBrowserAction) { // Turn this on so errors are reported. ExtensionActionSetIconFunction::SetReportErrorForInvisibleIconForTesting( true); @@ -443,7 +457,7 @@ IN_PROC_BROWSER_TEST_F(BrowserActionApiCanvasTest, InvisibleIconBrowserAction) { std::string result; EXPECT_TRUE(ExecuteScriptAndExtractString( background_page->host_contents(), - base::StringPrintf(kScript, "invisible"), &result)); + base::StringPrintf(kScript, "invisibleImageData"), &result)); EXPECT_EQ("Icon not sufficiently visible.", result); // The icon should not have changed. EXPECT_TRUE(gfx::test::AreImagesEqual(initial_bar_icon, @@ -459,7 +473,7 @@ IN_PROC_BROWSER_TEST_F(BrowserActionApiCanvasTest, InvisibleIconBrowserAction) { std::string result; EXPECT_TRUE(ExecuteScriptAndExtractString( background_page->host_contents(), - base::StringPrintf(kScript, "visible"), &result)); + base::StringPrintf(kScript, "visibleImageData"), &result)); EXPECT_EQ("", result); // The icon should have changed. EXPECT_FALSE(gfx::test::AreImagesEqual(initial_bar_icon, @@ -546,7 +560,7 @@ IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, BrowserActionAddPopup) { int tab_id = ExtensionTabUtil::GetTabId( browser()->tab_strip_model()->GetActiveWebContents()); - ExtensionAction* browser_action = GetBrowserAction(*extension); + ExtensionAction* browser_action = GetBrowserAction(browser(), *extension); ASSERT_TRUE(browser_action) << "Browser action test extension should have a browser action."; @@ -602,7 +616,7 @@ IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, BrowserActionRemovePopup) { int tab_id = ExtensionTabUtil::GetTabId( browser()->tab_strip_model()->GetActiveWebContents()); - ExtensionAction* browser_action = GetBrowserAction(*extension); + ExtensionAction* browser_action = GetBrowserAction(browser(), *extension); ASSERT_TRUE(browser_action) << "Browser action test extension should have a browser action."; @@ -628,10 +642,10 @@ IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, BrowserActionRemovePopup) { } IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, IncognitoBasic) { + ExtensionTestMessageListener ready_listener("ready", false); ASSERT_TRUE(embedded_test_server()->Start()); - - ASSERT_TRUE(RunExtensionTest("browser_action/basics")) << message_; - const Extension* extension = GetSingleLoadedExtension(); + const Extension* extension = + LoadExtension(test_data_dir_.AppendASCII("browser_action/basics")); ASSERT_TRUE(extension) << message_; // Test that there is a browser action in the toolbar. @@ -639,29 +653,92 @@ IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, IncognitoBasic) { // Open an incognito window and test that the browser action isn't there by // default. - Profile* incognito_profile = browser()->profile()->GetOffTheRecordProfile(); - base::RunLoop().RunUntilIdle(); // Wait for profile initialization. - Browser* incognito_browser = - new Browser(Browser::CreateParams(incognito_profile, true)); + Browser* incognito_browser = CreateIncognitoBrowser(browser()->profile()); ASSERT_EQ(0, BrowserActionTestUtil::Create(incognito_browser) ->NumberOfBrowserActions()); + ASSERT_TRUE(ready_listener.WaitUntilSatisfied()); + // Now enable the extension in incognito mode, and test that the browser // action shows up. // SetIsIncognitoEnabled() requires a reload of the extension, so we have to // wait for it. + ExtensionTestMessageListener incognito_ready_listener("ready", false); TestExtensionRegistryObserver registry_observer( ExtensionRegistry::Get(profile()), extension->id()); extensions::util::SetIsIncognitoEnabled( extension->id(), browser()->profile(), true); - registry_observer.WaitForExtensionLoaded(); + extension = registry_observer.WaitForExtensionLoaded(); ASSERT_EQ(1, BrowserActionTestUtil::Create(incognito_browser) ->NumberOfBrowserActions()); - // TODO(mpcomplete): simulate a click and have it do the right thing in - // incognito. + ASSERT_TRUE(incognito_ready_listener.WaitUntilSatisfied()); + + // Open a URL in the tab, so the event handler can check the tab's + // "url" and "title" properties. + ui_test_utils::NavigateToURL( + incognito_browser, + embedded_test_server()->GetURL("/extensions/test_file.txt")); + + ResultCatcher catcher; + // Simulate the browser action being clicked. + ExecuteExtensionAction(incognito_browser, extension); + + EXPECT_TRUE(catcher.GetNextResult()); +} + +IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, IncognitoUpdate) { + ASSERT_TRUE(embedded_test_server()->Start()); + const Extension* extension = + LoadExtension(test_data_dir_.AppendASCII("browser_action/update")); + ASSERT_TRUE(extension) << message_; + // Test that there is a browser action in the toolbar. + ASSERT_EQ(1, GetBrowserActionsBar()->NumberOfBrowserActions()); + + // Open an incognito window and test that the browser action isn't there by + // default. + Browser* incognito_browser = CreateIncognitoBrowser(browser()->profile()); + + ASSERT_EQ(0, BrowserActionTestUtil::Create(incognito_browser) + ->NumberOfBrowserActions()); + + // Set up a listener so we can reply for the extension to do the update. + ExtensionTestMessageListener incognito_ready_listener("incognito ready", + true); + // Now enable the extension in incognito mode, and test that the browser + // action shows up. + // SetIsIncognitoEnabled() requires a reload of the extension, so we have to + // wait for it. + TestExtensionRegistryObserver registry_observer( + ExtensionRegistry::Get(profile()), extension->id()); + extensions::util::SetIsIncognitoEnabled(extension->id(), browser()->profile(), + true); + extension = registry_observer.WaitForExtensionLoaded(); + ASSERT_EQ(1, BrowserActionTestUtil::Create(incognito_browser) + ->NumberOfBrowserActions()); + + ASSERT_TRUE(incognito_ready_listener.WaitUntilSatisfied()); + ExtensionAction* action = GetBrowserAction(incognito_browser, *extension); + EXPECT_EQ("This is the default title.", + action->GetTitle(ExtensionAction::kDefaultTabId)); + EXPECT_EQ("", + action->GetExplicitlySetBadgeText(ExtensionAction::kDefaultTabId)); + EXPECT_EQ(SkColorSetARGB(0, 0, 0, 0), + action->GetBadgeBackgroundColor(ExtensionAction::kDefaultTabId)); + // Tell the extension to update the browser action state and then + // catch the result. + ResultCatcher incognito_catcher; + incognito_ready_listener.Reply("incognito update"); + ASSERT_TRUE(incognito_catcher.GetNextResult()); + + // Test that we received the changes. + EXPECT_EQ("Modified", action->GetTitle(ExtensionAction::kDefaultTabId)); + EXPECT_EQ("badge", + action->GetExplicitlySetBadgeText(ExtensionAction::kDefaultTabId)); + EXPECT_EQ(SkColorSetARGB(255, 255, 255, 255), + action->GetBadgeBackgroundColor(ExtensionAction::kDefaultTabId)); } // Tests that events are dispatched to the correct profile for split mode @@ -673,13 +750,8 @@ IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, IncognitoSplit) { kFlagEnableIncognito); ASSERT_TRUE(extension) << message_; - // Open an incognito window. - Profile* incognito_profile = browser()->profile()->GetOffTheRecordProfile(); - Browser* incognito_browser = - new Browser(Browser::CreateParams(incognito_profile, true)); - base::RunLoop().RunUntilIdle(); // Wait for profile initialization. - // Navigate just to have a tab in this window, otherwise wonky things happen. - OpenURLOffTheRecord(browser()->profile(), GURL("about:blank")); + // Open an incognito browser. + Browser* incognito_browser = CreateIncognitoBrowser(browser()->profile()); ASSERT_EQ(1, BrowserActionTestUtil::Create(incognito_browser) ->NumberOfBrowserActions()); @@ -693,9 +765,7 @@ IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, IncognitoSplit) { ASSERT_TRUE(catcher.GetNextResult()) << catcher.message(); } -// Disabled because of failures (crashes) on ASAN bot. -// See http://crbug.com/98861. -IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, DISABLED_CloseBackgroundPage) { +IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, CloseBackgroundPage) { ASSERT_TRUE(LoadExtension( test_data_dir_.AppendASCII("browser_action/close_background"))); const Extension* extension = GetSingleLoadedExtension(); @@ -704,7 +774,7 @@ IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, DISABLED_CloseBackgroundPage) { extensions::ProcessManager* manager = extensions::ProcessManager::Get(browser()->profile()); ASSERT_TRUE(manager->GetBackgroundHostForExtension(extension->id())); - ExtensionAction* action = GetBrowserAction(*extension); + ExtensionAction* action = GetBrowserAction(browser(), *extension); ASSERT_EQ("", action->GetExplicitlySetBadgeText(ExtensionAction::kDefaultTabId)); @@ -734,7 +804,7 @@ IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, BadgeBackgroundColor) { ASSERT_EQ(1, GetBrowserActionsBar()->NumberOfBrowserActions()); // Test that CSS values (#FF0000) set color correctly. - ExtensionAction* action = GetBrowserAction(*extension); + ExtensionAction* action = GetBrowserAction(browser(), *extension); ASSERT_EQ(SkColorSetARGB(255, 255, 0, 0), action->GetBadgeBackgroundColor(ExtensionAction::kDefaultTabId)); @@ -807,8 +877,8 @@ IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, TestTriggerBrowserAction) { ui_test_utils::NavigateToURL(browser(), embedded_test_server()->GetURL("/simple.html")); - ExtensionAction* browser_action = GetBrowserAction(*extension); - EXPECT_TRUE(browser_action != NULL); + ExtensionAction* browser_action = GetBrowserAction(browser(), *extension); + EXPECT_TRUE(browser_action); // Simulate a click on the browser action icon. { @@ -819,7 +889,7 @@ IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, TestTriggerBrowserAction) { WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents(); - EXPECT_TRUE(tab != NULL); + EXPECT_TRUE(tab); // Verify that the browser action turned the background color red. const std::string script = @@ -1107,8 +1177,14 @@ class NavigatingExtensionPopupBrowserTest : public BrowserActionApiTest { const Extension* other_extension_; }; +// Flaky - crbug.com/1021172 +#if defined(OS_LINUX) +#define MAYBE_Webpage DISABLED_Webpage +#else +#define MAYBE_Webpage Webpage +#endif // Tests that an extension pop-up cannot be navigated to a web page. -IN_PROC_BROWSER_TEST_F(NavigatingExtensionPopupBrowserTest, Webpage) { +IN_PROC_BROWSER_TEST_F(NavigatingExtensionPopupBrowserTest, MAYBE_Webpage) { GURL web_url(embedded_test_server()->GetURL("foo.com", "/title1.html")); // The GET request will be blocked in ExtensionViewHost::OpenURLFromTab @@ -1202,7 +1278,7 @@ IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, // Test that there is a browser action in the toolbar. ASSERT_EQ(1, GetBrowserActionsBar()->NumberOfBrowserActions()); - ExtensionAction* browser_action = GetBrowserAction(*extension); + ExtensionAction* browser_action = GetBrowserAction(browser(), *extension); EXPECT_TRUE(browser_action); // Find the background page. diff --git a/chromium/chrome/browser/extensions/api/extension_action/extension_action_api.cc b/chromium/chrome/browser/extensions/api/extension_action/extension_action_api.cc index 28bdacc39e9..bb0799b4cc9 100644 --- a/chromium/chrome/browser/extensions/api/extension_action/extension_action_api.cc +++ b/chromium/chrome/browser/extensions/api/extension_action/extension_action_api.cc @@ -27,13 +27,14 @@ #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_window.h" +#include "chrome/browser/ui/extensions/extensions_container.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" -#include "chrome/browser/ui/toolbar/toolbar_actions_bar.h" #include "chrome/common/extensions/api/extension_action/action_info.h" #include "content/public/browser/notification_service.h" #include "extensions/browser/api/declarative_net_request/constants.h" #include "extensions/browser/event_router.h" #include "extensions/browser/extension_host.h" +#include "extensions/browser/extension_prefs.h" #include "extensions/browser/extension_registry.h" #include "extensions/browser/extension_util.h" #include "extensions/browser/notification_types.h" @@ -125,12 +126,12 @@ bool ExtensionActionAPI::ShowExtensionActionPopup( if (!browser->SupportsWindowFeature(Browser::FEATURE_TOOLBAR)) return false; - ToolbarActionsBar* toolbar_actions_bar = - browser->window()->GetToolbarActionsBar(); - // ToolbarActionsBar could be null if, e.g., this is a popup window with no - // toolbar. - return toolbar_actions_bar && - toolbar_actions_bar->ShowToolbarActionPopup( + ExtensionsContainer* extensions_container = + browser->window()->GetExtensionsContainer(); + // The ExtensionsContainer could be null if, e.g., this is a popup window with + // no toolbar. + return extensions_container && + extensions_container->ShowToolbarActionPopup( extension->id(), grant_active_tab_permissions); } diff --git a/chromium/chrome/browser/extensions/api/extension_action/extension_action_api_unittest.cc b/chromium/chrome/browser/extensions/api/extension_action/extension_action_api_unittest.cc index 1de8d899a41..2b73a6dd59b 100644 --- a/chromium/chrome/browser/extensions/api/extension_action/extension_action_api_unittest.cc +++ b/chromium/chrome/browser/extensions/api/extension_action/extension_action_api_unittest.cc @@ -101,7 +101,7 @@ TEST_P(ExtensionActionAPIUnitTest, MultiIcons) { EXPECT_EQ("icon38.png", icons.Get(38, ExtensionIconSet::MATCH_EXACTLY)); } -INSTANTIATE_TEST_SUITE_P(, +INSTANTIATE_TEST_SUITE_P(All, ExtensionActionAPIUnitTest, testing::Values(TestActionType::kBrowser, TestActionType::kPage)); diff --git a/chromium/chrome/browser/extensions/api/extension_action/extension_action_apitest.cc b/chromium/chrome/browser/extensions/api/extension_action/extension_action_apitest.cc index 760b0b8d622..dc6716b3ba6 100644 --- a/chromium/chrome/browser/extensions/api/extension_action/extension_action_apitest.cc +++ b/chromium/chrome/browser/extensions/api/extension_action/extension_action_apitest.cc @@ -999,13 +999,13 @@ IN_PROC_BROWSER_TEST_P(MultiActionAPITest, EnableAndDisable) { } } -INSTANTIATE_TEST_SUITE_P(, +INSTANTIATE_TEST_SUITE_P(All, MultiActionAPITest, testing::Values(ActionInfo::TYPE_ACTION, ActionInfo::TYPE_PAGE, ActionInfo::TYPE_BROWSER)); -INSTANTIATE_TEST_SUITE_P(, +INSTANTIATE_TEST_SUITE_P(All, MultiActionAPICanvasTest, testing::Values(ActionInfo::TYPE_ACTION, ActionInfo::TYPE_PAGE, diff --git a/chromium/chrome/browser/extensions/api/extension_action/test_icon_image_observer.cc b/chromium/chrome/browser/extensions/api/extension_action/test_icon_image_observer.cc index 5544ebace55..e8e1d6f98c1 100644 --- a/chromium/chrome/browser/extensions/api/extension_action/test_icon_image_observer.cc +++ b/chromium/chrome/browser/extensions/api/extension_action/test_icon_image_observer.cc @@ -6,6 +6,7 @@ #include "chrome/browser/extensions/extension_action.h" #include "chrome/browser/extensions/extension_action_manager.h" +#include "ui/gfx/image/image_skia.h" namespace extensions { @@ -14,6 +15,10 @@ TestIconImageObserver::~TestIconImageObserver() = default; void TestIconImageObserver::Wait(IconImage* icon) { if (!icon->did_complete_initial_load()) { + // Tricky: The icon might not actually be visible in the viewport yet (e.g., + // if it's for an extension that is buried in the menu). Force the icon to + // load by requesting a bitmap. + icon->image_skia().bitmap(); observer_.Add(icon); run_loop_.Run(); } |