1 // Copyright 2016 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 <chrome/browser/extensions/chrome_extension_test_notification_observer.h>
6 #include "base/bind.h"
7 #include "base/scoped_observer.h"
8 #include "chrome/browser/extensions/extension_action_test_util.h"
9 #include "chrome/browser/extensions/extension_util.h"
10 #include "chrome/browser/profiles/profile_manager.h"
11 #include "chrome/browser/ui/browser.h"
12 #include "chrome/browser/ui/tabs/tab_strip_model.h"
13 #include "content/public/browser/browser_context.h"
14 #include "content/public/browser/web_contents.h"
15 #include "content/public/test/test_utils.h"
16 #include "extensions/browser/notification_types.h"
17 #include "extensions/browser/process_manager.h"
18 #include "extensions/common/extension.h"
19 
20 namespace extensions {
21 
22 namespace {
23 
24 // A callback that returns true if the condition has been met and takes no
25 // arguments.
26 using ConditionCallback = base::Callback<bool(void)>;
27 
HasPageActionVisibilityReachedTarget(Browser * browser,size_t target_visible_page_action_count)28 bool HasPageActionVisibilityReachedTarget(
29     Browser* browser,
30     size_t target_visible_page_action_count) {
31   return extension_action_test_util::GetVisiblePageActionCount(
32              browser->tab_strip_model()->GetActiveWebContents()) ==
33          target_visible_page_action_count;
34 }
35 
HaveAllExtensionRenderFrameHostsFinishedLoading(ProcessManager * manager)36 bool HaveAllExtensionRenderFrameHostsFinishedLoading(ProcessManager* manager) {
37   ProcessManager::FrameSet all_views = manager->GetAllFrames();
38   for (content::RenderFrameHost* host : manager->GetAllFrames()) {
39     if (content::WebContents::FromRenderFrameHost(host)->IsLoading())
40       return false;
41   }
42   return true;
43 }
44 
45 }  // namespace
46 
47 ////////////////////////////////////////////////////////////////////////////////
48 // ExtensionTestNotificationObserver
49 
50 ChromeExtensionTestNotificationObserver::
ChromeExtensionTestNotificationObserver(Browser * browser)51     ChromeExtensionTestNotificationObserver(Browser* browser)
52     : ExtensionTestNotificationObserver(browser ? browser->profile() : nullptr),
53       browser_(browser) {}
54 
55 ChromeExtensionTestNotificationObserver::
ChromeExtensionTestNotificationObserver(content::BrowserContext * context)56     ChromeExtensionTestNotificationObserver(content::BrowserContext* context)
57     : ExtensionTestNotificationObserver(context), browser_(nullptr) {}
58 
59 ChromeExtensionTestNotificationObserver::
~ChromeExtensionTestNotificationObserver()60     ~ChromeExtensionTestNotificationObserver() {}
61 
62 content::BrowserContext*
GetBrowserContext()63 ChromeExtensionTestNotificationObserver::GetBrowserContext() {
64   if (!context_) {
65     if (browser_)
66       context_ = browser_->profile();
67     else
68       context_ = ProfileManager::GetActiveUserProfile();
69   }
70   return context_;
71 }
72 
73 bool ChromeExtensionTestNotificationObserver::
WaitForPageActionVisibilityChangeTo(int count)74     WaitForPageActionVisibilityChangeTo(int count) {
75   DCHECK(browser_);
76   ScopedObserver<ExtensionActionAPI, ExtensionActionAPI::Observer> observer(
77       this);
78   observer.Add(ExtensionActionAPI::Get(GetBrowserContext()));
79   WaitForCondition(
80       base::Bind(&HasPageActionVisibilityReachedTarget, browser_, count), NULL);
81   return true;
82 }
83 
WaitForExtensionViewsToLoad()84 bool ChromeExtensionTestNotificationObserver::WaitForExtensionViewsToLoad() {
85   // Some views might not be created yet. This call may become insufficient if
86   // e.g. implementation of ExtensionHostQueue changes.
87   base::RunLoop().RunUntilIdle();
88 
89   ProcessManager* manager = ProcessManager::Get(GetBrowserContext());
90   NotificationSet notification_set;
91   notification_set.Add(content::NOTIFICATION_WEB_CONTENTS_DESTROYED);
92   notification_set.Add(content::NOTIFICATION_LOAD_STOP);
93   notification_set.AddExtensionFrameUnregistration(manager);
94   WaitForCondition(
95       base::Bind(&HaveAllExtensionRenderFrameHostsFinishedLoading, manager),
96       &notification_set);
97   return true;
98 }
99 
WaitForExtensionIdle(const std::string & extension_id)100 bool ChromeExtensionTestNotificationObserver::WaitForExtensionIdle(
101     const std::string& extension_id) {
102   NotificationSet notification_set;
103   notification_set.Add(content::NOTIFICATION_RENDERER_PROCESS_TERMINATED);
104   WaitForCondition(
105       base::Bind(&util::IsExtensionIdle, extension_id, GetBrowserContext()),
106       &notification_set);
107   return true;
108 }
109 
WaitForExtensionNotIdle(const std::string & extension_id)110 bool ChromeExtensionTestNotificationObserver::WaitForExtensionNotIdle(
111     const std::string& extension_id) {
112   NotificationSet notification_set;
113   notification_set.Add(content::NOTIFICATION_LOAD_STOP);
114   WaitForCondition(base::Bind(
115                        [](const std::string& extension_id,
116                           content::BrowserContext* context) -> bool {
117                          return !util::IsExtensionIdle(extension_id, context);
118                        },
119                        extension_id, GetBrowserContext()),
120                    &notification_set);
121   return true;
122 }
123 
OnExtensionActionUpdated(ExtensionAction * extension_action,content::WebContents * web_contents,content::BrowserContext * browser_context)124 void ChromeExtensionTestNotificationObserver::OnExtensionActionUpdated(
125     ExtensionAction* extension_action,
126     content::WebContents* web_contents,
127     content::BrowserContext* browser_context) {
128   MaybeQuit();
129 }
130 
131 }  // namespace extensions
132