| // Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights |
| // reserved. Use of this source code is governed by a BSD-style license that |
| // can be found in the LICENSE file. |
| |
| #include "tests/cefclient/browser/root_window_gtk.h" |
| |
| #include <gdk/gdk.h> |
| #include <gdk/gdkx.h> |
| |
| #include <X11/Xlib.h> |
| #undef Success // Definition conflicts with cef_message_router.h |
| #undef RootWindow // Definition conflicts with root_window.h |
| |
| #include "include/base/cef_bind.h" |
| #include "include/cef_app.h" |
| #include "tests/cefclient/browser/browser_window_osr_gtk.h" |
| #include "tests/cefclient/browser/browser_window_std_gtk.h" |
| #include "tests/cefclient/browser/main_context.h" |
| #include "tests/cefclient/browser/resource.h" |
| #include "tests/cefclient/browser/temp_window.h" |
| #include "tests/cefclient/browser/util_gtk.h" |
| #include "tests/cefclient/browser/window_test_runner_gtk.h" |
| #include "tests/shared/browser/main_message_loop.h" |
| #include "tests/shared/common/client_switches.h" |
| |
| namespace client { |
| |
| namespace { |
| |
| const char kMenuIdKey[] = "menu_id"; |
| |
| bool IsWindowMaximized(GtkWindow* window) { |
| GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window)); |
| gint state = gdk_window_get_state(gdk_window); |
| return (state & GDK_WINDOW_STATE_MAXIMIZED) ? true : false; |
| } |
| |
| void MinimizeWindow(GtkWindow* window) { |
| // Unmaximize the window before minimizing so restore behaves correctly. |
| if (IsWindowMaximized(window)) |
| gtk_window_unmaximize(window); |
| |
| gtk_window_iconify(window); |
| } |
| |
| void MaximizeWindow(GtkWindow* window) { |
| gtk_window_maximize(window); |
| } |
| |
| } // namespace |
| |
| RootWindowGtk::RootWindowGtk() |
| : with_controls_(false), |
| always_on_top_(false), |
| with_osr_(false), |
| with_extension_(false), |
| is_popup_(false), |
| initialized_(false), |
| window_(nullptr), |
| back_button_(nullptr), |
| forward_button_(nullptr), |
| reload_button_(nullptr), |
| stop_button_(nullptr), |
| url_entry_(nullptr), |
| toolbar_height_(0), |
| menubar_height_(0), |
| window_destroyed_(false), |
| browser_destroyed_(false), |
| force_close_(false), |
| is_closing_(false) {} |
| |
| RootWindowGtk::~RootWindowGtk() { |
| REQUIRE_MAIN_THREAD(); |
| |
| // The window and browser should already have been destroyed. |
| DCHECK(window_destroyed_); |
| DCHECK(browser_destroyed_); |
| } |
| |
| void RootWindowGtk::Init(RootWindow::Delegate* delegate, |
| const RootWindowConfig& config, |
| const CefBrowserSettings& settings) { |
| DCHECK(delegate); |
| DCHECK(!initialized_); |
| |
| delegate_ = delegate; |
| with_controls_ = config.with_controls; |
| always_on_top_ = config.always_on_top; |
| with_osr_ = config.with_osr; |
| with_extension_ = config.with_extension; |
| start_rect_ = config.bounds; |
| |
| CreateBrowserWindow(config.url); |
| |
| initialized_ = true; |
| |
| // Create the native root window on the main thread. |
| if (CURRENTLY_ON_MAIN_THREAD()) { |
| CreateRootWindow(settings, config.initially_hidden); |
| } else { |
| MAIN_POST_CLOSURE(base::Bind(&RootWindowGtk::CreateRootWindow, this, |
| settings, config.initially_hidden)); |
| } |
| } |
| |
| void RootWindowGtk::InitAsPopup(RootWindow::Delegate* delegate, |
| bool with_controls, |
| bool with_osr, |
| const CefPopupFeatures& popupFeatures, |
| CefWindowInfo& windowInfo, |
| CefRefPtr<CefClient>& client, |
| CefBrowserSettings& settings) { |
| DCHECK(delegate); |
| DCHECK(!initialized_); |
| |
| delegate_ = delegate; |
| with_controls_ = with_controls; |
| with_osr_ = with_osr; |
| is_popup_ = true; |
| |
| if (popupFeatures.xSet) |
| start_rect_.x = popupFeatures.x; |
| if (popupFeatures.ySet) |
| start_rect_.y = popupFeatures.y; |
| if (popupFeatures.widthSet) |
| start_rect_.width = popupFeatures.width; |
| if (popupFeatures.heightSet) |
| start_rect_.height = popupFeatures.height; |
| |
| CreateBrowserWindow(std::string()); |
| |
| initialized_ = true; |
| |
| // The new popup is initially parented to a temporary window. The native root |
| // window will be created after the browser is created and the popup window |
| // will be re-parented to it at that time. |
| browser_window_->GetPopupConfig(TempWindow::GetWindowHandle(), windowInfo, |
| client, settings); |
| } |
| |
| void RootWindowGtk::Show(ShowMode mode) { |
| REQUIRE_MAIN_THREAD(); |
| |
| if (!window_) |
| return; |
| |
| ScopedGdkThreadsEnter scoped_gdk_threads; |
| |
| // Show the GTK window. |
| gtk_widget_show_all(window_); |
| |
| if (mode == ShowMinimized) |
| MinimizeWindow(GTK_WINDOW(window_)); |
| else if (mode == ShowMaximized) |
| MaximizeWindow(GTK_WINDOW(window_)); |
| |
| // Flush the display to make sure the underlying X11 window gets created |
| // immediately. |
| GdkWindow* gdk_window = gtk_widget_get_window(window_); |
| GdkDisplay* display = gdk_window_get_display(gdk_window); |
| gdk_display_flush(display); |
| } |
| |
| void RootWindowGtk::Hide() { |
| REQUIRE_MAIN_THREAD(); |
| |
| ScopedGdkThreadsEnter scoped_gdk_threads; |
| |
| if (window_) |
| gtk_widget_hide(window_); |
| } |
| |
| void RootWindowGtk::SetBounds(int x, int y, size_t width, size_t height) { |
| REQUIRE_MAIN_THREAD(); |
| |
| if (!window_) |
| return; |
| |
| ScopedGdkThreadsEnter scoped_gdk_threads; |
| |
| GtkWindow* window = GTK_WINDOW(window_); |
| GdkWindow* gdk_window = gtk_widget_get_window(window_); |
| |
| // Make sure the window isn't minimized or maximized. |
| if (IsWindowMaximized(window)) |
| gtk_window_unmaximize(window); |
| else |
| gtk_window_present(window); |
| |
| gdk_window_move_resize(gdk_window, x, y, width, height); |
| } |
| |
| void RootWindowGtk::Close(bool force) { |
| REQUIRE_MAIN_THREAD(); |
| |
| if (window_) { |
| ScopedGdkThreadsEnter scoped_gdk_threads; |
| |
| if (force) { |
| NotifyForceClose(); |
| } |
| gtk_widget_destroy(window_); |
| } |
| } |
| |
| void RootWindowGtk::SetDeviceScaleFactor(float device_scale_factor) { |
| REQUIRE_MAIN_THREAD(); |
| |
| if (browser_window_ && with_osr_) |
| browser_window_->SetDeviceScaleFactor(device_scale_factor); |
| } |
| |
| float RootWindowGtk::GetDeviceScaleFactor() const { |
| REQUIRE_MAIN_THREAD(); |
| |
| if (browser_window_ && with_osr_) |
| return browser_window_->GetDeviceScaleFactor(); |
| |
| NOTREACHED(); |
| return 0.0f; |
| } |
| |
| CefRefPtr<CefBrowser> RootWindowGtk::GetBrowser() const { |
| REQUIRE_MAIN_THREAD(); |
| |
| if (browser_window_) |
| return browser_window_->GetBrowser(); |
| return nullptr; |
| } |
| |
| ClientWindowHandle RootWindowGtk::GetWindowHandle() const { |
| REQUIRE_MAIN_THREAD(); |
| return window_; |
| } |
| |
| bool RootWindowGtk::WithWindowlessRendering() const { |
| REQUIRE_MAIN_THREAD(); |
| return with_osr_; |
| } |
| |
| bool RootWindowGtk::WithExtension() const { |
| REQUIRE_MAIN_THREAD(); |
| return with_extension_; |
| } |
| |
| void RootWindowGtk::CreateBrowserWindow(const std::string& startup_url) { |
| if (with_osr_) { |
| OsrRendererSettings settings = {}; |
| MainContext::Get()->PopulateOsrSettings(&settings); |
| browser_window_.reset(new BrowserWindowOsrGtk(this, startup_url, settings)); |
| } else { |
| browser_window_.reset(new BrowserWindowStdGtk(this, startup_url)); |
| } |
| } |
| |
| void RootWindowGtk::CreateRootWindow(const CefBrowserSettings& settings, |
| bool initially_hidden) { |
| REQUIRE_MAIN_THREAD(); |
| DCHECK(!window_); |
| |
| // TODO(port): If no x,y position is specified the window will always appear |
| // in the upper-left corner. Maybe there's a better default place to put it? |
| int x = start_rect_.x; |
| int y = start_rect_.y; |
| int width, height; |
| if (start_rect_.IsEmpty()) { |
| // TODO(port): Also, maybe there's a better way to choose the default size. |
| width = 800; |
| height = 600; |
| } else { |
| width = start_rect_.width; |
| height = start_rect_.height; |
| } |
| |
| ScopedGdkThreadsEnter scoped_gdk_threads; |
| |
| window_ = gtk_window_new(GTK_WINDOW_TOPLEVEL); |
| CHECK(window_); |
| |
| if (always_on_top_) { |
| gtk_window_set_keep_above(GTK_WINDOW(window_), TRUE); |
| } |
| |
| gtk_window_set_default_size(GTK_WINDOW(window_), width, height); |
| g_signal_connect(G_OBJECT(window_), "focus-in-event", |
| G_CALLBACK(&RootWindowGtk::WindowFocusIn), this); |
| g_signal_connect(G_OBJECT(window_), "window-state-event", |
| G_CALLBACK(&RootWindowGtk::WindowState), this); |
| g_signal_connect(G_OBJECT(window_), "configure-event", |
| G_CALLBACK(&RootWindowGtk::WindowConfigure), this); |
| g_signal_connect(G_OBJECT(window_), "destroy", |
| G_CALLBACK(&RootWindowGtk::WindowDestroy), this); |
| g_signal_connect(G_OBJECT(window_), "delete_event", |
| G_CALLBACK(&RootWindowGtk::WindowDelete), this); |
| |
| const cef_color_t background_color = MainContext::Get()->GetBackgroundColor(); |
| GdkColor color = {0}; |
| color.red = CefColorGetR(background_color) * 65535 / 255; |
| color.green = CefColorGetG(background_color) * 65535 / 255; |
| color.blue = CefColorGetB(background_color) * 65535 / 255; |
| gtk_widget_modify_bg(window_, GTK_STATE_NORMAL, &color); |
| |
| GtkWidget* vbox = gtk_vbox_new(FALSE, 0); |
| g_signal_connect(vbox, "size-allocate", |
| G_CALLBACK(&RootWindowGtk::VboxSizeAllocated), this); |
| gtk_container_add(GTK_CONTAINER(window_), vbox); |
| |
| if (with_controls_) { |
| GtkWidget* menu_bar = CreateMenuBar(); |
| g_signal_connect(menu_bar, "size-allocate", |
| G_CALLBACK(&RootWindowGtk::MenubarSizeAllocated), this); |
| |
| gtk_box_pack_start(GTK_BOX(vbox), menu_bar, FALSE, FALSE, 0); |
| |
| GtkWidget* toolbar = gtk_toolbar_new(); |
| // Turn off the labels on the toolbar buttons. |
| gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS); |
| g_signal_connect(toolbar, "size-allocate", |
| G_CALLBACK(&RootWindowGtk::ToolbarSizeAllocated), this); |
| |
| back_button_ = gtk_tool_button_new_from_stock(GTK_STOCK_GO_BACK); |
| g_signal_connect(back_button_, "clicked", |
| G_CALLBACK(&RootWindowGtk::BackButtonClicked), this); |
| gtk_toolbar_insert(GTK_TOOLBAR(toolbar), back_button_, -1 /* append */); |
| |
| forward_button_ = gtk_tool_button_new_from_stock(GTK_STOCK_GO_FORWARD); |
| g_signal_connect(forward_button_, "clicked", |
| G_CALLBACK(&RootWindowGtk::ForwardButtonClicked), this); |
| gtk_toolbar_insert(GTK_TOOLBAR(toolbar), forward_button_, -1 /* append */); |
| |
| reload_button_ = gtk_tool_button_new_from_stock(GTK_STOCK_REFRESH); |
| g_signal_connect(reload_button_, "clicked", |
| G_CALLBACK(&RootWindowGtk::ReloadButtonClicked), this); |
| gtk_toolbar_insert(GTK_TOOLBAR(toolbar), reload_button_, -1 /* append */); |
| |
| stop_button_ = gtk_tool_button_new_from_stock(GTK_STOCK_STOP); |
| g_signal_connect(stop_button_, "clicked", |
| G_CALLBACK(&RootWindowGtk::StopButtonClicked), this); |
| gtk_toolbar_insert(GTK_TOOLBAR(toolbar), stop_button_, -1 /* append */); |
| |
| url_entry_ = gtk_entry_new(); |
| g_signal_connect(url_entry_, "activate", |
| G_CALLBACK(&RootWindowGtk::URLEntryActivate), this); |
| g_signal_connect(url_entry_, "button-press-event", |
| G_CALLBACK(&RootWindowGtk::URLEntryButtonPress), this); |
| |
| GtkToolItem* tool_item = gtk_tool_item_new(); |
| gtk_container_add(GTK_CONTAINER(tool_item), url_entry_); |
| gtk_tool_item_set_expand(tool_item, TRUE); |
| gtk_toolbar_insert(GTK_TOOLBAR(toolbar), tool_item, -1); // append |
| |
| gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0); |
| } |
| |
| // Realize (show) the GTK widget. This must be done before the browser is |
| // created because the underlying X11 Window is required. |browser_bounds_| |
| // will be set at this point based on the GTK *SizeAllocated signal callbacks. |
| Show(ShowNormal); |
| |
| // Most window managers ignore requests for initial window positions (instead |
| // using a user-defined placement algorithm) and honor requests after the |
| // window has already been shown. |
| gtk_window_move(GTK_WINDOW(window_), x, y); |
| |
| // Windowed browsers are parented to the X11 Window underlying the GtkWindow* |
| // and must be sized manually. The OSR GTK widget, on the other hand, can be |
| // added to the Vbox container for automatic layout-based sizing. |
| GtkWidget* parent = with_osr_ ? vbox : window_; |
| |
| // Set the Display associated with the browser. |
| ::Display* xdisplay = GDK_WINDOW_XDISPLAY(gtk_widget_get_window(window_)); |
| CHECK(xdisplay); |
| if (with_osr_) { |
| static_cast<BrowserWindowOsrGtk*>(browser_window_.get()) |
| ->set_xdisplay(xdisplay); |
| } else { |
| static_cast<BrowserWindowStdGtk*>(browser_window_.get()) |
| ->set_xdisplay(xdisplay); |
| } |
| |
| if (!is_popup_) { |
| // Create the browser window. |
| browser_window_->CreateBrowser(parent, browser_bounds_, settings, nullptr, |
| delegate_->GetRequestContext(this)); |
| } else { |
| // With popups we already have a browser window. Parent the browser window |
| // to the root window and show it in the correct location. |
| browser_window_->ShowPopup(parent, browser_bounds_.x, browser_bounds_.y, |
| browser_bounds_.width, browser_bounds_.height); |
| } |
| } |
| |
| void RootWindowGtk::OnBrowserCreated(CefRefPtr<CefBrowser> browser) { |
| REQUIRE_MAIN_THREAD(); |
| |
| // For popup browsers create the root window once the browser has been |
| // created. |
| if (is_popup_) |
| CreateRootWindow(CefBrowserSettings(), false); |
| |
| delegate_->OnBrowserCreated(this, browser); |
| } |
| |
| void RootWindowGtk::OnBrowserWindowClosing() { |
| if (!CefCurrentlyOn(TID_UI)) { |
| CefPostTask(TID_UI, |
| base::Bind(&RootWindowGtk::OnBrowserWindowClosing, this)); |
| return; |
| } |
| |
| is_closing_ = true; |
| } |
| |
| void RootWindowGtk::OnBrowserWindowDestroyed() { |
| REQUIRE_MAIN_THREAD(); |
| |
| browser_window_.reset(); |
| |
| if (!window_destroyed_) { |
| // The browser was destroyed first. This could be due to the use of |
| // off-screen rendering or execution of JavaScript window.close(). |
| // Close the RootWindow. |
| Close(true); |
| } |
| |
| NotifyDestroyedIfDone(false, true); |
| } |
| |
| void RootWindowGtk::OnSetAddress(const std::string& url) { |
| REQUIRE_MAIN_THREAD(); |
| |
| if (url_entry_) { |
| ScopedGdkThreadsEnter scoped_gdk_threads; |
| |
| std::string urlStr(url); |
| gtk_entry_set_text(GTK_ENTRY(url_entry_), urlStr.c_str()); |
| } |
| } |
| |
| void RootWindowGtk::OnSetTitle(const std::string& title) { |
| REQUIRE_MAIN_THREAD(); |
| |
| if (window_) { |
| ScopedGdkThreadsEnter scoped_gdk_threads; |
| |
| std::string titleStr(title); |
| gtk_window_set_title(GTK_WINDOW(window_), titleStr.c_str()); |
| } |
| } |
| |
| void RootWindowGtk::OnSetFullscreen(bool fullscreen) { |
| REQUIRE_MAIN_THREAD(); |
| |
| CefRefPtr<CefBrowser> browser = GetBrowser(); |
| if (browser) { |
| scoped_ptr<window_test::WindowTestRunnerGtk> test_runner( |
| new window_test::WindowTestRunnerGtk()); |
| if (fullscreen) |
| test_runner->Maximize(browser); |
| else |
| test_runner->Restore(browser); |
| } |
| } |
| |
| void RootWindowGtk::OnAutoResize(const CefSize& new_size) { |
| REQUIRE_MAIN_THREAD(); |
| |
| if (!window_) |
| return; |
| |
| ScopedGdkThreadsEnter scoped_gdk_threads; |
| |
| GtkWindow* window = GTK_WINDOW(window_); |
| GdkWindow* gdk_window = gtk_widget_get_window(window_); |
| |
| // Make sure the window isn't minimized or maximized. |
| if (IsWindowMaximized(window)) |
| gtk_window_unmaximize(window); |
| else |
| gtk_window_present(window); |
| |
| gdk_window_resize(gdk_window, new_size.width, new_size.height); |
| } |
| |
| void RootWindowGtk::OnSetLoadingState(bool isLoading, |
| bool canGoBack, |
| bool canGoForward) { |
| REQUIRE_MAIN_THREAD(); |
| |
| if (with_controls_) { |
| ScopedGdkThreadsEnter scoped_gdk_threads; |
| |
| gtk_widget_set_sensitive(GTK_WIDGET(stop_button_), isLoading); |
| gtk_widget_set_sensitive(GTK_WIDGET(reload_button_), !isLoading); |
| gtk_widget_set_sensitive(GTK_WIDGET(back_button_), canGoBack); |
| gtk_widget_set_sensitive(GTK_WIDGET(forward_button_), canGoForward); |
| } |
| } |
| |
| void RootWindowGtk::OnSetDraggableRegions( |
| const std::vector<CefDraggableRegion>& regions) { |
| REQUIRE_MAIN_THREAD(); |
| // TODO(cef): Implement support for draggable regions on this platform. |
| } |
| |
| void RootWindowGtk::NotifyMoveOrResizeStarted() { |
| if (!CURRENTLY_ON_MAIN_THREAD()) { |
| MAIN_POST_CLOSURE( |
| base::Bind(&RootWindowGtk::NotifyMoveOrResizeStarted, this)); |
| return; |
| } |
| |
| // Called when size, position or stack order changes. |
| CefRefPtr<CefBrowser> browser = GetBrowser(); |
| if (browser.get()) { |
| // Notify the browser of move/resize events so that: |
| // - Popup windows are displayed in the correct location and dismissed |
| // when the window moves. |
| // - Drag&drop areas are updated accordingly. |
| browser->GetHost()->NotifyMoveOrResizeStarted(); |
| } |
| } |
| |
| void RootWindowGtk::NotifySetFocus() { |
| if (!CURRENTLY_ON_MAIN_THREAD()) { |
| MAIN_POST_CLOSURE(base::Bind(&RootWindowGtk::NotifySetFocus, this)); |
| return; |
| } |
| |
| if (!browser_window_.get()) |
| return; |
| |
| browser_window_->SetFocus(true); |
| delegate_->OnRootWindowActivated(this); |
| } |
| |
| void RootWindowGtk::NotifyVisibilityChange(bool show) { |
| if (!CURRENTLY_ON_MAIN_THREAD()) { |
| MAIN_POST_CLOSURE( |
| base::Bind(&RootWindowGtk::NotifyVisibilityChange, this, show)); |
| return; |
| } |
| |
| if (!browser_window_.get()) |
| return; |
| |
| if (show) |
| browser_window_->Show(); |
| else |
| browser_window_->Hide(); |
| } |
| |
| void RootWindowGtk::NotifyMenuBarHeight(int height) { |
| if (!CURRENTLY_ON_MAIN_THREAD()) { |
| MAIN_POST_CLOSURE( |
| base::Bind(&RootWindowGtk::NotifyMenuBarHeight, this, height)); |
| return; |
| } |
| |
| menubar_height_ = height; |
| } |
| |
| void RootWindowGtk::NotifyContentBounds(int x, int y, int width, int height) { |
| if (!CURRENTLY_ON_MAIN_THREAD()) { |
| MAIN_POST_CLOSURE(base::Bind(&RootWindowGtk::NotifyContentBounds, this, x, |
| y, width, height)); |
| return; |
| } |
| |
| // Offset browser positioning by any controls that will appear in the client |
| // area. |
| const int ux_height = toolbar_height_ + menubar_height_; |
| const int browser_x = x; |
| const int browser_y = y + ux_height; |
| const int browser_width = width; |
| const int browser_height = height - ux_height; |
| |
| // Size the browser window to match the GTK widget. |
| browser_bounds_ = |
| CefRect(browser_x, browser_y, browser_width, browser_height); |
| if (browser_window_.get()) { |
| browser_window_->SetBounds(browser_x, browser_y, browser_width, |
| browser_height); |
| } |
| } |
| |
| void RootWindowGtk::NotifyLoadURL(const std::string& url) { |
| if (!CURRENTLY_ON_MAIN_THREAD()) { |
| MAIN_POST_CLOSURE(base::Bind(&RootWindowGtk::NotifyLoadURL, this, url)); |
| return; |
| } |
| |
| CefRefPtr<CefBrowser> browser = GetBrowser(); |
| if (browser.get()) { |
| browser->GetMainFrame()->LoadURL(url); |
| } |
| } |
| |
| void RootWindowGtk::NotifyButtonClicked(int id) { |
| if (!CURRENTLY_ON_MAIN_THREAD()) { |
| MAIN_POST_CLOSURE( |
| base::Bind(&RootWindowGtk::NotifyButtonClicked, this, id)); |
| return; |
| } |
| |
| CefRefPtr<CefBrowser> browser = GetBrowser(); |
| if (!browser.get()) |
| return; |
| |
| switch (id) { |
| case IDC_NAV_BACK: |
| browser->GoBack(); |
| break; |
| case IDC_NAV_FORWARD: |
| browser->GoForward(); |
| break; |
| case IDC_NAV_RELOAD: |
| browser->Reload(); |
| break; |
| case IDC_NAV_STOP: |
| browser->StopLoad(); |
| break; |
| default: |
| NOTREACHED() << "id=" << id; |
| } |
| } |
| |
| void RootWindowGtk::NotifyMenuItem(int id) { |
| if (!CURRENTLY_ON_MAIN_THREAD()) { |
| MAIN_POST_CLOSURE(base::Bind(&RootWindowGtk::NotifyMenuItem, this, id)); |
| return; |
| } |
| |
| // Run the test. |
| if (delegate_) |
| delegate_->OnTest(this, id); |
| } |
| |
| void RootWindowGtk::NotifyForceClose() { |
| if (!CefCurrentlyOn(TID_UI)) { |
| CefPostTask(TID_UI, base::Bind(&RootWindowGtk::NotifyForceClose, this)); |
| return; |
| } |
| |
| force_close_ = true; |
| } |
| |
| void RootWindowGtk::NotifyCloseBrowser() { |
| if (!CURRENTLY_ON_MAIN_THREAD()) { |
| MAIN_POST_CLOSURE(base::Bind(&RootWindowGtk::NotifyCloseBrowser, this)); |
| return; |
| } |
| |
| CefRefPtr<CefBrowser> browser = GetBrowser(); |
| if (browser) { |
| browser->GetHost()->CloseBrowser(false); |
| } |
| } |
| |
| void RootWindowGtk::NotifyDestroyedIfDone(bool window_destroyed, |
| bool browser_destroyed) { |
| // Each call will to this method will set only one state flag. |
| DCHECK_EQ(1, window_destroyed + browser_destroyed); |
| |
| if (!CURRENTLY_ON_MAIN_THREAD()) { |
| MAIN_POST_CLOSURE(base::Bind(&RootWindowGtk::NotifyDestroyedIfDone, this, |
| window_destroyed, browser_destroyed)); |
| return; |
| } |
| |
| if (window_destroyed) |
| window_destroyed_ = true; |
| if (browser_destroyed) |
| browser_destroyed_ = true; |
| |
| // Notify once both the window and the browser have been destroyed. |
| if (window_destroyed_ && browser_destroyed_) |
| delegate_->OnRootWindowDestroyed(this); |
| } |
| |
| // static |
| gboolean RootWindowGtk::WindowFocusIn(GtkWidget* widget, |
| GdkEventFocus* event, |
| RootWindowGtk* self) { |
| CEF_REQUIRE_UI_THREAD(); |
| |
| if (event->in) { |
| self->NotifySetFocus(); |
| |
| // Return true for a windowed browser so that focus is not passed to GTK. |
| return self->with_osr_ ? FALSE : TRUE; |
| } |
| |
| return FALSE; |
| } |
| |
| // static |
| gboolean RootWindowGtk::WindowState(GtkWidget* widget, |
| GdkEventWindowState* event, |
| RootWindowGtk* self) { |
| CEF_REQUIRE_UI_THREAD(); |
| |
| // Called when the root window is iconified or restored. Hide the browser |
| // window when the root window is iconified to reduce resource usage. |
| if (event->changed_mask & GDK_WINDOW_STATE_ICONIFIED) { |
| self->NotifyVisibilityChange( |
| !(event->new_window_state & GDK_WINDOW_STATE_ICONIFIED)); |
| } |
| |
| return TRUE; |
| } |
| |
| // static |
| gboolean RootWindowGtk::WindowConfigure(GtkWindow* window, |
| GdkEvent* event, |
| RootWindowGtk* self) { |
| CEF_REQUIRE_UI_THREAD(); |
| self->NotifyMoveOrResizeStarted(); |
| return FALSE; // Don't stop this message. |
| } |
| |
| // static |
| void RootWindowGtk::WindowDestroy(GtkWidget* widget, RootWindowGtk* self) { |
| // May be called on the main thread or the UI thread. |
| self->NotifyDestroyedIfDone(true, false); |
| } |
| |
| // static |
| gboolean RootWindowGtk::WindowDelete(GtkWidget* widget, |
| GdkEvent* event, |
| RootWindowGtk* self) { |
| CEF_REQUIRE_UI_THREAD(); |
| |
| // Called to query whether the root window should be closed. |
| if (self->force_close_) |
| return FALSE; // Allow the close. |
| |
| if (!self->is_closing_) { |
| // Notify the browser window that we would like to close it. This |
| // will result in a call to ClientHandler::DoClose() if the |
| // JavaScript 'onbeforeunload' event handler allows it. |
| self->NotifyCloseBrowser(); |
| |
| // Cancel the close. |
| return TRUE; |
| } |
| |
| // Allow the close. |
| return FALSE; |
| } |
| |
| // static |
| void RootWindowGtk::VboxSizeAllocated(GtkWidget* widget, |
| GtkAllocation* allocation, |
| RootWindowGtk* self) { |
| // May be called on the main thread and the UI thread. |
| self->NotifyContentBounds(allocation->x, allocation->y, allocation->width, |
| allocation->height); |
| } |
| |
| // static |
| void RootWindowGtk::MenubarSizeAllocated(GtkWidget* widget, |
| GtkAllocation* allocation, |
| RootWindowGtk* self) { |
| // May be called on the main thread and the UI thread. |
| self->NotifyMenuBarHeight(allocation->height); |
| } |
| |
| // static |
| gboolean RootWindowGtk::MenuItemActivated(GtkWidget* widget, |
| RootWindowGtk* self) { |
| CEF_REQUIRE_UI_THREAD(); |
| |
| // Retrieve the menu ID set in AddMenuEntry. |
| int id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), kMenuIdKey)); |
| self->NotifyMenuItem(id); |
| |
| return FALSE; // Don't stop this message. |
| } |
| |
| // static |
| void RootWindowGtk::ToolbarSizeAllocated(GtkWidget* widget, |
| GtkAllocation* allocation, |
| RootWindowGtk* self) { |
| self->toolbar_height_ = allocation->height; |
| } |
| |
| // static |
| void RootWindowGtk::BackButtonClicked(GtkButton* button, RootWindowGtk* self) { |
| CEF_REQUIRE_UI_THREAD(); |
| self->NotifyButtonClicked(IDC_NAV_BACK); |
| } |
| |
| // static |
| void RootWindowGtk::ForwardButtonClicked(GtkButton* button, |
| RootWindowGtk* self) { |
| CEF_REQUIRE_UI_THREAD(); |
| self->NotifyButtonClicked(IDC_NAV_FORWARD); |
| } |
| |
| // static |
| void RootWindowGtk::StopButtonClicked(GtkButton* button, RootWindowGtk* self) { |
| CEF_REQUIRE_UI_THREAD(); |
| self->NotifyButtonClicked(IDC_NAV_STOP); |
| } |
| |
| // static |
| void RootWindowGtk::ReloadButtonClicked(GtkButton* button, |
| RootWindowGtk* self) { |
| CEF_REQUIRE_UI_THREAD(); |
| self->NotifyButtonClicked(IDC_NAV_RELOAD); |
| } |
| |
| // static |
| void RootWindowGtk::URLEntryActivate(GtkEntry* entry, RootWindowGtk* self) { |
| CEF_REQUIRE_UI_THREAD(); |
| const gchar* url = gtk_entry_get_text(entry); |
| self->NotifyLoadURL(std::string(url)); |
| } |
| |
| // static |
| gboolean RootWindowGtk::URLEntryButtonPress(GtkWidget* widget, |
| GdkEventButton* event, |
| RootWindowGtk* self) { |
| CEF_REQUIRE_UI_THREAD(); |
| |
| // Give focus to the GTK window. This is a work-around for bad focus-related |
| // interaction between the root window managed by GTK and the browser managed |
| // by X11. |
| GtkWidget* window = gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW); |
| GdkWindow* gdk_window = gtk_widget_get_window(window); |
| ::Display* xdisplay = GDK_WINDOW_XDISPLAY(gdk_window); |
| ::Window xwindow = GDK_WINDOW_XID(gdk_window); |
| |
| // Retrieve the atoms required by the below XSendEvent call. |
| const char* kAtoms[] = {"WM_PROTOCOLS", "WM_TAKE_FOCUS"}; |
| Atom atoms[2]; |
| int result = |
| XInternAtoms(xdisplay, const_cast<char**>(kAtoms), 2, false, atoms); |
| if (!result) |
| NOTREACHED(); |
| |
| XEvent e; |
| e.type = ClientMessage; |
| e.xany.display = xdisplay; |
| e.xany.window = xwindow; |
| e.xclient.format = 32; |
| e.xclient.message_type = atoms[0]; |
| e.xclient.data.l[0] = atoms[1]; |
| e.xclient.data.l[1] = CurrentTime; |
| e.xclient.data.l[2] = 0; |
| e.xclient.data.l[3] = 0; |
| e.xclient.data.l[4] = 0; |
| |
| XSendEvent(xdisplay, xwindow, false, 0, &e); |
| |
| return FALSE; |
| } |
| |
| GtkWidget* RootWindowGtk::CreateMenuBar() { |
| GtkWidget* menu_bar = gtk_menu_bar_new(); |
| |
| // Create the test menu. |
| GtkWidget* test_menu = CreateMenu(menu_bar, "Tests"); |
| AddMenuEntry(test_menu, "Get Source", ID_TESTS_GETSOURCE); |
| AddMenuEntry(test_menu, "Get Text", ID_TESTS_GETTEXT); |
| AddMenuEntry(test_menu, "New Window", ID_TESTS_WINDOW_NEW); |
| AddMenuEntry(test_menu, "Popup Window", ID_TESTS_WINDOW_POPUP); |
| AddMenuEntry(test_menu, "Request", ID_TESTS_REQUEST); |
| AddMenuEntry(test_menu, "Plugin Info", ID_TESTS_PLUGIN_INFO); |
| AddMenuEntry(test_menu, "Zoom In", ID_TESTS_ZOOM_IN); |
| AddMenuEntry(test_menu, "Zoom Out", ID_TESTS_ZOOM_OUT); |
| AddMenuEntry(test_menu, "Zoom Reset", ID_TESTS_ZOOM_RESET); |
| if (with_osr_) { |
| AddMenuEntry(test_menu, "Set FPS", ID_TESTS_OSR_FPS); |
| AddMenuEntry(test_menu, "Set Scale Factor", ID_TESTS_OSR_DSF); |
| } |
| AddMenuEntry(test_menu, "Begin Tracing", ID_TESTS_TRACING_BEGIN); |
| AddMenuEntry(test_menu, "End Tracing", ID_TESTS_TRACING_END); |
| AddMenuEntry(test_menu, "Print", ID_TESTS_PRINT); |
| AddMenuEntry(test_menu, "Print to PDF", ID_TESTS_PRINT_TO_PDF); |
| AddMenuEntry(test_menu, "Mute Audio", ID_TESTS_MUTE_AUDIO); |
| AddMenuEntry(test_menu, "Unmute Audio", ID_TESTS_UNMUTE_AUDIO); |
| AddMenuEntry(test_menu, "Other Tests", ID_TESTS_OTHER_TESTS); |
| |
| return menu_bar; |
| } |
| |
| GtkWidget* RootWindowGtk::CreateMenu(GtkWidget* menu_bar, const char* text) { |
| GtkWidget* menu_widget = gtk_menu_new(); |
| GtkWidget* menu_header = gtk_menu_item_new_with_label(text); |
| gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_header), menu_widget); |
| gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), menu_header); |
| return menu_widget; |
| } |
| |
| GtkWidget* RootWindowGtk::AddMenuEntry(GtkWidget* menu_widget, |
| const char* text, |
| int id) { |
| GtkWidget* entry = gtk_menu_item_new_with_label(text); |
| g_signal_connect(entry, "activate", |
| G_CALLBACK(&RootWindowGtk::MenuItemActivated), this); |
| |
| // Set the menu ID that will be retrieved in MenuItemActivated. |
| g_object_set_data(G_OBJECT(entry), kMenuIdKey, GINT_TO_POINTER(id)); |
| |
| gtk_menu_shell_append(GTK_MENU_SHELL(menu_widget), entry); |
| return entry; |
| } |
| |
| } // namespace client |