summaryrefslogtreecommitdiffstats
path: root/chrome/browser/ui/gtk/web_contents_modal_dialog_manager_gtk.cc
blob: 3adfcb88be50a2f45f373a0c0dae31a12478a2e9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// Copyright (c) 2013 The Chromium 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 "chrome/browser/ui/gtk/gtk_util.h"
#include "chrome/browser/ui/gtk/tab_contents/chrome_web_contents_view_delegate_gtk.h"
#include "chrome/browser/ui/native_web_contents_modal_dialog_manager.h"
#include "chrome/browser/ui/web_contents_modal_dialog_manager.h"
#include "content/public/browser/browser_thread.h"
#include "ui/base/gtk/focus_store_gtk.h"

namespace {

// Web contents modal dialog manager implementation for the GTK port. Unlike the
// Win32 system, ConstrainedWindowGtk doesn't draw draggable fake windows and
// instead just centers the dialog. It is thus an order of magnitude simpler.
class NativeWebContentsModalDialogManagerGtk
    : public NativeWebContentsModalDialogManager {
 public:
  NativeWebContentsModalDialogManagerGtk(
      NativeWebContentsModalDialogManagerDelegate* native_delegate)
          : native_delegate_(native_delegate),
            shown_widget_(NULL) {
  }

  virtual ~NativeWebContentsModalDialogManagerGtk() {
  }

  // NativeWebContentsModalDialogManager overrides
  virtual void ManageDialog(NativeWebContentsModalDialog dialog) OVERRIDE {
    DCHECK(g_object_is_floating(GetGtkWidget(dialog)));
    g_object_ref_sink(GetGtkWidget(dialog));

    g_signal_connect(GetGtkWidget(dialog), "hierarchy-changed",
                     G_CALLBACK(OnHierarchyChangedThunk), this);
    g_signal_connect(GetGtkWidget(dialog),
                     "destroy",
                     G_CALLBACK(OnDestroyThunk),
                     this);
  }

  virtual void ShowDialog(NativeWebContentsModalDialog dialog) OVERRIDE {
    GtkWidget* widget = GetGtkWidget(dialog);

    // Any previously-shown widget should be destroyed before showing a new
    // widget.
    DCHECK(shown_widget_ == widget || shown_widget_ == NULL);
    gtk_widget_show_all(widget);

    if (!shown_widget_) {
      // We collaborate with WebContentsView and stick ourselves in the
      // WebContentsView's floating container.
      ContainingView()->AttachWebContentsModalDialog(widget);
    }

    shown_widget_ = widget;
  }

  virtual void HideDialog(NativeWebContentsModalDialog dialog) OVERRIDE {
    gtk_widget_hide(GetGtkWidget(dialog));
  }

  virtual void CloseDialog(NativeWebContentsModalDialog dialog) OVERRIDE {
    gtk_widget_destroy(GetGtkWidget(dialog));
  }

  virtual void FocusDialog(NativeWebContentsModalDialog dialog) OVERRIDE {
    GtkWidget* focus_widget =
        reinterpret_cast<GtkWidget*>(
            g_object_get_data(G_OBJECT(GetGtkWidget(dialog)), "focus_widget"));

    if (!focus_widget)
      return;

    // The user may have focused another tab. In this case do not grab focus
    // until this tab is refocused.
    if (gtk_util::IsWidgetAncestryVisible(focus_widget))
      gtk_widget_grab_focus(focus_widget);
    else
      ContainingView()->focus_store()->SetWidget(focus_widget);
  }

  virtual void PulseDialog(NativeWebContentsModalDialog dialog) OVERRIDE {
  }

 private:
  typedef ChromeWebContentsViewDelegateGtk TabContentsViewType;

  GtkWidget* GetGtkWidget(NativeWebContentsModalDialog dialog) {
    return GTK_WIDGET(dialog);
  }

  // Returns the View that we collaborate with to position ourselves.
  TabContentsViewType* ContainingView() const {
    // WebContents may be destroyed already on tab shutdown.
     content::WebContents* web_contents = native_delegate_->GetWebContents();
    return web_contents ?
        ChromeWebContentsViewDelegateGtk::GetFor(web_contents) : NULL;
  }

  CHROMEGTK_CALLBACK_1(NativeWebContentsModalDialogManagerGtk,
                       void,
                       OnHierarchyChanged,
                       GtkWidget*);
  CHROMEGTK_CALLBACK_0(NativeWebContentsModalDialogManagerGtk, void, OnDestroy);

  NativeWebContentsModalDialogManagerDelegate* native_delegate_;

  // The widget currently being shown.
  GtkWidget* shown_widget_;

  DISALLOW_COPY_AND_ASSIGN(NativeWebContentsModalDialogManagerGtk);
};

void NativeWebContentsModalDialogManagerGtk::OnHierarchyChanged(
    GtkWidget* sender,
    GtkWidget* previous_toplevel) {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));

  if (!gtk_widget_is_toplevel(gtk_widget_get_toplevel(sender)))
    return;

  FocusDialog(sender);
}

void NativeWebContentsModalDialogManagerGtk::OnDestroy(
    GtkWidget* sender) {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));

  if (shown_widget_ == sender) {
    // The containing view may already be destroyed on tab shutdown.
    if (ContainingView())
      ContainingView()->RemoveWebContentsModalDialog(sender);

    shown_widget_ = NULL;
  }

  native_delegate_->WillClose(sender);

  g_object_unref(sender);
}

}  // namespace

NativeWebContentsModalDialogManager*
    WebContentsModalDialogManager::CreateNativeManager(
        NativeWebContentsModalDialogManagerDelegate* native_delegate) {
  return new NativeWebContentsModalDialogManagerGtk(native_delegate);
}