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.
5 #include "chrome/browser/ui/apps/chrome_app_delegate.h"
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_utils.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/common/extensions/chrome_extension_messages.h"
26 #include "components/ui/zoom/zoom_controller.h"
27 #include "content/public/browser/browser_context.h"
28 #include "content/public/browser/browser_thread.h"
29 #include "content/public/browser/host_zoom_map.h"
30 #include "content/public/browser/notification_service.h"
31 #include "content/public/browser/render_view_host.h"
32 #include "content/public/browser/web_contents.h"
33 #include "content/public/browser/web_contents_delegate.h"
34 #include "extensions/common/constants.h"
37 #include "ash/shelf/shelf_constants.h"
40 #if defined(ENABLE_PRINTING)
41 #if defined(ENABLE_PRINT_PREVIEW)
42 #include "chrome/browser/printing/print_preview_message_handler.h"
43 #include "chrome/browser/printing/print_view_manager.h"
45 #include "chrome/browser/printing/print_view_manager_basic.h"
46 #endif // defined(ENABLE_PRINT_PREVIEW)
47 #endif // defined(ENABLE_PRINTING)
51 // Time to wait for an app window to show before allowing Chrome to quit.
52 int kAppWindowFirstShowTimeoutSeconds
= 10;
54 bool disable_external_open_for_testing_
= false;
56 // Opens a URL with Chromium (not external browser) with the right profile.
57 content::WebContents
* OpenURLFromTabInternal(
58 content::BrowserContext
* context
,
59 const content::OpenURLParams
& params
) {
60 // Force all links to open in a new tab, even if they were trying to open a
62 chrome::NavigateParams
new_tab_params(
63 static_cast<Browser
*>(NULL
), params
.url
, params
.transition
);
64 if (params
.disposition
== NEW_BACKGROUND_TAB
) {
65 new_tab_params
.disposition
= NEW_BACKGROUND_TAB
;
67 new_tab_params
.disposition
= NEW_FOREGROUND_TAB
;
68 new_tab_params
.window_action
= chrome::NavigateParams::SHOW_WINDOW
;
71 new_tab_params
.initiating_profile
= Profile::FromBrowserContext(context
);
72 chrome::Navigate(&new_tab_params
);
74 return new_tab_params
.target_contents
;
77 // Helper class that opens a URL based on if this browser instance is the
78 // default system browser. If it is the default, open the URL directly instead
79 // of asking the system to open it.
80 class OpenURLFromTabBasedOnBrowserDefault
81 : public ShellIntegration::DefaultWebClientObserver
{
83 OpenURLFromTabBasedOnBrowserDefault(scoped_ptr
<content::WebContents
> source
,
84 const content::OpenURLParams
& params
)
85 : source_(source
.Pass()), params_(params
) {}
87 // Opens a URL when called with the result of if this is the default system
89 void SetDefaultWebClientUIState(
90 ShellIntegration::DefaultWebClientUIState state
) override
{
92 Profile::FromBrowserContext(source_
->GetBrowserContext());
97 case ShellIntegration::STATE_PROCESSING
:
99 case ShellIntegration::STATE_IS_DEFAULT
:
100 OpenURLFromTabInternal(profile
, params_
);
102 case ShellIntegration::STATE_NOT_DEFAULT
:
103 case ShellIntegration::STATE_UNKNOWN
:
104 platform_util::OpenExternal(profile
, params_
.url
);
109 bool IsOwnedByWorker() override
{ return true; }
112 scoped_ptr
<content::WebContents
> source_
;
113 const content::OpenURLParams params_
;
119 void ChromeAppDelegate::RelinquishKeepAliveAfterTimeout(
120 const base::WeakPtr
<ChromeAppDelegate
>& chrome_app_delegate
) {
121 // Resetting the ScopedKeepAlive may cause nested destruction of the
122 // ChromeAppDelegate which also resets the ScopedKeepAlive. To avoid this,
123 // move the ScopedKeepAlive out to here and let it fall out of scope.
124 if (chrome_app_delegate
.get() && chrome_app_delegate
->is_hidden_
)
125 scoped_ptr
<ScopedKeepAlive
>(chrome_app_delegate
->keep_alive_
.Pass());
128 class ChromeAppDelegate::NewWindowContentsDelegate
129 : public content::WebContentsDelegate
{
131 NewWindowContentsDelegate() {}
132 ~NewWindowContentsDelegate() override
{}
134 content::WebContents
* OpenURLFromTab(
135 content::WebContents
* source
,
136 const content::OpenURLParams
& params
) override
;
139 DISALLOW_COPY_AND_ASSIGN(NewWindowContentsDelegate
);
142 content::WebContents
*
143 ChromeAppDelegate::NewWindowContentsDelegate::OpenURLFromTab(
144 content::WebContents
* source
,
145 const content::OpenURLParams
& params
) {
147 // This NewWindowContentsDelegate was given ownership of the incoming
148 // WebContents by being assigned as its delegate within
149 // ChromeAppDelegate::AddNewContents, but this is the first time
150 // NewWindowContentsDelegate actually sees the WebContents.
151 // Here it is captured for deletion.
152 scoped_ptr
<content::WebContents
> owned_source(source
);
153 scoped_refptr
<ShellIntegration::DefaultWebClientWorker
>
154 check_if_default_browser_worker
=
155 new ShellIntegration::DefaultBrowserWorker(
156 new OpenURLFromTabBasedOnBrowserDefault(owned_source
.Pass(),
158 // Object lifetime notes: The OpenURLFromTabBasedOnBrowserDefault is owned
159 // by check_if_default_browser_worker. StartCheckIsDefault() takes lifetime
160 // ownership of check_if_default_browser_worker and will clean up after
161 // the asynchronous tasks.
162 check_if_default_browser_worker
->StartCheckIsDefault();
167 ChromeAppDelegate::ChromeAppDelegate(scoped_ptr
<ScopedKeepAlive
> keep_alive
)
168 : has_been_shown_(false),
170 keep_alive_(keep_alive
.Pass()),
171 new_window_contents_delegate_(new NewWindowContentsDelegate()),
172 weak_factory_(this) {
174 chrome::NOTIFICATION_APP_TERMINATING
,
175 content::NotificationService::AllSources());
178 ChromeAppDelegate::~ChromeAppDelegate() {
179 // Unregister now to prevent getting notified if |keep_alive_| is the last.
180 terminating_callback_
.Reset();
183 void ChromeAppDelegate::DisableExternalOpenForTesting() {
184 disable_external_open_for_testing_
= true;
187 void ChromeAppDelegate::InitWebContents(content::WebContents
* web_contents
) {
188 favicon::CreateContentFaviconDriverForWebContents(web_contents
);
190 #if defined(ENABLE_PRINTING)
191 #if defined(ENABLE_PRINT_PREVIEW)
192 printing::PrintViewManager::CreateForWebContents(web_contents
);
193 printing::PrintPreviewMessageHandler::CreateForWebContents(web_contents
);
195 printing::PrintViewManagerBasic::CreateForWebContents(web_contents
);
196 #endif // defined(ENABLE_PRINT_PREVIEW)
197 #endif // defined(ENABLE_PRINTING)
198 extensions::ChromeExtensionWebContentsObserver::CreateForWebContents(
201 // Kiosk app supports zooming.
202 if (chrome::IsRunningInForcedAppMode())
203 ui_zoom::ZoomController::CreateForWebContents(web_contents
);
206 void ChromeAppDelegate::RenderViewCreated(
207 content::RenderViewHost
* render_view_host
) {
208 if (!chrome::IsRunningInForcedAppMode()) {
209 // Due to a bug in the way apps reacted to default zoom changes, some apps
210 // can incorrectly have host level zoom settings. These aren't wanted as
211 // apps cannot be zoomed, so are removed. This should be removed if apps
212 // can be made to zoom again.
213 // See http://crbug.com/446759 for more details.
214 content::WebContents
* web_contents
=
215 content::WebContents::FromRenderViewHost(render_view_host
);
216 DCHECK(web_contents
);
217 content::HostZoomMap
* zoom_map
=
218 content::HostZoomMap::GetForWebContents(web_contents
);
220 zoom_map
->SetZoomLevelForHost(web_contents
->GetURL().host(), 0);
224 void ChromeAppDelegate::ResizeWebContents(content::WebContents
* web_contents
,
225 const gfx::Size
& size
) {
226 ::ResizeWebContents(web_contents
, size
);
229 content::WebContents
* ChromeAppDelegate::OpenURLFromTab(
230 content::BrowserContext
* context
,
231 content::WebContents
* source
,
232 const content::OpenURLParams
& params
) {
233 return OpenURLFromTabInternal(context
, params
);
236 void ChromeAppDelegate::AddNewContents(content::BrowserContext
* context
,
237 content::WebContents
* new_contents
,
238 WindowOpenDisposition disposition
,
239 const gfx::Rect
& initial_rect
,
242 if (!disable_external_open_for_testing_
) {
243 // We don't really want to open a window for |new_contents|, but we need to
244 // capture its intended navigation. Here we give ownership to the
245 // NewWindowContentsDelegate, which will dispose of the contents once
246 // a navigation is captured.
247 new_contents
->SetDelegate(new_window_contents_delegate_
.get());
250 chrome::ScopedTabbedBrowserDisplayer
displayer(
251 Profile::FromBrowserContext(context
), chrome::GetActiveDesktop());
252 // Force all links to open in a new tab, even if they were trying to open a
255 disposition
== NEW_BACKGROUND_TAB
? disposition
: NEW_FOREGROUND_TAB
;
256 chrome::AddWebContents(displayer
.browser(),
265 content::ColorChooser
* ChromeAppDelegate::ShowColorChooser(
266 content::WebContents
* web_contents
,
267 SkColor initial_color
) {
268 return chrome::ShowColorChooser(web_contents
, initial_color
);
271 void ChromeAppDelegate::RunFileChooser(
272 content::WebContents
* tab
,
273 const content::FileChooserParams
& params
) {
274 FileSelectHelper::RunFileChooser(tab
, params
);
277 void ChromeAppDelegate::RequestMediaAccessPermission(
278 content::WebContents
* web_contents
,
279 const content::MediaStreamRequest
& request
,
280 const content::MediaResponseCallback
& callback
,
281 const extensions::Extension
* extension
) {
282 MediaCaptureDevicesDispatcher::GetInstance()->ProcessMediaAccessRequest(
283 web_contents
, request
, callback
, extension
);
286 bool ChromeAppDelegate::CheckMediaAccessPermission(
287 content::WebContents
* web_contents
,
288 const GURL
& security_origin
,
289 content::MediaStreamType type
,
290 const extensions::Extension
* extension
) {
291 return MediaCaptureDevicesDispatcher::GetInstance()
292 ->CheckMediaAccessPermission(
293 web_contents
, security_origin
, type
, extension
);
296 int ChromeAppDelegate::PreferredIconSize() {
298 return ash::kShelfSize
;
300 return extension_misc::EXTENSION_ICON_SMALL
;
304 void ChromeAppDelegate::SetWebContentsBlocked(
305 content::WebContents
* web_contents
,
308 web_contents
->Focus();
309 // RenderViewHost may be NULL during shutdown.
310 content::RenderViewHost
* host
= web_contents
->GetRenderViewHost();
312 host
->Send(new ChromeViewMsg_SetVisuallyDeemphasized(host
->GetRoutingID(),
317 bool ChromeAppDelegate::IsWebContentsVisible(
318 content::WebContents
* web_contents
) {
319 return platform_util::IsVisible(web_contents
->GetNativeView());
322 void ChromeAppDelegate::SetTerminatingCallback(const base::Closure
& callback
) {
323 terminating_callback_
= callback
;
326 void ChromeAppDelegate::OnHide() {
328 if (has_been_shown_
) {
333 // Hold on to the keep alive for some time to give the app a chance to show
335 content::BrowserThread::PostDelayedTask(
336 content::BrowserThread::UI
, FROM_HERE
,
337 base::Bind(&ChromeAppDelegate::RelinquishKeepAliveAfterTimeout
,
338 weak_factory_
.GetWeakPtr()),
339 base::TimeDelta::FromSeconds(kAppWindowFirstShowTimeoutSeconds
));
342 void ChromeAppDelegate::OnShow() {
343 has_been_shown_
= true;
345 keep_alive_
.reset(new ScopedKeepAlive
);
348 void ChromeAppDelegate::Observe(int type
,
349 const content::NotificationSource
& source
,
350 const content::NotificationDetails
& details
) {
352 case chrome::NOTIFICATION_APP_TERMINATING
:
353 if (!terminating_callback_
.is_null())
354 terminating_callback_
.Run();
357 NOTREACHED() << "Received unexpected notification";