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