diff options
author | benwells@chromium.org <benwells@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-07-01 07:07:39 +0000 |
---|---|---|
committer | benwells@chromium.org <benwells@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-07-01 07:07:39 +0000 |
commit | d53417e9256a32e27d99d5bca5fa01b35d46ef65 (patch) | |
tree | 548251ac2823f1112015c4147b2260403d662604 /apps | |
parent | 43e54db458b7c37bb681cd34a7d991f8e18e1091 (diff) | |
download | chromium_src-d53417e9256a32e27d99d5bca5fa01b35d46ef65.zip chromium_src-d53417e9256a32e27d99d5bca5fa01b35d46ef65.tar.gz chromium_src-d53417e9256a32e27d99d5bca5fa01b35d46ef65.tar.bz2 |
Move ShellWindow into apps component.
This involves creating a new delegate type, ShellWindow::Delegate, which
is implemented in chrome.
BUG=159366
Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=208927
Review URL: https://chromiumcodereview.appspot.com/16702003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@209371 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'apps')
-rw-r--r-- | apps/DEPS | 7 | ||||
-rw-r--r-- | apps/app_lifetime_monitor.cc | 2 | ||||
-rw-r--r-- | apps/app_restore_service.cc | 2 | ||||
-rw-r--r-- | apps/app_shim/extension_app_shim_handler_mac.cc | 2 | ||||
-rw-r--r-- | apps/apps.gypi | 2 | ||||
-rw-r--r-- | apps/shell_window.cc | 649 | ||||
-rw-r--r-- | apps/shell_window.h | 380 |
7 files changed, 1041 insertions, 3 deletions
@@ -3,17 +3,22 @@ include_rules = [ "+content", "+components/browser_context_keyed_service", "+components/user_prefs/pref_registry_syncable.h", + "+components/web_modal", + "+extensions", + "+skia/ext", + "+third_party/skia/include", "+ui", "+win8", # Temporary allowed includes. # TODO(benwells): remove these (http://crbug.com/159366) "+chrome/browser/browser_process.h", "+chrome/browser/extensions", + "+chrome/browser/lifetime/application_lifetime.h", "+chrome/browser/profiles", + "+chrome/browser/sessions/session_id.h", "+chrome/browser/shell_integration.h", "+chrome/browser/ui/extensions/application_launch.h", "+chrome/browser/ui/extensions/native_app_window.h", - "+chrome/browser/ui/extensions/shell_window.h", "+chrome/browser/ui/host_desktop.h", "+chrome/browser/ui/web_applications/web_app_ui.h", "+chrome/browser/web_applications/web_app.h", diff --git a/apps/app_lifetime_monitor.cc b/apps/app_lifetime_monitor.cc index 6c2b9b9..c8c8235 100644 --- a/apps/app_lifetime_monitor.cc +++ b/apps/app_lifetime_monitor.cc @@ -4,8 +4,8 @@ #include "apps/app_lifetime_monitor.h" +#include "apps/shell_window.h" #include "chrome/browser/extensions/extension_host.h" -#include "chrome/browser/ui/extensions/shell_window.h" #include "chrome/common/chrome_notification_types.h" #include "chrome/common/extensions/extension.h" #include "content/public/browser/notification_details.h" diff --git a/apps/app_restore_service.cc b/apps/app_restore_service.cc index 210ccf3..d865fb2 100644 --- a/apps/app_restore_service.cc +++ b/apps/app_restore_service.cc @@ -7,12 +7,14 @@ #include "apps/app_lifetime_monitor_factory.h" #include "apps/app_restore_service_factory.h" #include "apps/saved_files_service.h" +#include "apps/shell_window.h" #include "chrome/browser/extensions/api/app_runtime/app_runtime_api.h" #include "chrome/browser/extensions/extension_host.h" #include "chrome/browser/extensions/extension_prefs.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" #include "chrome/browser/extensions/platform_app_launcher.h" +#include "chrome/common/chrome_notification_types.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/extension_set.h" diff --git a/apps/app_shim/extension_app_shim_handler_mac.cc b/apps/app_shim/extension_app_shim_handler_mac.cc index eea475c..c020fb6 100644 --- a/apps/app_shim/extension_app_shim_handler_mac.cc +++ b/apps/app_shim/extension_app_shim_handler_mac.cc @@ -6,6 +6,7 @@ #include "apps/app_lifetime_monitor_factory.h" #include "apps/app_shim/app_shim_messages.h" +#include "apps/shell_window.h" #include "base/files/file_path.h" #include "base/logging.h" #include "chrome/browser/browser_process.h" @@ -17,7 +18,6 @@ #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/ui/extensions/application_launch.h" #include "chrome/browser/ui/extensions/native_app_window.h" -#include "chrome/browser/ui/extensions/shell_window.h" #include "chrome/browser/ui/web_applications/web_app_ui.h" #include "chrome/browser/web_applications/web_app_mac.h" #include "chrome/common/chrome_notification_types.h" diff --git a/apps/apps.gypi b/apps/apps.gypi index 3d3add7..2d8eeae 100644 --- a/apps/apps.gypi +++ b/apps/apps.gypi @@ -57,6 +57,8 @@ 'saved_files_service.h', 'saved_files_service_factory.cc', 'saved_files_service_factory.h', + 'shell_window.cc', + 'shell_window.h', 'shell_window_geometry_cache.cc', 'shell_window_geometry_cache.h', 'shortcut_manager.cc', diff --git a/apps/shell_window.cc b/apps/shell_window.cc new file mode 100644 index 0000000..d3ba855 --- /dev/null +++ b/apps/shell_window.cc @@ -0,0 +1,649 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "apps/shell_window.h" + +#include "apps/shell_window_geometry_cache.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "base/values.h" +#include "chrome/browser/extensions/app_window_contents.h" +#include "chrome/browser/extensions/extension_process_manager.h" +#include "chrome/browser/extensions/extension_system.h" +#include "chrome/browser/extensions/image_loader.h" +#include "chrome/browser/extensions/shell_window_registry.h" +#include "chrome/browser/extensions/suggest_permission_util.h" +#include "chrome/browser/lifetime/application_lifetime.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/extensions/native_app_window.h" +#include "chrome/common/chrome_notification_types.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/extension_constants.h" +#include "chrome/common/extensions/extension_messages.h" +#include "chrome/common/extensions/manifest_handlers/icons_handler.h" +#include "components/web_modal/web_contents_modal_dialog_manager.h" +#include "content/public/browser/invalidate_type.h" +#include "content/public/browser/navigation_entry.h" +#include "content/public/browser/notification_details.h" +#include "content/public/browser/notification_service.h" +#include "content/public/browser/notification_source.h" +#include "content/public/browser/notification_types.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/browser/resource_dispatcher_host.h" +#include "content/public/browser/web_contents.h" +#include "content/public/common/media_stream_request.h" +#include "extensions/browser/view_type_utils.h" +#include "skia/ext/image_operations.h" +#include "third_party/skia/include/core/SkRegion.h" +#include "ui/gfx/image/image_skia.h" +#include "ui/gfx/screen.h" + +using content::ConsoleMessageLevel; +using content::WebContents; +using extensions::APIPermission; +using web_modal::WebContentsModalDialogHost; +using web_modal::WebContentsModalDialogManager; + +namespace { +const int kDefaultWidth = 512; +const int kDefaultHeight = 384; + +} // namespace + +namespace apps { + +ShellWindow::CreateParams::CreateParams() + : window_type(ShellWindow::WINDOW_TYPE_DEFAULT), + frame(ShellWindow::FRAME_CHROME), + transparent_background(false), + bounds(INT_MIN, INT_MIN, 0, 0), + creator_process_id(0), + state(ui::SHOW_STATE_DEFAULT), + hidden(false), + resizable(true), + focused(true) {} + +ShellWindow::CreateParams::~CreateParams() {} + +ShellWindow::Delegate::~Delegate() {} + +ShellWindow* ShellWindow::Create(Profile* profile, + Delegate* delegate, + const extensions::Extension* extension, + const GURL& url, + const CreateParams& params) { + // This object will delete itself when the window is closed. + ShellWindow* window = new ShellWindow(profile, delegate, extension); + window->Init(url, new AppWindowContents(window), params); + extensions::ShellWindowRegistry::Get(profile)->AddShellWindow(window); + return window; +} + +ShellWindow::ShellWindow(Profile* profile, + Delegate* delegate, + const extensions::Extension* extension) + : profile_(profile), + extension_(extension), + extension_id_(extension->id()), + window_type_(WINDOW_TYPE_DEFAULT), + delegate_(delegate), + image_loader_ptr_factory_(this), + fullscreen_for_window_api_(false), + fullscreen_for_tab_(false) { +} + +void ShellWindow::Init(const GURL& url, + ShellWindowContents* shell_window_contents, + const CreateParams& params) { + // Initialize the render interface and web contents + shell_window_contents_.reset(shell_window_contents); + shell_window_contents_->Initialize(profile(), url); + WebContents* web_contents = shell_window_contents_->GetWebContents(); + delegate_->InitWebContents(web_contents); + WebContentsModalDialogManager::CreateForWebContents(web_contents); + + web_contents->SetDelegate(this); + WebContentsModalDialogManager::FromWebContents(web_contents)-> + set_delegate(this); + extensions::SetViewType(web_contents, extensions::VIEW_TYPE_APP_SHELL); + + // Initialize the window + window_type_ = params.window_type; + + gfx::Rect bounds = params.bounds; + + if (bounds.width() == 0) + bounds.set_width(kDefaultWidth); + if (bounds.height() == 0) + bounds.set_height(kDefaultHeight); + + // If left and top are left undefined, the native shell window will center + // the window on the main screen in a platform-defined manner. + + ui::WindowShowState cached_state = ui::SHOW_STATE_DEFAULT; + if (!params.window_key.empty()) { + window_key_ = params.window_key; + + ShellWindowGeometryCache* cache = ShellWindowGeometryCache::Get(profile()); + + gfx::Rect cached_bounds; + gfx::Rect cached_screen_bounds; + if (cache->GetGeometry(extension()->id(), params.window_key, &cached_bounds, + &cached_screen_bounds, &cached_state)) { + bounds = cached_bounds; + // App window has cached screen bounds, make sure it fits on screen in + // case the screen resolution changed. + if (!cached_screen_bounds.IsEmpty()) { + gfx::Screen* screen = gfx::Screen::GetNativeScreen(); + gfx::Display display = screen->GetDisplayMatching(cached_bounds); + gfx::Rect current_screen_bounds = display.work_area(); + AdjustBoundsToBeVisibleOnScreen(cached_bounds, + cached_screen_bounds, + current_screen_bounds, + params.minimum_size, + &bounds); + } + } + } + + CreateParams new_params = params; + + gfx::Size& minimum_size = new_params.minimum_size; + gfx::Size& maximum_size = new_params.maximum_size; + + // In the case that minimum size > maximum size, we consider the minimum + // size to be more important. + if (maximum_size.width() && maximum_size.width() < minimum_size.width()) + maximum_size.set_width(minimum_size.width()); + if (maximum_size.height() && maximum_size.height() < minimum_size.height()) + maximum_size.set_height(minimum_size.height()); + + if (maximum_size.width() && bounds.width() > maximum_size.width()) + bounds.set_width(maximum_size.width()); + if (bounds.width() != INT_MIN && bounds.width() < minimum_size.width()) + bounds.set_width(minimum_size.width()); + + if (maximum_size.height() && bounds.height() > maximum_size.height()) + bounds.set_height(maximum_size.height()); + if (bounds.height() != INT_MIN && bounds.height() < minimum_size.height()) + bounds.set_height(minimum_size.height()); + + new_params.bounds = bounds; + + if (cached_state != ui::SHOW_STATE_DEFAULT) + new_params.state = cached_state; + + native_app_window_.reset(NativeAppWindow::Create(this, new_params)); + + if (!new_params.hidden) { + if (window_type_is_panel()) + GetBaseWindow()->ShowInactive(); // Panels are not activated by default. + else + GetBaseWindow()->Show(); + } + + if (new_params.state == ui::SHOW_STATE_FULLSCREEN) + Fullscreen(); + else if (new_params.state == ui::SHOW_STATE_MAXIMIZED) + Maximize(); + else if (new_params.state == ui::SHOW_STATE_MINIMIZED) + Minimize(); + + OnNativeWindowChanged(); + + // When the render view host is changed, the native window needs to know + // about it in case it has any setup to do to make the renderer appear + // properly. In particular, on Windows, the view's clickthrough region needs + // to be set. + registrar_.Add(this, content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED, + content::Source<content::NavigationController>( + &web_contents->GetController())); + registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, + content::Source<Profile>(profile_)); + // Close when the browser process is exiting. + registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING, + content::NotificationService::AllSources()); + + shell_window_contents_->LoadContents(params.creator_process_id); + + // Prevent the browser process from shutting down while this window is open. + chrome::StartKeepAlive(); + + UpdateExtensionAppIcon(); +} + +ShellWindow::~ShellWindow() { + // Unregister now to prevent getting NOTIFICATION_APP_TERMINATING if we're the + // last window open. + registrar_.RemoveAll(); + + // Remove shutdown prevention. + chrome::EndKeepAlive(); +} + +void ShellWindow::RequestMediaAccessPermission( + content::WebContents* web_contents, + const content::MediaStreamRequest& request, + const content::MediaResponseCallback& callback) { + delegate_->RequestMediaAccessPermission(web_contents, request, callback, + extension()); +} + +WebContents* ShellWindow::OpenURLFromTab(WebContents* source, + const content::OpenURLParams& params) { + // Don't allow the current tab to be navigated. It would be nice to map all + // anchor tags (even those without target="_blank") to new tabs, but right + // now we can't distinguish between those and <meta> refreshes or window.href + // navigations, which we don't want to allow. + // TOOD(mihaip): Can we check for user gestures instead? + WindowOpenDisposition disposition = params.disposition; + if (disposition == CURRENT_TAB) { + AddMessageToDevToolsConsole( + content::CONSOLE_MESSAGE_LEVEL_ERROR, + base::StringPrintf( + "Can't open same-window link to \"%s\"; try target=\"_blank\".", + params.url.spec().c_str())); + return NULL; + } + + // These dispositions aren't really navigations. + if (disposition == SUPPRESS_OPEN || disposition == SAVE_TO_DISK || + disposition == IGNORE_ACTION) { + return NULL; + } + + WebContents* contents = delegate_->OpenURLFromTab(profile_, source, + params); + if (!contents) { + AddMessageToDevToolsConsole( + content::CONSOLE_MESSAGE_LEVEL_ERROR, + base::StringPrintf( + "Can't navigate to \"%s\"; apps do not support navigation.", + params.url.spec().c_str())); + } + + return contents; +} + +void ShellWindow::AddNewContents(WebContents* source, + WebContents* new_contents, + WindowOpenDisposition disposition, + const gfx::Rect& initial_pos, + bool user_gesture, + bool* was_blocked) { + DCHECK(Profile::FromBrowserContext(new_contents->GetBrowserContext()) == + profile_); + delegate_->AddNewContents(profile_, new_contents, disposition, + initial_pos, user_gesture, was_blocked); +} + +void ShellWindow::HandleKeyboardEvent( + WebContents* source, + const content::NativeWebKeyboardEvent& event) { + native_app_window_->HandleKeyboardEvent(event); +} + +void ShellWindow::RequestToLockMouse(WebContents* web_contents, + bool user_gesture, + bool last_unlocked_by_target) { + bool has_permission = IsExtensionWithPermissionOrSuggestInConsole( + APIPermission::kPointerLock, + extension_, + web_contents->GetRenderViewHost()); + + web_contents->GotResponseToLockMouseRequest(has_permission); +} + +void ShellWindow::OnNativeClose() { + extensions::ShellWindowRegistry::Get(profile_)->RemoveShellWindow(this); + if (shell_window_contents_) + shell_window_contents_->NativeWindowClosed(); + delete this; +} + +void ShellWindow::OnNativeWindowChanged() { + SaveWindowPosition(); + if (shell_window_contents_ && native_app_window_) + shell_window_contents_->NativeWindowChanged(native_app_window_.get()); +} + +void ShellWindow::OnNativeWindowActivated() { + extensions::ShellWindowRegistry::Get(profile_)->ShellWindowActivated(this); +} + +scoped_ptr<gfx::Image> ShellWindow::GetAppListIcon() { + // TODO(skuhne): We might want to use LoadImages in UpdateExtensionAppIcon + // instead to let the extension give us pre-defined icons in the launcher + // and the launcher list sizes. Since there is no mock yet, doing this now + // seems a bit premature and we scale for the time being. + if (app_icon_.IsEmpty()) + return make_scoped_ptr(new gfx::Image()); + + SkBitmap bmp = skia::ImageOperations::Resize( + *app_icon_.ToSkBitmap(), skia::ImageOperations::RESIZE_BEST, + extension_misc::EXTENSION_ICON_SMALLISH, + extension_misc::EXTENSION_ICON_SMALLISH); + return make_scoped_ptr( + new gfx::Image(gfx::ImageSkia::CreateFrom1xBitmap(bmp))); +} + +content::WebContents* ShellWindow::web_contents() const { + return shell_window_contents_->GetWebContents(); +} + +NativeAppWindow* ShellWindow::GetBaseWindow() { + return native_app_window_.get(); +} + +gfx::NativeWindow ShellWindow::GetNativeWindow() { + return GetBaseWindow()->GetNativeWindow(); +} + +gfx::Rect ShellWindow::GetClientBounds() const { + gfx::Rect bounds = native_app_window_->GetBounds(); + bounds.Inset(native_app_window_->GetFrameInsets()); + return bounds; +} + +string16 ShellWindow::GetTitle() const { + // WebContents::GetTitle() will return the page's URL if there's no <title> + // specified. However, we'd prefer to show the name of the extension in that + // case, so we directly inspect the NavigationEntry's title. + string16 title; + if (!web_contents() || + !web_contents()->GetController().GetActiveEntry() || + web_contents()->GetController().GetActiveEntry()->GetTitle().empty()) { + title = UTF8ToUTF16(extension()->name()); + } else { + title = web_contents()->GetTitle(); + } + const char16 kBadChars[] = { '\n', 0 }; + RemoveChars(title, kBadChars, &title); + return title; +} + +void ShellWindow::SetAppIconUrl(const GURL& url) { + // Avoid using any previous app icons were are being downloaded. + image_loader_ptr_factory_.InvalidateWeakPtrs(); + + app_icon_url_ = url; + web_contents()->DownloadImage( + url, + true, // is a favicon + delegate_->PreferredIconSize(), + 0, // no maximum size + base::Bind(&ShellWindow::DidDownloadFavicon, + image_loader_ptr_factory_.GetWeakPtr())); +} + +void ShellWindow::UpdateDraggableRegions( + const std::vector<extensions::DraggableRegion>& regions) { + native_app_window_->UpdateDraggableRegions(regions); +} + +void ShellWindow::UpdateAppIcon(const gfx::Image& image) { + if (image.IsEmpty()) + return; + app_icon_ = image; + native_app_window_->UpdateWindowIcon(); + extensions::ShellWindowRegistry::Get(profile_)->ShellWindowIconChanged(this); +} + +void ShellWindow::Fullscreen() { + fullscreen_for_window_api_ = true; + GetBaseWindow()->SetFullscreen(true); +} + +void ShellWindow::Maximize() { + GetBaseWindow()->Maximize(); +} + +void ShellWindow::Minimize() { + GetBaseWindow()->Minimize(); +} + +void ShellWindow::Restore() { + fullscreen_for_window_api_ = false; + fullscreen_for_tab_ = false; + if (GetBaseWindow()->IsFullscreenOrPending()) { + GetBaseWindow()->SetFullscreen(false); + } else { + GetBaseWindow()->Restore(); + } +} + +//------------------------------------------------------------------------------ +// Private methods + +void ShellWindow::OnImageLoaded(const gfx::Image& image) { + UpdateAppIcon(image); +} + +void ShellWindow::DidDownloadFavicon(int id, + int http_status_code, + const GURL& image_url, + int requested_size, + const std::vector<SkBitmap>& bitmaps) { + if (image_url != app_icon_url_ || bitmaps.empty()) + return; + + // Bitmaps are ordered largest to smallest. Choose the smallest bitmap + // whose height >= the preferred size. + int largest_index = 0; + for (size_t i = 1; i < bitmaps.size(); ++i) { + if (bitmaps[i].height() < delegate_->PreferredIconSize()) + break; + largest_index = i; + } + const SkBitmap& largest = bitmaps[largest_index]; + UpdateAppIcon(gfx::Image::CreateFrom1xBitmap(largest)); +} + +void ShellWindow::UpdateExtensionAppIcon() { + // Avoid using any previous app icons were are being downloaded. + image_loader_ptr_factory_.InvalidateWeakPtrs(); + + // Enqueue OnImageLoaded callback. + extensions::ImageLoader* loader = extensions::ImageLoader::Get(profile()); + loader->LoadImageAsync( + extension(), + extensions::IconsInfo::GetIconResource(extension(), + delegate_->PreferredIconSize(), + ExtensionIconSet::MATCH_BIGGER), + gfx::Size(delegate_->PreferredIconSize(), delegate_->PreferredIconSize()), + base::Bind(&ShellWindow::OnImageLoaded, + image_loader_ptr_factory_.GetWeakPtr())); +} + +void ShellWindow::CloseContents(WebContents* contents) { + native_app_window_->Close(); +} + +bool ShellWindow::ShouldSuppressDialogs() { + return true; +} + +content::ColorChooser* ShellWindow::OpenColorChooser(WebContents* web_contents, + SkColor initial_color) { + return delegate_->ShowColorChooser(web_contents, initial_color); +} + +void ShellWindow::RunFileChooser(WebContents* tab, + const content::FileChooserParams& params) { + if (window_type_is_panel()) { + // Panels can't host a file dialog, abort. TODO(stevenjb): allow file + // dialogs to be unhosted but still close with the owning web contents. + // crbug.com/172502. + LOG(WARNING) << "File dialog opened by panel."; + return; + } + + delegate_->RunFileChooser(tab, params); +} + +bool ShellWindow::IsPopupOrPanel(const WebContents* source) const { + return true; +} + +void ShellWindow::MoveContents(WebContents* source, const gfx::Rect& pos) { + native_app_window_->SetBounds(pos); +} + +void ShellWindow::NavigationStateChanged( + const content::WebContents* source, unsigned changed_flags) { + if (changed_flags & content::INVALIDATE_TYPE_TITLE) + native_app_window_->UpdateWindowTitle(); + else if (changed_flags & content::INVALIDATE_TYPE_TAB) + native_app_window_->UpdateWindowIcon(); +} + +void ShellWindow::ToggleFullscreenModeForTab(content::WebContents* source, + bool enter_fullscreen) { + if (!IsExtensionWithPermissionOrSuggestInConsole( + APIPermission::kFullscreen, + extension_, + source->GetRenderViewHost())) { + return; + } + + fullscreen_for_tab_ = enter_fullscreen; + + if (enter_fullscreen) { + native_app_window_->SetFullscreen(true); + } else if (!fullscreen_for_window_api_) { + native_app_window_->SetFullscreen(false); + } +} + +bool ShellWindow::IsFullscreenForTabOrPending( + const content::WebContents* source) const { + return fullscreen_for_tab_; +} + +void ShellWindow::Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) { + switch (type) { + case content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED: { + // TODO(jianli): once http://crbug.com/123007 is fixed, we'll no longer + // need to make the native window (ShellWindowViews specially) update + // the clickthrough region for the new RVH. + native_app_window_->RenderViewHostChanged(); + break; + } + case chrome::NOTIFICATION_EXTENSION_UNLOADED: { + const extensions::Extension* unloaded_extension = + content::Details<extensions::UnloadedExtensionInfo>( + details)->extension; + if (extension_ == unloaded_extension) + native_app_window_->Close(); + break; + } + case chrome::NOTIFICATION_APP_TERMINATING: + native_app_window_->Close(); + break; + default: + NOTREACHED() << "Received unexpected notification"; + } +} + +void ShellWindow::SetWebContentsBlocked(content::WebContents* web_contents, + bool blocked) { + delegate_->SetWebContentsBlocked(web_contents, blocked); +} + +bool ShellWindow::IsWebContentsVisible(content::WebContents* web_contents) { + return delegate_->IsWebContentsVisible(web_contents); +} + +extensions::ActiveTabPermissionGranter* + ShellWindow::GetActiveTabPermissionGranter() { + // Shell windows don't support the activeTab permission. + return NULL; +} + +WebContentsModalDialogHost* ShellWindow::GetWebContentsModalDialogHost() { + return native_app_window_.get(); +} + +void ShellWindow::AddMessageToDevToolsConsole(ConsoleMessageLevel level, + const std::string& message) { + content::RenderViewHost* rvh = web_contents()->GetRenderViewHost(); + rvh->Send(new ExtensionMsg_AddMessageToConsole( + rvh->GetRoutingID(), level, message)); +} + +void ShellWindow::SaveWindowPosition() { + if (window_key_.empty()) + return; + if (!native_app_window_) + return; + + ShellWindowGeometryCache* cache = ShellWindowGeometryCache::Get(profile()); + + gfx::Rect bounds = native_app_window_->GetRestoredBounds(); + bounds.Inset(native_app_window_->GetFrameInsets()); + gfx::Rect screen_bounds = + gfx::Screen::GetNativeScreen()->GetDisplayMatching(bounds).work_area(); + ui::WindowShowState window_state = native_app_window_->GetRestoredState(); + cache->SaveGeometry(extension()->id(), + window_key_, + bounds, + screen_bounds, + window_state); +} + +void ShellWindow::AdjustBoundsToBeVisibleOnScreen( + const gfx::Rect& cached_bounds, + const gfx::Rect& cached_screen_bounds, + const gfx::Rect& current_screen_bounds, + const gfx::Size& minimum_size, + gfx::Rect* bounds) const { + if (!bounds) + return; + + *bounds = cached_bounds; + + // Reposition and resize the bounds if the cached_screen_bounds is different + // from the current screen bounds and the current screen bounds doesn't + // completely contain the bounds. + if (!cached_screen_bounds.IsEmpty() && + cached_screen_bounds != current_screen_bounds && + !current_screen_bounds.Contains(cached_bounds)) { + bounds->set_width( + std::max(minimum_size.width(), + std::min(bounds->width(), current_screen_bounds.width()))); + bounds->set_height( + std::max(minimum_size.height(), + std::min(bounds->height(), current_screen_bounds.height()))); + bounds->set_x( + std::max(current_screen_bounds.x(), + std::min(bounds->x(), + current_screen_bounds.right() - bounds->width()))); + bounds->set_y( + std::max(current_screen_bounds.y(), + std::min(bounds->y(), + current_screen_bounds.bottom() - bounds->height()))); + } +} + +// static +SkRegion* ShellWindow::RawDraggableRegionsToSkRegion( + const std::vector<extensions::DraggableRegion>& regions) { + SkRegion* sk_region = new SkRegion; + for (std::vector<extensions::DraggableRegion>::const_iterator iter = + regions.begin(); + iter != regions.end(); ++iter) { + const extensions::DraggableRegion& region = *iter; + sk_region->op( + region.bounds.x(), + region.bounds.y(), + region.bounds.right(), + region.bounds.bottom(), + region.draggable ? SkRegion::kUnion_Op : SkRegion::kDifference_Op); + } + return sk_region; +} + +} // namespace apps diff --git a/apps/shell_window.h b/apps/shell_window.h new file mode 100644 index 0000000..f283c80 --- /dev/null +++ b/apps/shell_window.h @@ -0,0 +1,380 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_UI_EXTENSIONS_SHELL_WINDOW_H_ +#define CHROME_BROWSER_UI_EXTENSIONS_SHELL_WINDOW_H_ + +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "chrome/browser/extensions/extension_keybinding_registry.h" +#include "chrome/browser/sessions/session_id.h" +#include "components/web_modal/web_contents_modal_dialog_manager_delegate.h" +#include "content/public/browser/notification_observer.h" +#include "content/public/browser/notification_registrar.h" +#include "content/public/browser/web_contents_delegate.h" +#include "content/public/common/console_message_level.h" +#include "ui/base/ui_base_types.h" // WindowShowState +#include "ui/gfx/image/image.h" +#include "ui/gfx/rect.h" + +class GURL; +class Profile; +class NativeAppWindow; +class SkRegion; + +namespace content { +class WebContents; +} + +namespace extensions { +class Extension; +class PlatformAppBrowserTest; +class WindowController; + +struct DraggableRegion; +} + +namespace ui { +class BaseWindow; +} + +namespace apps { + +// Manages the web contents for Shell Windows. The implementation for this +// class should create and maintain the WebContents for the window, and handle +// any message passing between the web contents and the extension system or +// native window. +class ShellWindowContents { + public: + ShellWindowContents() {} + virtual ~ShellWindowContents() {} + + // Called to initialize the WebContents, before the app window is created. + virtual void Initialize(Profile* profile, const GURL& url) = 0; + + // Called to load the contents, after the app window is created. + virtual void LoadContents(int32 creator_process_id) = 0; + + // Called when the native window changes. + virtual void NativeWindowChanged(NativeAppWindow* native_app_window) = 0; + + // Called when the native window closes. + virtual void NativeWindowClosed() = 0; + + virtual content::WebContents* GetWebContents() const = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(ShellWindowContents); +}; + +// ShellWindow is the type of window used by platform apps. Shell windows +// have a WebContents but none of the chrome of normal browser windows. +class ShellWindow : public content::NotificationObserver, + public content::WebContentsDelegate, + public web_modal::WebContentsModalDialogManagerDelegate, + public extensions::ExtensionKeybindingRegistry::Delegate { + public: + enum WindowType { + WINDOW_TYPE_DEFAULT = 1 << 0, // Default shell window. + WINDOW_TYPE_PANEL = 1 << 1, // OS controlled panel window (Ash only). + WINDOW_TYPE_V1_PANEL = 1 << 2, // For apps v1 support in Ash; deprecate + // with v1 apps. + }; + + enum Frame { + FRAME_CHROME, // Chrome-style window frame. + FRAME_NONE, // Frameless window. + }; + + struct CreateParams { + CreateParams(); + ~CreateParams(); + + WindowType window_type; + Frame frame; + bool transparent_background; // Only supported on ash. + + // Specify the initial content bounds of the window (excluding any window + // decorations). INT_MIN designates 'unspecified' for the position + // components, and 0 for the size components. When unspecified, they should + // be replaced with a default value. + gfx::Rect bounds; + + gfx::Size minimum_size; + gfx::Size maximum_size; + + std::string window_key; + + // The process ID of the process that requested the create. + int32 creator_process_id; + + // Initial state of the window. + ui::WindowShowState state; + + // If true, don't show the window after creation. + bool hidden; + + // If true, the window will be resizable by the user. Defaults to true. + bool resizable; + + // If true, the window will be focused on creation. Defaults to true. + bool focused; + }; + + class Delegate { + public: + virtual ~Delegate(); + + // General initialization. + virtual void InitWebContents(content::WebContents* web_contents) = 0; + + // Link handling. + virtual content::WebContents* OpenURLFromTab( + Profile* profile, + content::WebContents* source, + const content::OpenURLParams& params) = 0; + virtual void AddNewContents(Profile* profile, + content::WebContents* new_contents, + WindowOpenDisposition disposition, + const gfx::Rect& initial_pos, + bool user_gesture, + bool* was_blocked) = 0; + + // Feature support. + virtual content::ColorChooser* ShowColorChooser( + content::WebContents* web_contents, + SkColor initial_color) = 0; + virtual void RunFileChooser(content::WebContents* tab, + const content::FileChooserParams& params) = 0; + virtual void RequestMediaAccessPermission( + content::WebContents* web_contents, + const content::MediaStreamRequest& request, + const content::MediaResponseCallback& callback, + const extensions::Extension* extension) = 0; + virtual int PreferredIconSize() = 0; + + // Web contents modal dialog support. + virtual void SetWebContentsBlocked(content::WebContents* web_contents, + bool blocked) = 0; + virtual bool IsWebContentsVisible(content::WebContents* web_contents) = 0; + }; + + // Helper function for creating and intiailizing a v2 app window. + // The created shell window will take ownership of |delegate|. + static ShellWindow* Create(Profile* profile, + Delegate* delegate, + const extensions::Extension* extension, + const GURL& url, + const CreateParams& params); + + // Convert draggable regions in raw format to SkRegion format. Caller is + // responsible for deleting the returned SkRegion instance. + static SkRegion* RawDraggableRegionsToSkRegion( + const std::vector<extensions::DraggableRegion>& regions); + + // The constructor and Init methods are public for constructing a ShellWindow + // with a non-standard render interface (e.g. v1 apps using Ash Panels). + // Normally ShellWindow::Create should be used. + // The constructed shell window takes ownership of |delegate|. + ShellWindow(Profile* profile, + Delegate* delegate, + const extensions::Extension* extension); + + // Initializes the render interface, web contents, and native window. + // |shell_window_contents| will become owned by ShellWindow. + void Init(const GURL& url, + ShellWindowContents* shell_window_contents, + const CreateParams& params); + + + const std::string& window_key() const { return window_key_; } + const SessionID& session_id() const { return session_id_; } + const extensions::Extension* extension() const { return extension_; } + const std::string& extension_id() const { return extension_id_; } + content::WebContents* web_contents() const; + WindowType window_type() const { return window_type_; } + bool window_type_is_panel() const { + return (window_type_ == WINDOW_TYPE_PANEL || + window_type_ == WINDOW_TYPE_V1_PANEL); + } + Profile* profile() const { return profile_; } + const gfx::Image& app_icon() const { return app_icon_; } + const GURL& app_icon_url() { return app_icon_url_; } + + NativeAppWindow* GetBaseWindow(); + gfx::NativeWindow GetNativeWindow(); + + // Returns the bounds that should be reported to the renderer. + gfx::Rect GetClientBounds() const; + + // This will return a slightly smaller icon then the app_icon to be used in + // application lists. + scoped_ptr<gfx::Image> GetAppListIcon(); + + // NativeAppWindows should call this to determine what the window's title + // is on startup and from within UpdateWindowTitle(). + string16 GetTitle() const; + + // Call to notify ShellRegistry and delete the window. Subclasses should + // invoke this method instead of using "delete this". + void OnNativeClose(); + + // Should be called by native implementations when the window size, position, + // or minimized/maximized state has changed. + void OnNativeWindowChanged(); + + // Should be called by native implementations when the window is activated. + void OnNativeWindowActivated(); + + // Specifies a url for the launcher icon. + void SetAppIconUrl(const GURL& icon_url); + + // Called from the render interface to modify the draggable regions. + void UpdateDraggableRegions( + const std::vector<extensions::DraggableRegion>& regions); + + // Updates the app image to |image|. Called internally from the image loader + // callback. Also called externally for v1 apps using Ash Panels. + void UpdateAppIcon(const gfx::Image& image); + + // Transitions window into fullscreen, maximized, minimized or restores based + // on chrome.app.window API. + void Fullscreen(); + void Maximize(); + void Minimize(); + void Restore(); + + ShellWindowContents* shell_window_contents_for_test() { + return shell_window_contents_.get(); + } + + protected: + virtual ~ShellWindow(); + + private: + // PlatformAppBrowserTest needs access to web_contents() + friend class extensions::PlatformAppBrowserTest; + + // content::WebContentsDelegate implementation. + virtual void CloseContents(content::WebContents* contents) OVERRIDE; + virtual bool ShouldSuppressDialogs() OVERRIDE; + virtual content::ColorChooser* OpenColorChooser( + content::WebContents* web_contents, SkColor color) OVERRIDE; + virtual void RunFileChooser( + content::WebContents* tab, + const content::FileChooserParams& params) OVERRIDE; + virtual bool IsPopupOrPanel( + const content::WebContents* source) const OVERRIDE; + virtual void MoveContents( + content::WebContents* source, const gfx::Rect& pos) OVERRIDE; + virtual void NavigationStateChanged(const content::WebContents* source, + unsigned changed_flags) OVERRIDE; + virtual void ToggleFullscreenModeForTab(content::WebContents* source, + bool enter_fullscreen) OVERRIDE; + virtual bool IsFullscreenForTabOrPending( + const content::WebContents* source) const OVERRIDE; + virtual void RequestMediaAccessPermission( + content::WebContents* web_contents, + const content::MediaStreamRequest& request, + const content::MediaResponseCallback& callback) OVERRIDE; + virtual content::WebContents* OpenURLFromTab( + content::WebContents* source, + const content::OpenURLParams& params) OVERRIDE; + virtual void AddNewContents(content::WebContents* source, + content::WebContents* new_contents, + WindowOpenDisposition disposition, + const gfx::Rect& initial_pos, + bool user_gesture, + bool* was_blocked) OVERRIDE; + virtual void HandleKeyboardEvent( + content::WebContents* source, + const content::NativeWebKeyboardEvent& event) OVERRIDE; + virtual void RequestToLockMouse(content::WebContents* web_contents, + bool user_gesture, + bool last_unlocked_by_target) OVERRIDE; + + // content::NotificationObserver implementation. + virtual void Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) OVERRIDE; + + // web_modal::WebContentsModalDialogManagerDelegate implementation. + virtual void SetWebContentsBlocked(content::WebContents* web_contents, + bool blocked) OVERRIDE; + virtual bool IsWebContentsVisible( + content::WebContents* web_contents) OVERRIDE; + + // Helper method to add a message to the renderer's DevTools console. + void AddMessageToDevToolsConsole(content::ConsoleMessageLevel level, + const std::string& message); + + // Saves the window geometry/position/screen bounds. + void SaveWindowPosition(); + + // Helper method to adjust the cached bounds so that we can make sure it can + // be visible on the screen. See http://crbug.com/145752 . + void AdjustBoundsToBeVisibleOnScreen( + const gfx::Rect& cached_bounds, + const gfx::Rect& cached_screen_bounds, + const gfx::Rect& current_screen_bounds, + const gfx::Size& minimum_size, + gfx::Rect* bounds) const; + + // Load the app's image, firing a load state change when loaded. + void UpdateExtensionAppIcon(); + + void OnImageLoaded(const gfx::Image& image); + + // extensions::ExtensionKeybindingRegistry::Delegate implementation. + virtual extensions::ActiveTabPermissionGranter* + GetActiveTabPermissionGranter() OVERRIDE; + + // web_modal::WebContentsModalDialogManagerDelegate implementation. + virtual web_modal::WebContentsModalDialogHost* + GetWebContentsModalDialogHost() OVERRIDE; + + // Callback from web_contents()->DownloadFavicon. + void DidDownloadFavicon(int id, + int http_status_code, + const GURL& image_url, + int requested_size, + const std::vector<SkBitmap>& bitmaps); + + Profile* profile_; // weak pointer - owned by ProfileManager. + // weak pointer - owned by ExtensionService. + const extensions::Extension* extension_; + const std::string extension_id_; + + // Identifier that is used when saving and restoring geometry for this + // window. + std::string window_key_; + + const SessionID session_id_; + WindowType window_type_; + content::NotificationRegistrar registrar_; + + // Icon shown in the task bar. + gfx::Image app_icon_; + + // Icon URL to be used for setting the app icon. If not empty, app_icon_ will + // be fetched and set using this URL. + GURL app_icon_url_; + + scoped_ptr<NativeAppWindow> native_app_window_; + scoped_ptr<ShellWindowContents> shell_window_contents_; + scoped_ptr<Delegate> delegate_; + + base::WeakPtrFactory<ShellWindow> image_loader_ptr_factory_; + + // Fullscreen entered by app.window api. + bool fullscreen_for_window_api_; + // Fullscreen entered by HTML requestFullscreen. + bool fullscreen_for_tab_; + + DISALLOW_COPY_AND_ASSIGN(ShellWindow); +}; + +} // namespace apps + +#endif // CHROME_BROWSER_UI_EXTENSIONS_SHELL_WINDOW_H_ |