• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2016 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_views.h"
6 
7 #include <memory>
8 
9 #include "include/base/cef_build.h"
10 #include "include/base/cef_callback.h"
11 #include "include/base/cef_cxx17_backports.h"
12 #include "include/cef_app.h"
13 #include "include/wrapper/cef_helpers.h"
14 #include "tests/cefclient/browser/client_handler_std.h"
15 
16 namespace client {
17 
18 namespace {
19 
20 // Images that are loaded early and cached.
21 static const char* kDefaultImageCache[] = {"menu_icon", "window_icon"};
22 
23 }  // namespace
24 
25 RootWindowViews::RootWindowViews() = default;
26 
~RootWindowViews()27 RootWindowViews::~RootWindowViews() {
28   REQUIRE_MAIN_THREAD();
29 }
30 
Init(RootWindow::Delegate * delegate,std::unique_ptr<RootWindowConfig> config,const CefBrowserSettings & settings)31 void RootWindowViews::Init(RootWindow::Delegate* delegate,
32                            std::unique_ptr<RootWindowConfig> config,
33                            const CefBrowserSettings& settings) {
34   DCHECK(delegate);
35   DCHECK(!config->with_osr);  // Windowless rendering is not supported.
36   DCHECK(!initialized_);
37 
38   delegate_ = delegate;
39   config_ = std::move(config);
40 
41   if (config_->initially_hidden && !config_->source_bounds.IsEmpty()) {
42     // The window will be sized and positioned in OnAutoResize().
43     initial_bounds_ = config_->source_bounds;
44     position_on_resize_ = true;
45   } else {
46     initial_bounds_ = config_->bounds;
47   }
48 
49   CreateClientHandler(config_->url);
50   initialized_ = true;
51 
52   // Continue initialization on the UI thread.
53   InitOnUIThread(settings, config_->url, delegate_->GetRequestContext(this));
54 }
55 
InitAsPopup(RootWindow::Delegate * delegate,bool with_controls,bool with_osr,const CefPopupFeatures & popupFeatures,CefWindowInfo & windowInfo,CefRefPtr<CefClient> & client,CefBrowserSettings & settings)56 void RootWindowViews::InitAsPopup(RootWindow::Delegate* delegate,
57                                   bool with_controls,
58                                   bool with_osr,
59                                   const CefPopupFeatures& popupFeatures,
60                                   CefWindowInfo& windowInfo,
61                                   CefRefPtr<CefClient>& client,
62                                   CefBrowserSettings& settings) {
63   CEF_REQUIRE_UI_THREAD();
64 
65   DCHECK(delegate);
66   DCHECK(!with_osr);  // Windowless rendering is not supported.
67   DCHECK(!initialized_);
68 
69   delegate_ = delegate;
70   config_ = std::make_unique<RootWindowConfig>();
71   config_->with_controls = with_controls;
72   is_popup_ = true;
73 
74   if (popupFeatures.xSet)
75     initial_bounds_.x = popupFeatures.x;
76   if (popupFeatures.ySet)
77     initial_bounds_.y = popupFeatures.y;
78   if (popupFeatures.widthSet)
79     initial_bounds_.width = popupFeatures.width;
80   if (popupFeatures.heightSet)
81     initial_bounds_.height = popupFeatures.height;
82 
83   CreateClientHandler(std::string());
84   initialized_ = true;
85 
86   // The Window will be created in ViewsWindow::OnPopupBrowserViewCreated().
87   client = client_handler_;
88 }
89 
Show(ShowMode mode)90 void RootWindowViews::Show(ShowMode mode) {
91   if (!CefCurrentlyOn(TID_UI)) {
92     // Execute this method on the UI thread.
93     CefPostTask(TID_UI, base::BindOnce(&RootWindowViews::Show, this, mode));
94     return;
95   }
96 
97   if (!window_)
98     return;
99 
100   window_->Show();
101 
102   switch (mode) {
103     case ShowMinimized:
104       window_->Minimize();
105       break;
106     case ShowMaximized:
107       window_->Maximize();
108       break;
109     default:
110       break;
111   }
112 }
113 
Hide()114 void RootWindowViews::Hide() {
115   if (!CefCurrentlyOn(TID_UI)) {
116     // Execute this method on the UI thread.
117     CefPostTask(TID_UI, base::BindOnce(&RootWindowViews::Hide, this));
118     return;
119   }
120 
121   if (window_)
122     window_->Hide();
123 }
124 
SetBounds(int x,int y,size_t width,size_t height)125 void RootWindowViews::SetBounds(int x, int y, size_t width, size_t height) {
126   if (!CefCurrentlyOn(TID_UI)) {
127     // Execute this method on the UI thread.
128     CefPostTask(TID_UI, base::BindOnce(&RootWindowViews::SetBounds, this, x, y,
129                                        width, height));
130     return;
131   }
132 
133   if (window_) {
134     window_->SetBounds(
135         CefRect(x, y, static_cast<int>(width), static_cast<int>(height)));
136   }
137 }
138 
Close(bool force)139 void RootWindowViews::Close(bool force) {
140   if (!CefCurrentlyOn(TID_UI)) {
141     // Execute this method on the UI thread.
142     CefPostTask(TID_UI, base::BindOnce(&RootWindowViews::Close, this, force));
143     return;
144   }
145 
146   if (window_)
147     window_->Close(force);
148 }
149 
SetDeviceScaleFactor(float device_scale_factor)150 void RootWindowViews::SetDeviceScaleFactor(float device_scale_factor) {
151   REQUIRE_MAIN_THREAD();
152   // Windowless rendering is not supported.
153   NOTREACHED();
154 }
155 
GetDeviceScaleFactor() const156 float RootWindowViews::GetDeviceScaleFactor() const {
157   REQUIRE_MAIN_THREAD();
158   // Windowless rendering is not supported.
159   NOTREACHED();
160   return 0.0;
161 }
162 
GetBrowser() const163 CefRefPtr<CefBrowser> RootWindowViews::GetBrowser() const {
164   REQUIRE_MAIN_THREAD();
165   return browser_;
166 }
167 
GetWindowHandle() const168 ClientWindowHandle RootWindowViews::GetWindowHandle() const {
169   REQUIRE_MAIN_THREAD();
170 #if defined(OS_LINUX)
171   // ClientWindowHandle is a GtkWidget* on Linux and we don't have one of those.
172   return nullptr;
173 #else
174   if (browser_)
175     return browser_->GetHost()->GetWindowHandle();
176   return kNullWindowHandle;
177 #endif
178 }
179 
WithExtension() const180 bool RootWindowViews::WithExtension() const {
181   DCHECK(initialized_);
182   return config_->with_extension;
183 }
184 
WithControls()185 bool RootWindowViews::WithControls() {
186   DCHECK(initialized_);
187   return config_->with_controls;
188 }
189 
WithExtension()190 bool RootWindowViews::WithExtension() {
191   DCHECK(initialized_);
192   return config_->with_extension;
193 }
194 
OnExtensionsChanged(const ExtensionSet & extensions)195 void RootWindowViews::OnExtensionsChanged(const ExtensionSet& extensions) {
196   if (!CefCurrentlyOn(TID_UI)) {
197     // Execute this method on the UI thread.
198     CefPostTask(TID_UI, base::BindOnce(&RootWindowViews::OnExtensionsChanged,
199                                        this, extensions));
200     return;
201   }
202 
203   if (window_) {
204     window_->OnExtensionsChanged(extensions);
205   } else {
206     // Window may not exist yet for popups.
207     pending_extensions_ = extensions;
208   }
209 }
210 
InitiallyHidden()211 bool RootWindowViews::InitiallyHidden() {
212   CEF_REQUIRE_UI_THREAD();
213   return config_->initially_hidden;
214 }
215 
GetParentWindow()216 CefRefPtr<CefWindow> RootWindowViews::GetParentWindow() {
217   CEF_REQUIRE_UI_THREAD();
218   return config_->parent_window;
219 }
220 
GetWindowBounds()221 CefRect RootWindowViews::GetWindowBounds() {
222   CEF_REQUIRE_UI_THREAD();
223   return initial_bounds_;
224 }
225 
GetImageCache()226 scoped_refptr<ImageCache> RootWindowViews::GetImageCache() {
227   CEF_REQUIRE_UI_THREAD();
228   return image_cache_;
229 }
230 
OnViewsWindowCreated(CefRefPtr<ViewsWindow> window)231 void RootWindowViews::OnViewsWindowCreated(CefRefPtr<ViewsWindow> window) {
232   CEF_REQUIRE_UI_THREAD();
233   DCHECK(!window_);
234   window_ = window;
235   window_->SetAlwaysOnTop(config_->always_on_top);
236 
237   if (!pending_extensions_.empty()) {
238     window_->OnExtensionsChanged(pending_extensions_);
239     pending_extensions_.clear();
240   }
241 }
242 
OnViewsWindowDestroyed(CefRefPtr<ViewsWindow> window)243 void RootWindowViews::OnViewsWindowDestroyed(CefRefPtr<ViewsWindow> window) {
244   CEF_REQUIRE_UI_THREAD();
245   window_ = nullptr;
246 
247   // Continue on the main thread.
248   MAIN_POST_CLOSURE(
249       base::BindOnce(&RootWindowViews::NotifyViewsWindowDestroyed, this));
250 }
251 
OnViewsWindowActivated(CefRefPtr<ViewsWindow> window)252 void RootWindowViews::OnViewsWindowActivated(CefRefPtr<ViewsWindow> window) {
253   CEF_REQUIRE_UI_THREAD();
254 
255   // Continue on the main thread.
256   MAIN_POST_CLOSURE(
257       base::BindOnce(&RootWindowViews::NotifyViewsWindowActivated, this));
258 }
259 
GetDelegateForPopup(CefRefPtr<CefClient> client)260 ViewsWindow::Delegate* RootWindowViews::GetDelegateForPopup(
261     CefRefPtr<CefClient> client) {
262   CEF_REQUIRE_UI_THREAD();
263   // |handler| was created in RootWindowViews::InitAsPopup().
264   ClientHandlerStd* handler = static_cast<ClientHandlerStd*>(client.get());
265   RootWindowViews* root_window =
266       static_cast<RootWindowViews*>(handler->delegate());
267 
268   // Transfer some state to the child RootWindowViews.
269   root_window->image_cache_ = image_cache_;
270 
271   return root_window;
272 }
273 
CreateExtensionWindow(CefRefPtr<CefExtension> extension,const CefRect & source_bounds,CefRefPtr<CefWindow> parent_window,base::OnceClosure close_callback)274 void RootWindowViews::CreateExtensionWindow(CefRefPtr<CefExtension> extension,
275                                             const CefRect& source_bounds,
276                                             CefRefPtr<CefWindow> parent_window,
277                                             base::OnceClosure close_callback) {
278   if (!CURRENTLY_ON_MAIN_THREAD()) {
279     // Execute this method on the main thread.
280     MAIN_POST_CLOSURE(base::BindOnce(&RootWindowViews::CreateExtensionWindow,
281                                      this, extension, source_bounds,
282                                      parent_window, std::move(close_callback)));
283     return;
284   }
285 
286   delegate_->CreateExtensionWindow(extension, source_bounds, parent_window,
287                                    std::move(close_callback), false);
288 }
289 
OnTest(int test_id)290 void RootWindowViews::OnTest(int test_id) {
291   if (!CURRENTLY_ON_MAIN_THREAD()) {
292     // Execute this method on the main thread.
293     MAIN_POST_CLOSURE(base::BindOnce(&RootWindowViews::OnTest, this, test_id));
294     return;
295   }
296 
297   delegate_->OnTest(this, test_id);
298 }
299 
OnExit()300 void RootWindowViews::OnExit() {
301   if (!CURRENTLY_ON_MAIN_THREAD()) {
302     // Execute this method on the main thread.
303     MAIN_POST_CLOSURE(base::BindOnce(&RootWindowViews::OnExit, this));
304     return;
305   }
306 
307   delegate_->OnExit(this);
308 }
309 
OnBrowserCreated(CefRefPtr<CefBrowser> browser)310 void RootWindowViews::OnBrowserCreated(CefRefPtr<CefBrowser> browser) {
311   REQUIRE_MAIN_THREAD();
312   DCHECK(!browser_);
313   browser_ = browser;
314   delegate_->OnBrowserCreated(this, browser);
315 }
316 
OnBrowserClosing(CefRefPtr<CefBrowser> browser)317 void RootWindowViews::OnBrowserClosing(CefRefPtr<CefBrowser> browser) {
318   REQUIRE_MAIN_THREAD();
319   // Nothing to do here.
320 }
321 
OnBrowserClosed(CefRefPtr<CefBrowser> browser)322 void RootWindowViews::OnBrowserClosed(CefRefPtr<CefBrowser> browser) {
323   REQUIRE_MAIN_THREAD();
324   if (browser_) {
325     DCHECK_EQ(browser->GetIdentifier(), browser_->GetIdentifier());
326     browser_ = nullptr;
327   }
328 
329   client_handler_->DetachDelegate();
330   client_handler_ = nullptr;
331 
332   browser_destroyed_ = true;
333   NotifyDestroyedIfDone();
334 }
335 
OnSetAddress(const std::string & url)336 void RootWindowViews::OnSetAddress(const std::string& url) {
337   if (!CefCurrentlyOn(TID_UI)) {
338     // Execute this method on the UI thread.
339     CefPostTask(TID_UI,
340                 base::BindOnce(&RootWindowViews::OnSetAddress, this, url));
341     return;
342   }
343 
344   if (window_)
345     window_->SetAddress(url);
346 }
347 
OnSetTitle(const std::string & title)348 void RootWindowViews::OnSetTitle(const std::string& title) {
349   if (!CefCurrentlyOn(TID_UI)) {
350     // Execute this method on the UI thread.
351     CefPostTask(TID_UI,
352                 base::BindOnce(&RootWindowViews::OnSetTitle, this, title));
353     return;
354   }
355 
356   if (window_)
357     window_->SetTitle(title);
358 }
359 
OnSetFavicon(CefRefPtr<CefImage> image)360 void RootWindowViews::OnSetFavicon(CefRefPtr<CefImage> image) {
361   if (!CefCurrentlyOn(TID_UI)) {
362     // Execute this method on the UI thread.
363     CefPostTask(TID_UI,
364                 base::BindOnce(&RootWindowViews::OnSetFavicon, this, image));
365     return;
366   }
367 
368   if (window_)
369     window_->SetFavicon(image);
370 }
371 
OnSetFullscreen(bool fullscreen)372 void RootWindowViews::OnSetFullscreen(bool fullscreen) {
373   if (!CefCurrentlyOn(TID_UI)) {
374     // Execute this method on the UI thread.
375     CefPostTask(TID_UI, base::BindOnce(&RootWindowViews::OnSetFullscreen, this,
376                                        fullscreen));
377     return;
378   }
379 
380   if (window_)
381     window_->SetFullscreen(fullscreen);
382 }
383 
OnAutoResize(const CefSize & new_size)384 void RootWindowViews::OnAutoResize(const CefSize& new_size) {
385   if (!CefCurrentlyOn(TID_UI)) {
386     // Execute this method on the UI thread.
387     CefPostTask(TID_UI,
388                 base::BindOnce(&RootWindowViews::OnAutoResize, this, new_size));
389     return;
390   }
391 
392   bool has_position = false;
393   CefPoint position;
394   if (position_on_resize_) {
395     // Position the window centered on and immediately below the source.
396     const int x_offset = (initial_bounds_.width - new_size.width) / 2;
397     position.Set(initial_bounds_.x + x_offset,
398                  initial_bounds_.y + initial_bounds_.height);
399     has_position = true;
400 
401     // Don't change the window position on future resizes.
402     position_on_resize_ = false;
403   }
404 
405   if (window_) {
406     window_->SetBrowserSize(new_size, has_position, position);
407     window_->Show();
408   }
409 }
410 
OnSetLoadingState(bool isLoading,bool canGoBack,bool canGoForward)411 void RootWindowViews::OnSetLoadingState(bool isLoading,
412                                         bool canGoBack,
413                                         bool canGoForward) {
414   if (!CefCurrentlyOn(TID_UI)) {
415     // Execute this method on the UI thread.
416     CefPostTask(TID_UI,
417                 base::BindOnce(&RootWindowViews::OnSetLoadingState, this,
418                                isLoading, canGoBack, canGoForward));
419     return;
420   }
421 
422   if (window_) {
423     window_->SetLoadingState(isLoading, canGoBack, canGoForward);
424 
425     if (isLoading) {
426       // Reset to the default window icon when loading begins.
427       window_->SetFavicon(
428           delegate_->GetImageCache()->GetCachedImage("window_icon"));
429     }
430   }
431 }
432 
OnSetDraggableRegions(const std::vector<CefDraggableRegion> & regions)433 void RootWindowViews::OnSetDraggableRegions(
434     const std::vector<CefDraggableRegion>& regions) {
435   if (!CefCurrentlyOn(TID_UI)) {
436     // Execute this method on the UI thread.
437     CefPostTask(TID_UI, base::BindOnce(&RootWindowViews::OnSetDraggableRegions,
438                                        this, regions));
439     return;
440   }
441 
442   if (window_)
443     window_->SetDraggableRegions(regions);
444 }
445 
OnTakeFocus(bool next)446 void RootWindowViews::OnTakeFocus(bool next) {
447   if (!CefCurrentlyOn(TID_UI)) {
448     // Execute this method on the UI thread.
449     CefPostTask(TID_UI,
450                 base::BindOnce(&RootWindowViews::OnTakeFocus, this, next));
451     return;
452   }
453 
454   if (window_)
455     window_->TakeFocus(next);
456 }
457 
OnBeforeContextMenu(CefRefPtr<CefMenuModel> model)458 void RootWindowViews::OnBeforeContextMenu(CefRefPtr<CefMenuModel> model) {
459   CEF_REQUIRE_UI_THREAD();
460   if (window_)
461     window_->OnBeforeContextMenu(model);
462 }
463 
CreateClientHandler(const std::string & url)464 void RootWindowViews::CreateClientHandler(const std::string& url) {
465   DCHECK(!client_handler_);
466 
467   client_handler_ = new ClientHandlerStd(this, url);
468   client_handler_->set_download_favicon_images(true);
469 }
470 
InitOnUIThread(const CefBrowserSettings & settings,const std::string & startup_url,CefRefPtr<CefRequestContext> request_context)471 void RootWindowViews::InitOnUIThread(
472     const CefBrowserSettings& settings,
473     const std::string& startup_url,
474     CefRefPtr<CefRequestContext> request_context) {
475   if (!CefCurrentlyOn(TID_UI)) {
476     // Execute this method on the UI thread.
477     CefPostTask(TID_UI, base::BindOnce(&RootWindowViews::InitOnUIThread, this,
478                                        settings, startup_url, request_context));
479     return;
480   }
481 
482   image_cache_ = delegate_->GetImageCache();
483 
484   // Populate the default image cache.
485   ImageCache::ImageInfoSet image_set;
486   for (size_t i = 0U; i < base::size(kDefaultImageCache); ++i)
487     image_set.push_back(ImageCache::ImageInfo::Create2x(kDefaultImageCache[i]));
488 
489   image_cache_->LoadImages(
490       image_set, base::BindOnce(&RootWindowViews::CreateViewsWindow, this,
491                                 settings, startup_url, request_context));
492 }
493 
CreateViewsWindow(const CefBrowserSettings & settings,const std::string & startup_url,CefRefPtr<CefRequestContext> request_context,const ImageCache::ImageSet & images)494 void RootWindowViews::CreateViewsWindow(
495     const CefBrowserSettings& settings,
496     const std::string& startup_url,
497     CefRefPtr<CefRequestContext> request_context,
498     const ImageCache::ImageSet& images) {
499   CEF_REQUIRE_UI_THREAD();
500   DCHECK(!window_);
501 
502 #ifndef NDEBUG
503   // Make sure the default images loaded successfully.
504   DCHECK_EQ(images.size(), base::size(kDefaultImageCache));
505   for (size_t i = 0U; i < base::size(kDefaultImageCache); ++i) {
506     DCHECK(images[i]) << "Default image " << i << " failed to load";
507   }
508 #endif
509 
510   // Create the ViewsWindow. It will show itself after creation.
511   ViewsWindow::Create(this, client_handler_, startup_url, settings,
512                       request_context);
513 }
514 
NotifyViewsWindowDestroyed()515 void RootWindowViews::NotifyViewsWindowDestroyed() {
516   REQUIRE_MAIN_THREAD();
517   window_destroyed_ = true;
518   NotifyDestroyedIfDone();
519 }
520 
NotifyViewsWindowActivated()521 void RootWindowViews::NotifyViewsWindowActivated() {
522   REQUIRE_MAIN_THREAD();
523   delegate_->OnRootWindowActivated(this);
524 }
525 
NotifyDestroyedIfDone()526 void RootWindowViews::NotifyDestroyedIfDone() {
527   // Notify once both the window and the browser have been destroyed.
528   if (window_destroyed_ && browser_destroyed_) {
529     delegate_->OnRootWindowDestroyed(this);
530     if (!config_->close_callback.is_null())
531       std::move(config_->close_callback).Run();
532   }
533 }
534 
535 }  // namespace client
536