Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(531)

Side by Side Diff: apps/shell_window.cc

Issue 166573005: Rename apps::ShellWindow to apps::AppWindow (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebase, nits (rename) Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « apps/shell_window.h ('k') | apps/shell_window_geometry_cache.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2013 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 "apps/shell_window.h"
6
7 #include "apps/shell_window_geometry_cache.h"
8 #include "apps/shell_window_registry.h"
9 #include "apps/ui/native_app_window.h"
10 #include "base/command_line.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/values.h"
14 #include "chrome/browser/chrome_notification_types.h"
15 #include "chrome/browser/extensions/extension_web_contents_observer.h"
16 #include "chrome/browser/extensions/suggest_permission_util.h"
17 #include "chrome/browser/lifetime/application_lifetime.h"
18 #include "chrome/common/chrome_switches.h"
19 #include "chrome/common/extensions/extension_messages.h"
20 #include "chrome/common/extensions/manifest_handlers/icons_handler.h"
21 #include "components/web_modal/web_contents_modal_dialog_manager.h"
22 #include "content/public/browser/browser_context.h"
23 #include "content/public/browser/invalidate_type.h"
24 #include "content/public/browser/navigation_entry.h"
25 #include "content/public/browser/notification_details.h"
26 #include "content/public/browser/notification_service.h"
27 #include "content/public/browser/notification_source.h"
28 #include "content/public/browser/notification_types.h"
29 #include "content/public/browser/render_view_host.h"
30 #include "content/public/browser/resource_dispatcher_host.h"
31 #include "content/public/browser/web_contents.h"
32 #include "content/public/browser/web_contents_view.h"
33 #include "content/public/common/media_stream_request.h"
34 #include "extensions/browser/extension_system.h"
35 #include "extensions/browser/extensions_browser_client.h"
36 #include "extensions/browser/process_manager.h"
37 #include "extensions/browser/view_type_utils.h"
38 #include "extensions/common/extension.h"
39 #include "third_party/skia/include/core/SkRegion.h"
40 #include "ui/gfx/screen.h"
41
42 #if !defined(OS_MACOSX)
43 #include "apps/pref_names.h"
44 #include "base/prefs/pref_service.h"
45 #endif
46
47 using content::BrowserContext;
48 using content::ConsoleMessageLevel;
49 using content::WebContents;
50 using extensions::APIPermission;
51 using web_modal::WebContentsModalDialogHost;
52 using web_modal::WebContentsModalDialogManager;
53
54 namespace {
55
56 const int kDefaultWidth = 512;
57 const int kDefaultHeight = 384;
58
59 bool IsFullscreen(int fullscreen_types) {
60 return fullscreen_types != apps::ShellWindow::FULLSCREEN_TYPE_NONE;
61 }
62
63 } // namespace
64
65 namespace apps {
66
67 ShellWindow::SizeConstraints::SizeConstraints()
68 : maximum_size_(kUnboundedSize, kUnboundedSize) {
69 }
70
71 ShellWindow::SizeConstraints::SizeConstraints(const gfx::Size& min_size,
72 const gfx::Size& max_size)
73 : minimum_size_(min_size),
74 maximum_size_(max_size) {
75 }
76
77 ShellWindow::SizeConstraints::~SizeConstraints() {}
78
79 gfx::Size ShellWindow::SizeConstraints::ClampSize(gfx::Size size) const {
80 const gfx::Size max_size = GetMaximumSize();
81 if (max_size.width() != kUnboundedSize)
82 size.set_width(std::min(size.width(), GetMaximumSize().width()));
83 if (max_size.height() != kUnboundedSize)
84 size.set_height(std::min(size.height(), GetMaximumSize().height()));
85 size.SetToMax(GetMinimumSize());
86 return size;
87 }
88
89 bool ShellWindow::SizeConstraints::HasMinimumSize() const {
90 return GetMinimumSize().width() != kUnboundedSize ||
91 GetMinimumSize().height() != kUnboundedSize;
92 }
93
94 bool ShellWindow::SizeConstraints::HasMaximumSize() const {
95 const gfx::Size max_size = GetMaximumSize();
96 return max_size.width() != kUnboundedSize ||
97 max_size.height() != kUnboundedSize;
98 }
99
100 bool ShellWindow::SizeConstraints::HasFixedSize() const {
101 return !GetMinimumSize().IsEmpty() && GetMinimumSize() == GetMaximumSize();
102 }
103
104 gfx::Size ShellWindow::SizeConstraints::GetMinimumSize() const {
105 return minimum_size_;
106 }
107
108 gfx::Size ShellWindow::SizeConstraints::GetMaximumSize() const {
109 return gfx::Size(
110 maximum_size_.width() == kUnboundedSize ?
111 kUnboundedSize :
112 std::max(maximum_size_.width(), minimum_size_.width()),
113 maximum_size_.height() == kUnboundedSize ?
114 kUnboundedSize :
115 std::max(maximum_size_.height(), minimum_size_.height()));
116 }
117
118 void ShellWindow::SizeConstraints::set_minimum_size(const gfx::Size& min_size) {
119 minimum_size_ = min_size;
120 }
121
122 void ShellWindow::SizeConstraints::set_maximum_size(const gfx::Size& max_size) {
123 maximum_size_ = max_size;
124 }
125
126 ShellWindow::CreateParams::CreateParams()
127 : window_type(ShellWindow::WINDOW_TYPE_DEFAULT),
128 frame(ShellWindow::FRAME_CHROME),
129 transparent_background(false),
130 bounds(INT_MIN, INT_MIN, 0, 0),
131 creator_process_id(0),
132 state(ui::SHOW_STATE_DEFAULT),
133 hidden(false),
134 resizable(true),
135 focused(true),
136 always_on_top(false) {}
137
138 ShellWindow::CreateParams::~CreateParams() {}
139
140 ShellWindow::Delegate::~Delegate() {}
141
142 ShellWindow::ShellWindow(BrowserContext* context,
143 Delegate* delegate,
144 const extensions::Extension* extension)
145 : browser_context_(context),
146 extension_(extension),
147 extension_id_(extension->id()),
148 window_type_(WINDOW_TYPE_DEFAULT),
149 delegate_(delegate),
150 image_loader_ptr_factory_(this),
151 fullscreen_types_(FULLSCREEN_TYPE_NONE),
152 show_on_first_paint_(false),
153 first_paint_complete_(false),
154 cached_always_on_top_(false) {
155 extensions::ExtensionsBrowserClient* client =
156 extensions::ExtensionsBrowserClient::Get();
157 CHECK(!client->IsGuestSession(context) || context->IsOffTheRecord())
158 << "Only off the record window may be opened in the guest mode.";
159 }
160
161 void ShellWindow::Init(const GURL& url,
162 ShellWindowContents* shell_window_contents,
163 const CreateParams& params) {
164 // Initialize the render interface and web contents
165 shell_window_contents_.reset(shell_window_contents);
166 shell_window_contents_->Initialize(browser_context(), url);
167 WebContents* web_contents = shell_window_contents_->GetWebContents();
168 if (CommandLine::ForCurrentProcess()->HasSwitch(
169 switches::kEnableAppsShowOnFirstPaint)) {
170 content::WebContentsObserver::Observe(web_contents);
171 }
172 delegate_->InitWebContents(web_contents);
173 WebContentsModalDialogManager::CreateForWebContents(web_contents);
174 extensions::ExtensionWebContentsObserver::CreateForWebContents(web_contents);
175
176 web_contents->SetDelegate(this);
177 WebContentsModalDialogManager::FromWebContents(web_contents)->
178 SetDelegate(this);
179 extensions::SetViewType(web_contents, extensions::VIEW_TYPE_APP_SHELL);
180
181 // Initialize the window
182 CreateParams new_params = LoadDefaultsAndConstrain(params);
183 window_type_ = new_params.window_type;
184 window_key_ = new_params.window_key;
185 size_constraints_ = SizeConstraints(new_params.minimum_size,
186 new_params.maximum_size);
187
188 // Windows cannot be always-on-top in fullscreen mode for security reasons.
189 cached_always_on_top_ = new_params.always_on_top;
190 if (new_params.state == ui::SHOW_STATE_FULLSCREEN)
191 new_params.always_on_top = false;
192
193 native_app_window_.reset(delegate_->CreateNativeAppWindow(this, new_params));
194
195 if (!new_params.hidden) {
196 // Panels are not activated by default.
197 Show(window_type_is_panel() || !new_params.focused ? SHOW_INACTIVE
198 : SHOW_ACTIVE);
199 }
200
201 if (new_params.state == ui::SHOW_STATE_FULLSCREEN)
202 Fullscreen();
203 else if (new_params.state == ui::SHOW_STATE_MAXIMIZED)
204 Maximize();
205 else if (new_params.state == ui::SHOW_STATE_MINIMIZED)
206 Minimize();
207
208 OnNativeWindowChanged();
209
210 // When the render view host is changed, the native window needs to know
211 // about it in case it has any setup to do to make the renderer appear
212 // properly. In particular, on Windows, the view's clickthrough region needs
213 // to be set.
214 extensions::ExtensionsBrowserClient* client =
215 extensions::ExtensionsBrowserClient::Get();
216 registrar_.Add(this,
217 chrome::NOTIFICATION_EXTENSION_UNLOADED,
218 content::Source<content::BrowserContext>(
219 client->GetOriginalContext(browser_context_)));
220 // Close when the browser process is exiting.
221 registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
222 content::NotificationService::AllSources());
223
224 shell_window_contents_->LoadContents(new_params.creator_process_id);
225
226 if (CommandLine::ForCurrentProcess()->HasSwitch(
227 switches::kEnableAppsShowOnFirstPaint)) {
228 // We want to show the window only when the content has been painted. For
229 // that to happen, we need to define a size for the content, otherwise the
230 // layout will happen in a 0x0 area.
231 // Note: WebContents::GetView() is guaranteed to be non-null.
232 web_contents->GetView()->SizeContents(new_params.bounds.size());
233 }
234
235 // Prevent the browser process from shutting down while this window is open.
236 chrome::StartKeepAlive();
237
238 UpdateExtensionAppIcon();
239
240 ShellWindowRegistry::Get(browser_context_)->AddShellWindow(this);
241 }
242
243 ShellWindow::~ShellWindow() {
244 // Unregister now to prevent getting NOTIFICATION_APP_TERMINATING if we're the
245 // last window open.
246 registrar_.RemoveAll();
247
248 // Remove shutdown prevention.
249 chrome::EndKeepAlive();
250 }
251
252 void ShellWindow::RequestMediaAccessPermission(
253 content::WebContents* web_contents,
254 const content::MediaStreamRequest& request,
255 const content::MediaResponseCallback& callback) {
256 delegate_->RequestMediaAccessPermission(web_contents, request, callback,
257 extension());
258 }
259
260 WebContents* ShellWindow::OpenURLFromTab(WebContents* source,
261 const content::OpenURLParams& params) {
262 // Don't allow the current tab to be navigated. It would be nice to map all
263 // anchor tags (even those without target="_blank") to new tabs, but right
264 // now we can't distinguish between those and <meta> refreshes or window.href
265 // navigations, which we don't want to allow.
266 // TOOD(mihaip): Can we check for user gestures instead?
267 WindowOpenDisposition disposition = params.disposition;
268 if (disposition == CURRENT_TAB) {
269 AddMessageToDevToolsConsole(
270 content::CONSOLE_MESSAGE_LEVEL_ERROR,
271 base::StringPrintf(
272 "Can't open same-window link to \"%s\"; try target=\"_blank\".",
273 params.url.spec().c_str()));
274 return NULL;
275 }
276
277 // These dispositions aren't really navigations.
278 if (disposition == SUPPRESS_OPEN || disposition == SAVE_TO_DISK ||
279 disposition == IGNORE_ACTION) {
280 return NULL;
281 }
282
283 WebContents* contents =
284 delegate_->OpenURLFromTab(browser_context_, source, params);
285 if (!contents) {
286 AddMessageToDevToolsConsole(
287 content::CONSOLE_MESSAGE_LEVEL_ERROR,
288 base::StringPrintf(
289 "Can't navigate to \"%s\"; apps do not support navigation.",
290 params.url.spec().c_str()));
291 }
292
293 return contents;
294 }
295
296 void ShellWindow::AddNewContents(WebContents* source,
297 WebContents* new_contents,
298 WindowOpenDisposition disposition,
299 const gfx::Rect& initial_pos,
300 bool user_gesture,
301 bool* was_blocked) {
302 DCHECK(new_contents->GetBrowserContext() == browser_context_);
303 delegate_->AddNewContents(browser_context_,
304 new_contents,
305 disposition,
306 initial_pos,
307 user_gesture,
308 was_blocked);
309 }
310
311 bool ShellWindow::PreHandleKeyboardEvent(
312 content::WebContents* source,
313 const content::NativeWebKeyboardEvent& event,
314 bool* is_keyboard_shortcut) {
315 // Here, we can handle a key event before the content gets it. When we are
316 // fullscreen and it is not forced, we want to allow the user to leave
317 // when ESC is pressed.
318 // However, if the application has the "overrideEscFullscreen" permission, we
319 // should let it override that behavior.
320 // ::HandleKeyboardEvent() will only be called if the KeyEvent's default
321 // action is not prevented.
322 // Thus, we should handle the KeyEvent here only if the permission is not set.
323 if (event.windowsKeyCode == ui::VKEY_ESCAPE &&
324 (fullscreen_types_ != FULLSCREEN_TYPE_NONE) &&
325 ((fullscreen_types_ & FULLSCREEN_TYPE_FORCED) == 0) &&
326 !extension_->HasAPIPermission(APIPermission::kOverrideEscFullscreen)) {
327 Restore();
328 return true;
329 }
330
331 return false;
332 }
333
334 void ShellWindow::HandleKeyboardEvent(
335 WebContents* source,
336 const content::NativeWebKeyboardEvent& event) {
337 // If the window is currently fullscreen and not forced, ESC should leave
338 // fullscreen. If this code is being called for ESC, that means that the
339 // KeyEvent's default behavior was not prevented by the content.
340 if (event.windowsKeyCode == ui::VKEY_ESCAPE &&
341 (fullscreen_types_ != FULLSCREEN_TYPE_NONE) &&
342 ((fullscreen_types_ & FULLSCREEN_TYPE_FORCED) == 0)) {
343 Restore();
344 return;
345 }
346
347 native_app_window_->HandleKeyboardEvent(event);
348 }
349
350 void ShellWindow::RequestToLockMouse(WebContents* web_contents,
351 bool user_gesture,
352 bool last_unlocked_by_target) {
353 bool has_permission = IsExtensionWithPermissionOrSuggestInConsole(
354 APIPermission::kPointerLock,
355 extension_,
356 web_contents->GetRenderViewHost());
357
358 web_contents->GotResponseToLockMouseRequest(has_permission);
359 }
360
361 bool ShellWindow::PreHandleGestureEvent(
362 WebContents* source,
363 const blink::WebGestureEvent& event) {
364 // Disable pinch zooming in shell windows.
365 return event.type == blink::WebGestureEvent::GesturePinchBegin ||
366 event.type == blink::WebGestureEvent::GesturePinchUpdate ||
367 event.type == blink::WebGestureEvent::GesturePinchEnd;
368 }
369
370 void ShellWindow::DidFirstVisuallyNonEmptyPaint(int32 page_id) {
371 first_paint_complete_ = true;
372 if (show_on_first_paint_) {
373 DCHECK(delayed_show_type_ == SHOW_ACTIVE ||
374 delayed_show_type_ == SHOW_INACTIVE);
375 Show(delayed_show_type_);
376 }
377 }
378
379 void ShellWindow::OnNativeClose() {
380 ShellWindowRegistry::Get(browser_context_)->RemoveShellWindow(this);
381 if (shell_window_contents_) {
382 WebContents* web_contents = shell_window_contents_->GetWebContents();
383 WebContentsModalDialogManager::FromWebContents(web_contents)->
384 SetDelegate(NULL);
385 shell_window_contents_->NativeWindowClosed();
386 }
387 delete this;
388 }
389
390 void ShellWindow::OnNativeWindowChanged() {
391 SaveWindowPosition();
392
393 #if defined(OS_WIN)
394 if (native_app_window_ &&
395 cached_always_on_top_ &&
396 !IsFullscreen(fullscreen_types_) &&
397 !native_app_window_->IsMaximized() &&
398 !native_app_window_->IsMinimized()) {
399 UpdateNativeAlwaysOnTop();
400 }
401 #endif
402
403 if (shell_window_contents_ && native_app_window_)
404 shell_window_contents_->NativeWindowChanged(native_app_window_.get());
405 }
406
407 void ShellWindow::OnNativeWindowActivated() {
408 ShellWindowRegistry::Get(browser_context_)->ShellWindowActivated(this);
409 }
410
411 content::WebContents* ShellWindow::web_contents() const {
412 return shell_window_contents_->GetWebContents();
413 }
414
415 NativeAppWindow* ShellWindow::GetBaseWindow() {
416 return native_app_window_.get();
417 }
418
419 gfx::NativeWindow ShellWindow::GetNativeWindow() {
420 return GetBaseWindow()->GetNativeWindow();
421 }
422
423 gfx::Rect ShellWindow::GetClientBounds() const {
424 gfx::Rect bounds = native_app_window_->GetBounds();
425 bounds.Inset(native_app_window_->GetFrameInsets());
426 return bounds;
427 }
428
429 base::string16 ShellWindow::GetTitle() const {
430 // WebContents::GetTitle() will return the page's URL if there's no <title>
431 // specified. However, we'd prefer to show the name of the extension in that
432 // case, so we directly inspect the NavigationEntry's title.
433 base::string16 title;
434 if (!web_contents() ||
435 !web_contents()->GetController().GetActiveEntry() ||
436 web_contents()->GetController().GetActiveEntry()->GetTitle().empty()) {
437 title = base::UTF8ToUTF16(extension()->name());
438 } else {
439 title = web_contents()->GetTitle();
440 }
441 const base::char16 kBadChars[] = { '\n', 0 };
442 base::RemoveChars(title, kBadChars, &title);
443 return title;
444 }
445
446 void ShellWindow::SetAppIconUrl(const GURL& url) {
447 // If the same url is being used for the badge, ignore it.
448 if (url == badge_icon_url_)
449 return;
450
451 // Avoid using any previous icons that were being downloaded.
452 image_loader_ptr_factory_.InvalidateWeakPtrs();
453
454 // Reset |app_icon_image_| to abort pending image load (if any).
455 app_icon_image_.reset();
456
457 app_icon_url_ = url;
458 web_contents()->DownloadImage(
459 url,
460 true, // is a favicon
461 0, // no maximum size
462 base::Bind(&ShellWindow::DidDownloadFavicon,
463 image_loader_ptr_factory_.GetWeakPtr()));
464 }
465
466 void ShellWindow::SetBadgeIconUrl(const GURL& url) {
467 // Avoid using any previous icons that were being downloaded.
468 image_loader_ptr_factory_.InvalidateWeakPtrs();
469
470 // Reset |app_icon_image_| to abort pending image load (if any).
471 badge_icon_image_.reset();
472
473 badge_icon_url_ = url;
474 web_contents()->DownloadImage(
475 url,
476 true, // is a favicon
477 0, // no maximum size
478 base::Bind(&ShellWindow::DidDownloadFavicon,
479 image_loader_ptr_factory_.GetWeakPtr()));
480 }
481
482 void ShellWindow::ClearBadge() {
483 badge_icon_image_.reset();
484 badge_icon_url_ = GURL();
485 UpdateBadgeIcon(gfx::Image());
486 }
487
488 void ShellWindow::UpdateShape(scoped_ptr<SkRegion> region) {
489 native_app_window_->UpdateShape(region.Pass());
490 }
491
492 void ShellWindow::UpdateDraggableRegions(
493 const std::vector<extensions::DraggableRegion>& regions) {
494 native_app_window_->UpdateDraggableRegions(regions);
495 }
496
497 void ShellWindow::UpdateAppIcon(const gfx::Image& image) {
498 if (image.IsEmpty())
499 return;
500 app_icon_ = image;
501 native_app_window_->UpdateWindowIcon();
502 ShellWindowRegistry::Get(browser_context_)->ShellWindowIconChanged(this);
503 }
504
505 void ShellWindow::Fullscreen() {
506 #if !defined(OS_MACOSX)
507 // Do not enter fullscreen mode if disallowed by pref.
508 PrefService* prefs =
509 extensions::ExtensionsBrowserClient::Get()->GetPrefServiceForContext(
510 browser_context());
511 if (!prefs->GetBoolean(prefs::kAppFullscreenAllowed))
512 return;
513 #endif
514 fullscreen_types_ |= FULLSCREEN_TYPE_WINDOW_API;
515 SetNativeWindowFullscreen();
516 }
517
518 void ShellWindow::Maximize() {
519 GetBaseWindow()->Maximize();
520 }
521
522 void ShellWindow::Minimize() {
523 GetBaseWindow()->Minimize();
524 }
525
526 void ShellWindow::Restore() {
527 if (IsFullscreen(fullscreen_types_)) {
528 fullscreen_types_ = FULLSCREEN_TYPE_NONE;
529 SetNativeWindowFullscreen();
530 } else {
531 GetBaseWindow()->Restore();
532 }
533 }
534
535 void ShellWindow::OSFullscreen() {
536 #if !defined(OS_MACOSX)
537 // Do not enter fullscreen mode if disallowed by pref.
538 PrefService* prefs =
539 extensions::ExtensionsBrowserClient::Get()->GetPrefServiceForContext(
540 browser_context());
541 if (!prefs->GetBoolean(prefs::kAppFullscreenAllowed))
542 return;
543 #endif
544 fullscreen_types_ |= FULLSCREEN_TYPE_OS;
545 SetNativeWindowFullscreen();
546 }
547
548 void ShellWindow::ForcedFullscreen() {
549 fullscreen_types_ |= FULLSCREEN_TYPE_FORCED;
550 SetNativeWindowFullscreen();
551 }
552
553 void ShellWindow::SetMinimumSize(const gfx::Size& min_size) {
554 size_constraints_.set_minimum_size(min_size);
555 OnSizeConstraintsChanged();
556 }
557
558 void ShellWindow::SetMaximumSize(const gfx::Size& max_size) {
559 size_constraints_.set_maximum_size(max_size);
560 OnSizeConstraintsChanged();
561 }
562
563 void ShellWindow::Show(ShowType show_type) {
564 if (CommandLine::ForCurrentProcess()->HasSwitch(
565 switches::kEnableAppsShowOnFirstPaint)) {
566 show_on_first_paint_ = true;
567
568 if (!first_paint_complete_) {
569 delayed_show_type_ = show_type;
570 return;
571 }
572 }
573
574 switch (show_type) {
575 case SHOW_ACTIVE:
576 GetBaseWindow()->Show();
577 break;
578 case SHOW_INACTIVE:
579 GetBaseWindow()->ShowInactive();
580 break;
581 }
582 }
583
584 void ShellWindow::Hide() {
585 // This is there to prevent race conditions with Hide() being called before
586 // there was a non-empty paint. It should have no effect in a non-racy
587 // scenario where the application is hiding then showing a window: the second
588 // show will not be delayed.
589 show_on_first_paint_ = false;
590 GetBaseWindow()->Hide();
591 }
592
593 void ShellWindow::SetAlwaysOnTop(bool always_on_top) {
594 if (cached_always_on_top_ == always_on_top)
595 return;
596
597 cached_always_on_top_ = always_on_top;
598
599 // As a security measure, do not allow fullscreen windows or windows that
600 // overlap the taskbar to be on top. The property will be applied when the
601 // window exits fullscreen and moves away from the taskbar.
602 if (!IsFullscreen(fullscreen_types_) && !IntersectsWithTaskbar())
603 native_app_window_->SetAlwaysOnTop(always_on_top);
604
605 OnNativeWindowChanged();
606 }
607
608 bool ShellWindow::IsAlwaysOnTop() const {
609 return cached_always_on_top_;
610 }
611
612 void ShellWindow::GetSerializedState(base::DictionaryValue* properties) const {
613 DCHECK(properties);
614
615 properties->SetBoolean("fullscreen",
616 native_app_window_->IsFullscreenOrPending());
617 properties->SetBoolean("minimized", native_app_window_->IsMinimized());
618 properties->SetBoolean("maximized", native_app_window_->IsMaximized());
619 properties->SetBoolean("alwaysOnTop", IsAlwaysOnTop());
620 scoped_ptr<base::DictionaryValue> boundsValue(new base::DictionaryValue());
621 gfx::Rect bounds = GetClientBounds();
622 boundsValue->SetInteger("left", bounds.x());
623 boundsValue->SetInteger("top", bounds.y());
624 boundsValue->SetInteger("width", bounds.width());
625 boundsValue->SetInteger("height", bounds.height());
626 properties->Set("bounds", boundsValue.release());
627
628 const SizeConstraints& constraints = size_constraints();
629 gfx::Size min_size = constraints.GetMinimumSize();
630 gfx::Size max_size = constraints.GetMaximumSize();
631 if (min_size.width() != SizeConstraints::kUnboundedSize)
632 properties->SetInteger("minWidth", min_size.width());
633 if (min_size.height() != SizeConstraints::kUnboundedSize)
634 properties->SetInteger("minHeight", min_size.height());
635 if (max_size.width() != SizeConstraints::kUnboundedSize)
636 properties->SetInteger("maxWidth", max_size.width());
637 if (max_size.height() != SizeConstraints::kUnboundedSize)
638 properties->SetInteger("maxHeight", max_size.height());
639 }
640
641 //------------------------------------------------------------------------------
642 // Private methods
643
644 void ShellWindow::UpdateBadgeIcon(const gfx::Image& image) {
645 badge_icon_ = image;
646 native_app_window_->UpdateBadgeIcon();
647 }
648
649 void ShellWindow::DidDownloadFavicon(
650 int id,
651 int http_status_code,
652 const GURL& image_url,
653 const std::vector<SkBitmap>& bitmaps,
654 const std::vector<gfx::Size>& original_bitmap_sizes) {
655 if ((image_url != app_icon_url_ && image_url != badge_icon_url_) ||
656 bitmaps.empty()) {
657 return;
658 }
659
660 // Bitmaps are ordered largest to smallest. Choose the smallest bitmap
661 // whose height >= the preferred size.
662 int largest_index = 0;
663 for (size_t i = 1; i < bitmaps.size(); ++i) {
664 if (bitmaps[i].height() < delegate_->PreferredIconSize())
665 break;
666 largest_index = i;
667 }
668 const SkBitmap& largest = bitmaps[largest_index];
669 if (image_url == app_icon_url_) {
670 UpdateAppIcon(gfx::Image::CreateFrom1xBitmap(largest));
671 return;
672 }
673
674 UpdateBadgeIcon(gfx::Image::CreateFrom1xBitmap(largest));
675 }
676
677 void ShellWindow::OnExtensionIconImageChanged(extensions::IconImage* image) {
678 DCHECK_EQ(app_icon_image_.get(), image);
679
680 UpdateAppIcon(gfx::Image(app_icon_image_->image_skia()));
681 }
682
683 void ShellWindow::UpdateExtensionAppIcon() {
684 // Avoid using any previous app icons were being downloaded.
685 image_loader_ptr_factory_.InvalidateWeakPtrs();
686
687 app_icon_image_.reset(
688 new extensions::IconImage(browser_context(),
689 extension(),
690 extensions::IconsInfo::GetIcons(extension()),
691 delegate_->PreferredIconSize(),
692 extensions::IconsInfo::GetDefaultAppIcon(),
693 this));
694
695 // Triggers actual image loading with 1x resources. The 2x resource will
696 // be handled by IconImage class when requested.
697 app_icon_image_->image_skia().GetRepresentation(1.0f);
698 }
699
700 void ShellWindow::OnSizeConstraintsChanged() {
701 native_app_window_->UpdateWindowMinMaxSize();
702 gfx::Rect bounds = GetClientBounds();
703 gfx::Size constrained_size = size_constraints_.ClampSize(bounds.size());
704 if (bounds.size() != constrained_size) {
705 bounds.set_size(constrained_size);
706 native_app_window_->SetBounds(bounds);
707 }
708 OnNativeWindowChanged();
709 }
710
711 void ShellWindow::SetNativeWindowFullscreen() {
712 native_app_window_->SetFullscreen(fullscreen_types_);
713
714 if (cached_always_on_top_)
715 UpdateNativeAlwaysOnTop();
716 }
717
718 bool ShellWindow::IntersectsWithTaskbar() const {
719 #if defined(OS_WIN)
720 gfx::Screen* screen = gfx::Screen::GetNativeScreen();
721 gfx::Rect window_bounds = native_app_window_->GetRestoredBounds();
722 std::vector<gfx::Display> displays = screen->GetAllDisplays();
723
724 for (std::vector<gfx::Display>::const_iterator it = displays.begin();
725 it != displays.end(); ++it) {
726 gfx::Rect taskbar_bounds = it->bounds();
727 taskbar_bounds.Subtract(it->work_area());
728 if (taskbar_bounds.IsEmpty())
729 continue;
730
731 if (window_bounds.Intersects(taskbar_bounds))
732 return true;
733 }
734 #endif
735
736 return false;
737 }
738
739 void ShellWindow::UpdateNativeAlwaysOnTop() {
740 DCHECK(cached_always_on_top_);
741 bool is_on_top = native_app_window_->IsAlwaysOnTop();
742 bool fullscreen = IsFullscreen(fullscreen_types_);
743 bool intersects_taskbar = IntersectsWithTaskbar();
744
745 if (is_on_top && (fullscreen || intersects_taskbar)) {
746 // When entering fullscreen or overlapping the taskbar, ensure windows are
747 // not always-on-top.
748 native_app_window_->SetAlwaysOnTop(false);
749 } else if (!is_on_top && !fullscreen && !intersects_taskbar) {
750 // When exiting fullscreen and moving away from the taskbar, reinstate
751 // always-on-top.
752 native_app_window_->SetAlwaysOnTop(true);
753 }
754 }
755
756 void ShellWindow::CloseContents(WebContents* contents) {
757 native_app_window_->Close();
758 }
759
760 bool ShellWindow::ShouldSuppressDialogs() {
761 return true;
762 }
763
764 content::ColorChooser* ShellWindow::OpenColorChooser(
765 WebContents* web_contents,
766 SkColor initial_color,
767 const std::vector<content::ColorSuggestion>& suggestionss) {
768 return delegate_->ShowColorChooser(web_contents, initial_color);
769 }
770
771 void ShellWindow::RunFileChooser(WebContents* tab,
772 const content::FileChooserParams& params) {
773 if (window_type_is_panel()) {
774 // Panels can't host a file dialog, abort. TODO(stevenjb): allow file
775 // dialogs to be unhosted but still close with the owning web contents.
776 // crbug.com/172502.
777 LOG(WARNING) << "File dialog opened by panel.";
778 return;
779 }
780
781 delegate_->RunFileChooser(tab, params);
782 }
783
784 bool ShellWindow::IsPopupOrPanel(const WebContents* source) const {
785 return true;
786 }
787
788 void ShellWindow::MoveContents(WebContents* source, const gfx::Rect& pos) {
789 native_app_window_->SetBounds(pos);
790 }
791
792 void ShellWindow::NavigationStateChanged(
793 const content::WebContents* source, unsigned changed_flags) {
794 if (changed_flags & content::INVALIDATE_TYPE_TITLE)
795 native_app_window_->UpdateWindowTitle();
796 else if (changed_flags & content::INVALIDATE_TYPE_TAB)
797 native_app_window_->UpdateWindowIcon();
798 }
799
800 void ShellWindow::ToggleFullscreenModeForTab(content::WebContents* source,
801 bool enter_fullscreen) {
802 #if !defined(OS_MACOSX)
803 // Do not enter fullscreen mode if disallowed by pref.
804 // TODO(bartfab): Add a test once it becomes possible to simulate a user
805 // gesture. http://crbug.com/174178
806 PrefService* prefs =
807 extensions::ExtensionsBrowserClient::Get()->GetPrefServiceForContext(
808 browser_context());
809 if (enter_fullscreen && !prefs->GetBoolean(prefs::kAppFullscreenAllowed)) {
810 return;
811 }
812 #endif
813
814 if (!IsExtensionWithPermissionOrSuggestInConsole(
815 APIPermission::kFullscreen,
816 extension_,
817 source->GetRenderViewHost())) {
818 return;
819 }
820
821 if (enter_fullscreen)
822 fullscreen_types_ |= FULLSCREEN_TYPE_HTML_API;
823 else
824 fullscreen_types_ &= ~FULLSCREEN_TYPE_HTML_API;
825 SetNativeWindowFullscreen();
826 }
827
828 bool ShellWindow::IsFullscreenForTabOrPending(
829 const content::WebContents* source) const {
830 return ((fullscreen_types_ & FULLSCREEN_TYPE_HTML_API) != 0);
831 }
832
833 void ShellWindow::Observe(int type,
834 const content::NotificationSource& source,
835 const content::NotificationDetails& details) {
836 switch (type) {
837 case chrome::NOTIFICATION_EXTENSION_UNLOADED: {
838 const extensions::Extension* unloaded_extension =
839 content::Details<extensions::UnloadedExtensionInfo>(
840 details)->extension;
841 if (extension_ == unloaded_extension)
842 native_app_window_->Close();
843 break;
844 }
845 case chrome::NOTIFICATION_APP_TERMINATING:
846 native_app_window_->Close();
847 break;
848 default:
849 NOTREACHED() << "Received unexpected notification";
850 }
851 }
852
853 void ShellWindow::SetWebContentsBlocked(content::WebContents* web_contents,
854 bool blocked) {
855 delegate_->SetWebContentsBlocked(web_contents, blocked);
856 }
857
858 bool ShellWindow::IsWebContentsVisible(content::WebContents* web_contents) {
859 return delegate_->IsWebContentsVisible(web_contents);
860 }
861
862 extensions::ActiveTabPermissionGranter*
863 ShellWindow::GetActiveTabPermissionGranter() {
864 // Shell windows don't support the activeTab permission.
865 return NULL;
866 }
867
868 WebContentsModalDialogHost* ShellWindow::GetWebContentsModalDialogHost() {
869 return native_app_window_.get();
870 }
871
872 void ShellWindow::AddMessageToDevToolsConsole(ConsoleMessageLevel level,
873 const std::string& message) {
874 content::RenderViewHost* rvh = web_contents()->GetRenderViewHost();
875 rvh->Send(new ExtensionMsg_AddMessageToConsole(
876 rvh->GetRoutingID(), level, message));
877 }
878
879 void ShellWindow::SaveWindowPosition() {
880 if (window_key_.empty())
881 return;
882 if (!native_app_window_)
883 return;
884
885 ShellWindowGeometryCache* cache =
886 ShellWindowGeometryCache::Get(browser_context());
887
888 gfx::Rect bounds = native_app_window_->GetRestoredBounds();
889 bounds.Inset(native_app_window_->GetFrameInsets());
890 gfx::Rect screen_bounds =
891 gfx::Screen::GetNativeScreen()->GetDisplayMatching(bounds).work_area();
892 ui::WindowShowState window_state = native_app_window_->GetRestoredState();
893 cache->SaveGeometry(extension()->id(),
894 window_key_,
895 bounds,
896 screen_bounds,
897 window_state);
898 }
899
900 void ShellWindow::AdjustBoundsToBeVisibleOnScreen(
901 const gfx::Rect& cached_bounds,
902 const gfx::Rect& cached_screen_bounds,
903 const gfx::Rect& current_screen_bounds,
904 const gfx::Size& minimum_size,
905 gfx::Rect* bounds) const {
906 *bounds = cached_bounds;
907
908 // Reposition and resize the bounds if the cached_screen_bounds is different
909 // from the current screen bounds and the current screen bounds doesn't
910 // completely contain the bounds.
911 if (cached_screen_bounds != current_screen_bounds &&
912 !current_screen_bounds.Contains(cached_bounds)) {
913 bounds->set_width(
914 std::max(minimum_size.width(),
915 std::min(bounds->width(), current_screen_bounds.width())));
916 bounds->set_height(
917 std::max(minimum_size.height(),
918 std::min(bounds->height(), current_screen_bounds.height())));
919 bounds->set_x(
920 std::max(current_screen_bounds.x(),
921 std::min(bounds->x(),
922 current_screen_bounds.right() - bounds->width())));
923 bounds->set_y(
924 std::max(current_screen_bounds.y(),
925 std::min(bounds->y(),
926 current_screen_bounds.bottom() - bounds->height())));
927 }
928 }
929
930 ShellWindow::CreateParams ShellWindow::LoadDefaultsAndConstrain(
931 CreateParams params) const {
932 if (params.bounds.width() == 0)
933 params.bounds.set_width(kDefaultWidth);
934 if (params.bounds.height() == 0)
935 params.bounds.set_height(kDefaultHeight);
936
937 // If left and top are left undefined, the native shell window will center
938 // the window on the main screen in a platform-defined manner.
939
940 // Load cached state if it exists.
941 if (!params.window_key.empty()) {
942 ShellWindowGeometryCache* cache =
943 ShellWindowGeometryCache::Get(browser_context());
944
945 gfx::Rect cached_bounds;
946 gfx::Rect cached_screen_bounds;
947 ui::WindowShowState cached_state = ui::SHOW_STATE_DEFAULT;
948 if (cache->GetGeometry(extension()->id(), params.window_key,
949 &cached_bounds, &cached_screen_bounds,
950 &cached_state)) {
951 // App window has cached screen bounds, make sure it fits on screen in
952 // case the screen resolution changed.
953 gfx::Screen* screen = gfx::Screen::GetNativeScreen();
954 gfx::Display display = screen->GetDisplayMatching(cached_bounds);
955 gfx::Rect current_screen_bounds = display.work_area();
956 AdjustBoundsToBeVisibleOnScreen(cached_bounds,
957 cached_screen_bounds,
958 current_screen_bounds,
959 params.minimum_size,
960 &params.bounds);
961 params.state = cached_state;
962 }
963 }
964
965 SizeConstraints size_constraints(params.minimum_size, params.maximum_size);
966 params.bounds.set_size(size_constraints.ClampSize(params.bounds.size()));
967 params.minimum_size = size_constraints.GetMinimumSize();
968 params.maximum_size = size_constraints.GetMaximumSize();
969
970 return params;
971 }
972
973 // static
974 SkRegion* ShellWindow::RawDraggableRegionsToSkRegion(
975 const std::vector<extensions::DraggableRegion>& regions) {
976 SkRegion* sk_region = new SkRegion;
977 for (std::vector<extensions::DraggableRegion>::const_iterator iter =
978 regions.begin();
979 iter != regions.end(); ++iter) {
980 const extensions::DraggableRegion& region = *iter;
981 sk_region->op(
982 region.bounds.x(),
983 region.bounds.y(),
984 region.bounds.right(),
985 region.bounds.bottom(),
986 region.draggable ? SkRegion::kUnion_Op : SkRegion::kDifference_Op);
987 }
988 return sk_region;
989 }
990
991 } // namespace apps
OLDNEW
« no previous file with comments | « apps/shell_window.h ('k') | apps/shell_window_geometry_cache.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698