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