chrome_app_delegate.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/ui/apps/chrome_app_delegate.h"
6
7#include "base/memory/scoped_ptr.h"
8#include "base/strings/stringprintf.h"
9#include "chrome/browser/app_mode/app_mode_utils.h"
10#include "chrome/browser/apps/scoped_keep_alive.h"
11#include "chrome/browser/chrome_notification_types.h"
12#include "chrome/browser/extensions/chrome_extension_web_contents_observer.h"
13#include "chrome/browser/favicon/favicon_tab_helper.h"
14#include "chrome/browser/file_select_helper.h"
15#include "chrome/browser/media/media_capture_devices_dispatcher.h"
16#include "chrome/browser/platform_util.h"
17#include "chrome/browser/profiles/profile.h"
18#include "chrome/browser/shell_integration.h"
19#include "chrome/browser/ui/browser.h"
20#include "chrome/browser/ui/browser_dialogs.h"
21#include "chrome/browser/ui/browser_tabstrip.h"
22#include "chrome/browser/ui/browser_window.h"
23#include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
24#include "chrome/browser/ui/web_contents_sizer.h"
25#include "chrome/browser/ui/zoom/zoom_controller.h"
26#include "chrome/common/extensions/chrome_extension_messages.h"
27#include "content/public/browser/browser_context.h"
28#include "content/public/browser/notification_service.h"
29#include "content/public/browser/render_view_host.h"
30#include "content/public/browser/web_contents.h"
31#include "content/public/browser/web_contents_delegate.h"
32#include "extensions/common/constants.h"
33
34#if defined(USE_ASH)
35#include "ash/shelf/shelf_constants.h"
36#endif
37
38#if defined(ENABLE_PRINTING)
39#if defined(ENABLE_FULL_PRINTING)
40#include "chrome/browser/printing/print_preview_message_handler.h"
41#include "chrome/browser/printing/print_view_manager.h"
42#else
43#include "chrome/browser/printing/print_view_manager_basic.h"
44#endif  // defined(ENABLE_FULL_PRINTING)
45#endif  // defined(ENABLE_PRINTING)
46
47namespace {
48
49bool disable_external_open_for_testing_ = false;
50
51// Opens a URL with Chromium (not external browser) with the right profile.
52content::WebContents* OpenURLFromTabInternal(
53    content::BrowserContext* context,
54    const content::OpenURLParams& params) {
55  // Force all links to open in a new tab, even if they were trying to open a
56  // window.
57  chrome::NavigateParams new_tab_params(
58      static_cast<Browser*>(NULL), params.url, params.transition);
59  if (params.disposition == NEW_BACKGROUND_TAB) {
60    new_tab_params.disposition = NEW_BACKGROUND_TAB;
61  } else {
62    new_tab_params.disposition = NEW_FOREGROUND_TAB;
63    new_tab_params.window_action = chrome::NavigateParams::SHOW_WINDOW;
64  }
65
66  new_tab_params.initiating_profile = Profile::FromBrowserContext(context);
67  chrome::Navigate(&new_tab_params);
68
69  return new_tab_params.target_contents;
70}
71
72// Helper class that opens a URL based on if this browser instance is the
73// default system browser. If it is the default, open the URL directly instead
74// of asking the system to open it.
75class OpenURLFromTabBasedOnBrowserDefault
76    : public ShellIntegration::DefaultWebClientObserver {
77 public:
78  OpenURLFromTabBasedOnBrowserDefault(scoped_ptr<content::WebContents> source,
79                                      const content::OpenURLParams& params)
80      : source_(source.Pass()), params_(params) {}
81
82  // Opens a URL when called with the result of if this is the default system
83  // browser or not.
84  virtual void SetDefaultWebClientUIState(
85      ShellIntegration::DefaultWebClientUIState state) OVERRIDE {
86    Profile* profile =
87        Profile::FromBrowserContext(source_->GetBrowserContext());
88    DCHECK(profile);
89    if (!profile)
90      return;
91    switch (state) {
92      case ShellIntegration::STATE_PROCESSING:
93        break;
94      case ShellIntegration::STATE_IS_DEFAULT:
95        OpenURLFromTabInternal(profile, params_);
96        break;
97      case ShellIntegration::STATE_NOT_DEFAULT:
98      case ShellIntegration::STATE_UNKNOWN:
99        platform_util::OpenExternal(profile, params_.url);
100        break;
101    }
102  }
103
104  virtual bool IsOwnedByWorker() OVERRIDE { return true; }
105
106 private:
107  scoped_ptr<content::WebContents> source_;
108  const content::OpenURLParams params_;
109};
110
111}  // namespace
112
113class ChromeAppDelegate::NewWindowContentsDelegate
114    : public content::WebContentsDelegate {
115 public:
116  NewWindowContentsDelegate() {}
117  virtual ~NewWindowContentsDelegate() {}
118
119  virtual content::WebContents* OpenURLFromTab(
120      content::WebContents* source,
121      const content::OpenURLParams& params) OVERRIDE;
122
123 private:
124  DISALLOW_COPY_AND_ASSIGN(NewWindowContentsDelegate);
125};
126
127content::WebContents*
128ChromeAppDelegate::NewWindowContentsDelegate::OpenURLFromTab(
129    content::WebContents* source,
130    const content::OpenURLParams& params) {
131  if (source) {
132    // This NewWindowContentsDelegate was given ownership of the incoming
133    // WebContents by being assigned as its delegate within
134    // ChromeAppDelegate::AddNewContents, but this is the first time
135    // NewWindowContentsDelegate actually sees the WebContents.
136    // Here it is captured for deletion.
137    scoped_ptr<content::WebContents> owned_source(source);
138    scoped_refptr<ShellIntegration::DefaultWebClientWorker>
139        check_if_default_browser_worker =
140            new ShellIntegration::DefaultBrowserWorker(
141                new OpenURLFromTabBasedOnBrowserDefault(owned_source.Pass(),
142                                                        params));
143    // Object lifetime notes: The OpenURLFromTabBasedOnBrowserDefault is owned
144    // by check_if_default_browser_worker. StartCheckIsDefault() takes lifetime
145    // ownership of check_if_default_browser_worker and will clean up after
146    // the asynchronous tasks.
147    check_if_default_browser_worker->StartCheckIsDefault();
148  }
149  return NULL;
150}
151
152ChromeAppDelegate::ChromeAppDelegate(scoped_ptr<ScopedKeepAlive> keep_alive)
153    : keep_alive_(keep_alive.Pass()),
154      new_window_contents_delegate_(new NewWindowContentsDelegate()) {
155  registrar_.Add(this,
156                 chrome::NOTIFICATION_APP_TERMINATING,
157                 content::NotificationService::AllSources());
158}
159
160ChromeAppDelegate::~ChromeAppDelegate() {
161  // Unregister now to prevent getting notified if |keep_alive_| is the last.
162  terminating_callback_.Reset();
163}
164
165void ChromeAppDelegate::DisableExternalOpenForTesting() {
166  disable_external_open_for_testing_ = true;
167}
168
169void ChromeAppDelegate::InitWebContents(content::WebContents* web_contents) {
170  FaviconTabHelper::CreateForWebContents(web_contents);
171
172#if defined(ENABLE_PRINTING)
173#if defined(ENABLE_FULL_PRINTING)
174  printing::PrintViewManager::CreateForWebContents(web_contents);
175  printing::PrintPreviewMessageHandler::CreateForWebContents(web_contents);
176#else
177  printing::PrintViewManagerBasic::CreateForWebContents(web_contents);
178#endif  // defined(ENABLE_FULL_PRINTING)
179#endif  // defined(ENABLE_PRINTING)
180  extensions::ChromeExtensionWebContentsObserver::CreateForWebContents(
181      web_contents);
182
183  // Kiosk app supports zooming.
184  if (chrome::IsRunningInForcedAppMode())
185    ZoomController::CreateForWebContents(web_contents);
186}
187
188void ChromeAppDelegate::ResizeWebContents(content::WebContents* web_contents,
189                                          const gfx::Size& size) {
190  ::ResizeWebContents(web_contents, size);
191}
192
193content::WebContents* ChromeAppDelegate::OpenURLFromTab(
194    content::BrowserContext* context,
195    content::WebContents* source,
196    const content::OpenURLParams& params) {
197  return OpenURLFromTabInternal(context, params);
198}
199
200void ChromeAppDelegate::AddNewContents(content::BrowserContext* context,
201                                       content::WebContents* new_contents,
202                                       WindowOpenDisposition disposition,
203                                       const gfx::Rect& initial_pos,
204                                       bool user_gesture,
205                                       bool* was_blocked) {
206  if (!disable_external_open_for_testing_) {
207    // We don't really want to open a window for |new_contents|, but we need to
208    // capture its intended navigation. Here we give ownership to the
209    // NewWindowContentsDelegate, which will dispose of the contents once
210    // a navigation is captured.
211    new_contents->SetDelegate(new_window_contents_delegate_.get());
212    return;
213  }
214  chrome::ScopedTabbedBrowserDisplayer displayer(
215      Profile::FromBrowserContext(context), chrome::GetActiveDesktop());
216  // Force all links to open in a new tab, even if they were trying to open a
217  // new window.
218  disposition =
219      disposition == NEW_BACKGROUND_TAB ? disposition : NEW_FOREGROUND_TAB;
220  chrome::AddWebContents(displayer.browser(),
221                         NULL,
222                         new_contents,
223                         disposition,
224                         initial_pos,
225                         user_gesture,
226                         was_blocked);
227}
228
229content::ColorChooser* ChromeAppDelegate::ShowColorChooser(
230    content::WebContents* web_contents,
231    SkColor initial_color) {
232  return chrome::ShowColorChooser(web_contents, initial_color);
233}
234
235void ChromeAppDelegate::RunFileChooser(
236    content::WebContents* tab,
237    const content::FileChooserParams& params) {
238  FileSelectHelper::RunFileChooser(tab, params);
239}
240
241void ChromeAppDelegate::RequestMediaAccessPermission(
242    content::WebContents* web_contents,
243    const content::MediaStreamRequest& request,
244    const content::MediaResponseCallback& callback,
245    const extensions::Extension* extension) {
246  MediaCaptureDevicesDispatcher::GetInstance()->ProcessMediaAccessRequest(
247      web_contents, request, callback, extension);
248}
249
250bool ChromeAppDelegate::CheckMediaAccessPermission(
251    content::WebContents* web_contents,
252    const GURL& security_origin,
253    content::MediaStreamType type,
254    const extensions::Extension* extension) {
255  return MediaCaptureDevicesDispatcher::GetInstance()
256      ->CheckMediaAccessPermission(
257          web_contents, security_origin, type, extension);
258}
259
260int ChromeAppDelegate::PreferredIconSize() {
261#if defined(USE_ASH)
262  return ash::kShelfSize;
263#else
264  return extension_misc::EXTENSION_ICON_SMALL;
265#endif
266}
267
268void ChromeAppDelegate::SetWebContentsBlocked(
269    content::WebContents* web_contents,
270    bool blocked) {
271  // RenderViewHost may be NULL during shutdown.
272  content::RenderViewHost* host = web_contents->GetRenderViewHost();
273  if (host) {
274    host->Send(new ChromeViewMsg_SetVisuallyDeemphasized(host->GetRoutingID(),
275                                                         blocked));
276  }
277}
278
279bool ChromeAppDelegate::IsWebContentsVisible(
280    content::WebContents* web_contents) {
281  return platform_util::IsVisible(web_contents->GetNativeView());
282}
283
284void ChromeAppDelegate::SetTerminatingCallback(const base::Closure& callback) {
285  terminating_callback_ = callback;
286}
287
288void ChromeAppDelegate::Observe(int type,
289                                const content::NotificationSource& source,
290                                const content::NotificationDetails& details) {
291  switch (type) {
292    case chrome::NOTIFICATION_APP_TERMINATING:
293      if (!terminating_callback_.is_null())
294        terminating_callback_.Run();
295      break;
296    default:
297      NOTREACHED() << "Received unexpected notification";
298  }
299}
300