• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
2 // reserved. Use of this source code is governed by a BSD-style license that
3 // can be found in the LICENSE file.
4 
5 #include "tests/cefclient/browser/root_window_manager.h"
6 
7 #include <sstream>
8 
9 #include "include/base/cef_bind.h"
10 #include "include/base/cef_logging.h"
11 #include "include/wrapper/cef_helpers.h"
12 #include "tests/cefclient/browser/main_context.h"
13 #include "tests/cefclient/browser/test_runner.h"
14 #include "tests/shared/browser/extension_util.h"
15 #include "tests/shared/browser/file_util.h"
16 #include "tests/shared/browser/resource_util.h"
17 #include "tests/shared/common/client_switches.h"
18 
19 namespace client {
20 
21 namespace {
22 
23 class ClientRequestContextHandler : public CefRequestContextHandler,
24                                     public CefExtensionHandler {
25  public:
ClientRequestContextHandler()26   ClientRequestContextHandler() {}
27 
28   // CefRequestContextHandler methods:
OnBeforePluginLoad(const CefString & mime_type,const CefString & plugin_url,bool is_main_frame,const CefString & top_origin_url,CefRefPtr<CefWebPluginInfo> plugin_info,PluginPolicy * plugin_policy)29   bool OnBeforePluginLoad(const CefString& mime_type,
30                           const CefString& plugin_url,
31                           bool is_main_frame,
32                           const CefString& top_origin_url,
33                           CefRefPtr<CefWebPluginInfo> plugin_info,
34                           PluginPolicy* plugin_policy) OVERRIDE {
35     // Always allow the PDF plugin to load.
36     if (*plugin_policy != PLUGIN_POLICY_ALLOW &&
37         mime_type == "application/pdf") {
38       *plugin_policy = PLUGIN_POLICY_ALLOW;
39       return true;
40     }
41 
42     return false;
43   }
44 
OnRequestContextInitialized(CefRefPtr<CefRequestContext> request_context)45   void OnRequestContextInitialized(
46       CefRefPtr<CefRequestContext> request_context) OVERRIDE {
47     CEF_REQUIRE_UI_THREAD();
48 
49     CefRefPtr<CefCommandLine> command_line =
50         CefCommandLine::GetGlobalCommandLine();
51     if (command_line->HasSwitch(switches::kLoadExtension)) {
52       if (MainContext::Get()
53               ->GetRootWindowManager()
54               ->request_context_per_browser()) {
55         // The example extension loading implementation requires all browsers to
56         // share the same request context.
57         LOG(ERROR)
58             << "Cannot mix --load-extension and --request-context-per-browser";
59         return;
60       }
61 
62       // Load one or more extension paths specified on the command-line and
63       // delimited with semicolon.
64       const std::string& extension_path =
65           command_line->GetSwitchValue(switches::kLoadExtension);
66       if (!extension_path.empty()) {
67         std::string part;
68         std::istringstream f(extension_path);
69         while (getline(f, part, ';')) {
70           if (!part.empty())
71             extension_util::LoadExtension(request_context, part, this);
72         }
73       }
74     }
75   }
76 
77   // CefExtensionHandler methods:
OnExtensionLoaded(CefRefPtr<CefExtension> extension)78   void OnExtensionLoaded(CefRefPtr<CefExtension> extension) OVERRIDE {
79     CEF_REQUIRE_UI_THREAD();
80     MainContext::Get()->GetRootWindowManager()->AddExtension(extension);
81   }
82 
GetActiveBrowser(CefRefPtr<CefExtension> extension,CefRefPtr<CefBrowser> browser,bool include_incognito)83   CefRefPtr<CefBrowser> GetActiveBrowser(CefRefPtr<CefExtension> extension,
84                                          CefRefPtr<CefBrowser> browser,
85                                          bool include_incognito) OVERRIDE {
86     CEF_REQUIRE_UI_THREAD();
87 
88     // Return the browser for the active/foreground window.
89     CefRefPtr<CefBrowser> active_browser =
90         MainContext::Get()->GetRootWindowManager()->GetActiveBrowser();
91     if (!active_browser) {
92       LOG(WARNING)
93           << "No active browser available for extension "
94           << browser->GetHost()->GetExtension()->GetIdentifier().ToString();
95     } else {
96       // The active browser should not be hosting an extension.
97       DCHECK(!active_browser->GetHost()->GetExtension());
98     }
99     return active_browser;
100   }
101 
102  private:
103   IMPLEMENT_REFCOUNTING(ClientRequestContextHandler);
104   DISALLOW_COPY_AND_ASSIGN(ClientRequestContextHandler);
105 };
106 
107 }  // namespace
108 
RootWindowManager(bool terminate_when_all_windows_closed)109 RootWindowManager::RootWindowManager(bool terminate_when_all_windows_closed)
110     : terminate_when_all_windows_closed_(terminate_when_all_windows_closed) {
111   CefRefPtr<CefCommandLine> command_line =
112       CefCommandLine::GetGlobalCommandLine();
113   DCHECK(command_line.get());
114   request_context_per_browser_ =
115       command_line->HasSwitch(switches::kRequestContextPerBrowser);
116   request_context_shared_cache_ =
117       command_line->HasSwitch(switches::kRequestContextSharedCache);
118 }
119 
~RootWindowManager()120 RootWindowManager::~RootWindowManager() {
121   // All root windows should already have been destroyed.
122   DCHECK(root_windows_.empty());
123 }
124 
CreateRootWindow(const RootWindowConfig & config)125 scoped_refptr<RootWindow> RootWindowManager::CreateRootWindow(
126     const RootWindowConfig& config) {
127   CefBrowserSettings settings;
128   MainContext::Get()->PopulateBrowserSettings(&settings);
129 
130   scoped_refptr<RootWindow> root_window =
131       RootWindow::Create(MainContext::Get()->UseViews());
132   root_window->Init(this, config, settings);
133 
134   // Store a reference to the root window on the main thread.
135   OnRootWindowCreated(root_window);
136 
137   return root_window;
138 }
139 
CreateRootWindowAsPopup(bool with_controls,bool with_osr,const CefPopupFeatures & popupFeatures,CefWindowInfo & windowInfo,CefRefPtr<CefClient> & client,CefBrowserSettings & settings)140 scoped_refptr<RootWindow> RootWindowManager::CreateRootWindowAsPopup(
141     bool with_controls,
142     bool with_osr,
143     const CefPopupFeatures& popupFeatures,
144     CefWindowInfo& windowInfo,
145     CefRefPtr<CefClient>& client,
146     CefBrowserSettings& settings) {
147   CEF_REQUIRE_UI_THREAD();
148 
149   if (!temp_window_) {
150     // TempWindow must be created on the UI thread.
151     temp_window_.reset(new TempWindow());
152   }
153 
154   MainContext::Get()->PopulateBrowserSettings(&settings);
155 
156   scoped_refptr<RootWindow> root_window =
157       RootWindow::Create(MainContext::Get()->UseViews());
158   root_window->InitAsPopup(this, with_controls, with_osr, popupFeatures,
159                            windowInfo, client, settings);
160 
161   // Store a reference to the root window on the main thread.
162   OnRootWindowCreated(root_window);
163 
164   return root_window;
165 }
166 
CreateRootWindowAsExtension(CefRefPtr<CefExtension> extension,const CefRect & source_bounds,CefRefPtr<CefWindow> parent_window,const base::Closure & close_callback,bool with_controls,bool with_osr)167 scoped_refptr<RootWindow> RootWindowManager::CreateRootWindowAsExtension(
168     CefRefPtr<CefExtension> extension,
169     const CefRect& source_bounds,
170     CefRefPtr<CefWindow> parent_window,
171     const base::Closure& close_callback,
172     bool with_controls,
173     bool with_osr) {
174   const std::string& extension_url = extension_util::GetExtensionURL(extension);
175   if (extension_url.empty()) {
176     NOTREACHED() << "Extension cannot be loaded directly.";
177     return nullptr;
178   }
179 
180   // Create an initially hidden browser window that loads the extension URL.
181   // We'll show the window when the desired size becomes available via
182   // ClientHandler::OnAutoResize.
183   RootWindowConfig config;
184   config.with_controls = with_controls;
185   config.with_osr = with_osr;
186   config.with_extension = true;
187   config.initially_hidden = true;
188   config.source_bounds = source_bounds;
189   config.parent_window = parent_window;
190   config.close_callback = close_callback;
191   config.url = extension_url;
192   return CreateRootWindow(config);
193 }
194 
HasRootWindowAsExtension(CefRefPtr<CefExtension> extension)195 bool RootWindowManager::HasRootWindowAsExtension(
196     CefRefPtr<CefExtension> extension) {
197   REQUIRE_MAIN_THREAD();
198 
199   RootWindowSet::const_iterator it = root_windows_.begin();
200   for (; it != root_windows_.end(); ++it) {
201     const RootWindow* root_window = (*it);
202     if (!root_window->WithExtension())
203       continue;
204 
205     CefRefPtr<CefBrowser> browser = root_window->GetBrowser();
206     if (!browser)
207       continue;
208 
209     CefRefPtr<CefExtension> browser_extension =
210         browser->GetHost()->GetExtension();
211     DCHECK(browser_extension);
212     if (browser_extension->GetIdentifier() == extension->GetIdentifier())
213       return true;
214   }
215 
216   return false;
217 }
218 
GetWindowForBrowser(int browser_id) const219 scoped_refptr<RootWindow> RootWindowManager::GetWindowForBrowser(
220     int browser_id) const {
221   REQUIRE_MAIN_THREAD();
222 
223   RootWindowSet::const_iterator it = root_windows_.begin();
224   for (; it != root_windows_.end(); ++it) {
225     CefRefPtr<CefBrowser> browser = (*it)->GetBrowser();
226     if (browser.get() && browser->GetIdentifier() == browser_id)
227       return *it;
228   }
229   return nullptr;
230 }
231 
GetActiveRootWindow() const232 scoped_refptr<RootWindow> RootWindowManager::GetActiveRootWindow() const {
233   REQUIRE_MAIN_THREAD();
234   return active_root_window_;
235 }
236 
GetActiveBrowser() const237 CefRefPtr<CefBrowser> RootWindowManager::GetActiveBrowser() const {
238   base::AutoLock lock_scope(active_browser_lock_);
239   return active_browser_;
240 }
241 
CloseAllWindows(bool force)242 void RootWindowManager::CloseAllWindows(bool force) {
243   if (!CURRENTLY_ON_MAIN_THREAD()) {
244     // Execute this method on the main thread.
245     MAIN_POST_CLOSURE(base::Bind(&RootWindowManager::CloseAllWindows,
246                                  base::Unretained(this), force));
247     return;
248   }
249 
250   if (root_windows_.empty())
251     return;
252 
253   // Use a copy of |root_windows_| because the original set may be modified
254   // in OnRootWindowDestroyed while iterating.
255   RootWindowSet root_windows = root_windows_;
256 
257   RootWindowSet::const_iterator it = root_windows.begin();
258   for (; it != root_windows.end(); ++it)
259     (*it)->Close(force);
260 }
261 
AddExtension(CefRefPtr<CefExtension> extension)262 void RootWindowManager::AddExtension(CefRefPtr<CefExtension> extension) {
263   if (!CURRENTLY_ON_MAIN_THREAD()) {
264     // Execute this method on the main thread.
265     MAIN_POST_CLOSURE(base::Bind(&RootWindowManager::AddExtension,
266                                  base::Unretained(this), extension));
267     return;
268   }
269 
270   // Don't track extensions that can't be loaded directly.
271   if (extension_util::GetExtensionURL(extension).empty())
272     return;
273 
274   // Don't add the same extension multiple times.
275   ExtensionSet::const_iterator it = extensions_.begin();
276   for (; it != extensions_.end(); ++it) {
277     if ((*it)->GetIdentifier() == extension->GetIdentifier())
278       return;
279   }
280 
281   extensions_.insert(extension);
282   NotifyExtensionsChanged();
283 }
284 
OnRootWindowCreated(scoped_refptr<RootWindow> root_window)285 void RootWindowManager::OnRootWindowCreated(
286     scoped_refptr<RootWindow> root_window) {
287   if (!CURRENTLY_ON_MAIN_THREAD()) {
288     // Execute this method on the main thread.
289     MAIN_POST_CLOSURE(base::Bind(&RootWindowManager::OnRootWindowCreated,
290                                  base::Unretained(this), root_window));
291     return;
292   }
293 
294   root_windows_.insert(root_window);
295   if (!root_window->WithExtension()) {
296     root_window->OnExtensionsChanged(extensions_);
297 
298     if (root_windows_.size() == 1U) {
299       // The first non-extension root window should be considered the active
300       // window.
301       OnRootWindowActivated(root_window);
302     }
303   }
304 }
305 
NotifyExtensionsChanged()306 void RootWindowManager::NotifyExtensionsChanged() {
307   REQUIRE_MAIN_THREAD();
308 
309   RootWindowSet::const_iterator it = root_windows_.begin();
310   for (; it != root_windows_.end(); ++it) {
311     RootWindow* root_window = *it;
312     if (!root_window->WithExtension())
313       root_window->OnExtensionsChanged(extensions_);
314   }
315 }
316 
GetRequestContext(RootWindow * root_window)317 CefRefPtr<CefRequestContext> RootWindowManager::GetRequestContext(
318     RootWindow* root_window) {
319   REQUIRE_MAIN_THREAD();
320 
321   if (request_context_per_browser_) {
322     // Create a new request context for each browser.
323     CefRequestContextSettings settings;
324 
325     CefRefPtr<CefCommandLine> command_line =
326         CefCommandLine::GetGlobalCommandLine();
327     if (command_line->HasSwitch(switches::kCachePath)) {
328       if (request_context_shared_cache_) {
329         // Give each browser the same cache path. The resulting context objects
330         // will share the same storage internally.
331         CefString(&settings.cache_path) =
332             command_line->GetSwitchValue(switches::kCachePath);
333       } else {
334         // Give each browser a unique cache path. This will create completely
335         // isolated context objects.
336         std::stringstream ss;
337         ss << command_line->GetSwitchValue(switches::kCachePath).ToString()
338            << file_util::kPathSep << time(NULL);
339         CefString(&settings.cache_path) = ss.str();
340       }
341     }
342 
343     return CefRequestContext::CreateContext(settings,
344                                             new ClientRequestContextHandler);
345   }
346 
347   // All browsers will share the global request context.
348   if (!shared_request_context_.get()) {
349     shared_request_context_ = CefRequestContext::CreateContext(
350         CefRequestContext::GetGlobalContext(), new ClientRequestContextHandler);
351   }
352   return shared_request_context_;
353 }
354 
GetImageCache()355 scoped_refptr<ImageCache> RootWindowManager::GetImageCache() {
356   CEF_REQUIRE_UI_THREAD();
357 
358   if (!image_cache_) {
359     image_cache_ = new ImageCache;
360   }
361   return image_cache_;
362 }
363 
OnTest(RootWindow * root_window,int test_id)364 void RootWindowManager::OnTest(RootWindow* root_window, int test_id) {
365   REQUIRE_MAIN_THREAD();
366 
367   test_runner::RunTest(root_window->GetBrowser(), test_id);
368 }
369 
OnExit(RootWindow * root_window)370 void RootWindowManager::OnExit(RootWindow* root_window) {
371   REQUIRE_MAIN_THREAD();
372 
373   CloseAllWindows(false);
374 }
375 
OnRootWindowDestroyed(RootWindow * root_window)376 void RootWindowManager::OnRootWindowDestroyed(RootWindow* root_window) {
377   REQUIRE_MAIN_THREAD();
378 
379   RootWindowSet::iterator it = root_windows_.find(root_window);
380   DCHECK(it != root_windows_.end());
381   if (it != root_windows_.end())
382     root_windows_.erase(it);
383 
384   if (root_window == active_root_window_) {
385     active_root_window_ = nullptr;
386 
387     base::AutoLock lock_scope(active_browser_lock_);
388     active_browser_ = nullptr;
389   }
390 
391   if (terminate_when_all_windows_closed_ && root_windows_.empty()) {
392     // All windows have closed. Clean up on the UI thread.
393     CefPostTask(TID_UI, base::Bind(&RootWindowManager::CleanupOnUIThread,
394                                    base::Unretained(this)));
395   }
396 }
397 
OnRootWindowActivated(RootWindow * root_window)398 void RootWindowManager::OnRootWindowActivated(RootWindow* root_window) {
399   REQUIRE_MAIN_THREAD();
400 
401   if (root_window->WithExtension()) {
402     // We don't want extension apps to become the active RootWindow.
403     return;
404   }
405 
406   if (root_window == active_root_window_)
407     return;
408 
409   active_root_window_ = root_window;
410 
411   {
412     base::AutoLock lock_scope(active_browser_lock_);
413     // May be NULL at this point, in which case we'll make the association in
414     // OnBrowserCreated.
415     active_browser_ = active_root_window_->GetBrowser();
416   }
417 }
418 
OnBrowserCreated(RootWindow * root_window,CefRefPtr<CefBrowser> browser)419 void RootWindowManager::OnBrowserCreated(RootWindow* root_window,
420                                          CefRefPtr<CefBrowser> browser) {
421   REQUIRE_MAIN_THREAD();
422 
423   if (root_window == active_root_window_) {
424     base::AutoLock lock_scope(active_browser_lock_);
425     active_browser_ = browser;
426   }
427 }
428 
CreateExtensionWindow(CefRefPtr<CefExtension> extension,const CefRect & source_bounds,CefRefPtr<CefWindow> parent_window,const base::Closure & close_callback,bool with_osr)429 void RootWindowManager::CreateExtensionWindow(
430     CefRefPtr<CefExtension> extension,
431     const CefRect& source_bounds,
432     CefRefPtr<CefWindow> parent_window,
433     const base::Closure& close_callback,
434     bool with_osr) {
435   REQUIRE_MAIN_THREAD();
436 
437   if (!HasRootWindowAsExtension(extension)) {
438     CreateRootWindowAsExtension(extension, source_bounds, parent_window,
439                                 close_callback, false, with_osr);
440   }
441 }
442 
CleanupOnUIThread()443 void RootWindowManager::CleanupOnUIThread() {
444   CEF_REQUIRE_UI_THREAD();
445 
446   if (temp_window_) {
447     // TempWindow must be destroyed on the UI thread.
448     temp_window_.reset(nullptr);
449   }
450 
451   if (image_cache_) {
452     image_cache_ = nullptr;
453   }
454 
455   // Quit the main message loop.
456   MainMessageLoop::Get()->Quit();
457 }
458 
459 }  // namespace client
460