12385ea399aae016c0806a4f9ef3c9cfe3d2a39dfBen Murdoch// Copyright 2013 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
52385ea399aae016c0806a4f9ef3c9cfe3d2a39dfBen Murdoch#include "chrome/browser/ui/gtk/apps/native_app_window_gtk.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch#include <gdk/gdkx.h>
8ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch#include <vector>
9ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
10ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch#include "base/message_loop/message_pump_gtk.h"
11868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/profiles/profile.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/gtk/extensions/extension_keybinding_registry_gtk.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/gtk/gtk_util.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/gtk/gtk_window_util.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/web_applications/web_app.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/render_view_host.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/render_widget_host_view.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/web_contents.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/web_contents_view.h"
21f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "extensions/common/extension.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/x/active_window_watcher_x.h"
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/gfx/gtk_util.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/image/image.h"
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/rect.h"
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
27eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochusing apps::ShellWindow;
28eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The timeout in milliseconds before we'll get the true window position with
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// gtk_window_get_position() after the last GTK configure-event signal.
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kDebounceTimeoutMilliseconds = 100;
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
35ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochconst char* kAtomsToCache[] = {
36ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  "_NET_WM_STATE",
37ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  "_NET_WM_STATE_HIDDEN",
38ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  NULL
39ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch};
40ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)NativeAppWindowGtk::NativeAppWindowGtk(ShellWindow* shell_window,
442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                       const ShellWindow::CreateParams& params)
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : shell_window_(shell_window),
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      window_(NULL),
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      state_(GDK_WINDOW_STATE_WITHDRAWN),
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      is_active_(false),
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      content_thinks_its_fullscreen_(false),
501e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      maximize_pending_(false),
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      frameless_(params.frame == ShellWindow::FRAME_NONE),
524e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      always_on_top_(params.always_on_top),
53ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      frame_cursor_(NULL),
54ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      atom_cache_(base::MessagePumpGtk::GetDefaultXDisplay(), kAtomsToCache),
55ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      is_x_event_listened_(false) {
564e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  Observe(web_contents());
574e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  window_ = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::NativeView native_view =
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      web_contents()->GetView()->GetNativeView();
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gtk_container_add(GTK_CONTAINER(window_), native_view);
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (params.bounds.x() != INT_MIN && params.bounds.y() != INT_MIN)
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gtk_window_move(window_, params.bounds.x(), params.bounds.y());
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // This is done to avoid a WM "feature" where setting the window size to
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the monitor size causes the WM to set the EWMH for full screen mode.
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int win_height = params.bounds.height();
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (frameless_ &&
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      gtk_window_util::BoundsMatchMonitorSize(window_, params.bounds)) {
722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    win_height -= 1;
732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  gtk_window_set_default_size(window_, params.bounds.width(), win_height);
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  resizable_ = params.resizable;
772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!resizable_) {
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // If the window doesn't have a size request when we set resizable to
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // false, GTK will shrink the window to 1x1px.
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    gtk_widget_set_size_request(GTK_WIDGET(window_),
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        params.bounds.width(), win_height);
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    gtk_window_set_resizable(window_, FALSE);
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // make sure bounds_ and restored_bounds_ have correct values until we
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // get our first configure-event
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bounds_ = restored_bounds_ = params.bounds;
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gint x, y;
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gtk_window_get_position(window_, &x, &y);
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bounds_.set_origin(gfx::Point(x, y));
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Hide titlebar when {frame: 'none'} specified on ShellWindow.
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (frameless_)
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gtk_window_set_decorated(window_, false);
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
964e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (always_on_top_)
974e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    gtk_window_set_keep_above(window_, TRUE);
984e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
998bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  UpdateWindowMinMaxSize();
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // In some (older) versions of compiz, raising top-level windows when they
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // are partially off-screen causes them to get snapped back on screen, not
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // always even on the current virtual desktop.  If we are running under
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // compiz, suppress such raises, as they are not necessary in compiz anyway.
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (ui::GuessWindowManager() == ui::WM_COMPIZ)
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    suppress_window_raise_ = true;
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gtk_window_set_title(window_, extension()->name().c_str());
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::string app_name = web_app::GenerateApplicationNameFromExtensionId(
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      extension()->id());
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gtk_window_util::SetWindowCustomClass(window_,
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      web_app::GetWMClassFromAppName(app_name));
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  g_signal_connect(window_, "delete-event",
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   G_CALLBACK(OnMainWindowDeleteEventThunk), this);
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  g_signal_connect(window_, "configure-event",
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   G_CALLBACK(OnConfigureThunk), this);
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  g_signal_connect(window_, "window-state-event",
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   G_CALLBACK(OnWindowStateThunk), this);
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (frameless_) {
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    g_signal_connect(window_, "button-press-event",
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     G_CALLBACK(OnButtonPressThunk), this);
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    g_signal_connect(window_, "motion-notify-event",
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                     G_CALLBACK(OnMouseMoveEventThunk), this);
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
128ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  // If _NET_WM_STATE_HIDDEN is in _NET_SUPPORTED, listen for XEvent to work
129ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  // around GTK+ not reporting minimization state changes. See comment in the
130ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  // |OnXEvent|.
131ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  std::vector< ::Atom> supported_atoms;
132ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  if (ui::GetAtomArrayProperty(ui::GetX11RootWindow(),
133ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch                               "_NET_SUPPORTED",
134ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch                               &supported_atoms)) {
135ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    if (std::find(supported_atoms.begin(),
136ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch                  supported_atoms.end(),
137ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch                  atom_cache_.GetAtom("_NET_WM_STATE_HIDDEN")) !=
138ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        supported_atoms.end()) {
139ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(window_));
140ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      gdk_window_add_filter(window,
141ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch                            &NativeAppWindowGtk::OnXEventThunk,
142ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch                            this);
143ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      is_x_event_listened_ = true;
144ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    }
145ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  }
146ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Add the keybinding registry.
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  extension_keybinding_registry_.reset(new ExtensionKeybindingRegistryGtk(
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      shell_window_->profile(),
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      window_,
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      extensions::ExtensionKeybindingRegistry::PLATFORM_APPS_ONLY,
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      shell_window_));
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ui::ActiveWindowWatcherX::AddObserver(this);
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)NativeAppWindowGtk::~NativeAppWindowGtk() {
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ui::ActiveWindowWatcherX::RemoveObserver(this);
159ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  if (is_x_event_listened_) {
160ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    gdk_window_remove_filter(NULL,
161ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch                             &NativeAppWindowGtk::OnXEventThunk,
162ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch                             this);
163ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  }
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool NativeAppWindowGtk::IsActive() const {
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (ui::ActiveWindowWatcherX::WMSupportsActivation())
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return is_active_;
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // This still works even though we don't get the activation notification.
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return gtk_window_is_active(window_);
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool NativeAppWindowGtk::IsMaximized() const {
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return (state_ & GDK_WINDOW_STATE_MAXIMIZED);
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool NativeAppWindowGtk::IsMinimized() const {
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return (state_ & GDK_WINDOW_STATE_ICONIFIED);
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool NativeAppWindowGtk::IsFullscreen() const {
183c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return (state_ & GDK_WINDOW_STATE_FULLSCREEN);
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)gfx::NativeWindow NativeAppWindowGtk::GetNativeWindow() {
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return window_;
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)gfx::Rect NativeAppWindowGtk::GetRestoredBounds() const {
1912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  gfx::Rect window_bounds = restored_bounds_;
1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  window_bounds.Inset(-GetFrameInsets());
1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return window_bounds;
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
196b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)ui::WindowShowState NativeAppWindowGtk::GetRestoredState() const {
197b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  if (IsMaximized())
198b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    return ui::SHOW_STATE_MAXIMIZED;
19968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  if (IsFullscreen())
20068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    return ui::SHOW_STATE_FULLSCREEN;
201b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  return ui::SHOW_STATE_NORMAL;
202b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)}
203b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
2042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)gfx::Rect NativeAppWindowGtk::GetBounds() const {
205f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // :GetBounds() is expecting the outer window bounds to be returned (ie.
206f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // including window decorations). The internal |bounds_| is not including them
207f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // and trying to add the decoration to |bounds_| would require calling
208f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // gdk_window_get_frame_extents. The best thing to do is to directly get the
209f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // frame bounds and only use the internal value if we can't.
210f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window_));
211f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (!gdk_window)
212f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return bounds_;
213f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
214f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  GdkRectangle window_bounds = {0};
215f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  gdk_window_get_frame_extents(gdk_window, &window_bounds);
216f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  return gfx::Rect(window_bounds.x, window_bounds.y,
217f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                   window_bounds.width, window_bounds.height);
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void NativeAppWindowGtk::Show() {
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gtk_window_present(window_);
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void NativeAppWindowGtk::ShowInactive() {
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gtk_window_set_focus_on_map(window_, false);
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gtk_widget_show(GTK_WIDGET(window_));
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void NativeAppWindowGtk::Hide() {
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gtk_widget_hide(GTK_WIDGET(window_));
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void NativeAppWindowGtk::Close() {
2342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  shell_window_->OnNativeWindowChanged();
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Cancel any pending callback from the window configure debounce timer.
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  window_configure_debounce_timer_.Stop();
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GtkWidget* window = GTK_WIDGET(window_);
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // To help catch bugs in any event handlers that might get fired during the
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // destruction, set window_ to NULL before any handlers will run.
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  window_ = NULL;
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // OnNativeClose does a delete this so no other members should
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // be accessed after. gtk_widget_destroy is safe (and must
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // be last).
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  shell_window_->OnNativeClose();
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gtk_widget_destroy(window);
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void NativeAppWindowGtk::Activate() {
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gtk_window_present(window_);
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void NativeAppWindowGtk::Deactivate() {
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gdk_window_lower(gtk_widget_get_window(GTK_WIDGET(window_)));
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void NativeAppWindowGtk::Maximize() {
260ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  // Represent the window first in order to keep the maximization behavior
261ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  // consistency with Windows platform. Otherwise the window will be hidden if
262ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  // it has been minimized.
263ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  gtk_window_present(window_);
2641e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
2651e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  if (!resizable_) {
2661e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    // When the window is not resizable, we still want to make this call succeed
2671e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    // but gtk will not allow it if the window is not resizable. The actual
2681e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    // maximization will happen with the subsequent OnConfigureDebounced call,
2691e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    // that will be triggered when the window manager's resizable property
2701e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    // changes.
2711e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    maximize_pending_ = true;
2721e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    gtk_window_set_resizable(window_, TRUE);
2731e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  } else {
2741e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    gtk_window_maximize(window_);
2751e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  }
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void NativeAppWindowGtk::Minimize() {
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gtk_window_iconify(window_);
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void NativeAppWindowGtk::Restore() {
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (IsMaximized())
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gtk_window_unmaximize(window_);
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else if (IsMinimized())
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gtk_window_deiconify(window_);
287ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
288ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  // Represent the window to keep restoration behavior consistency with Windows
289ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  // platform.
290ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  // TODO(zhchbin): verify whether we need this until http://crbug.com/261013 is
291ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  // fixed.
292ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  gtk_window_present(window_);
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void NativeAppWindowGtk::SetBounds(const gfx::Rect& bounds) {
2962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  gfx::Rect content_bounds = bounds;
2972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  gtk_window_move(window_, content_bounds.x(), content_bounds.y());
2982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!resizable_) {
2992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (frameless_ &&
3002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        gtk_window_util::BoundsMatchMonitorSize(window_, content_bounds)) {
3012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      content_bounds.set_height(content_bounds.height() - 1);
3022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
3032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // TODO(jeremya): set_size_request doesn't honor min/max size, so the
3042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // bounds should be constrained manually.
3052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    gtk_widget_set_size_request(GTK_WIDGET(window_),
3062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        content_bounds.width(), content_bounds.height());
3072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else {
3082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    gtk_window_util::SetWindowSize(window_,
3092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        gfx::Size(bounds.width(), bounds.height()));
3102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
313ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben MurdochGdkFilterReturn NativeAppWindowGtk::OnXEvent(GdkXEvent* gdk_x_event,
314ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch                                             GdkEvent* gdk_event) {
315ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  // Work around GTK+ not reporting minimization state changes. Listen
316ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  // for _NET_WM_STATE property changes and use _NET_WM_STATE_HIDDEN's
317ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  // presence to set or clear the iconified bit if _NET_WM_STATE_HIDDEN
318ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  // is supported. http://crbug.com/162794.
319ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  XEvent* x_event = static_cast<XEvent*>(gdk_x_event);
320ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  std::vector< ::Atom> atom_list;
321ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
322ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  if (x_event->type == PropertyNotify &&
323ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      x_event->xproperty.atom == atom_cache_.GetAtom("_NET_WM_STATE") &&
3240f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)      GTK_WIDGET(window_)->window &&
325ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch      ui::GetAtomArrayProperty(GDK_WINDOW_XWINDOW(GTK_WIDGET(window_)->window),
326ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch                               "_NET_WM_STATE",
327ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch                               &atom_list)) {
328ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    std::vector< ::Atom>::iterator it =
329ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        std::find(atom_list.begin(),
330ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch                  atom_list.end(),
331ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch                  atom_cache_.GetAtom("_NET_WM_STATE_HIDDEN"));
332f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
333f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    GdkWindowState previous_state = state_;
334ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    state_ = (it != atom_list.end()) ? GDK_WINDOW_STATE_ICONIFIED :
335ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        static_cast<GdkWindowState>(state_ & ~GDK_WINDOW_STATE_ICONIFIED);
336ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
337f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    if (previous_state != state_) {
338f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      shell_window_->OnNativeWindowChanged();
339f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    }
340ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  }
341ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
342ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch  return GDK_FILTER_CONTINUE;
343ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch}
344ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
3452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void NativeAppWindowGtk::FlashFrame(bool flash) {
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gtk_window_set_urgency_hint(window_, flash);
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool NativeAppWindowGtk::IsAlwaysOnTop() const {
3504e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  return always_on_top_;
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3534e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)void NativeAppWindowGtk::RenderViewHostChanged(
3544e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    content::RenderViewHost* old_host,
3554e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    content::RenderViewHost* new_host) {
3562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  web_contents()->GetView()->Focus();
3572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3594e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)void NativeAppWindowGtk::SetAlwaysOnTop(bool always_on_top) {
3604e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (always_on_top_ != always_on_top) {
3614e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // gdk_window_get_state() does not give us the correct value for the
3624e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // GDK_WINDOW_STATE_ABOVE bit. Cache the current state.
3634e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    always_on_top_ = always_on_top;
3644e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    gtk_window_set_keep_above(window_, always_on_top_ ? TRUE : FALSE);
3654e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  }
3662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
368a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)gfx::NativeView NativeAppWindowGtk::GetHostView() const {
369a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  NOTIMPLEMENTED();
370a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  return NULL;
371a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)}
372a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)
373c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)gfx::Point NativeAppWindowGtk::GetDialogPosition(const gfx::Size& size) {
374c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  gint current_width = 0;
375c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  gint current_height = 0;
376c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  gtk_window_get_size(window_, &current_width, &current_height);
377c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return gfx::Point(current_width / 2 - size.width() / 2,
378c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                    current_height / 2 - size.height() / 2);
379c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
380c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
38158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)gfx::Size NativeAppWindowGtk::GetMaximumDialogSize() {
38258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  gint current_width = 0;
38358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  gint current_height = 0;
38458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  gtk_window_get_size(window_, &current_width, &current_height);
38558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  return gfx::Size(current_width, current_height);
38658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
38758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
388c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void NativeAppWindowGtk::AddObserver(
3894e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    web_modal::ModalDialogHostObserver* observer) {
390c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  observer_list_.AddObserver(observer);
391c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
392c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
393c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void NativeAppWindowGtk::RemoveObserver(
3944e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    web_modal::ModalDialogHostObserver* observer) {
395c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  observer_list_.RemoveObserver(observer);
396c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
397c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
3982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void NativeAppWindowGtk::ActiveWindowChanged(GdkWindow* active_window) {
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Do nothing if we're in the process of closing the browser window.
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!window_)
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  is_active_ = gtk_widget_get_window(GTK_WIDGET(window_)) == active_window;
40490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  if (is_active_)
40590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    shell_window_->OnNativeWindowActivated();
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Callback for the delete event.  This event is fired when the user tries to
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// close the window (e.g., clicking on the X in the window manager title bar).
4102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)gboolean NativeAppWindowGtk::OnMainWindowDeleteEvent(GtkWidget* widget,
4112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                                     GdkEvent* event) {
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Close();
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Return true to prevent the GTK window from being destroyed.  Close will
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // destroy it for us.
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return TRUE;
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)gboolean NativeAppWindowGtk::OnConfigure(GtkWidget* widget,
4202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                         GdkEventConfigure* event) {
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We update |bounds_| but not |restored_bounds_| here.  The latter needs
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // to be updated conditionally when the window is non-maximized and non-
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // fullscreen, but whether those state updates have been processed yet is
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // window-manager specific.  We update |restored_bounds_| in the debounced
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // handler below, after the window state has been updated.
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bounds_.SetRect(event->x, event->y, event->width, event->height);
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The GdkEventConfigure* we get here doesn't have quite the right
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // coordinates though (they're relative to the drawable window area, rather
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // than any window manager decorations, if enabled), so we need to call
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // gtk_window_get_position() to get the right values. (Otherwise session
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // restore, if enabled, will restore windows to incorrect positions.) That's
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // a round trip to the X server though, so we set a debounce timer and only
4347dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  // call it (in OnConfigureDebounced() below) after we haven't seen a
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // reconfigure event in a short while.
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We don't use Reset() because the timer may not yet be running.
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // (In that case Stop() is a no-op.)
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  window_configure_debounce_timer_.Stop();
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  window_configure_debounce_timer_.Start(FROM_HERE,
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::TimeDelta::FromMilliseconds(kDebounceTimeoutMilliseconds), this,
4417dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      &NativeAppWindowGtk::OnConfigureDebounced);
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return FALSE;
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4467dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochvoid NativeAppWindowGtk::OnConfigureDebounced() {
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gtk_window_util::UpdateWindowPosition(this, &bounds_, &restored_bounds_);
4482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  shell_window_->OnNativeWindowChanged();
449c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
4504e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  FOR_EACH_OBSERVER(web_modal::ModalDialogHostObserver,
451c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                    observer_list_,
452c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                    OnPositionRequiresUpdate());
4537dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
4547dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  // Fullscreen of non-resizable windows requires them to be made resizable
4557dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  // first. After that takes effect and OnConfigure is called we transition
4567dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  // to fullscreen.
4577dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  if (!IsFullscreen() && IsFullscreenOrPending()) {
4587dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    gtk_window_fullscreen(window_);
4597dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  }
4601e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
4611e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  // maximize_pending_ is the boolean that lets us know that the window is in
4621e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  // the process of being maximized but was set as not resizable.
4631e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  // This function will be called twice during the maximization process:
4641e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  // 1. gtk_window_maximize() is called to maximize the window;
4651e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  // 2. gtk_set_resizable(, FALSE) is called to make the window no longer
4661e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  //    resizable.
4671e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  // gtk_window_maximize() will cause ::OnConfigureDebounced to be called
4681e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  // again, at which time we will run into the second step.
4691e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  if (maximize_pending_) {
4701e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    if (!(state_ & GDK_WINDOW_STATE_MAXIMIZED)) {
4711e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      gtk_window_maximize(window_);
4721e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    } else {
4731e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      maximize_pending_ = false;
4741e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      if (!resizable_)
4751e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        gtk_window_set_resizable(window_, FALSE);
4761e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    }
4771e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  }
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)gboolean NativeAppWindowGtk::OnWindowState(GtkWidget* sender,
4812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                           GdkEventWindowState* event) {
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  state_ = event->new_window_state;
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (content_thinks_its_fullscreen_ &&
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      !(state_ & GDK_WINDOW_STATE_FULLSCREEN)) {
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    content_thinks_its_fullscreen_ = false;
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    content::RenderViewHost* rvh = web_contents()->GetRenderViewHost();
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (rvh)
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rvh->ExitFullscreen();
4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return FALSE;
4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool NativeAppWindowGtk::GetWindowEdge(int x, int y, GdkWindowEdge* edge) {
4962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!frameless_)
4972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (IsMaximized() || IsFullscreen())
5002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
5012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return gtk_window_util::GetWindowEdge(bounds_.size(), 0, x, y, edge);
5032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
5042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)gboolean NativeAppWindowGtk::OnMouseMoveEvent(GtkWidget* widget,
5062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                              GdkEventMotion* event) {
5072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!frameless_) {
5082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Reset the cursor.
5092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (frame_cursor_) {
5102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      frame_cursor_ = NULL;
5112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window_)), NULL);
5122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
5132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return FALSE;
5142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
5152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!resizable_)
5172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return FALSE;
5182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Update the cursor if we're on the custom frame border.
5202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  GdkWindowEdge edge;
5212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool has_hit_edge = GetWindowEdge(static_cast<int>(event->x),
5222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                    static_cast<int>(event->y), &edge);
5232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  GdkCursorType new_cursor = GDK_LAST_CURSOR;
5242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (has_hit_edge)
5252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    new_cursor = gtk_window_util::GdkWindowEdgeToGdkCursorType(edge);
5262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  GdkCursorType last_cursor = GDK_LAST_CURSOR;
5282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (frame_cursor_)
5292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    last_cursor = frame_cursor_->type;
5302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (last_cursor != new_cursor) {
5322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    frame_cursor_ = has_hit_edge ? gfx::GetCursor(new_cursor) : NULL;
5332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window_)),
5342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                          frame_cursor_);
5352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
5362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return FALSE;
5372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
5382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)gboolean NativeAppWindowGtk::OnButtonPress(GtkWidget* widget,
5402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                           GdkEventButton* event) {
5412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(frameless_);
5422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Make the button press coordinate relative to the browser window.
5432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int win_x, win_y;
5442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window_));
5452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  gdk_window_get_origin(gdk_window, &win_x, &win_y);
5462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  GdkWindowEdge edge;
5482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  gfx::Point point(static_cast<int>(event->x_root - win_x),
5492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                   static_cast<int>(event->y_root - win_y));
5502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool has_hit_edge = resizable_ && GetWindowEdge(point.x(), point.y(), &edge);
5512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool has_hit_titlebar =
5522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      draggable_region_ && draggable_region_->contains(event->x, event->y);
5532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (event->button == 1) {
5552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (GDK_BUTTON_PRESS == event->type) {
5562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // Raise the window after a click on either the titlebar or the border to
5572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // match the behavior of most window managers, unless that behavior has
5582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // been suppressed.
5592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if ((has_hit_titlebar || has_hit_edge) && !suppress_window_raise_)
5602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        gdk_window_raise(GTK_WIDGET(widget)->window);
5612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (has_hit_edge) {
5632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        gtk_window_begin_resize_drag(window_, edge, event->button,
5642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                     static_cast<gint>(event->x_root),
5652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                     static_cast<gint>(event->y_root),
5662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                     event->time);
5672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return TRUE;
5682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      } else if (has_hit_titlebar) {
5695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return gtk_window_util::HandleTitleBarLeftMousePress(
5702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            window_, bounds_, event);
5712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
5722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    } else if (GDK_2BUTTON_PRESS == event->type) {
5732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (has_hit_titlebar && resizable_) {
5742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // Maximize/restore on double click.
5752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (IsMaximized()) {
5765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          gtk_window_util::UnMaximize(GTK_WINDOW(widget),
5775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              bounds_, restored_bounds_);
5785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        } else {
5792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          gtk_window_maximize(window_);
5805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
5815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return TRUE;
5825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
5835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
5842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else if (event->button == 2) {
5852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (has_hit_titlebar || has_hit_edge)
5862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      gdk_window_lower(gdk_window);
5872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return TRUE;
5885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return FALSE;
5915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5934e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// NativeAppWindow implementation:
5944e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
595f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void NativeAppWindowGtk::SetFullscreen(int fullscreen_types) {
596f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  bool fullscreen = (fullscreen_types != ShellWindow::FULLSCREEN_TYPE_NONE);
5975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  content_thinks_its_fullscreen_ = fullscreen;
598f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (fullscreen) {
5997dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    if (resizable_) {
6007dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      gtk_window_fullscreen(window_);
6017dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    } else {
6027dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      // We must first make the window resizable. That won't take effect
6037dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      // immediately, so OnConfigureDebounced completes the fullscreen call.
6047dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      gtk_window_set_resizable(window_, TRUE);
6057dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    }
6067dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  } else {
6075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gtk_window_unfullscreen(window_);
6087dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    if (!resizable_)
6097dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      gtk_window_set_resizable(window_, FALSE);
6107dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  }
6115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool NativeAppWindowGtk::IsFullscreenOrPending() const {
61468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  // |content_thinks_its_fullscreen_| is used when transitioning, and when
61568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  // the state change will not be made for some time. However, it is possible
61668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  // for a state update to be made before the final fullscreen state comes.
61768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  // In that case, |content_thinks_its_fullscreen_| will be cleared, but we
61868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  // will fall back to |IsFullscreen| which will soon have the correct state.
61968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  return content_thinks_its_fullscreen_ || IsFullscreen();
6205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
622b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)bool NativeAppWindowGtk::IsDetached() const {
623b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  return false;
624b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)}
625b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
6262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void NativeAppWindowGtk::UpdateWindowIcon() {
6275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Profile* profile = shell_window_->profile();
6285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Image app_icon = shell_window_->app_icon();
6295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!app_icon.IsEmpty())
6305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gtk_util::SetWindowIcon(window_, profile, app_icon.ToGdkPixbuf());
6315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
6325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gtk_util::SetWindowIcon(window_, profile);
6335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void NativeAppWindowGtk::UpdateWindowTitle() {
636a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::string16 title = shell_window_->GetTitle();
6375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gtk_window_set_title(window_, UTF16ToUTF8(title).c_str());
6385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6404e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)void NativeAppWindowGtk::UpdateDraggableRegions(
6414e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    const std::vector<extensions::DraggableRegion>& regions) {
6424e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // Draggable region is not supported for non-frameless window.
6434e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (!frameless_)
6444e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    return;
6454e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
6464e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  draggable_region_.reset(ShellWindow::RawDraggableRegionsToSkRegion(regions));
6474e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
6484e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
6494e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)SkRegion* NativeAppWindowGtk::GetDraggableRegion() {
6504e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  return draggable_region_.get();
6514e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
6524e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
653f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void NativeAppWindowGtk::UpdateShape(scoped_ptr<SkRegion> region) {
6544e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  NOTIMPLEMENTED();
6554e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
6564e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
6572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void NativeAppWindowGtk::HandleKeyboardEvent(
6585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const content::NativeWebKeyboardEvent& event) {
6595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // No-op.
6605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6624e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)bool NativeAppWindowGtk::IsFrameless() const {
6634e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  return frameless_;
6643551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)}
6653551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
6664e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)gfx::Insets NativeAppWindowGtk::GetFrameInsets() const {
6674e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (frameless_)
6684e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    return gfx::Insets();
6694e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window_));
6704e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (!gdk_window)
6714e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    return gfx::Insets();
6725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6734e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  gint current_width = 0;
6744e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  gint current_height = 0;
6754e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  gtk_window_get_size(window_, &current_width, &current_height);
6764e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  gint current_x = 0;
6774e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  gint current_y = 0;
6784e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  gdk_window_get_position(gdk_window, &current_x, &current_y);
6794e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  GdkRectangle rect_with_decorations = {0};
6804e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  gdk_window_get_frame_extents(gdk_window,
6814e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                               &rect_with_decorations);
6824e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
6834e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  int left_inset = current_x - rect_with_decorations.x;
6844e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  int top_inset = current_y - rect_with_decorations.y;
6854e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  return gfx::Insets(
6864e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      top_inset,
6874e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      left_inset,
6884e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      rect_with_decorations.height - current_height - top_inset,
6894e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      rect_with_decorations.width - current_width - left_inset);
6905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6914e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
6924e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)void NativeAppWindowGtk::HideWithApp() {}
6934e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)void NativeAppWindowGtk::ShowWithApp() {}
6948bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
6958bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)void NativeAppWindowGtk::UpdateWindowMinMaxSize() {
6968bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  GdkGeometry hints;
6978bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  int hints_mask = 0;
6988bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  if (shell_window_->size_constraints().HasMinimumSize()) {
6998bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    gfx::Size min_size = shell_window_->size_constraints().GetMinimumSize();
7008bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    hints.min_height = min_size.height();
7018bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    hints.min_width = min_size.width();
7028bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    hints_mask |= GDK_HINT_MIN_SIZE;
7038bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  }
7048bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  if (shell_window_->size_constraints().HasMaximumSize()) {
7058bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    gfx::Size max_size = shell_window_->size_constraints().GetMaximumSize();
7068bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    const int kUnboundedSize = ShellWindow::SizeConstraints::kUnboundedSize;
7078bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    hints.max_height = max_size.height() == kUnboundedSize ?
7088bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)        G_MAXINT : max_size.height();
7098bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    hints.max_width = max_size.width() == kUnboundedSize ?
7108bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)        G_MAXINT : max_size.width();
7118bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    hints_mask |= GDK_HINT_MAX_SIZE;
7128bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  }
7138bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  if (hints_mask) {
7148bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    gtk_window_set_geometry_hints(
7158bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)        window_,
7168bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)        GTK_WIDGET(window_),
7178bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)        &hints,
7188bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)        static_cast<GdkWindowHints>(hints_mask));
7198bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  }
7208bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)}
721