| OLD | NEW |
| (Empty) |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "apps/app_window.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 #include <string> | |
| 9 #include <vector> | |
| 10 | |
| 11 #include "apps/app_window_registry.h" | |
| 12 #include "apps/ui/apps_client.h" | |
| 13 #include "base/command_line.h" | |
| 14 #include "base/strings/string_util.h" | |
| 15 #include "base/strings/utf_string_conversions.h" | |
| 16 #include "base/values.h" | |
| 17 #include "components/web_modal/web_contents_modal_dialog_manager.h" | |
| 18 #include "content/public/browser/browser_context.h" | |
| 19 #include "content/public/browser/invalidate_type.h" | |
| 20 #include "content/public/browser/navigation_entry.h" | |
| 21 #include "content/public/browser/notification_details.h" | |
| 22 #include "content/public/browser/notification_service.h" | |
| 23 #include "content/public/browser/notification_source.h" | |
| 24 #include "content/public/browser/notification_types.h" | |
| 25 #include "content/public/browser/render_view_host.h" | |
| 26 #include "content/public/browser/resource_dispatcher_host.h" | |
| 27 #include "content/public/browser/web_contents.h" | |
| 28 #include "content/public/common/content_switches.h" | |
| 29 #include "content/public/common/media_stream_request.h" | |
| 30 #include "extensions/browser/app_window/app_delegate.h" | |
| 31 #include "extensions/browser/app_window/app_web_contents_helper.h" | |
| 32 #include "extensions/browser/app_window/app_window_geometry_cache.h" | |
| 33 #include "extensions/browser/app_window/native_app_window.h" | |
| 34 #include "extensions/browser/app_window/size_constraints.h" | |
| 35 #include "extensions/browser/extension_registry.h" | |
| 36 #include "extensions/browser/extension_system.h" | |
| 37 #include "extensions/browser/extensions_browser_client.h" | |
| 38 #include "extensions/browser/notification_types.h" | |
| 39 #include "extensions/browser/process_manager.h" | |
| 40 #include "extensions/browser/suggest_permission_util.h" | |
| 41 #include "extensions/browser/view_type_utils.h" | |
| 42 #include "extensions/common/draggable_region.h" | |
| 43 #include "extensions/common/extension.h" | |
| 44 #include "extensions/common/manifest_handlers/icons_handler.h" | |
| 45 #include "extensions/common/permissions/permissions_data.h" | |
| 46 #include "extensions/common/switches.h" | |
| 47 #include "third_party/skia/include/core/SkRegion.h" | |
| 48 #include "ui/gfx/screen.h" | |
| 49 | |
| 50 #if !defined(OS_MACOSX) | |
| 51 #include "base/prefs/pref_service.h" | |
| 52 #include "extensions/browser/pref_names.h" | |
| 53 #endif | |
| 54 | |
| 55 using content::BrowserContext; | |
| 56 using content::ConsoleMessageLevel; | |
| 57 using content::WebContents; | |
| 58 using extensions::APIPermission; | |
| 59 using extensions::NativeAppWindow; | |
| 60 using web_modal::WebContentsModalDialogHost; | |
| 61 using web_modal::WebContentsModalDialogManager; | |
| 62 | |
| 63 namespace apps { | |
| 64 | |
| 65 namespace { | |
| 66 | |
| 67 const int kDefaultWidth = 512; | |
| 68 const int kDefaultHeight = 384; | |
| 69 | |
| 70 void SetConstraintProperty(const std::string& name, | |
| 71 int value, | |
| 72 base::DictionaryValue* bounds_properties) { | |
| 73 if (value != extensions::SizeConstraints::kUnboundedSize) | |
| 74 bounds_properties->SetInteger(name, value); | |
| 75 else | |
| 76 bounds_properties->Set(name, base::Value::CreateNullValue()); | |
| 77 } | |
| 78 | |
| 79 void SetBoundsProperties(const gfx::Rect& bounds, | |
| 80 const gfx::Size& min_size, | |
| 81 const gfx::Size& max_size, | |
| 82 const std::string& bounds_name, | |
| 83 base::DictionaryValue* window_properties) { | |
| 84 scoped_ptr<base::DictionaryValue> bounds_properties( | |
| 85 new base::DictionaryValue()); | |
| 86 | |
| 87 bounds_properties->SetInteger("left", bounds.x()); | |
| 88 bounds_properties->SetInteger("top", bounds.y()); | |
| 89 bounds_properties->SetInteger("width", bounds.width()); | |
| 90 bounds_properties->SetInteger("height", bounds.height()); | |
| 91 | |
| 92 SetConstraintProperty("minWidth", min_size.width(), bounds_properties.get()); | |
| 93 SetConstraintProperty( | |
| 94 "minHeight", min_size.height(), bounds_properties.get()); | |
| 95 SetConstraintProperty("maxWidth", max_size.width(), bounds_properties.get()); | |
| 96 SetConstraintProperty( | |
| 97 "maxHeight", max_size.height(), bounds_properties.get()); | |
| 98 | |
| 99 window_properties->Set(bounds_name, bounds_properties.release()); | |
| 100 } | |
| 101 | |
| 102 // Combines the constraints of the content and window, and returns constraints | |
| 103 // for the window. | |
| 104 gfx::Size GetCombinedWindowConstraints(const gfx::Size& window_constraints, | |
| 105 const gfx::Size& content_constraints, | |
| 106 const gfx::Insets& frame_insets) { | |
| 107 gfx::Size combined_constraints(window_constraints); | |
| 108 if (content_constraints.width() > 0) { | |
| 109 combined_constraints.set_width( | |
| 110 content_constraints.width() + frame_insets.width()); | |
| 111 } | |
| 112 if (content_constraints.height() > 0) { | |
| 113 combined_constraints.set_height( | |
| 114 content_constraints.height() + frame_insets.height()); | |
| 115 } | |
| 116 return combined_constraints; | |
| 117 } | |
| 118 | |
| 119 // Combines the constraints of the content and window, and returns constraints | |
| 120 // for the content. | |
| 121 gfx::Size GetCombinedContentConstraints(const gfx::Size& window_constraints, | |
| 122 const gfx::Size& content_constraints, | |
| 123 const gfx::Insets& frame_insets) { | |
| 124 gfx::Size combined_constraints(content_constraints); | |
| 125 if (window_constraints.width() > 0) { | |
| 126 combined_constraints.set_width( | |
| 127 std::max(0, window_constraints.width() - frame_insets.width())); | |
| 128 } | |
| 129 if (window_constraints.height() > 0) { | |
| 130 combined_constraints.set_height( | |
| 131 std::max(0, window_constraints.height() - frame_insets.height())); | |
| 132 } | |
| 133 return combined_constraints; | |
| 134 } | |
| 135 | |
| 136 } // namespace | |
| 137 | |
| 138 // AppWindow::BoundsSpecification | |
| 139 | |
| 140 const int AppWindow::BoundsSpecification::kUnspecifiedPosition = INT_MIN; | |
| 141 | |
| 142 AppWindow::BoundsSpecification::BoundsSpecification() | |
| 143 : bounds(kUnspecifiedPosition, kUnspecifiedPosition, 0, 0) {} | |
| 144 | |
| 145 AppWindow::BoundsSpecification::~BoundsSpecification() {} | |
| 146 | |
| 147 void AppWindow::BoundsSpecification::ResetBounds() { | |
| 148 bounds.SetRect(kUnspecifiedPosition, kUnspecifiedPosition, 0, 0); | |
| 149 } | |
| 150 | |
| 151 // AppWindow::CreateParams | |
| 152 | |
| 153 AppWindow::CreateParams::CreateParams() | |
| 154 : window_type(AppWindow::WINDOW_TYPE_DEFAULT), | |
| 155 frame(AppWindow::FRAME_CHROME), | |
| 156 has_frame_color(false), | |
| 157 active_frame_color(SK_ColorBLACK), | |
| 158 inactive_frame_color(SK_ColorBLACK), | |
| 159 alpha_enabled(false), | |
| 160 creator_process_id(0), | |
| 161 state(ui::SHOW_STATE_DEFAULT), | |
| 162 hidden(false), | |
| 163 resizable(true), | |
| 164 focused(true), | |
| 165 always_on_top(false) { | |
| 166 } | |
| 167 | |
| 168 AppWindow::CreateParams::~CreateParams() {} | |
| 169 | |
| 170 gfx::Rect AppWindow::CreateParams::GetInitialWindowBounds( | |
| 171 const gfx::Insets& frame_insets) const { | |
| 172 // Combine into a single window bounds. | |
| 173 gfx::Rect combined_bounds(window_spec.bounds); | |
| 174 if (content_spec.bounds.x() != BoundsSpecification::kUnspecifiedPosition) | |
| 175 combined_bounds.set_x(content_spec.bounds.x() - frame_insets.left()); | |
| 176 if (content_spec.bounds.y() != BoundsSpecification::kUnspecifiedPosition) | |
| 177 combined_bounds.set_y(content_spec.bounds.y() - frame_insets.top()); | |
| 178 if (content_spec.bounds.width() > 0) { | |
| 179 combined_bounds.set_width( | |
| 180 content_spec.bounds.width() + frame_insets.width()); | |
| 181 } | |
| 182 if (content_spec.bounds.height() > 0) { | |
| 183 combined_bounds.set_height( | |
| 184 content_spec.bounds.height() + frame_insets.height()); | |
| 185 } | |
| 186 | |
| 187 // Constrain the bounds. | |
| 188 extensions::SizeConstraints constraints( | |
| 189 GetCombinedWindowConstraints( | |
| 190 window_spec.minimum_size, content_spec.minimum_size, frame_insets), | |
| 191 GetCombinedWindowConstraints( | |
| 192 window_spec.maximum_size, content_spec.maximum_size, frame_insets)); | |
| 193 combined_bounds.set_size(constraints.ClampSize(combined_bounds.size())); | |
| 194 | |
| 195 return combined_bounds; | |
| 196 } | |
| 197 | |
| 198 gfx::Size AppWindow::CreateParams::GetContentMinimumSize( | |
| 199 const gfx::Insets& frame_insets) const { | |
| 200 return GetCombinedContentConstraints(window_spec.minimum_size, | |
| 201 content_spec.minimum_size, | |
| 202 frame_insets); | |
| 203 } | |
| 204 | |
| 205 gfx::Size AppWindow::CreateParams::GetContentMaximumSize( | |
| 206 const gfx::Insets& frame_insets) const { | |
| 207 return GetCombinedContentConstraints(window_spec.maximum_size, | |
| 208 content_spec.maximum_size, | |
| 209 frame_insets); | |
| 210 } | |
| 211 | |
| 212 gfx::Size AppWindow::CreateParams::GetWindowMinimumSize( | |
| 213 const gfx::Insets& frame_insets) const { | |
| 214 return GetCombinedWindowConstraints(window_spec.minimum_size, | |
| 215 content_spec.minimum_size, | |
| 216 frame_insets); | |
| 217 } | |
| 218 | |
| 219 gfx::Size AppWindow::CreateParams::GetWindowMaximumSize( | |
| 220 const gfx::Insets& frame_insets) const { | |
| 221 return GetCombinedWindowConstraints(window_spec.maximum_size, | |
| 222 content_spec.maximum_size, | |
| 223 frame_insets); | |
| 224 } | |
| 225 | |
| 226 // AppWindow | |
| 227 | |
| 228 AppWindow::AppWindow(BrowserContext* context, | |
| 229 extensions::AppDelegate* app_delegate, | |
| 230 const extensions::Extension* extension) | |
| 231 : browser_context_(context), | |
| 232 extension_id_(extension->id()), | |
| 233 window_type_(WINDOW_TYPE_DEFAULT), | |
| 234 app_delegate_(app_delegate), | |
| 235 image_loader_ptr_factory_(this), | |
| 236 fullscreen_types_(FULLSCREEN_TYPE_NONE), | |
| 237 show_on_first_paint_(false), | |
| 238 first_paint_complete_(false), | |
| 239 has_been_shown_(false), | |
| 240 can_send_events_(false), | |
| 241 is_hidden_(false), | |
| 242 cached_always_on_top_(false), | |
| 243 requested_alpha_enabled_(false) { | |
| 244 extensions::ExtensionsBrowserClient* client = | |
| 245 extensions::ExtensionsBrowserClient::Get(); | |
| 246 CHECK(!client->IsGuestSession(context) || context->IsOffTheRecord()) | |
| 247 << "Only off the record window may be opened in the guest mode."; | |
| 248 } | |
| 249 | |
| 250 void AppWindow::Init(const GURL& url, | |
| 251 AppWindowContents* app_window_contents, | |
| 252 const CreateParams& params) { | |
| 253 // Initialize the render interface and web contents | |
| 254 app_window_contents_.reset(app_window_contents); | |
| 255 app_window_contents_->Initialize(browser_context(), url); | |
| 256 WebContents* web_contents = app_window_contents_->GetWebContents(); | |
| 257 if (CommandLine::ForCurrentProcess()->HasSwitch( | |
| 258 extensions::switches::kEnableAppsShowOnFirstPaint)) { | |
| 259 content::WebContentsObserver::Observe(web_contents); | |
| 260 } | |
| 261 app_delegate_->InitWebContents(web_contents); | |
| 262 | |
| 263 WebContentsModalDialogManager::CreateForWebContents(web_contents); | |
| 264 | |
| 265 web_contents->SetDelegate(this); | |
| 266 WebContentsModalDialogManager::FromWebContents(web_contents) | |
| 267 ->SetDelegate(this); | |
| 268 extensions::SetViewType(web_contents, extensions::VIEW_TYPE_APP_WINDOW); | |
| 269 | |
| 270 // Initialize the window | |
| 271 CreateParams new_params = LoadDefaults(params); | |
| 272 window_type_ = new_params.window_type; | |
| 273 window_key_ = new_params.window_key; | |
| 274 | |
| 275 // Windows cannot be always-on-top in fullscreen mode for security reasons. | |
| 276 cached_always_on_top_ = new_params.always_on_top; | |
| 277 if (new_params.state == ui::SHOW_STATE_FULLSCREEN) | |
| 278 new_params.always_on_top = false; | |
| 279 | |
| 280 requested_alpha_enabled_ = new_params.alpha_enabled; | |
| 281 | |
| 282 AppsClient* apps_client = AppsClient::Get(); | |
| 283 native_app_window_.reset( | |
| 284 apps_client->CreateNativeAppWindow(this, new_params)); | |
| 285 | |
| 286 helper_.reset(new extensions::AppWebContentsHelper( | |
| 287 browser_context_, extension_id_, web_contents, app_delegate_.get())); | |
| 288 | |
| 289 popup_manager_.reset( | |
| 290 new web_modal::PopupManager(GetWebContentsModalDialogHost())); | |
| 291 popup_manager_->RegisterWith(web_contents); | |
| 292 | |
| 293 // Prevent the browser process from shutting down while this window exists. | |
| 294 apps_client->IncrementKeepAliveCount(); | |
| 295 UpdateExtensionAppIcon(); | |
| 296 AppWindowRegistry::Get(browser_context_)->AddAppWindow(this); | |
| 297 | |
| 298 if (new_params.hidden) { | |
| 299 // Although the window starts hidden by default, calling Hide() here | |
| 300 // notifies observers of the window being hidden. | |
| 301 Hide(); | |
| 302 } else { | |
| 303 // Panels are not activated by default. | |
| 304 Show(window_type_is_panel() || !new_params.focused ? SHOW_INACTIVE | |
| 305 : SHOW_ACTIVE); | |
| 306 } | |
| 307 | |
| 308 if (new_params.state == ui::SHOW_STATE_FULLSCREEN) | |
| 309 Fullscreen(); | |
| 310 else if (new_params.state == ui::SHOW_STATE_MAXIMIZED) | |
| 311 Maximize(); | |
| 312 else if (new_params.state == ui::SHOW_STATE_MINIMIZED) | |
| 313 Minimize(); | |
| 314 | |
| 315 OnNativeWindowChanged(); | |
| 316 | |
| 317 // When the render view host is changed, the native window needs to know | |
| 318 // about it in case it has any setup to do to make the renderer appear | |
| 319 // properly. In particular, on Windows, the view's clickthrough region needs | |
| 320 // to be set. | |
| 321 extensions::ExtensionsBrowserClient* client = | |
| 322 extensions::ExtensionsBrowserClient::Get(); | |
| 323 registrar_.Add(this, | |
| 324 extensions::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED, | |
| 325 content::Source<content::BrowserContext>( | |
| 326 client->GetOriginalContext(browser_context_))); | |
| 327 // Update the app menu if an ephemeral app becomes installed. | |
| 328 registrar_.Add( | |
| 329 this, | |
| 330 extensions::NOTIFICATION_EXTENSION_WILL_BE_INSTALLED_DEPRECATED, | |
| 331 content::Source<content::BrowserContext>( | |
| 332 client->GetOriginalContext(browser_context_))); | |
| 333 | |
| 334 // Close when the browser process is exiting. | |
| 335 app_delegate_->SetTerminatingCallback( | |
| 336 base::Bind(&NativeAppWindow::Close, | |
| 337 base::Unretained(native_app_window_.get()))); | |
| 338 | |
| 339 app_window_contents_->LoadContents(new_params.creator_process_id); | |
| 340 | |
| 341 if (CommandLine::ForCurrentProcess()->HasSwitch( | |
| 342 extensions::switches::kEnableAppsShowOnFirstPaint)) { | |
| 343 // We want to show the window only when the content has been painted. For | |
| 344 // that to happen, we need to define a size for the content, otherwise the | |
| 345 // layout will happen in a 0x0 area. | |
| 346 gfx::Insets frame_insets = native_app_window_->GetFrameInsets(); | |
| 347 gfx::Rect initial_bounds = new_params.GetInitialWindowBounds(frame_insets); | |
| 348 initial_bounds.Inset(frame_insets); | |
| 349 app_delegate_->ResizeWebContents(web_contents, initial_bounds.size()); | |
| 350 } | |
| 351 } | |
| 352 | |
| 353 AppWindow::~AppWindow() { | |
| 354 // Unregister now to prevent getting notified if we're the last window open. | |
| 355 app_delegate_->SetTerminatingCallback(base::Closure()); | |
| 356 | |
| 357 // Remove shutdown prevention. | |
| 358 AppsClient::Get()->DecrementKeepAliveCount(); | |
| 359 } | |
| 360 | |
| 361 void AppWindow::RequestMediaAccessPermission( | |
| 362 content::WebContents* web_contents, | |
| 363 const content::MediaStreamRequest& request, | |
| 364 const content::MediaResponseCallback& callback) { | |
| 365 DCHECK_EQ(AppWindow::web_contents(), web_contents); | |
| 366 helper_->RequestMediaAccessPermission(request, callback); | |
| 367 } | |
| 368 | |
| 369 WebContents* AppWindow::OpenURLFromTab(WebContents* source, | |
| 370 const content::OpenURLParams& params) { | |
| 371 DCHECK_EQ(web_contents(), source); | |
| 372 return helper_->OpenURLFromTab(params); | |
| 373 } | |
| 374 | |
| 375 void AppWindow::AddNewContents(WebContents* source, | |
| 376 WebContents* new_contents, | |
| 377 WindowOpenDisposition disposition, | |
| 378 const gfx::Rect& initial_pos, | |
| 379 bool user_gesture, | |
| 380 bool* was_blocked) { | |
| 381 DCHECK(new_contents->GetBrowserContext() == browser_context_); | |
| 382 app_delegate_->AddNewContents(browser_context_, | |
| 383 new_contents, | |
| 384 disposition, | |
| 385 initial_pos, | |
| 386 user_gesture, | |
| 387 was_blocked); | |
| 388 } | |
| 389 | |
| 390 bool AppWindow::PreHandleKeyboardEvent( | |
| 391 content::WebContents* source, | |
| 392 const content::NativeWebKeyboardEvent& event, | |
| 393 bool* is_keyboard_shortcut) { | |
| 394 const extensions::Extension* extension = GetExtension(); | |
| 395 if (!extension) | |
| 396 return false; | |
| 397 | |
| 398 // Here, we can handle a key event before the content gets it. When we are | |
| 399 // fullscreen and it is not forced, we want to allow the user to leave | |
| 400 // when ESC is pressed. | |
| 401 // However, if the application has the "overrideEscFullscreen" permission, we | |
| 402 // should let it override that behavior. | |
| 403 // ::HandleKeyboardEvent() will only be called if the KeyEvent's default | |
| 404 // action is not prevented. | |
| 405 // Thus, we should handle the KeyEvent here only if the permission is not set. | |
| 406 if (event.windowsKeyCode == ui::VKEY_ESCAPE && IsFullscreen() && | |
| 407 !IsForcedFullscreen() && | |
| 408 !extension->permissions_data()->HasAPIPermission( | |
| 409 APIPermission::kOverrideEscFullscreen)) { | |
| 410 Restore(); | |
| 411 return true; | |
| 412 } | |
| 413 | |
| 414 return false; | |
| 415 } | |
| 416 | |
| 417 void AppWindow::HandleKeyboardEvent( | |
| 418 WebContents* source, | |
| 419 const content::NativeWebKeyboardEvent& event) { | |
| 420 // If the window is currently fullscreen and not forced, ESC should leave | |
| 421 // fullscreen. If this code is being called for ESC, that means that the | |
| 422 // KeyEvent's default behavior was not prevented by the content. | |
| 423 if (event.windowsKeyCode == ui::VKEY_ESCAPE && IsFullscreen() && | |
| 424 !IsForcedFullscreen()) { | |
| 425 Restore(); | |
| 426 return; | |
| 427 } | |
| 428 | |
| 429 native_app_window_->HandleKeyboardEvent(event); | |
| 430 } | |
| 431 | |
| 432 void AppWindow::RequestToLockMouse(WebContents* web_contents, | |
| 433 bool user_gesture, | |
| 434 bool last_unlocked_by_target) { | |
| 435 DCHECK_EQ(AppWindow::web_contents(), web_contents); | |
| 436 helper_->RequestToLockMouse(); | |
| 437 } | |
| 438 | |
| 439 bool AppWindow::PreHandleGestureEvent(WebContents* source, | |
| 440 const blink::WebGestureEvent& event) { | |
| 441 return extensions::AppWebContentsHelper::ShouldSuppressGestureEvent(event); | |
| 442 } | |
| 443 | |
| 444 void AppWindow::DidFirstVisuallyNonEmptyPaint() { | |
| 445 first_paint_complete_ = true; | |
| 446 if (show_on_first_paint_) { | |
| 447 DCHECK(delayed_show_type_ == SHOW_ACTIVE || | |
| 448 delayed_show_type_ == SHOW_INACTIVE); | |
| 449 Show(delayed_show_type_); | |
| 450 } | |
| 451 } | |
| 452 | |
| 453 void AppWindow::OnNativeClose() { | |
| 454 AppWindowRegistry::Get(browser_context_)->RemoveAppWindow(this); | |
| 455 if (app_window_contents_) { | |
| 456 WebContents* web_contents = app_window_contents_->GetWebContents(); | |
| 457 WebContentsModalDialogManager::FromWebContents(web_contents) | |
| 458 ->SetDelegate(NULL); | |
| 459 app_window_contents_->NativeWindowClosed(); | |
| 460 } | |
| 461 delete this; | |
| 462 } | |
| 463 | |
| 464 void AppWindow::OnNativeWindowChanged() { | |
| 465 SaveWindowPosition(); | |
| 466 | |
| 467 #if defined(OS_WIN) | |
| 468 if (native_app_window_ && cached_always_on_top_ && !IsFullscreen() && | |
| 469 !native_app_window_->IsMaximized() && | |
| 470 !native_app_window_->IsMinimized()) { | |
| 471 UpdateNativeAlwaysOnTop(); | |
| 472 } | |
| 473 #endif | |
| 474 | |
| 475 if (app_window_contents_ && native_app_window_) | |
| 476 app_window_contents_->NativeWindowChanged(native_app_window_.get()); | |
| 477 } | |
| 478 | |
| 479 void AppWindow::OnNativeWindowActivated() { | |
| 480 AppWindowRegistry::Get(browser_context_)->AppWindowActivated(this); | |
| 481 } | |
| 482 | |
| 483 content::WebContents* AppWindow::web_contents() const { | |
| 484 return app_window_contents_->GetWebContents(); | |
| 485 } | |
| 486 | |
| 487 const extensions::Extension* AppWindow::GetExtension() const { | |
| 488 return extensions::ExtensionRegistry::Get(browser_context_) | |
| 489 ->enabled_extensions() | |
| 490 .GetByID(extension_id_); | |
| 491 } | |
| 492 | |
| 493 NativeAppWindow* AppWindow::GetBaseWindow() { return native_app_window_.get(); } | |
| 494 | |
| 495 gfx::NativeWindow AppWindow::GetNativeWindow() { | |
| 496 return GetBaseWindow()->GetNativeWindow(); | |
| 497 } | |
| 498 | |
| 499 gfx::Rect AppWindow::GetClientBounds() const { | |
| 500 gfx::Rect bounds = native_app_window_->GetBounds(); | |
| 501 bounds.Inset(native_app_window_->GetFrameInsets()); | |
| 502 return bounds; | |
| 503 } | |
| 504 | |
| 505 base::string16 AppWindow::GetTitle() const { | |
| 506 const extensions::Extension* extension = GetExtension(); | |
| 507 if (!extension) | |
| 508 return base::string16(); | |
| 509 | |
| 510 // WebContents::GetTitle() will return the page's URL if there's no <title> | |
| 511 // specified. However, we'd prefer to show the name of the extension in that | |
| 512 // case, so we directly inspect the NavigationEntry's title. | |
| 513 base::string16 title; | |
| 514 if (!web_contents() || !web_contents()->GetController().GetActiveEntry() || | |
| 515 web_contents()->GetController().GetActiveEntry()->GetTitle().empty()) { | |
| 516 title = base::UTF8ToUTF16(extension->name()); | |
| 517 } else { | |
| 518 title = web_contents()->GetTitle(); | |
| 519 } | |
| 520 base::RemoveChars(title, base::ASCIIToUTF16("\n"), &title); | |
| 521 return title; | |
| 522 } | |
| 523 | |
| 524 void AppWindow::SetAppIconUrl(const GURL& url) { | |
| 525 // If the same url is being used for the badge, ignore it. | |
| 526 if (url == badge_icon_url_) | |
| 527 return; | |
| 528 | |
| 529 // Avoid using any previous icons that were being downloaded. | |
| 530 image_loader_ptr_factory_.InvalidateWeakPtrs(); | |
| 531 | |
| 532 // Reset |app_icon_image_| to abort pending image load (if any). | |
| 533 app_icon_image_.reset(); | |
| 534 | |
| 535 app_icon_url_ = url; | |
| 536 web_contents()->DownloadImage( | |
| 537 url, | |
| 538 true, // is a favicon | |
| 539 0, // no maximum size | |
| 540 base::Bind(&AppWindow::DidDownloadFavicon, | |
| 541 image_loader_ptr_factory_.GetWeakPtr())); | |
| 542 } | |
| 543 | |
| 544 void AppWindow::SetBadgeIconUrl(const GURL& url) { | |
| 545 // Avoid using any previous icons that were being downloaded. | |
| 546 image_loader_ptr_factory_.InvalidateWeakPtrs(); | |
| 547 | |
| 548 // Reset |app_icon_image_| to abort pending image load (if any). | |
| 549 badge_icon_image_.reset(); | |
| 550 | |
| 551 badge_icon_url_ = url; | |
| 552 web_contents()->DownloadImage( | |
| 553 url, | |
| 554 true, // is a favicon | |
| 555 0, // no maximum size | |
| 556 base::Bind(&AppWindow::DidDownloadFavicon, | |
| 557 image_loader_ptr_factory_.GetWeakPtr())); | |
| 558 } | |
| 559 | |
| 560 void AppWindow::ClearBadge() { | |
| 561 badge_icon_image_.reset(); | |
| 562 badge_icon_url_ = GURL(); | |
| 563 UpdateBadgeIcon(gfx::Image()); | |
| 564 } | |
| 565 | |
| 566 void AppWindow::UpdateShape(scoped_ptr<SkRegion> region) { | |
| 567 native_app_window_->UpdateShape(region.Pass()); | |
| 568 } | |
| 569 | |
| 570 void AppWindow::UpdateDraggableRegions( | |
| 571 const std::vector<extensions::DraggableRegion>& regions) { | |
| 572 native_app_window_->UpdateDraggableRegions(regions); | |
| 573 } | |
| 574 | |
| 575 void AppWindow::UpdateAppIcon(const gfx::Image& image) { | |
| 576 if (image.IsEmpty()) | |
| 577 return; | |
| 578 app_icon_ = image; | |
| 579 native_app_window_->UpdateWindowIcon(); | |
| 580 AppWindowRegistry::Get(browser_context_)->AppWindowIconChanged(this); | |
| 581 } | |
| 582 | |
| 583 void AppWindow::SetFullscreen(FullscreenType type, bool enable) { | |
| 584 DCHECK_NE(FULLSCREEN_TYPE_NONE, type); | |
| 585 | |
| 586 if (enable) { | |
| 587 #if !defined(OS_MACOSX) | |
| 588 // Do not enter fullscreen mode if disallowed by pref. | |
| 589 // TODO(bartfab): Add a test once it becomes possible to simulate a user | |
| 590 // gesture. http://crbug.com/174178 | |
| 591 if (type != FULLSCREEN_TYPE_FORCED) { | |
| 592 PrefService* prefs = | |
| 593 extensions::ExtensionsBrowserClient::Get()->GetPrefServiceForContext( | |
| 594 browser_context()); | |
| 595 if (!prefs->GetBoolean(extensions::pref_names::kAppFullscreenAllowed)) | |
| 596 return; | |
| 597 } | |
| 598 #endif | |
| 599 fullscreen_types_ |= type; | |
| 600 } else { | |
| 601 fullscreen_types_ &= ~type; | |
| 602 } | |
| 603 SetNativeWindowFullscreen(); | |
| 604 } | |
| 605 | |
| 606 bool AppWindow::IsFullscreen() const { | |
| 607 return fullscreen_types_ != FULLSCREEN_TYPE_NONE; | |
| 608 } | |
| 609 | |
| 610 bool AppWindow::IsForcedFullscreen() const { | |
| 611 return (fullscreen_types_ & FULLSCREEN_TYPE_FORCED) != 0; | |
| 612 } | |
| 613 | |
| 614 bool AppWindow::IsHtmlApiFullscreen() const { | |
| 615 return (fullscreen_types_ & FULLSCREEN_TYPE_HTML_API) != 0; | |
| 616 } | |
| 617 | |
| 618 void AppWindow::Fullscreen() { | |
| 619 SetFullscreen(FULLSCREEN_TYPE_WINDOW_API, true); | |
| 620 } | |
| 621 | |
| 622 void AppWindow::Maximize() { GetBaseWindow()->Maximize(); } | |
| 623 | |
| 624 void AppWindow::Minimize() { GetBaseWindow()->Minimize(); } | |
| 625 | |
| 626 void AppWindow::Restore() { | |
| 627 if (IsFullscreen()) { | |
| 628 fullscreen_types_ = FULLSCREEN_TYPE_NONE; | |
| 629 SetNativeWindowFullscreen(); | |
| 630 } else { | |
| 631 GetBaseWindow()->Restore(); | |
| 632 } | |
| 633 } | |
| 634 | |
| 635 void AppWindow::OSFullscreen() { | |
| 636 SetFullscreen(FULLSCREEN_TYPE_OS, true); | |
| 637 } | |
| 638 | |
| 639 void AppWindow::ForcedFullscreen() { | |
| 640 SetFullscreen(FULLSCREEN_TYPE_FORCED, true); | |
| 641 } | |
| 642 | |
| 643 void AppWindow::SetContentSizeConstraints(const gfx::Size& min_size, | |
| 644 const gfx::Size& max_size) { | |
| 645 extensions::SizeConstraints constraints(min_size, max_size); | |
| 646 native_app_window_->SetContentSizeConstraints(constraints.GetMinimumSize(), | |
| 647 constraints.GetMaximumSize()); | |
| 648 | |
| 649 gfx::Rect bounds = GetClientBounds(); | |
| 650 gfx::Size constrained_size = constraints.ClampSize(bounds.size()); | |
| 651 if (bounds.size() != constrained_size) { | |
| 652 bounds.set_size(constrained_size); | |
| 653 bounds.Inset(-native_app_window_->GetFrameInsets()); | |
| 654 native_app_window_->SetBounds(bounds); | |
| 655 } | |
| 656 OnNativeWindowChanged(); | |
| 657 } | |
| 658 | |
| 659 void AppWindow::Show(ShowType show_type) { | |
| 660 is_hidden_ = false; | |
| 661 | |
| 662 if (CommandLine::ForCurrentProcess()->HasSwitch( | |
| 663 extensions::switches::kEnableAppsShowOnFirstPaint)) { | |
| 664 show_on_first_paint_ = true; | |
| 665 | |
| 666 if (!first_paint_complete_) { | |
| 667 delayed_show_type_ = show_type; | |
| 668 return; | |
| 669 } | |
| 670 } | |
| 671 | |
| 672 switch (show_type) { | |
| 673 case SHOW_ACTIVE: | |
| 674 GetBaseWindow()->Show(); | |
| 675 break; | |
| 676 case SHOW_INACTIVE: | |
| 677 GetBaseWindow()->ShowInactive(); | |
| 678 break; | |
| 679 } | |
| 680 AppWindowRegistry::Get(browser_context_)->AppWindowShown(this); | |
| 681 | |
| 682 has_been_shown_ = true; | |
| 683 SendOnWindowShownIfShown(); | |
| 684 } | |
| 685 | |
| 686 void AppWindow::Hide() { | |
| 687 // This is there to prevent race conditions with Hide() being called before | |
| 688 // there was a non-empty paint. It should have no effect in a non-racy | |
| 689 // scenario where the application is hiding then showing a window: the second | |
| 690 // show will not be delayed. | |
| 691 is_hidden_ = true; | |
| 692 show_on_first_paint_ = false; | |
| 693 GetBaseWindow()->Hide(); | |
| 694 AppWindowRegistry::Get(browser_context_)->AppWindowHidden(this); | |
| 695 } | |
| 696 | |
| 697 void AppWindow::SetAlwaysOnTop(bool always_on_top) { | |
| 698 if (cached_always_on_top_ == always_on_top) | |
| 699 return; | |
| 700 | |
| 701 cached_always_on_top_ = always_on_top; | |
| 702 | |
| 703 // As a security measure, do not allow fullscreen windows or windows that | |
| 704 // overlap the taskbar to be on top. The property will be applied when the | |
| 705 // window exits fullscreen and moves away from the taskbar. | |
| 706 if (!IsFullscreen() && !IntersectsWithTaskbar()) | |
| 707 native_app_window_->SetAlwaysOnTop(always_on_top); | |
| 708 | |
| 709 OnNativeWindowChanged(); | |
| 710 } | |
| 711 | |
| 712 bool AppWindow::IsAlwaysOnTop() const { return cached_always_on_top_; } | |
| 713 | |
| 714 void AppWindow::WindowEventsReady() { | |
| 715 can_send_events_ = true; | |
| 716 SendOnWindowShownIfShown(); | |
| 717 } | |
| 718 | |
| 719 void AppWindow::GetSerializedState(base::DictionaryValue* properties) const { | |
| 720 DCHECK(properties); | |
| 721 | |
| 722 properties->SetBoolean("fullscreen", | |
| 723 native_app_window_->IsFullscreenOrPending()); | |
| 724 properties->SetBoolean("minimized", native_app_window_->IsMinimized()); | |
| 725 properties->SetBoolean("maximized", native_app_window_->IsMaximized()); | |
| 726 properties->SetBoolean("alwaysOnTop", IsAlwaysOnTop()); | |
| 727 properties->SetBoolean("hasFrameColor", native_app_window_->HasFrameColor()); | |
| 728 properties->SetBoolean( | |
| 729 "alphaEnabled", | |
| 730 requested_alpha_enabled_ && native_app_window_->CanHaveAlphaEnabled()); | |
| 731 | |
| 732 // These properties are undocumented and are to enable testing. Alpha is | |
| 733 // removed to | |
| 734 // make the values easier to check. | |
| 735 SkColor transparent_white = ~SK_ColorBLACK; | |
| 736 properties->SetInteger( | |
| 737 "activeFrameColor", | |
| 738 native_app_window_->ActiveFrameColor() & transparent_white); | |
| 739 properties->SetInteger( | |
| 740 "inactiveFrameColor", | |
| 741 native_app_window_->InactiveFrameColor() & transparent_white); | |
| 742 | |
| 743 gfx::Rect content_bounds = GetClientBounds(); | |
| 744 gfx::Size content_min_size = native_app_window_->GetContentMinimumSize(); | |
| 745 gfx::Size content_max_size = native_app_window_->GetContentMaximumSize(); | |
| 746 SetBoundsProperties(content_bounds, | |
| 747 content_min_size, | |
| 748 content_max_size, | |
| 749 "innerBounds", | |
| 750 properties); | |
| 751 | |
| 752 gfx::Insets frame_insets = native_app_window_->GetFrameInsets(); | |
| 753 gfx::Rect frame_bounds = native_app_window_->GetBounds(); | |
| 754 gfx::Size frame_min_size = extensions::SizeConstraints::AddFrameToConstraints( | |
| 755 content_min_size, frame_insets); | |
| 756 gfx::Size frame_max_size = extensions::SizeConstraints::AddFrameToConstraints( | |
| 757 content_max_size, frame_insets); | |
| 758 SetBoundsProperties(frame_bounds, | |
| 759 frame_min_size, | |
| 760 frame_max_size, | |
| 761 "outerBounds", | |
| 762 properties); | |
| 763 } | |
| 764 | |
| 765 //------------------------------------------------------------------------------ | |
| 766 // Private methods | |
| 767 | |
| 768 void AppWindow::UpdateBadgeIcon(const gfx::Image& image) { | |
| 769 badge_icon_ = image; | |
| 770 native_app_window_->UpdateBadgeIcon(); | |
| 771 } | |
| 772 | |
| 773 void AppWindow::DidDownloadFavicon( | |
| 774 int id, | |
| 775 int http_status_code, | |
| 776 const GURL& image_url, | |
| 777 const std::vector<SkBitmap>& bitmaps, | |
| 778 const std::vector<gfx::Size>& original_bitmap_sizes) { | |
| 779 if ((image_url != app_icon_url_ && image_url != badge_icon_url_) || | |
| 780 bitmaps.empty()) { | |
| 781 return; | |
| 782 } | |
| 783 | |
| 784 // Bitmaps are ordered largest to smallest. Choose the smallest bitmap | |
| 785 // whose height >= the preferred size. | |
| 786 int largest_index = 0; | |
| 787 for (size_t i = 1; i < bitmaps.size(); ++i) { | |
| 788 if (bitmaps[i].height() < app_delegate_->PreferredIconSize()) | |
| 789 break; | |
| 790 largest_index = i; | |
| 791 } | |
| 792 const SkBitmap& largest = bitmaps[largest_index]; | |
| 793 if (image_url == app_icon_url_) { | |
| 794 UpdateAppIcon(gfx::Image::CreateFrom1xBitmap(largest)); | |
| 795 return; | |
| 796 } | |
| 797 | |
| 798 UpdateBadgeIcon(gfx::Image::CreateFrom1xBitmap(largest)); | |
| 799 } | |
| 800 | |
| 801 void AppWindow::OnExtensionIconImageChanged(extensions::IconImage* image) { | |
| 802 DCHECK_EQ(app_icon_image_.get(), image); | |
| 803 | |
| 804 UpdateAppIcon(gfx::Image(app_icon_image_->image_skia())); | |
| 805 } | |
| 806 | |
| 807 void AppWindow::UpdateExtensionAppIcon() { | |
| 808 // Avoid using any previous app icons were being downloaded. | |
| 809 image_loader_ptr_factory_.InvalidateWeakPtrs(); | |
| 810 | |
| 811 const extensions::Extension* extension = GetExtension(); | |
| 812 if (!extension) | |
| 813 return; | |
| 814 | |
| 815 app_icon_image_.reset( | |
| 816 new extensions::IconImage(browser_context(), | |
| 817 extension, | |
| 818 extensions::IconsInfo::GetIcons(extension), | |
| 819 app_delegate_->PreferredIconSize(), | |
| 820 app_delegate_->GetAppDefaultIcon(), | |
| 821 this)); | |
| 822 | |
| 823 // Triggers actual image loading with 1x resources. The 2x resource will | |
| 824 // be handled by IconImage class when requested. | |
| 825 app_icon_image_->image_skia().GetRepresentation(1.0f); | |
| 826 } | |
| 827 | |
| 828 void AppWindow::SetNativeWindowFullscreen() { | |
| 829 native_app_window_->SetFullscreen(fullscreen_types_); | |
| 830 | |
| 831 if (cached_always_on_top_) | |
| 832 UpdateNativeAlwaysOnTop(); | |
| 833 } | |
| 834 | |
| 835 bool AppWindow::IntersectsWithTaskbar() const { | |
| 836 #if defined(OS_WIN) | |
| 837 gfx::Screen* screen = gfx::Screen::GetNativeScreen(); | |
| 838 gfx::Rect window_bounds = native_app_window_->GetRestoredBounds(); | |
| 839 std::vector<gfx::Display> displays = screen->GetAllDisplays(); | |
| 840 | |
| 841 for (std::vector<gfx::Display>::const_iterator it = displays.begin(); | |
| 842 it != displays.end(); | |
| 843 ++it) { | |
| 844 gfx::Rect taskbar_bounds = it->bounds(); | |
| 845 taskbar_bounds.Subtract(it->work_area()); | |
| 846 if (taskbar_bounds.IsEmpty()) | |
| 847 continue; | |
| 848 | |
| 849 if (window_bounds.Intersects(taskbar_bounds)) | |
| 850 return true; | |
| 851 } | |
| 852 #endif | |
| 853 | |
| 854 return false; | |
| 855 } | |
| 856 | |
| 857 void AppWindow::UpdateNativeAlwaysOnTop() { | |
| 858 DCHECK(cached_always_on_top_); | |
| 859 bool is_on_top = native_app_window_->IsAlwaysOnTop(); | |
| 860 bool fullscreen = IsFullscreen(); | |
| 861 bool intersects_taskbar = IntersectsWithTaskbar(); | |
| 862 | |
| 863 if (is_on_top && (fullscreen || intersects_taskbar)) { | |
| 864 // When entering fullscreen or overlapping the taskbar, ensure windows are | |
| 865 // not always-on-top. | |
| 866 native_app_window_->SetAlwaysOnTop(false); | |
| 867 } else if (!is_on_top && !fullscreen && !intersects_taskbar) { | |
| 868 // When exiting fullscreen and moving away from the taskbar, reinstate | |
| 869 // always-on-top. | |
| 870 native_app_window_->SetAlwaysOnTop(true); | |
| 871 } | |
| 872 } | |
| 873 | |
| 874 void AppWindow::SendOnWindowShownIfShown() { | |
| 875 if (!can_send_events_ || !has_been_shown_) | |
| 876 return; | |
| 877 | |
| 878 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kTestType)) { | |
| 879 app_window_contents_->DispatchWindowShownForTests(); | |
| 880 } | |
| 881 } | |
| 882 | |
| 883 void AppWindow::CloseContents(WebContents* contents) { | |
| 884 native_app_window_->Close(); | |
| 885 } | |
| 886 | |
| 887 bool AppWindow::ShouldSuppressDialogs() { return true; } | |
| 888 | |
| 889 content::ColorChooser* AppWindow::OpenColorChooser( | |
| 890 WebContents* web_contents, | |
| 891 SkColor initial_color, | |
| 892 const std::vector<content::ColorSuggestion>& suggestions) { | |
| 893 return app_delegate_->ShowColorChooser(web_contents, initial_color); | |
| 894 } | |
| 895 | |
| 896 void AppWindow::RunFileChooser(WebContents* tab, | |
| 897 const content::FileChooserParams& params) { | |
| 898 if (window_type_is_panel()) { | |
| 899 // Panels can't host a file dialog, abort. TODO(stevenjb): allow file | |
| 900 // dialogs to be unhosted but still close with the owning web contents. | |
| 901 // crbug.com/172502. | |
| 902 LOG(WARNING) << "File dialog opened by panel."; | |
| 903 return; | |
| 904 } | |
| 905 | |
| 906 app_delegate_->RunFileChooser(tab, params); | |
| 907 } | |
| 908 | |
| 909 bool AppWindow::IsPopupOrPanel(const WebContents* source) const { return true; } | |
| 910 | |
| 911 void AppWindow::MoveContents(WebContents* source, const gfx::Rect& pos) { | |
| 912 native_app_window_->SetBounds(pos); | |
| 913 } | |
| 914 | |
| 915 void AppWindow::NavigationStateChanged(const content::WebContents* source, | |
| 916 content::InvalidateTypes changed_flags) { | |
| 917 if (changed_flags & content::INVALIDATE_TYPE_TITLE) | |
| 918 native_app_window_->UpdateWindowTitle(); | |
| 919 else if (changed_flags & content::INVALIDATE_TYPE_TAB) | |
| 920 native_app_window_->UpdateWindowIcon(); | |
| 921 } | |
| 922 | |
| 923 void AppWindow::ToggleFullscreenModeForTab(content::WebContents* source, | |
| 924 bool enter_fullscreen) { | |
| 925 const extensions::Extension* extension = GetExtension(); | |
| 926 if (!extension) | |
| 927 return; | |
| 928 | |
| 929 if (!IsExtensionWithPermissionOrSuggestInConsole( | |
| 930 APIPermission::kFullscreen, extension, source->GetRenderViewHost())) { | |
| 931 return; | |
| 932 } | |
| 933 | |
| 934 SetFullscreen(FULLSCREEN_TYPE_HTML_API, enter_fullscreen); | |
| 935 } | |
| 936 | |
| 937 bool AppWindow::IsFullscreenForTabOrPending(const content::WebContents* source) | |
| 938 const { | |
| 939 return IsHtmlApiFullscreen(); | |
| 940 } | |
| 941 | |
| 942 void AppWindow::Observe(int type, | |
| 943 const content::NotificationSource& source, | |
| 944 const content::NotificationDetails& details) { | |
| 945 switch (type) { | |
| 946 case extensions::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED: { | |
| 947 const extensions::Extension* unloaded_extension = | |
| 948 content::Details<extensions::UnloadedExtensionInfo>(details) | |
| 949 ->extension; | |
| 950 if (extension_id_ == unloaded_extension->id()) | |
| 951 native_app_window_->Close(); | |
| 952 break; | |
| 953 } | |
| 954 case extensions::NOTIFICATION_EXTENSION_WILL_BE_INSTALLED_DEPRECATED: { | |
| 955 const extensions::Extension* installed_extension = | |
| 956 content::Details<const extensions::InstalledExtensionInfo>(details) | |
| 957 ->extension; | |
| 958 DCHECK(installed_extension); | |
| 959 if (installed_extension->id() == extension_id()) | |
| 960 native_app_window_->UpdateShelfMenu(); | |
| 961 break; | |
| 962 } | |
| 963 default: | |
| 964 NOTREACHED() << "Received unexpected notification"; | |
| 965 } | |
| 966 } | |
| 967 | |
| 968 void AppWindow::SetWebContentsBlocked(content::WebContents* web_contents, | |
| 969 bool blocked) { | |
| 970 app_delegate_->SetWebContentsBlocked(web_contents, blocked); | |
| 971 } | |
| 972 | |
| 973 bool AppWindow::IsWebContentsVisible(content::WebContents* web_contents) { | |
| 974 return app_delegate_->IsWebContentsVisible(web_contents); | |
| 975 } | |
| 976 | |
| 977 WebContentsModalDialogHost* AppWindow::GetWebContentsModalDialogHost() { | |
| 978 return native_app_window_.get(); | |
| 979 } | |
| 980 | |
| 981 void AppWindow::SaveWindowPosition() { | |
| 982 if (window_key_.empty()) | |
| 983 return; | |
| 984 if (!native_app_window_) | |
| 985 return; | |
| 986 | |
| 987 extensions::AppWindowGeometryCache* cache = | |
| 988 extensions::AppWindowGeometryCache::Get(browser_context()); | |
| 989 | |
| 990 gfx::Rect bounds = native_app_window_->GetRestoredBounds(); | |
| 991 gfx::Rect screen_bounds = | |
| 992 gfx::Screen::GetNativeScreen()->GetDisplayMatching(bounds).work_area(); | |
| 993 ui::WindowShowState window_state = native_app_window_->GetRestoredState(); | |
| 994 cache->SaveGeometry( | |
| 995 extension_id(), window_key_, bounds, screen_bounds, window_state); | |
| 996 } | |
| 997 | |
| 998 void AppWindow::AdjustBoundsToBeVisibleOnScreen( | |
| 999 const gfx::Rect& cached_bounds, | |
| 1000 const gfx::Rect& cached_screen_bounds, | |
| 1001 const gfx::Rect& current_screen_bounds, | |
| 1002 const gfx::Size& minimum_size, | |
| 1003 gfx::Rect* bounds) const { | |
| 1004 *bounds = cached_bounds; | |
| 1005 | |
| 1006 // Reposition and resize the bounds if the cached_screen_bounds is different | |
| 1007 // from the current screen bounds and the current screen bounds doesn't | |
| 1008 // completely contain the bounds. | |
| 1009 if (cached_screen_bounds != current_screen_bounds && | |
| 1010 !current_screen_bounds.Contains(cached_bounds)) { | |
| 1011 bounds->set_width( | |
| 1012 std::max(minimum_size.width(), | |
| 1013 std::min(bounds->width(), current_screen_bounds.width()))); | |
| 1014 bounds->set_height( | |
| 1015 std::max(minimum_size.height(), | |
| 1016 std::min(bounds->height(), current_screen_bounds.height()))); | |
| 1017 bounds->set_x( | |
| 1018 std::max(current_screen_bounds.x(), | |
| 1019 std::min(bounds->x(), | |
| 1020 current_screen_bounds.right() - bounds->width()))); | |
| 1021 bounds->set_y( | |
| 1022 std::max(current_screen_bounds.y(), | |
| 1023 std::min(bounds->y(), | |
| 1024 current_screen_bounds.bottom() - bounds->height()))); | |
| 1025 } | |
| 1026 } | |
| 1027 | |
| 1028 AppWindow::CreateParams AppWindow::LoadDefaults(CreateParams params) | |
| 1029 const { | |
| 1030 // Ensure width and height are specified. | |
| 1031 if (params.content_spec.bounds.width() == 0 && | |
| 1032 params.window_spec.bounds.width() == 0) { | |
| 1033 params.content_spec.bounds.set_width(kDefaultWidth); | |
| 1034 } | |
| 1035 if (params.content_spec.bounds.height() == 0 && | |
| 1036 params.window_spec.bounds.height() == 0) { | |
| 1037 params.content_spec.bounds.set_height(kDefaultHeight); | |
| 1038 } | |
| 1039 | |
| 1040 // If left and top are left undefined, the native app window will center | |
| 1041 // the window on the main screen in a platform-defined manner. | |
| 1042 | |
| 1043 // Load cached state if it exists. | |
| 1044 if (!params.window_key.empty()) { | |
| 1045 extensions::AppWindowGeometryCache* cache = | |
| 1046 extensions::AppWindowGeometryCache::Get(browser_context()); | |
| 1047 | |
| 1048 gfx::Rect cached_bounds; | |
| 1049 gfx::Rect cached_screen_bounds; | |
| 1050 ui::WindowShowState cached_state = ui::SHOW_STATE_DEFAULT; | |
| 1051 if (cache->GetGeometry(extension_id(), | |
| 1052 params.window_key, | |
| 1053 &cached_bounds, | |
| 1054 &cached_screen_bounds, | |
| 1055 &cached_state)) { | |
| 1056 // App window has cached screen bounds, make sure it fits on screen in | |
| 1057 // case the screen resolution changed. | |
| 1058 gfx::Screen* screen = gfx::Screen::GetNativeScreen(); | |
| 1059 gfx::Display display = screen->GetDisplayMatching(cached_bounds); | |
| 1060 gfx::Rect current_screen_bounds = display.work_area(); | |
| 1061 extensions::SizeConstraints constraints( | |
| 1062 params.GetWindowMinimumSize(gfx::Insets()), | |
| 1063 params.GetWindowMaximumSize(gfx::Insets())); | |
| 1064 AdjustBoundsToBeVisibleOnScreen(cached_bounds, | |
| 1065 cached_screen_bounds, | |
| 1066 current_screen_bounds, | |
| 1067 constraints.GetMinimumSize(), | |
| 1068 ¶ms.window_spec.bounds); | |
| 1069 params.state = cached_state; | |
| 1070 | |
| 1071 // Since we are restoring a cached state, reset the content bounds spec to | |
| 1072 // ensure it is not used. | |
| 1073 params.content_spec.ResetBounds(); | |
| 1074 } | |
| 1075 } | |
| 1076 | |
| 1077 return params; | |
| 1078 } | |
| 1079 | |
| 1080 // static | |
| 1081 SkRegion* AppWindow::RawDraggableRegionsToSkRegion( | |
| 1082 const std::vector<extensions::DraggableRegion>& regions) { | |
| 1083 SkRegion* sk_region = new SkRegion; | |
| 1084 for (std::vector<extensions::DraggableRegion>::const_iterator iter = | |
| 1085 regions.begin(); | |
| 1086 iter != regions.end(); | |
| 1087 ++iter) { | |
| 1088 const extensions::DraggableRegion& region = *iter; | |
| 1089 sk_region->op( | |
| 1090 region.bounds.x(), | |
| 1091 region.bounds.y(), | |
| 1092 region.bounds.right(), | |
| 1093 region.bounds.bottom(), | |
| 1094 region.draggable ? SkRegion::kUnion_Op : SkRegion::kDifference_Op); | |
| 1095 } | |
| 1096 return sk_region; | |
| 1097 } | |
| 1098 | |
| 1099 } // namespace apps | |
| OLD | NEW |