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_MACOSX) || defined(OS_WIN) || \ | |
306 (defined(OS_LINUX) && !defined(OS_CHROMEOS)) | |
307 if (disable_external_open_for_testing_) { | |
308 Browser* browser = | |
309 chrome::FindOrCreateTabbedBrowser(profile_, chrome::GetActiveDesktop()); | |
310 // Force all links to open in a new tab, even if they were trying to open a | |
311 // new window. | |
312 disposition = | |
313 disposition == NEW_BACKGROUND_TAB ? disposition : NEW_FOREGROUND_TAB; | |
314 chrome::AddWebContents(browser, NULL, new_contents, disposition, | |
315 initial_pos, user_gesture, was_blocked); | |
316 } else { | |
317 new_contents->SetDelegate(new ShellWindowLinkDelegate()); | |
318 } | |
319 #else | |
320 Browser* browser = | |
321 chrome::FindOrCreateTabbedBrowser(profile_, chrome::GetActiveDesktop()); | |
322 // Force all links to open in a new tab, even if they were trying to open a | |
323 // new window. | |
324 disposition = | |
325 disposition == NEW_BACKGROUND_TAB ? disposition : NEW_FOREGROUND_TAB; | |
326 chrome::AddWebContents(browser, NULL, new_contents, disposition, initial_pos, | |
327 user_gesture, was_blocked); | |
328 #endif | |
329 } | |
330 | |
331 void ShellWindow::HandleKeyboardEvent( | |
332 WebContents* source, | |
333 const content::NativeWebKeyboardEvent& event) { | |
334 native_app_window_->HandleKeyboardEvent(event); | |
335 } | |
336 | |
337 void ShellWindow::RequestToLockMouse(WebContents* web_contents, | |
338 bool user_gesture, | |
339 bool last_unlocked_by_target) { | |
340 bool has_permission = IsExtensionWithPermissionOrSuggestInConsole( | |
341 APIPermission::kPointerLock, | |
342 extension_, | |
343 web_contents->GetRenderViewHost()); | |
344 | |
345 web_contents->GotResponseToLockMouseRequest(has_permission); | |
346 } | |
347 | |
348 void ShellWindow::OnNativeClose() { | |
349 extensions::ShellWindowRegistry::Get(profile_)->RemoveShellWindow(this); | |
350 if (shell_window_contents_) | |
351 shell_window_contents_->NativeWindowClosed(); | |
352 delete this; | |
353 } | |
354 | |
355 void ShellWindow::OnNativeWindowChanged() { | |
356 SaveWindowPosition(); | |
357 if (shell_window_contents_ && native_app_window_) | |
358 shell_window_contents_->NativeWindowChanged(native_app_window_.get()); | |
359 } | |
360 | |
361 void ShellWindow::OnNativeWindowActivated() { | |
362 extensions::ShellWindowRegistry::Get(profile_)->ShellWindowActivated(this); | |
363 } | |
364 | |
365 scoped_ptr<gfx::Image> ShellWindow::GetAppListIcon() { | |
366 // TODO(skuhne): We might want to use LoadImages in UpdateExtensionAppIcon | |
367 // instead to let the extension give us pre-defined icons in the launcher | |
368 // and the launcher list sizes. Since there is no mock yet, doing this now | |
369 // seems a bit premature and we scale for the time being. | |
370 if (app_icon_.IsEmpty()) | |
371 return make_scoped_ptr(new gfx::Image()); | |
372 | |
373 SkBitmap bmp = skia::ImageOperations::Resize( | |
374 *app_icon_.ToSkBitmap(), skia::ImageOperations::RESIZE_BEST, | |
375 extension_misc::EXTENSION_ICON_SMALLISH, | |
376 extension_misc::EXTENSION_ICON_SMALLISH); | |
377 return make_scoped_ptr( | |
378 new gfx::Image(gfx::ImageSkia::CreateFrom1xBitmap(bmp))); | |
379 } | |
380 | |
381 content::WebContents* ShellWindow::web_contents() const { | |
382 return shell_window_contents_->GetWebContents(); | |
383 } | |
384 | |
385 NativeAppWindow* ShellWindow::GetBaseWindow() { | |
386 return native_app_window_.get(); | |
387 } | |
388 | |
389 gfx::NativeWindow ShellWindow::GetNativeWindow() { | |
390 return GetBaseWindow()->GetNativeWindow(); | |
391 } | |
392 | |
393 gfx::Rect ShellWindow::GetClientBounds() const { | |
394 gfx::Rect bounds = native_app_window_->GetBounds(); | |
395 bounds.Inset(native_app_window_->GetFrameInsets()); | |
396 return bounds; | |
397 } | |
398 | |
399 string16 ShellWindow::GetTitle() const { | |
400 // WebContents::GetTitle() will return the page's URL if there's no <title> | |
401 // specified. However, we'd prefer to show the name of the extension in that | |
402 // case, so we directly inspect the NavigationEntry's title. | |
403 if (!web_contents() || | |
404 !web_contents()->GetController().GetActiveEntry() || | |
405 web_contents()->GetController().GetActiveEntry()->GetTitle().empty()) | |
406 return UTF8ToUTF16(extension()->name()); | |
407 string16 title = web_contents()->GetTitle(); | |
408 Browser::FormatTitleForDisplay(&title); | |
409 return title; | |
410 } | |
411 | |
412 void ShellWindow::SetAppIconUrl(const GURL& url) { | |
413 // Avoid using any previous app icons were are being downloaded. | |
414 image_loader_ptr_factory_.InvalidateWeakPtrs(); | |
415 | |
416 app_icon_url_ = url; | |
417 web_contents()->DownloadImage( | |
418 url, true, kPreferredIconSize, | |
419 base::Bind(&ShellWindow::DidDownloadFavicon, | |
420 image_loader_ptr_factory_.GetWeakPtr())); | |
421 } | |
422 | |
423 void ShellWindow::UpdateDraggableRegions( | |
424 const std::vector<extensions::DraggableRegion>& regions) { | |
425 native_app_window_->UpdateDraggableRegions(regions); | |
426 } | |
427 | |
428 void ShellWindow::UpdateAppIcon(const gfx::Image& image) { | |
429 if (image.IsEmpty()) | |
430 return; | |
431 app_icon_ = image; | |
432 native_app_window_->UpdateWindowIcon(); | |
433 extensions::ShellWindowRegistry::Get(profile_)->ShellWindowIconChanged(this); | |
434 } | |
435 | |
436 void ShellWindow::Fullscreen() { | |
437 fullscreen_for_window_api_ = true; | |
438 GetBaseWindow()->SetFullscreen(true); | |
439 } | |
440 | |
441 void ShellWindow::Maximize() { | |
442 GetBaseWindow()->Maximize(); | |
443 } | |
444 | |
445 void ShellWindow::Minimize() { | |
446 GetBaseWindow()->Minimize(); | |
447 } | |
448 | |
449 void ShellWindow::Restore() { | |
450 fullscreen_for_window_api_ = false; | |
451 fullscreen_for_tab_ = false; | |
452 if (GetBaseWindow()->IsFullscreenOrPending()) { | |
453 GetBaseWindow()->SetFullscreen(false); | |
454 } else { | |
455 GetBaseWindow()->Restore(); | |
456 } | |
457 } | |
458 | |
459 //------------------------------------------------------------------------------ | |
460 // Private methods | |
461 | |
462 void ShellWindow::OnImageLoaded(const gfx::Image& image) { | |
463 UpdateAppIcon(image); | |
464 } | |
465 | |
466 void ShellWindow::DidDownloadFavicon(int id, | |
467 int http_status_code, | |
468 const GURL& image_url, | |
469 int requested_size, | |
470 const std::vector<SkBitmap>& bitmaps) { | |
471 if (image_url != app_icon_url_ || bitmaps.empty()) | |
472 return; | |
473 | |
474 // Bitmaps are ordered largest to smallest. Choose the smallest bitmap | |
475 // whose height >= the preferred size. | |
476 int largest_index = 0; | |
477 for (size_t i = 1; i < bitmaps.size(); ++i) { | |
478 if (bitmaps[i].height() < kPreferredIconSize) | |
479 break; | |
480 largest_index = i; | |
481 } | |
482 const SkBitmap& largest = bitmaps[largest_index]; | |
483 UpdateAppIcon(gfx::Image::CreateFrom1xBitmap(largest)); | |
484 } | |
485 | |
486 void ShellWindow::UpdateExtensionAppIcon() { | |
487 // Avoid using any previous app icons were are being downloaded. | |
488 image_loader_ptr_factory_.InvalidateWeakPtrs(); | |
489 | |
490 // Enqueue OnImageLoaded callback. | |
491 extensions::ImageLoader* loader = extensions::ImageLoader::Get(profile()); | |
492 loader->LoadImageAsync( | |
493 extension(), | |
494 extensions::IconsInfo::GetIconResource(extension(), | |
495 kPreferredIconSize, | |
496 ExtensionIconSet::MATCH_BIGGER), | |
497 gfx::Size(kPreferredIconSize, kPreferredIconSize), | |
498 base::Bind(&ShellWindow::OnImageLoaded, | |
499 image_loader_ptr_factory_.GetWeakPtr())); | |
500 } | |
501 | |
502 void ShellWindow::CloseContents(WebContents* contents) { | |
503 native_app_window_->Close(); | |
504 } | |
505 | |
506 bool ShellWindow::ShouldSuppressDialogs() { | |
507 return true; | |
508 } | |
509 | |
510 content::ColorChooser* ShellWindow::OpenColorChooser(WebContents* web_contents, | |
511 SkColor initial_color) { | |
512 return chrome::ShowColorChooser(web_contents, initial_color); | |
513 } | |
514 | |
515 void ShellWindow::RunFileChooser(WebContents* tab, | |
516 const content::FileChooserParams& params) { | |
517 if (window_type_is_panel()) { | |
518 // Panels can't host a file dialog, abort. TODO(stevenjb): allow file | |
519 // dialogs to be unhosted but still close with the owning web contents. | |
520 // crbug.com/172502. | |
521 LOG(WARNING) << "File dialog opened by panel."; | |
522 return; | |
523 } | |
524 FileSelectHelper::RunFileChooser(tab, params); | |
525 } | |
526 | |
527 bool ShellWindow::IsPopupOrPanel(const WebContents* source) const { | |
528 return true; | |
529 } | |
530 | |
531 void ShellWindow::MoveContents(WebContents* source, const gfx::Rect& pos) { | |
532 native_app_window_->SetBounds(pos); | |
533 } | |
534 | |
535 void ShellWindow::NavigationStateChanged( | |
536 const content::WebContents* source, unsigned changed_flags) { | |
537 if (changed_flags & content::INVALIDATE_TYPE_TITLE) | |
538 native_app_window_->UpdateWindowTitle(); | |
539 else if (changed_flags & content::INVALIDATE_TYPE_TAB) | |
540 native_app_window_->UpdateWindowIcon(); | |
541 } | |
542 | |
543 void ShellWindow::ToggleFullscreenModeForTab(content::WebContents* source, | |
544 bool enter_fullscreen) { | |
545 if (!IsExtensionWithPermissionOrSuggestInConsole( | |
546 APIPermission::kFullscreen, | |
547 extension_, | |
548 source->GetRenderViewHost())) { | |
549 return; | |
550 } | |
551 | |
552 fullscreen_for_tab_ = enter_fullscreen; | |
553 | |
554 if (enter_fullscreen) { | |
555 native_app_window_->SetFullscreen(true); | |
556 } else if (!fullscreen_for_window_api_) { | |
557 native_app_window_->SetFullscreen(false); | |
558 } | |
559 } | |
560 | |
561 bool ShellWindow::IsFullscreenForTabOrPending( | |
562 const content::WebContents* source) const { | |
563 return fullscreen_for_tab_; | |
564 } | |
565 | |
566 void ShellWindow::Observe(int type, | |
567 const content::NotificationSource& source, | |
568 const content::NotificationDetails& details) { | |
569 switch (type) { | |
570 case content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED: { | |
571 // TODO(jianli): once http://crbug.com/123007 is fixed, we'll no longer | |
572 // need to make the native window (ShellWindowViews specially) update | |
573 // the clickthrough region for the new RVH. | |
574 native_app_window_->RenderViewHostChanged(); | |
575 break; | |
576 } | |
577 case chrome::NOTIFICATION_EXTENSION_UNLOADED: { | |
578 const extensions::Extension* unloaded_extension = | |
579 content::Details<extensions::UnloadedExtensionInfo>( | |
580 details)->extension; | |
581 if (extension_ == unloaded_extension) | |
582 native_app_window_->Close(); | |
583 break; | |
584 } | |
585 case chrome::NOTIFICATION_APP_TERMINATING: | |
586 native_app_window_->Close(); | |
587 break; | |
588 default: | |
589 NOTREACHED() << "Received unexpected notification"; | |
590 } | |
591 } | |
592 | |
593 extensions::ActiveTabPermissionGranter* | |
594 ShellWindow::GetActiveTabPermissionGranter() { | |
595 // Shell windows don't support the activeTab permission. | |
596 return NULL; | |
597 } | |
598 | |
599 WebContentsModalDialogHost* ShellWindow::GetWebContentsModalDialogHost() { | |
600 return native_app_window_.get(); | |
601 } | |
602 | |
603 void ShellWindow::AddMessageToDevToolsConsole(ConsoleMessageLevel level, | |
604 const std::string& message) { | |
605 content::RenderViewHost* rvh = web_contents()->GetRenderViewHost(); | |
606 rvh->Send(new ExtensionMsg_AddMessageToConsole( | |
607 rvh->GetRoutingID(), level, message)); | |
608 } | |
609 | |
610 void ShellWindow::SaveWindowPosition() { | |
611 if (window_key_.empty()) | |
612 return; | |
613 if (!native_app_window_) | |
614 return; | |
615 | |
616 apps::ShellWindowGeometryCache* cache = | |
617 apps::ShellWindowGeometryCache::Get(profile()); | |
618 | |
619 gfx::Rect bounds = native_app_window_->GetRestoredBounds(); | |
620 bounds.Inset(native_app_window_->GetFrameInsets()); | |
621 ui::WindowShowState window_state = native_app_window_->GetRestoredState(); | |
622 cache->SaveGeometry(extension()->id(), window_key_, bounds, window_state); | |
623 } | |
624 | |
625 // static | |
626 SkRegion* ShellWindow::RawDraggableRegionsToSkRegion( | |
627 const std::vector<extensions::DraggableRegion>& regions) { | |
628 SkRegion* sk_region = new SkRegion; | |
629 for (std::vector<extensions::DraggableRegion>::const_iterator iter = | |
630 regions.begin(); | |
631 iter != regions.end(); ++iter) { | |
632 const extensions::DraggableRegion& region = *iter; | |
633 sk_region->op( | |
634 region.bounds.x(), | |
635 region.bounds.y(), | |
636 region.bounds.right(), | |
637 region.bounds.bottom(), | |
638 region.draggable ? SkRegion::kUnion_Op : SkRegion::kDifference_Op); | |
639 } | |
640 return sk_region; | |
641 } | |
642 | |
643 void ShellWindow::DisableExternalOpenForTesting() { | |
644 disable_external_open_for_testing_ = true; | |
645 } | |
646 | |
OLD | NEW |