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