| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 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 "chrome/browser/ui/extensions/shell_window.h" | |
| 6 | |
| 7 #include "apps/shell_window_geometry_cache.h" | |
| 8 #include "base/strings/utf_string_conversions.h" | |
| 9 #include "base/values.h" | |
| 10 #include "chrome/browser/extensions/app_window_contents.h" | |
| 11 #include "chrome/browser/extensions/extension_process_manager.h" | |
| 12 #include "chrome/browser/extensions/extension_system.h" | |
| 13 #include "chrome/browser/extensions/image_loader.h" | |
| 14 #include "chrome/browser/extensions/shell_window_registry.h" | |
| 15 #include "chrome/browser/extensions/suggest_permission_util.h" | |
| 16 #include "chrome/browser/favicon/favicon_tab_helper.h" | |
| 17 #include "chrome/browser/file_select_helper.h" | |
| 18 #include "chrome/browser/lifetime/application_lifetime.h" | |
| 19 #include "chrome/browser/media/media_capture_devices_dispatcher.h" | |
| 20 #include "chrome/browser/platform_util.h" | |
| 21 #include "chrome/browser/profiles/profile.h" | |
| 22 #include "chrome/browser/sessions/session_id.h" | |
| 23 #include "chrome/browser/ui/browser.h" | |
| 24 #include "chrome/browser/ui/browser_dialogs.h" | |
| 25 #include "chrome/browser/ui/browser_finder.h" | |
| 26 #include "chrome/browser/ui/browser_tabstrip.h" | |
| 27 #include "chrome/browser/ui/browser_window.h" | |
| 28 #include "chrome/browser/ui/extensions/native_app_window.h" | |
| 29 #include "chrome/common/chrome_notification_types.h" | |
| 30 #include "chrome/common/extensions/extension.h" | |
| 31 #include "chrome/common/extensions/extension_constants.h" | |
| 32 #include "chrome/common/extensions/extension_messages.h" | |
| 33 #include "chrome/common/extensions/manifest_handlers/icons_handler.h" | |
| 34 #include "components/web_modal/web_contents_modal_dialog_manager.h" | |
| 35 #include "content/public/browser/invalidate_type.h" | |
| 36 #include "content/public/browser/navigation_entry.h" | |
| 37 #include "content/public/browser/notification_details.h" | |
| 38 #include "content/public/browser/notification_service.h" | |
| 39 #include "content/public/browser/notification_source.h" | |
| 40 #include "content/public/browser/notification_types.h" | |
| 41 #include "content/public/browser/render_view_host.h" | |
| 42 #include "content/public/browser/resource_dispatcher_host.h" | |
| 43 #include "content/public/browser/web_contents.h" | |
| 44 #include "content/public/common/media_stream_request.h" | |
| 45 #include "extensions/browser/view_type_utils.h" | |
| 46 #include "skia/ext/image_operations.h" | |
| 47 #include "third_party/skia/include/core/SkRegion.h" | |
| 48 #include "ui/gfx/image/image_skia.h" | |
| 49 | |
| 50 #if defined(USE_ASH) | |
| 51 #include "ash/launcher/launcher_types.h" | |
| 52 #endif | |
| 53 | |
| 54 using content::ConsoleMessageLevel; | |
| 55 using content::WebContents; | |
| 56 using extensions::APIPermission; | |
| 57 using web_modal::WebContentsModalDialogHost; | |
| 58 using web_modal::WebContentsModalDialogManager; | |
| 59 | |
| 60 namespace { | |
| 61 const int kDefaultWidth = 512; | |
| 62 const int kDefaultHeight = 384; | |
| 63 | |
| 64 // The preferred icon size for displaying the app icon. | |
| 65 #if defined(USE_ASH) | |
| 66 const int kPreferredIconSize = ash::kLauncherPreferredSize; | |
| 67 #else | |
| 68 const int kPreferredIconSize = extension_misc::EXTENSION_ICON_SMALL; | |
| 69 #endif | |
| 70 | |
| 71 static bool disable_external_open_for_testing_ = false; | |
| 72 | |
| 73 class ShellWindowLinkDelegate : public content::WebContentsDelegate { | |
| 74 private: | |
| 75 virtual content::WebContents* OpenURLFromTab( | |
| 76 content::WebContents* source, | |
| 77 const content::OpenURLParams& params) OVERRIDE; | |
| 78 }; | |
| 79 | |
| 80 content::WebContents* ShellWindowLinkDelegate::OpenURLFromTab( | |
| 81 content::WebContents* source, | |
| 82 const content::OpenURLParams& params) { | |
| 83 platform_util::OpenExternal(params.url); | |
| 84 delete source; | |
| 85 return NULL; | |
| 86 } | |
| 87 | |
| 88 } // namespace | |
| 89 | |
| 90 ShellWindow::CreateParams::CreateParams() | |
| 91 : window_type(ShellWindow::WINDOW_TYPE_DEFAULT), | |
| 92 frame(ShellWindow::FRAME_CHROME), | |
| 93 transparent_background(false), | |
| 94 bounds(INT_MIN, INT_MIN, 0, 0), | |
| 95 creator_process_id(0), | |
| 96 state(ui::SHOW_STATE_DEFAULT), | |
| 97 hidden(false), | |
| 98 resizable(true), | |
| 99 focused(true) { | |
| 100 } | |
| 101 | |
| 102 ShellWindow::CreateParams::~CreateParams() { | |
| 103 } | |
| 104 | |
| 105 ShellWindow* ShellWindow::Create(Profile* profile, | |
| 106 const extensions::Extension* extension, | |
| 107 const GURL& url, | |
| 108 const CreateParams& params) { | |
| 109 // This object will delete itself when the window is closed. | |
| 110 ShellWindow* window = new ShellWindow(profile, extension); | |
| 111 window->Init(url, new AppWindowContents(window), params); | |
| 112 extensions::ShellWindowRegistry::Get(profile)->AddShellWindow(window); | |
| 113 return window; | |
| 114 } | |
| 115 | |
| 116 ShellWindow::ShellWindow(Profile* profile, | |
| 117 const extensions::Extension* extension) | |
| 118 : profile_(profile), | |
| 119 extension_(extension), | |
| 120 extension_id_(extension->id()), | |
| 121 window_type_(WINDOW_TYPE_DEFAULT), | |
| 122 image_loader_ptr_factory_(this), | |
| 123 fullscreen_for_window_api_(false), | |
| 124 fullscreen_for_tab_(false) { | |
| 125 } | |
| 126 | |
| 127 void ShellWindow::Init(const GURL& url, | |
| 128 ShellWindowContents* shell_window_contents, | |
| 129 const CreateParams& params) { | |
| 130 // Initialize the render interface and web contents | |
| 131 shell_window_contents_.reset(shell_window_contents); | |
| 132 shell_window_contents_->Initialize(profile(), url); | |
| 133 WebContents* web_contents = shell_window_contents_->GetWebContents(); | |
| 134 WebContentsModalDialogManager::CreateForWebContents(web_contents); | |
| 135 FaviconTabHelper::CreateForWebContents(web_contents); | |
| 136 | |
| 137 web_contents->SetDelegate(this); | |
| 138 WebContentsModalDialogManager::FromWebContents(web_contents)-> | |
| 139 set_delegate(this); | |
| 140 extensions::SetViewType(web_contents, extensions::VIEW_TYPE_APP_SHELL); | |
| 141 | |
| 142 // Initialize the window | |
| 143 window_type_ = params.window_type; | |
| 144 | |
| 145 gfx::Rect bounds = params.bounds; | |
| 146 | |
| 147 if (bounds.width() == 0) | |
| 148 bounds.set_width(kDefaultWidth); | |
| 149 if (bounds.height() == 0) | |
| 150 bounds.set_height(kDefaultHeight); | |
| 151 | |
| 152 // If left and top are left undefined, the native shell window will center | |
| 153 // the window on the main screen in a platform-defined manner. | |
| 154 | |
| 155 ui::WindowShowState cached_state = ui::SHOW_STATE_DEFAULT; | |
| 156 if (!params.window_key.empty()) { | |
| 157 window_key_ = params.window_key; | |
| 158 | |
| 159 apps::ShellWindowGeometryCache* cache = | |
| 160 apps::ShellWindowGeometryCache::Get(profile()); | |
| 161 | |
| 162 gfx::Rect cached_bounds; | |
| 163 if (cache->GetGeometry(extension()->id(), params.window_key, | |
| 164 &cached_bounds, &cached_state)) { | |
| 165 bounds = cached_bounds; | |
| 166 } | |
| 167 } | |
| 168 | |
| 169 CreateParams new_params = params; | |
| 170 | |
| 171 gfx::Size& minimum_size = new_params.minimum_size; | |
| 172 gfx::Size& maximum_size = new_params.maximum_size; | |
| 173 | |
| 174 // In the case that minimum size > maximum size, we consider the minimum | |
| 175 // size to be more important. | |
| 176 if (maximum_size.width() && maximum_size.width() < minimum_size.width()) | |
| 177 maximum_size.set_width(minimum_size.width()); | |
| 178 if (maximum_size.height() && maximum_size.height() < minimum_size.height()) | |
| 179 maximum_size.set_height(minimum_size.height()); | |
| 180 | |
| 181 if (maximum_size.width() && bounds.width() > maximum_size.width()) | |
| 182 bounds.set_width(maximum_size.width()); | |
| 183 if (bounds.width() != INT_MIN && bounds.width() < minimum_size.width()) | |
| 184 bounds.set_width(minimum_size.width()); | |
| 185 | |
| 186 if (maximum_size.height() && bounds.height() > maximum_size.height()) | |
| 187 bounds.set_height(maximum_size.height()); | |
| 188 if (bounds.height() != INT_MIN && bounds.height() < minimum_size.height()) | |
| 189 bounds.set_height(minimum_size.height()); | |
| 190 | |
| 191 new_params.bounds = bounds; | |
| 192 | |
| 193 if (cached_state != ui::SHOW_STATE_DEFAULT) | |
| 194 new_params.state = cached_state; | |
| 195 | |
| 196 native_app_window_.reset(NativeAppWindow::Create(this, new_params)); | |
| 197 | |
| 198 if (!new_params.hidden) { | |
| 199 if (window_type_is_panel()) | |
| 200 GetBaseWindow()->ShowInactive(); // Panels are not activated by default. | |
| 201 else | |
| 202 GetBaseWindow()->Show(); | |
| 203 } | |
| 204 | |
| 205 if (new_params.state == ui::SHOW_STATE_FULLSCREEN) | |
| 206 Fullscreen(); | |
| 207 else if (new_params.state == ui::SHOW_STATE_MAXIMIZED) | |
| 208 Maximize(); | |
| 209 else if (new_params.state == ui::SHOW_STATE_MINIMIZED) | |
| 210 Minimize(); | |
| 211 | |
| 212 OnNativeWindowChanged(); | |
| 213 | |
| 214 // When the render view host is changed, the native window needs to know | |
| 215 // about it in case it has any setup to do to make the renderer appear | |
| 216 // properly. In particular, on Windows, the view's clickthrough region needs | |
| 217 // to be set. | |
| 218 registrar_.Add(this, content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED, | |
| 219 content::Source<content::NavigationController>( | |
| 220 &web_contents->GetController())); | |
| 221 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, | |
| 222 content::Source<Profile>(profile_)); | |
| 223 // Close when the browser is exiting. | |
| 224 // TODO(mihaip): we probably don't want this in the long run (when platform | |
| 225 // apps are no longer tied to the browser process). | |
| 226 registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING, | |
| 227 content::NotificationService::AllSources()); | |
| 228 | |
| 229 shell_window_contents_->LoadContents(params.creator_process_id); | |
| 230 | |
| 231 // Prevent the browser process from shutting down while this window is open. | |
| 232 chrome::StartKeepAlive(); | |
| 233 | |
| 234 UpdateExtensionAppIcon(); | |
| 235 } | |
| 236 | |
| 237 ShellWindow::~ShellWindow() { | |
| 238 // Unregister now to prevent getting NOTIFICATION_APP_TERMINATING if we're the | |
| 239 // last window open. | |
| 240 registrar_.RemoveAll(); | |
| 241 | |
| 242 // Remove shutdown prevention. | |
| 243 chrome::EndKeepAlive(); | |
| 244 } | |
| 245 | |
| 246 void ShellWindow::RequestMediaAccessPermission( | |
| 247 content::WebContents* web_contents, | |
| 248 const content::MediaStreamRequest& request, | |
| 249 const content::MediaResponseCallback& callback) { | |
| 250 MediaCaptureDevicesDispatcher::GetInstance()->ProcessMediaAccessRequest( | |
| 251 web_contents, request, callback, extension()); | |
| 252 } | |
| 253 | |
| 254 WebContents* ShellWindow::OpenURLFromTab(WebContents* source, | |
| 255 const content::OpenURLParams& params) { | |
| 256 // Don't allow the current tab to be navigated. It would be nice to map all | |
| 257 // anchor tags (even those without target="_blank") to new tabs, but right | |
| 258 // now we can't distinguish between those and <meta> refreshes or window.href | |
| 259 // navigations, which we don't want to allow. | |
| 260 // TOOD(mihaip): Can we check for user gestures instead? | |
| 261 WindowOpenDisposition disposition = params.disposition; | |
| 262 if (disposition == CURRENT_TAB) { | |
| 263 AddMessageToDevToolsConsole( | |
| 264 content::CONSOLE_MESSAGE_LEVEL_ERROR, | |
| 265 base::StringPrintf( | |
| 266 "Can't open same-window link to \"%s\"; try target=\"_blank\".", | |
| 267 params.url.spec().c_str())); | |
| 268 return NULL; | |
| 269 } | |
| 270 | |
| 271 // These dispositions aren't really navigations. | |
| 272 if (disposition == SUPPRESS_OPEN || disposition == SAVE_TO_DISK || | |
| 273 disposition == IGNORE_ACTION) { | |
| 274 return NULL; | |
| 275 } | |
| 276 | |
| 277 // Force all links to open in a new tab, even if they were trying to open a | |
| 278 // window. | |
| 279 chrome::NavigateParams new_tab_params( | |
| 280 static_cast<Browser*>(NULL), params.url, params.transition); | |
| 281 new_tab_params.disposition = | |
| 282 disposition == NEW_BACKGROUND_TAB ? disposition : NEW_FOREGROUND_TAB; | |
| 283 new_tab_params.initiating_profile = profile_; | |
| 284 chrome::Navigate(&new_tab_params); | |
| 285 | |
| 286 if (!new_tab_params.target_contents) { | |
| 287 AddMessageToDevToolsConsole( | |
| 288 content::CONSOLE_MESSAGE_LEVEL_ERROR, | |
| 289 base::StringPrintf( | |
| 290 "Can't navigate to \"%s\"; apps do not support navigation.", | |
| 291 params.url.spec().c_str())); | |
| 292 } | |
| 293 | |
| 294 return new_tab_params.target_contents; | |
| 295 } | |
| 296 | |
| 297 void ShellWindow::AddNewContents(WebContents* source, | |
| 298 WebContents* new_contents, | |
| 299 WindowOpenDisposition disposition, | |
| 300 const gfx::Rect& initial_pos, | |
| 301 bool user_gesture, | |
| 302 bool* was_blocked) { | |
| 303 DCHECK(Profile::FromBrowserContext(new_contents->GetBrowserContext()) == | |
| 304 profile_); | |
| 305 #if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_WIN) | |
| 306 if (disable_external_open_for_testing_) { | |
| 307 Browser* browser = | |
| 308 chrome::FindOrCreateTabbedBrowser(profile_, chrome::GetActiveDesktop()); | |
| 309 // Force all links to open in a new tab, even if they were trying to open a | |
| 310 // new window. | |
| 311 disposition = | |
| 312 disposition == NEW_BACKGROUND_TAB ? disposition : NEW_FOREGROUND_TAB; | |
| 313 chrome::AddWebContents(browser, NULL, new_contents, disposition, | |
| 314 initial_pos, user_gesture, was_blocked); | |
| 315 } else { | |
| 316 new_contents->SetDelegate(new ShellWindowLinkDelegate()); | |
| 317 } | |
| 318 #else | |
| 319 Browser* browser = | |
| 320 chrome::FindOrCreateTabbedBrowser(profile_, chrome::GetActiveDesktop()); | |
| 321 // Force all links to open in a new tab, even if they were trying to open a | |
| 322 // new window. | |
| 323 disposition = | |
| 324 disposition == NEW_BACKGROUND_TAB ? disposition : NEW_FOREGROUND_TAB; | |
| 325 chrome::AddWebContents(browser, NULL, new_contents, disposition, initial_pos, | |
| 326 user_gesture, was_blocked); | |
| 327 #endif | |
| 328 } | |
| 329 | |
| 330 void ShellWindow::HandleKeyboardEvent( | |
| 331 WebContents* source, | |
| 332 const content::NativeWebKeyboardEvent& event) { | |
| 333 native_app_window_->HandleKeyboardEvent(event); | |
| 334 } | |
| 335 | |
| 336 void ShellWindow::RequestToLockMouse(WebContents* web_contents, | |
| 337 bool user_gesture, | |
| 338 bool last_unlocked_by_target) { | |
| 339 bool has_permission = IsExtensionWithPermissionOrSuggestInConsole( | |
| 340 APIPermission::kPointerLock, | |
| 341 extension_, | |
| 342 web_contents->GetRenderViewHost()); | |
| 343 | |
| 344 web_contents->GotResponseToLockMouseRequest(has_permission); | |
| 345 } | |
| 346 | |
| 347 void ShellWindow::OnNativeClose() { | |
| 348 extensions::ShellWindowRegistry::Get(profile_)->RemoveShellWindow(this); | |
| 349 if (shell_window_contents_) | |
| 350 shell_window_contents_->NativeWindowClosed(); | |
| 351 delete this; | |
| 352 } | |
| 353 | |
| 354 void ShellWindow::OnNativeWindowChanged() { | |
| 355 SaveWindowPosition(); | |
| 356 if (shell_window_contents_ && native_app_window_) | |
| 357 shell_window_contents_->NativeWindowChanged(native_app_window_.get()); | |
| 358 } | |
| 359 | |
| 360 void ShellWindow::OnNativeWindowActivated() { | |
| 361 extensions::ShellWindowRegistry::Get(profile_)->ShellWindowActivated(this); | |
| 362 } | |
| 363 | |
| 364 scoped_ptr<gfx::Image> ShellWindow::GetAppListIcon() { | |
| 365 // TODO(skuhne): We might want to use LoadImages in UpdateExtensionAppIcon | |
| 366 // instead to let the extension give us pre-defined icons in the launcher | |
| 367 // and the launcher list sizes. Since there is no mock yet, doing this now | |
| 368 // seems a bit premature and we scale for the time being. | |
| 369 if (app_icon_.IsEmpty()) | |
| 370 return make_scoped_ptr(new gfx::Image()); | |
| 371 | |
| 372 SkBitmap bmp = skia::ImageOperations::Resize( | |
| 373 *app_icon_.ToSkBitmap(), skia::ImageOperations::RESIZE_BEST, | |
| 374 extension_misc::EXTENSION_ICON_SMALLISH, | |
| 375 extension_misc::EXTENSION_ICON_SMALLISH); | |
| 376 return make_scoped_ptr( | |
| 377 new gfx::Image(gfx::ImageSkia::CreateFrom1xBitmap(bmp))); | |
| 378 } | |
| 379 | |
| 380 content::WebContents* ShellWindow::web_contents() const { | |
| 381 return shell_window_contents_->GetWebContents(); | |
| 382 } | |
| 383 | |
| 384 NativeAppWindow* ShellWindow::GetBaseWindow() { | |
| 385 return native_app_window_.get(); | |
| 386 } | |
| 387 | |
| 388 gfx::NativeWindow ShellWindow::GetNativeWindow() { | |
| 389 return GetBaseWindow()->GetNativeWindow(); | |
| 390 } | |
| 391 | |
| 392 gfx::Rect ShellWindow::GetClientBounds() const { | |
| 393 gfx::Rect bounds = native_app_window_->GetBounds(); | |
| 394 bounds.Inset(native_app_window_->GetFrameInsets()); | |
| 395 return bounds; | |
| 396 } | |
| 397 | |
| 398 string16 ShellWindow::GetTitle() const { | |
| 399 // WebContents::GetTitle() will return the page's URL if there's no <title> | |
| 400 // specified. However, we'd prefer to show the name of the extension in that | |
| 401 // case, so we directly inspect the NavigationEntry's title. | |
| 402 if (!web_contents() || | |
| 403 !web_contents()->GetController().GetActiveEntry() || | |
| 404 web_contents()->GetController().GetActiveEntry()->GetTitle().empty()) | |
| 405 return UTF8ToUTF16(extension()->name()); | |
| 406 string16 title = web_contents()->GetTitle(); | |
| 407 Browser::FormatTitleForDisplay(&title); | |
| 408 return title; | |
| 409 } | |
| 410 | |
| 411 void ShellWindow::SetAppIconUrl(const GURL& url) { | |
| 412 // Avoid using any previous app icons were are being downloaded. | |
| 413 image_loader_ptr_factory_.InvalidateWeakPtrs(); | |
| 414 | |
| 415 app_icon_url_ = url; | |
| 416 web_contents()->DownloadImage( | |
| 417 url, true, kPreferredIconSize, | |
| 418 base::Bind(&ShellWindow::DidDownloadFavicon, | |
| 419 image_loader_ptr_factory_.GetWeakPtr())); | |
| 420 } | |
| 421 | |
| 422 void ShellWindow::UpdateDraggableRegions( | |
| 423 const std::vector<extensions::DraggableRegion>& regions) { | |
| 424 native_app_window_->UpdateDraggableRegions(regions); | |
| 425 } | |
| 426 | |
| 427 void ShellWindow::UpdateAppIcon(const gfx::Image& image) { | |
| 428 if (image.IsEmpty()) | |
| 429 return; | |
| 430 app_icon_ = image; | |
| 431 native_app_window_->UpdateWindowIcon(); | |
| 432 extensions::ShellWindowRegistry::Get(profile_)->ShellWindowIconChanged(this); | |
| 433 } | |
| 434 | |
| 435 void ShellWindow::Fullscreen() { | |
| 436 fullscreen_for_window_api_ = true; | |
| 437 GetBaseWindow()->SetFullscreen(true); | |
| 438 } | |
| 439 | |
| 440 void ShellWindow::Maximize() { | |
| 441 GetBaseWindow()->Maximize(); | |
| 442 } | |
| 443 | |
| 444 void ShellWindow::Minimize() { | |
| 445 GetBaseWindow()->Minimize(); | |
| 446 } | |
| 447 | |
| 448 void ShellWindow::Restore() { | |
| 449 fullscreen_for_window_api_ = false; | |
| 450 fullscreen_for_tab_ = false; | |
| 451 if (GetBaseWindow()->IsFullscreenOrPending()) { | |
| 452 GetBaseWindow()->SetFullscreen(false); | |
| 453 } else { | |
| 454 GetBaseWindow()->Restore(); | |
| 455 } | |
| 456 } | |
| 457 | |
| 458 //------------------------------------------------------------------------------ | |
| 459 // Private methods | |
| 460 | |
| 461 void ShellWindow::OnImageLoaded(const gfx::Image& image) { | |
| 462 UpdateAppIcon(image); | |
| 463 } | |
| 464 | |
| 465 void ShellWindow::DidDownloadFavicon(int id, | |
| 466 int http_status_code, | |
| 467 const GURL& image_url, | |
| 468 int requested_size, | |
| 469 const std::vector<SkBitmap>& bitmaps) { | |
| 470 if (image_url != app_icon_url_ || bitmaps.empty()) | |
| 471 return; | |
| 472 | |
| 473 // Bitmaps are ordered largest to smallest. Choose the smallest bitmap | |
| 474 // whose height >= the preferred size. | |
| 475 int largest_index = 0; | |
| 476 for (size_t i = 1; i < bitmaps.size(); ++i) { | |
| 477 if (bitmaps[i].height() < kPreferredIconSize) | |
| 478 break; | |
| 479 largest_index = i; | |
| 480 } | |
| 481 const SkBitmap& largest = bitmaps[largest_index]; | |
| 482 UpdateAppIcon(gfx::Image::CreateFrom1xBitmap(largest)); | |
| 483 } | |
| 484 | |
| 485 void ShellWindow::UpdateExtensionAppIcon() { | |
| 486 // Avoid using any previous app icons were are being downloaded. | |
| 487 image_loader_ptr_factory_.InvalidateWeakPtrs(); | |
| 488 | |
| 489 // Enqueue OnImageLoaded callback. | |
| 490 extensions::ImageLoader* loader = extensions::ImageLoader::Get(profile()); | |
| 491 loader->LoadImageAsync( | |
| 492 extension(), | |
| 493 extensions::IconsInfo::GetIconResource(extension(), | |
| 494 kPreferredIconSize, | |
| 495 ExtensionIconSet::MATCH_BIGGER), | |
| 496 gfx::Size(kPreferredIconSize, kPreferredIconSize), | |
| 497 base::Bind(&ShellWindow::OnImageLoaded, | |
| 498 image_loader_ptr_factory_.GetWeakPtr())); | |
| 499 } | |
| 500 | |
| 501 void ShellWindow::CloseContents(WebContents* contents) { | |
| 502 native_app_window_->Close(); | |
| 503 } | |
| 504 | |
| 505 bool ShellWindow::ShouldSuppressDialogs() { | |
| 506 return true; | |
| 507 } | |
| 508 | |
| 509 content::ColorChooser* ShellWindow::OpenColorChooser(WebContents* web_contents, | |
| 510 SkColor initial_color) { | |
| 511 return chrome::ShowColorChooser(web_contents, initial_color); | |
| 512 } | |
| 513 | |
| 514 void ShellWindow::RunFileChooser(WebContents* tab, | |
| 515 const content::FileChooserParams& params) { | |
| 516 if (window_type_is_panel()) { | |
| 517 // Panels can't host a file dialog, abort. TODO(stevenjb): allow file | |
| 518 // dialogs to be unhosted but still close with the owning web contents. | |
| 519 // crbug.com/172502. | |
| 520 LOG(WARNING) << "File dialog opened by panel."; | |
| 521 return; | |
| 522 } | |
| 523 FileSelectHelper::RunFileChooser(tab, params); | |
| 524 } | |
| 525 | |
| 526 bool ShellWindow::IsPopupOrPanel(const WebContents* source) const { | |
| 527 return true; | |
| 528 } | |
| 529 | |
| 530 void ShellWindow::MoveContents(WebContents* source, const gfx::Rect& pos) { | |
| 531 native_app_window_->SetBounds(pos); | |
| 532 } | |
| 533 | |
| 534 void ShellWindow::NavigationStateChanged( | |
| 535 const content::WebContents* source, unsigned changed_flags) { | |
| 536 if (changed_flags & content::INVALIDATE_TYPE_TITLE) | |
| 537 native_app_window_->UpdateWindowTitle(); | |
| 538 else if (changed_flags & content::INVALIDATE_TYPE_TAB) | |
| 539 native_app_window_->UpdateWindowIcon(); | |
| 540 } | |
| 541 | |
| 542 void ShellWindow::ToggleFullscreenModeForTab(content::WebContents* source, | |
| 543 bool enter_fullscreen) { | |
| 544 if (!IsExtensionWithPermissionOrSuggestInConsole( | |
| 545 APIPermission::kFullscreen, | |
| 546 extension_, | |
| 547 source->GetRenderViewHost())) { | |
| 548 return; | |
| 549 } | |
| 550 | |
| 551 fullscreen_for_tab_ = enter_fullscreen; | |
| 552 | |
| 553 if (enter_fullscreen) { | |
| 554 native_app_window_->SetFullscreen(true); | |
| 555 } else if (!fullscreen_for_window_api_) { | |
| 556 native_app_window_->SetFullscreen(false); | |
| 557 } | |
| 558 } | |
| 559 | |
| 560 bool ShellWindow::IsFullscreenForTabOrPending( | |
| 561 const content::WebContents* source) const { | |
| 562 return fullscreen_for_tab_; | |
| 563 } | |
| 564 | |
| 565 void ShellWindow::Observe(int type, | |
| 566 const content::NotificationSource& source, | |
| 567 const content::NotificationDetails& details) { | |
| 568 switch (type) { | |
| 569 case content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED: { | |
| 570 // TODO(jianli): once http://crbug.com/123007 is fixed, we'll no longer | |
| 571 // need to make the native window (ShellWindowViews specially) update | |
| 572 // the clickthrough region for the new RVH. | |
| 573 native_app_window_->RenderViewHostChanged(); | |
| 574 break; | |
| 575 } | |
| 576 case chrome::NOTIFICATION_EXTENSION_UNLOADED: { | |
| 577 const extensions::Extension* unloaded_extension = | |
| 578 content::Details<extensions::UnloadedExtensionInfo>( | |
| 579 details)->extension; | |
| 580 if (extension_ == unloaded_extension) | |
| 581 native_app_window_->Close(); | |
| 582 break; | |
| 583 } | |
| 584 case chrome::NOTIFICATION_APP_TERMINATING: | |
| 585 native_app_window_->Close(); | |
| 586 break; | |
| 587 default: | |
| 588 NOTREACHED() << "Received unexpected notification"; | |
| 589 } | |
| 590 } | |
| 591 | |
| 592 extensions::ActiveTabPermissionGranter* | |
| 593 ShellWindow::GetActiveTabPermissionGranter() { | |
| 594 // Shell windows don't support the activeTab permission. | |
| 595 return NULL; | |
| 596 } | |
| 597 | |
| 598 WebContentsModalDialogHost* ShellWindow::GetWebContentsModalDialogHost() { | |
| 599 return native_app_window_.get(); | |
| 600 } | |
| 601 | |
| 602 void ShellWindow::AddMessageToDevToolsConsole(ConsoleMessageLevel level, | |
| 603 const std::string& message) { | |
| 604 content::RenderViewHost* rvh = web_contents()->GetRenderViewHost(); | |
| 605 rvh->Send(new ExtensionMsg_AddMessageToConsole( | |
| 606 rvh->GetRoutingID(), level, message)); | |
| 607 } | |
| 608 | |
| 609 void ShellWindow::SaveWindowPosition() { | |
| 610 if (window_key_.empty()) | |
| 611 return; | |
| 612 if (!native_app_window_) | |
| 613 return; | |
| 614 | |
| 615 apps::ShellWindowGeometryCache* cache = | |
| 616 apps::ShellWindowGeometryCache::Get(profile()); | |
| 617 | |
| 618 gfx::Rect bounds = native_app_window_->GetRestoredBounds(); | |
| 619 bounds.Inset(native_app_window_->GetFrameInsets()); | |
| 620 ui::WindowShowState window_state = native_app_window_->GetRestoredState(); | |
| 621 cache->SaveGeometry(extension()->id(), window_key_, bounds, window_state); | |
| 622 } | |
| 623 | |
| 624 // static | |
| 625 SkRegion* ShellWindow::RawDraggableRegionsToSkRegion( | |
| 626 const std::vector<extensions::DraggableRegion>& regions) { | |
| 627 SkRegion* sk_region = new SkRegion; | |
| 628 for (std::vector<extensions::DraggableRegion>::const_iterator iter = | |
| 629 regions.begin(); | |
| 630 iter != regions.end(); ++iter) { | |
| 631 const extensions::DraggableRegion& region = *iter; | |
| 632 sk_region->op( | |
| 633 region.bounds.x(), | |
| 634 region.bounds.y(), | |
| 635 region.bounds.right(), | |
| 636 region.bounds.bottom(), | |
| 637 region.draggable ? SkRegion::kUnion_Op : SkRegion::kDifference_Op); | |
| 638 } | |
| 639 return sk_region; | |
| 640 } | |
| 641 | |
| 642 void ShellWindow::DisableExternalOpenForTesting() { | |
| 643 disable_external_open_for_testing_ = true; | |
| 644 } | |
| 645 | |
| OLD | NEW |