Hidden windows should not keep Chrome alive.
[chromium-blink-merge.git] / chrome / browser / ui / apps / chrome_app_delegate.cc
blob74218670f39f280b94de61dab69670e69b24bd66
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_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/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"
36 #if defined(USE_ASH)
37 #include "ash/shelf/shelf_constants.h"
38 #endif
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"
44 #else
45 #include "chrome/browser/printing/print_view_manager_basic.h"
46 #endif // defined(ENABLE_PRINT_PREVIEW)
47 #endif // defined(ENABLE_PRINTING)
49 namespace {
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
61 // window.
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;
66 } else {
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 {
82 public:
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
88 // browser or not.
89 void SetDefaultWebClientUIState(
90 ShellIntegration::DefaultWebClientUIState state) override {
91 Profile* profile =
92 Profile::FromBrowserContext(source_->GetBrowserContext());
93 DCHECK(profile);
94 if (!profile)
95 return;
96 switch (state) {
97 case ShellIntegration::STATE_PROCESSING:
98 break;
99 case ShellIntegration::STATE_IS_DEFAULT:
100 OpenURLFromTabInternal(profile, params_);
101 break;
102 case ShellIntegration::STATE_NOT_DEFAULT:
103 case ShellIntegration::STATE_UNKNOWN:
104 platform_util::OpenExternal(profile, params_.url);
105 break;
109 bool IsOwnedByWorker() override { return true; }
111 private:
112 scoped_ptr<content::WebContents> source_;
113 const content::OpenURLParams params_;
116 } // namespace
118 // static
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 {
130 public:
131 NewWindowContentsDelegate() {}
132 ~NewWindowContentsDelegate() override {}
134 content::WebContents* OpenURLFromTab(
135 content::WebContents* source,
136 const content::OpenURLParams& params) override;
138 private:
139 DISALLOW_COPY_AND_ASSIGN(NewWindowContentsDelegate);
142 content::WebContents*
143 ChromeAppDelegate::NewWindowContentsDelegate::OpenURLFromTab(
144 content::WebContents* source,
145 const content::OpenURLParams& params) {
146 if (source) {
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(),
157 params));
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();
164 return NULL;
167 ChromeAppDelegate::ChromeAppDelegate(scoped_ptr<ScopedKeepAlive> keep_alive)
168 : has_been_shown_(false),
169 is_hidden_(true),
170 keep_alive_(keep_alive.Pass()),
171 new_window_contents_delegate_(new NewWindowContentsDelegate()),
172 weak_factory_(this) {
173 registrar_.Add(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 FaviconTabHelper::CreateForWebContents(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);
194 #else
195 printing::PrintViewManagerBasic::CreateForWebContents(web_contents);
196 #endif // defined(ENABLE_PRINT_PREVIEW)
197 #endif // defined(ENABLE_PRINTING)
198 extensions::ChromeExtensionWebContentsObserver::CreateForWebContents(
199 web_contents);
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);
219 DCHECK(zoom_map);
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,
240 bool user_gesture,
241 bool* was_blocked) {
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());
248 return;
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
253 // new window.
254 disposition =
255 disposition == NEW_BACKGROUND_TAB ? disposition : NEW_FOREGROUND_TAB;
256 chrome::AddWebContents(displayer.browser(),
257 NULL,
258 new_contents,
259 disposition,
260 initial_rect,
261 user_gesture,
262 was_blocked);
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() {
297 #if defined(USE_ASH)
298 return ash::kShelfSize;
299 #else
300 return extension_misc::EXTENSION_ICON_SMALL;
301 #endif
304 void ChromeAppDelegate::SetWebContentsBlocked(
305 content::WebContents* web_contents,
306 bool blocked) {
307 // RenderViewHost may be NULL during shutdown.
308 content::RenderViewHost* host = web_contents->GetRenderViewHost();
309 if (host) {
310 host->Send(new ChromeViewMsg_SetVisuallyDeemphasized(host->GetRoutingID(),
311 blocked));
315 bool ChromeAppDelegate::IsWebContentsVisible(
316 content::WebContents* web_contents) {
317 return platform_util::IsVisible(web_contents->GetNativeView());
320 void ChromeAppDelegate::SetTerminatingCallback(const base::Closure& callback) {
321 terminating_callback_ = callback;
324 void ChromeAppDelegate::OnHide() {
325 is_hidden_ = true;
326 if (has_been_shown_) {
327 keep_alive_.reset();
328 return;
331 // Hold on to the keep alive for some time to give the app a chance to show
332 // the window.
333 content::BrowserThread::PostDelayedTask(
334 content::BrowserThread::UI, FROM_HERE,
335 base::Bind(&ChromeAppDelegate::RelinquishKeepAliveAfterTimeout,
336 weak_factory_.GetWeakPtr()),
337 base::TimeDelta::FromSeconds(kAppWindowFirstShowTimeoutSeconds));
340 void ChromeAppDelegate::OnShow() {
341 has_been_shown_ = true;
342 is_hidden_ = false;
343 keep_alive_.reset(new ScopedKeepAlive);
346 void ChromeAppDelegate::Observe(int type,
347 const content::NotificationSource& source,
348 const content::NotificationDetails& details) {
349 switch (type) {
350 case chrome::NOTIFICATION_APP_TERMINATING:
351 if (!terminating_callback_.is_null())
352 terminating_callback_.Run();
353 break;
354 default:
355 NOTREACHED() << "Received unexpected notification";