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/panels/panel.h" | |
6 | |
7 #include <stddef.h> | |
8 | |
9 #include <memory> | |
10 | |
11 #include "base/logging.h" | |
12 #include "base/macros.h" | |
13 #include "base/message_loop/message_loop.h" | |
14 #include "base/strings/utf_string_conversions.h" | |
15 #include "chrome/app/chrome_command_ids.h" | |
16 #include "chrome/browser/chrome_notification_types.h" | |
17 #include "chrome/browser/devtools/devtools_window.h" | |
18 #include "chrome/browser/extensions/api/tabs/tabs_constants.h" | |
19 #include "chrome/browser/extensions/api/tabs/tabs_windows_api.h" | |
20 #include "chrome/browser/extensions/api/tabs/windows_event_router.h" | |
21 #include "chrome/browser/extensions/extension_service.h" | |
22 #include "chrome/browser/extensions/extension_tab_util.h" | |
23 #include "chrome/browser/extensions/window_controller.h" | |
24 #include "chrome/browser/extensions/window_controller_list.h" | |
25 #include "chrome/browser/lifetime/application_lifetime.h" | |
26 #include "chrome/browser/lifetime/keep_alive_types.h" | |
27 #include "chrome/browser/lifetime/scoped_keep_alive.h" | |
28 #include "chrome/browser/profiles/profile.h" | |
29 #include "chrome/browser/sessions/session_tab_helper.h" | |
30 #include "chrome/browser/task_manager/web_contents_tags.h" | |
31 #include "chrome/browser/themes/theme_service.h" | |
32 #include "chrome/browser/themes/theme_service_factory.h" | |
33 #include "chrome/browser/ui/panels/native_panel.h" | |
34 #include "chrome/browser/ui/panels/panel_collection.h" | |
35 #include "chrome/browser/ui/panels/panel_host.h" | |
36 #include "chrome/browser/ui/panels/panel_manager.h" | |
37 #include "chrome/browser/ui/panels/stacked_panel_collection.h" | |
38 #include "chrome/browser/web_applications/web_app.h" | |
39 #include "content/public/browser/notification_service.h" | |
40 #include "content/public/browser/notification_source.h" | |
41 #include "content/public/browser/notification_types.h" | |
42 #include "content/public/browser/render_view_host.h" | |
43 #include "content/public/browser/user_metrics.h" | |
44 #include "content/public/browser/web_contents.h" | |
45 #include "extensions/browser/extension_registry.h" | |
46 #include "extensions/browser/extension_system.h" | |
47 #include "extensions/browser/image_loader.h" | |
48 #include "extensions/common/constants.h" | |
49 #include "extensions/common/extension.h" | |
50 #include "extensions/common/manifest_handlers/icons_handler.h" | |
51 #include "ui/gfx/geometry/rect.h" | |
52 #include "ui/gfx/image/image.h" | |
53 | |
54 using base::UserMetricsAction; | |
55 using content::RenderViewHost; | |
56 | |
57 namespace panel_internal { | |
58 | |
59 class PanelExtensionWindowController : public extensions::WindowController { | |
60 public: | |
61 PanelExtensionWindowController(Panel* panel, Profile* profile); | |
62 ~PanelExtensionWindowController() override; | |
63 | |
64 // Overridden from extensions::WindowController. | |
65 int GetWindowId() const override; | |
66 std::string GetWindowTypeText() const override; | |
67 std::unique_ptr<base::DictionaryValue> CreateWindowValueWithTabs( | |
68 const extensions::Extension* extension) const override; | |
69 base::DictionaryValue* CreateTabValue(const extensions::Extension* extension, | |
70 int tab_index) const override; | |
71 std::unique_ptr<extensions::api::tabs::Tab> CreateTabObject( | |
72 const extensions::Extension* extension, | |
73 int tab_index) const override; | |
74 bool CanClose(Reason* reason) const override; | |
75 void SetFullscreenMode(bool is_fullscreen, | |
76 const GURL& extension_url) const override; | |
77 bool IsVisibleToExtension( | |
78 const extensions::Extension* extension) const override; | |
79 | |
80 private: | |
81 Panel* panel_; // Weak pointer. Owns us. | |
82 DISALLOW_COPY_AND_ASSIGN(PanelExtensionWindowController); | |
83 }; | |
84 | |
85 PanelExtensionWindowController::PanelExtensionWindowController( | |
86 Panel* panel, Profile* profile) | |
87 : extensions::WindowController(panel, profile), | |
88 panel_(panel) { | |
89 extensions::WindowControllerList::GetInstance()->AddExtensionWindow(this); | |
90 } | |
91 | |
92 PanelExtensionWindowController::~PanelExtensionWindowController() { | |
93 extensions::WindowControllerList::GetInstance()->RemoveExtensionWindow(this); | |
94 } | |
95 | |
96 int PanelExtensionWindowController::GetWindowId() const { | |
97 return static_cast<int>(panel_->session_id().id()); | |
98 } | |
99 | |
100 std::string PanelExtensionWindowController::GetWindowTypeText() const { | |
101 return extensions::tabs_constants::kWindowTypeValuePanel; | |
102 } | |
103 | |
104 std::unique_ptr<base::DictionaryValue> | |
105 PanelExtensionWindowController::CreateWindowValueWithTabs( | |
106 const extensions::Extension* extension) const { | |
107 std::unique_ptr<base::DictionaryValue> result = CreateWindowValue(); | |
108 | |
109 base::DictionaryValue* tab_value = CreateTabValue(extension, 0); | |
110 if (tab_value) { | |
111 base::ListValue* tab_list = new base::ListValue(); | |
112 tab_list->Append(tab_value); | |
113 result->Set(extensions::tabs_constants::kTabsKey, tab_list); | |
114 } | |
115 return result; | |
116 } | |
117 | |
118 base::DictionaryValue* PanelExtensionWindowController::CreateTabValue( | |
119 const extensions::Extension* extension, int tab_index) const { | |
120 return CreateTabObject(extension, tab_index)->ToValue().release(); | |
121 } | |
122 | |
123 std::unique_ptr<extensions::api::tabs::Tab> | |
124 PanelExtensionWindowController::CreateTabObject( | |
125 const extensions::Extension* extension, | |
126 int tab_index) const { | |
127 if (tab_index > 0) | |
128 return nullptr; | |
129 | |
130 content::WebContents* web_contents = panel_->GetWebContents(); | |
131 if (!web_contents) | |
132 return nullptr; | |
133 | |
134 std::unique_ptr<extensions::api::tabs::Tab> tab_object( | |
135 new extensions::api::tabs::Tab); | |
136 tab_object->id.reset(new int(SessionTabHelper::IdForTab(web_contents))); | |
137 tab_object->index = 0; | |
138 tab_object->window_id = | |
139 SessionTabHelper::IdForWindowContainingTab(web_contents); | |
140 tab_object->url.reset(new std::string(web_contents->GetURL().spec())); | |
141 tab_object->status.reset( | |
142 new std::string(extensions::ExtensionTabUtil::GetTabStatusText( | |
143 web_contents->IsLoading()))); | |
144 tab_object->active = panel_->IsActive(); | |
145 tab_object->selected = true; | |
146 tab_object->highlighted = true; | |
147 tab_object->pinned = false; | |
148 tab_object->title.reset( | |
149 new std::string(base::UTF16ToUTF8(web_contents->GetTitle()))); | |
150 tab_object->incognito = web_contents->GetBrowserContext()->IsOffTheRecord(); | |
151 return tab_object; | |
152 } | |
153 | |
154 bool PanelExtensionWindowController::CanClose(Reason* reason) const { | |
155 return true; | |
156 } | |
157 | |
158 void PanelExtensionWindowController::SetFullscreenMode( | |
159 bool is_fullscreen, const GURL& extension_url) const { | |
160 // Do nothing. Panels cannot be fullscreen. | |
161 } | |
162 | |
163 bool PanelExtensionWindowController::IsVisibleToExtension( | |
164 const extensions::Extension* extension) const { | |
165 DCHECK(extension); | |
166 return extension->id() == panel_->extension_id(); | |
167 } | |
168 | |
169 } // namespace panel_internal | |
170 | |
171 Panel::~Panel() { | |
172 DCHECK(!collection_); | |
173 } | |
174 | |
175 PanelManager* Panel::manager() const { | |
176 return PanelManager::GetInstance(); | |
177 } | |
178 | |
179 const std::string Panel::extension_id() const { | |
180 return web_app::GetExtensionIdFromApplicationName(app_name_); | |
181 } | |
182 | |
183 CommandUpdater* Panel::command_updater() { | |
184 return &command_updater_; | |
185 } | |
186 | |
187 Profile* Panel::profile() const { | |
188 return profile_; | |
189 } | |
190 | |
191 const extensions::Extension* Panel::GetExtension() const { | |
192 ExtensionService* extension_service = | |
193 extensions::ExtensionSystem::Get(profile())->extension_service(); | |
194 if (!extension_service || !extension_service->is_ready()) | |
195 return NULL; | |
196 return extension_service->GetExtensionById(extension_id(), false); | |
197 } | |
198 | |
199 content::WebContents* Panel::GetWebContents() const { | |
200 return panel_host_.get() ? panel_host_->web_contents() : NULL; | |
201 } | |
202 | |
203 void Panel::SetExpansionState(ExpansionState new_state) { | |
204 if (expansion_state_ == new_state) | |
205 return; | |
206 native_panel_->PanelExpansionStateChanging(expansion_state_, new_state); | |
207 expansion_state_ = new_state; | |
208 | |
209 manager()->OnPanelExpansionStateChanged(this); | |
210 | |
211 DCHECK(initialized_ && collection_ != NULL); | |
212 native_panel_->PreventActivationByOS(collection_->IsPanelMinimized(this)); | |
213 UpdateMinimizeRestoreButtonVisibility(); | |
214 | |
215 content::NotificationService::current()->Notify( | |
216 chrome::NOTIFICATION_PANEL_CHANGED_EXPANSION_STATE, | |
217 content::Source<Panel>(this), | |
218 content::NotificationService::NoDetails()); | |
219 } | |
220 | |
221 bool Panel::IsDrawingAttention() const { | |
222 return native_panel_->IsDrawingAttention(); | |
223 } | |
224 | |
225 void Panel::FullScreenModeChanged(bool is_full_screen) { | |
226 native_panel_->FullScreenModeChanged(is_full_screen); | |
227 } | |
228 | |
229 int Panel::TitleOnlyHeight() const { | |
230 return native_panel_->TitleOnlyHeight(); | |
231 } | |
232 | |
233 bool Panel::CanShowMinimizeButton() const { | |
234 return collection_ && collection_->CanShowMinimizeButton(this); | |
235 } | |
236 | |
237 bool Panel::CanShowRestoreButton() const { | |
238 return collection_ && collection_->CanShowRestoreButton(this); | |
239 } | |
240 | |
241 bool Panel::IsActive() const { | |
242 return native_panel_->IsPanelActive(); | |
243 } | |
244 | |
245 bool Panel::IsMaximized() const { | |
246 // Size of panels is managed by PanelManager, they are never 'zoomed'. | |
247 return false; | |
248 } | |
249 | |
250 bool Panel::IsMinimized() const { | |
251 return !collection_ || collection_->IsPanelMinimized(this); | |
252 } | |
253 | |
254 bool Panel::IsFullscreen() const { | |
255 return false; | |
256 } | |
257 | |
258 gfx::NativeWindow Panel::GetNativeWindow() const { | |
259 return native_panel_->GetNativePanelWindow(); | |
260 } | |
261 | |
262 gfx::Rect Panel::GetRestoredBounds() const { | |
263 gfx::Rect bounds = native_panel_->GetPanelBounds(); | |
264 bounds.set_y(bounds.bottom() - full_size_.height()); | |
265 bounds.set_x(bounds.right() - full_size_.width()); | |
266 bounds.set_size(full_size_); | |
267 return bounds; | |
268 } | |
269 | |
270 ui::WindowShowState Panel::GetRestoredState() const { | |
271 return ui::SHOW_STATE_NORMAL; | |
272 } | |
273 | |
274 gfx::Rect Panel::GetBounds() const { | |
275 return native_panel_->GetPanelBounds(); | |
276 } | |
277 | |
278 void Panel::Show() { | |
279 if (manager()->display_settings_provider()->is_full_screen() || !collection_) | |
280 return; | |
281 | |
282 native_panel_->ShowPanel(); | |
283 } | |
284 | |
285 void Panel::Hide() { | |
286 // Not implemented. | |
287 } | |
288 | |
289 void Panel::ShowInactive() { | |
290 if (manager()->display_settings_provider()->is_full_screen() || !collection_) | |
291 return; | |
292 | |
293 native_panel_->ShowPanelInactive(); | |
294 } | |
295 | |
296 // Close() may be called multiple times if the panel window is not ready to | |
297 // close on the first attempt. | |
298 void Panel::Close() { | |
299 native_panel_->ClosePanel(); | |
300 } | |
301 | |
302 void Panel::Activate() { | |
303 if (!collection_) | |
304 return; | |
305 | |
306 collection_->ActivatePanel(this); | |
307 native_panel_->ActivatePanel(); | |
308 } | |
309 | |
310 void Panel::Deactivate() { | |
311 native_panel_->DeactivatePanel(); | |
312 } | |
313 | |
314 void Panel::Maximize() { | |
315 Restore(); | |
316 } | |
317 | |
318 void Panel::Minimize() { | |
319 if (collection_) | |
320 collection_->MinimizePanel(this); | |
321 } | |
322 | |
323 bool Panel::IsMinimizedBySystem() const { | |
324 return native_panel_->IsPanelMinimizedBySystem(); | |
325 } | |
326 | |
327 bool Panel::IsShownOnActiveDesktop() const { | |
328 return native_panel_->IsPanelShownOnActiveDesktop(); | |
329 } | |
330 | |
331 void Panel::ShowShadow(bool show) { | |
332 native_panel_->ShowShadow(show); | |
333 } | |
334 | |
335 void Panel::Restore() { | |
336 if (collection_) | |
337 collection_->RestorePanel(this); | |
338 } | |
339 | |
340 void Panel::SetBounds(const gfx::Rect& bounds) { | |
341 // Ignore bounds position as the panel manager controls all positioning. | |
342 if (!collection_) | |
343 return; | |
344 collection_->ResizePanelWindow(this, bounds.size()); | |
345 SetAutoResizable(false); | |
346 } | |
347 | |
348 void Panel::FlashFrame(bool draw_attention) { | |
349 if (IsDrawingAttention() == draw_attention || !collection_) | |
350 return; | |
351 | |
352 // Don't draw attention for an active panel. | |
353 if (draw_attention && IsActive()) | |
354 return; | |
355 | |
356 // Invoking native panel to draw attention must be done before informing the | |
357 // panel collection because it needs to check internal state of the panel to | |
358 // determine if the panel has been drawing attention. | |
359 native_panel_->DrawAttention(draw_attention); | |
360 collection_->OnPanelAttentionStateChanged(this); | |
361 } | |
362 | |
363 bool Panel::IsAlwaysOnTop() const { | |
364 return native_panel_->IsPanelAlwaysOnTop(); | |
365 } | |
366 | |
367 void Panel::SetAlwaysOnTop(bool on_top) { | |
368 native_panel_->SetPanelAlwaysOnTop(on_top); | |
369 } | |
370 | |
371 void Panel::ExecuteCommandWithDisposition(int id, | |
372 WindowOpenDisposition disposition) { | |
373 DCHECK(command_updater_.IsCommandEnabled(id)) << "Invalid/disabled command " | |
374 << id; | |
375 | |
376 if (!GetWebContents()) | |
377 return; | |
378 | |
379 switch (id) { | |
380 // Navigation | |
381 case IDC_RELOAD: | |
382 panel_host_->Reload(); | |
383 break; | |
384 case IDC_RELOAD_BYPASSING_CACHE: | |
385 panel_host_->ReloadBypassingCache(); | |
386 break; | |
387 case IDC_STOP: | |
388 panel_host_->StopLoading(); | |
389 break; | |
390 | |
391 // Window management | |
392 case IDC_CLOSE_WINDOW: | |
393 content::RecordAction(UserMetricsAction("CloseWindow")); | |
394 Close(); | |
395 break; | |
396 case IDC_EXIT: | |
397 content::RecordAction(UserMetricsAction("Exit")); | |
398 chrome::AttemptUserExit(); | |
399 break; | |
400 | |
401 // Clipboard | |
402 case IDC_COPY: | |
403 content::RecordAction(UserMetricsAction("Copy")); | |
404 native_panel_->PanelCopy(); | |
405 break; | |
406 case IDC_CUT: | |
407 content::RecordAction(UserMetricsAction("Cut")); | |
408 native_panel_->PanelCut(); | |
409 break; | |
410 case IDC_PASTE: | |
411 content::RecordAction(UserMetricsAction("Paste")); | |
412 native_panel_->PanelPaste(); | |
413 break; | |
414 | |
415 // Zoom | |
416 case IDC_ZOOM_PLUS: | |
417 panel_host_->Zoom(content::PAGE_ZOOM_IN); | |
418 break; | |
419 case IDC_ZOOM_NORMAL: | |
420 panel_host_->Zoom(content::PAGE_ZOOM_RESET); | |
421 break; | |
422 case IDC_ZOOM_MINUS: | |
423 panel_host_->Zoom(content::PAGE_ZOOM_OUT); | |
424 break; | |
425 | |
426 // DevTools | |
427 case IDC_DEV_TOOLS: | |
428 content::RecordAction(UserMetricsAction("DevTools_ToggleWindow")); | |
429 DevToolsWindow::OpenDevToolsWindow(GetWebContents(), | |
430 DevToolsToggleAction::Show()); | |
431 break; | |
432 case IDC_DEV_TOOLS_CONSOLE: | |
433 content::RecordAction(UserMetricsAction("DevTools_ToggleConsole")); | |
434 DevToolsWindow::OpenDevToolsWindow(GetWebContents(), | |
435 DevToolsToggleAction::ShowConsole()); | |
436 break; | |
437 | |
438 default: | |
439 LOG(WARNING) << "Received unimplemented command: " << id; | |
440 break; | |
441 } | |
442 } | |
443 | |
444 void Panel::Observe(int type, | |
445 const content::NotificationSource& source, | |
446 const content::NotificationDetails& details) { | |
447 DCHECK_EQ(chrome::NOTIFICATION_APP_TERMINATING, type); | |
448 Close(); | |
449 } | |
450 | |
451 void Panel::RenderViewHostChanged(content::RenderViewHost* old_host, | |
452 content::RenderViewHost* new_host) { | |
453 ConfigureAutoResize(web_contents()); | |
454 } | |
455 | |
456 void Panel::OnExtensionUnloaded( | |
457 content::BrowserContext* browser_context, | |
458 const extensions::Extension* extension, | |
459 extensions::UnloadedExtensionInfo::Reason reason) { | |
460 if (extension->id() == extension_id()) | |
461 Close(); | |
462 } | |
463 void Panel::OnTitlebarClicked(panel::ClickModifier modifier) { | |
464 if (collection_) | |
465 collection_->OnPanelTitlebarClicked(this, modifier); | |
466 | |
467 // Normally the system activates a window when the titlebar is clicked. | |
468 // However, we prevent system activation of minimized panels, thus the | |
469 // activation may not have occurred. Also, some OSes (Windows) will | |
470 // activate a minimized panel on mouse-down regardless of our attempts to | |
471 // prevent system activation. Attention state is not cleared in that case. | |
472 // See Panel::OnActiveStateChanged(). | |
473 // Therefore, we ensure activation and clearing of attention state if the | |
474 // panel has been expanded. If the panel is in a stack, the titlebar click | |
475 // might minimize the panel and we do not want to activate it to make it | |
476 // expand again. | |
477 // These are no-ops if no changes are needed. | |
478 if (IsMinimized()) | |
479 return; | |
480 Activate(); | |
481 FlashFrame(false); | |
482 } | |
483 | |
484 void Panel::OnMinimizeButtonClicked(panel::ClickModifier modifier) { | |
485 if (collection_) | |
486 collection_->OnMinimizeButtonClicked(this, modifier); | |
487 } | |
488 | |
489 void Panel::OnRestoreButtonClicked(panel::ClickModifier modifier) { | |
490 // Clicking the restore button has the same behavior as clicking the titlebar. | |
491 OnTitlebarClicked(modifier); | |
492 } | |
493 | |
494 void Panel::OnWindowSizeAvailable() { | |
495 ConfigureAutoResize(GetWebContents()); | |
496 } | |
497 | |
498 void Panel::OnNativePanelClosed() { | |
499 // Ensure previously enqueued OnImageLoaded callbacks are ignored. | |
500 image_loader_ptr_factory_.InvalidateWeakPtrs(); | |
501 registrar_.RemoveAll(); | |
502 extension_registry_->RemoveObserver(this); | |
503 manager()->OnPanelClosed(this); | |
504 DCHECK(!collection_); | |
505 } | |
506 | |
507 StackedPanelCollection* Panel::stack() const { | |
508 return collection_ && collection_->type() == PanelCollection::STACKED ? | |
509 static_cast<StackedPanelCollection*>(collection_) : NULL; | |
510 } | |
511 | |
512 panel::Resizability Panel::CanResizeByMouse() const { | |
513 if (!collection_) | |
514 return panel::NOT_RESIZABLE; | |
515 | |
516 return collection_->GetPanelResizability(this); | |
517 } | |
518 | |
519 void Panel::Initialize(const GURL& url, | |
520 content::SiteInstance* source_site_instance, | |
521 const gfx::Rect& bounds, | |
522 bool always_on_top) { | |
523 DCHECK(!initialized_); | |
524 DCHECK(!collection_); // Cannot be added to a collection until fully created. | |
525 DCHECK_EQ(EXPANDED, expansion_state_); | |
526 DCHECK(!bounds.IsEmpty()); | |
527 initialized_ = true; | |
528 full_size_ = bounds.size(); | |
529 native_panel_ = CreateNativePanel(this, bounds, always_on_top); | |
530 | |
531 extension_window_controller_.reset( | |
532 new panel_internal::PanelExtensionWindowController(this, profile_)); | |
533 | |
534 InitCommandState(); | |
535 | |
536 // Set up hosting for web contents. | |
537 panel_host_.reset(new PanelHost(this, profile_)); | |
538 panel_host_->Init(url, source_site_instance); | |
539 content::WebContents* web_contents = GetWebContents(); | |
540 // The contents might be NULL for most of our tests. | |
541 if (web_contents) { | |
542 native_panel_->AttachWebContents(web_contents); | |
543 | |
544 // Make the panel show up in the task manager. | |
545 task_manager::WebContentsTags::CreateForPanel(web_contents, this); | |
546 } | |
547 | |
548 // Close when the extension is unloaded or the browser is exiting. | |
549 extension_registry_->AddObserver(this); | |
550 registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING, | |
551 content::NotificationService::AllSources()); | |
552 registrar_.Add(this, chrome::NOTIFICATION_BROWSER_THEME_CHANGED, | |
553 content::Source<ThemeService>( | |
554 ThemeServiceFactory::GetForProfile(profile_))); | |
555 | |
556 // TODO(dgn): Should keep_alive be always registered regardless of the platform | |
557 // here? (https://crbug.com/590173) | |
558 #if !defined(USE_AURA) | |
559 // Keep alive for AURA has been moved to panel_view. | |
560 // Prevent the browser process from shutting down while this window is open. | |
561 keep_alive_.reset(new ScopedKeepAlive(KeepAliveOrigin::PANEL, | |
562 KeepAliveRestartOption::DISABLED)); | |
563 #endif | |
564 | |
565 UpdateAppIcon(); | |
566 } | |
567 | |
568 void Panel::SetPanelBounds(const gfx::Rect& bounds) { | |
569 if (bounds != native_panel_->GetPanelBounds()) | |
570 native_panel_->SetPanelBounds(bounds); | |
571 } | |
572 | |
573 void Panel::SetPanelBoundsInstantly(const gfx::Rect& bounds) { | |
574 native_panel_->SetPanelBoundsInstantly(bounds); | |
575 } | |
576 | |
577 void Panel::LimitSizeToWorkArea(const gfx::Rect& work_area) { | |
578 int max_width = manager()->GetMaxPanelWidth(work_area); | |
579 int max_height = manager()->GetMaxPanelHeight(work_area); | |
580 | |
581 // If the custom max size is used, ensure that it does not exceed the display | |
582 // area. | |
583 if (max_size_policy_ == CUSTOM_MAX_SIZE) { | |
584 int current_max_width = max_size_.width(); | |
585 if (current_max_width > max_width) | |
586 max_width = std::min(current_max_width, work_area.width()); | |
587 int current_max_height = max_size_.height(); | |
588 if (current_max_height > max_height) | |
589 max_height = std::min(current_max_height, work_area.height()); | |
590 } | |
591 | |
592 SetSizeRange(min_size_, gfx::Size(max_width, max_height)); | |
593 | |
594 // Ensure that full size does not exceed max size. | |
595 full_size_ = ClampSize(full_size_); | |
596 } | |
597 | |
598 void Panel::SetAutoResizable(bool resizable) { | |
599 if (auto_resizable_ == resizable) | |
600 return; | |
601 | |
602 auto_resizable_ = resizable; | |
603 content::WebContents* web_contents = GetWebContents(); | |
604 if (auto_resizable_) { | |
605 if (web_contents) | |
606 EnableWebContentsAutoResize(web_contents); | |
607 } else { | |
608 if (web_contents) { | |
609 content::WebContentsObserver::Observe(nullptr); | |
610 | |
611 // NULL might be returned if the tab has not been added. | |
612 RenderViewHost* render_view_host = web_contents->GetRenderViewHost(); | |
613 if (render_view_host) | |
614 render_view_host->DisableAutoResize(full_size_); | |
615 } | |
616 } | |
617 } | |
618 | |
619 void Panel::EnableWebContentsAutoResize(content::WebContents* web_contents) { | |
620 DCHECK(web_contents); | |
621 ConfigureAutoResize(web_contents); | |
622 | |
623 // We also need to know when the render view host changes in order | |
624 // to turn on auto-resize notifications in the new render view host. | |
625 content::WebContentsObserver::Observe(web_contents); | |
626 } | |
627 | |
628 void Panel::OnContentsAutoResized(const gfx::Size& new_content_size) { | |
629 DCHECK(auto_resizable_); | |
630 if (!collection_) | |
631 return; | |
632 | |
633 gfx::Size new_window_size = | |
634 native_panel_->WindowSizeFromContentSize(new_content_size); | |
635 | |
636 // Ignore content auto resizes until window frame size is known. | |
637 // This reduces extra resizes when panel is first shown. | |
638 // After window frame size is known, it will trigger another content | |
639 // auto resize. | |
640 if (new_content_size == new_window_size) | |
641 return; | |
642 | |
643 collection_->ResizePanelWindow(this, new_window_size); | |
644 } | |
645 | |
646 void Panel::OnWindowResizedByMouse(const gfx::Rect& new_bounds) { | |
647 if (collection_) | |
648 collection_->OnPanelResizedByMouse(this, new_bounds); | |
649 } | |
650 | |
651 void Panel::SetSizeRange(const gfx::Size& min_size, const gfx::Size& max_size) { | |
652 if (min_size == min_size_ && max_size == max_size_) | |
653 return; | |
654 | |
655 DCHECK(min_size.width() <= max_size.width()); | |
656 DCHECK(min_size.height() <= max_size.height()); | |
657 min_size_ = min_size; | |
658 max_size_ = max_size; | |
659 | |
660 ConfigureAutoResize(GetWebContents()); | |
661 } | |
662 | |
663 void Panel::IncreaseMaxSize(const gfx::Size& desired_panel_size) { | |
664 gfx::Size new_max_size = max_size_; | |
665 if (new_max_size.width() < desired_panel_size.width()) | |
666 new_max_size.set_width(desired_panel_size.width()); | |
667 if (new_max_size.height() < desired_panel_size.height()) | |
668 new_max_size.set_height(desired_panel_size.height()); | |
669 | |
670 SetSizeRange(min_size_, new_max_size); | |
671 } | |
672 | |
673 void Panel::HandleKeyboardEvent(const content::NativeWebKeyboardEvent& event) { | |
674 native_panel_->HandlePanelKeyboardEvent(event); | |
675 } | |
676 | |
677 void Panel::SetPreviewMode(bool in_preview) { | |
678 DCHECK_NE(in_preview_mode_, in_preview); | |
679 in_preview_mode_ = in_preview; | |
680 } | |
681 | |
682 void Panel::UpdateMinimizeRestoreButtonVisibility() { | |
683 native_panel_->UpdatePanelMinimizeRestoreButtonVisibility(); | |
684 } | |
685 | |
686 gfx::Size Panel::ClampSize(const gfx::Size& size) const { | |
687 // The panel width: | |
688 // * cannot grow or shrink to go beyond [min_width, max_width] | |
689 int new_width = size.width(); | |
690 if (new_width > max_size_.width()) | |
691 new_width = max_size_.width(); | |
692 if (new_width < min_size_.width()) | |
693 new_width = min_size_.width(); | |
694 | |
695 // The panel height: | |
696 // * cannot grow or shrink to go beyond [min_height, max_height] | |
697 int new_height = size.height(); | |
698 if (new_height > max_size_.height()) | |
699 new_height = max_size_.height(); | |
700 if (new_height < min_size_.height()) | |
701 new_height = min_size_.height(); | |
702 | |
703 return gfx::Size(new_width, new_height); | |
704 } | |
705 | |
706 void Panel::OnActiveStateChanged(bool active) { | |
707 // Clear attention state when an expanded panel becomes active. | |
708 // On some systems (e.g. Win), mouse-down activates a panel regardless of | |
709 // its expansion state. However, we don't want to clear draw attention if | |
710 // contents are not visible. In that scenario, if the mouse-down results | |
711 // in a mouse-click, draw attention will be cleared then. | |
712 // See Panel::OnTitlebarClicked(). | |
713 if (active && IsDrawingAttention() && !IsMinimized()) | |
714 FlashFrame(false); | |
715 | |
716 if (collection_) | |
717 collection_->OnPanelActiveStateChanged(this); | |
718 | |
719 // Send extension event about window changing active state. | |
720 extensions::TabsWindowsAPI* tabs_windows_api = | |
721 extensions::TabsWindowsAPI::Get(profile()); | |
722 if (tabs_windows_api) { | |
723 tabs_windows_api->windows_event_router()->OnActiveWindowChanged( | |
724 active ? extension_window_controller_.get() : NULL); | |
725 } | |
726 | |
727 content::NotificationService::current()->Notify( | |
728 chrome::NOTIFICATION_PANEL_CHANGED_ACTIVE_STATUS, | |
729 content::Source<Panel>(this), | |
730 content::NotificationService::NoDetails()); | |
731 } | |
732 | |
733 void Panel::OnPanelStartUserResizing() { | |
734 SetAutoResizable(false); | |
735 SetPreviewMode(true); | |
736 max_size_policy_ = CUSTOM_MAX_SIZE; | |
737 } | |
738 | |
739 void Panel::OnPanelEndUserResizing() { | |
740 SetPreviewMode(false); | |
741 } | |
742 | |
743 bool Panel::ShouldCloseWindow() { | |
744 return true; | |
745 } | |
746 | |
747 void Panel::OnWindowClosing() { | |
748 if (GetWebContents()) { | |
749 native_panel_->DetachWebContents(GetWebContents()); | |
750 panel_host_->DestroyWebContents(); | |
751 } | |
752 } | |
753 | |
754 bool Panel::ExecuteCommandIfEnabled(int id) { | |
755 if (command_updater()->SupportsCommand(id) && | |
756 command_updater()->IsCommandEnabled(id)) { | |
757 ExecuteCommandWithDisposition(id, CURRENT_TAB); | |
758 return true; | |
759 } | |
760 return false; | |
761 } | |
762 | |
763 base::string16 Panel::GetWindowTitle() const { | |
764 content::WebContents* contents = GetWebContents(); | |
765 base::string16 title; | |
766 | |
767 // |contents| can be NULL during the window's creation. | |
768 if (contents) { | |
769 title = contents->GetTitle(); | |
770 FormatTitleForDisplay(&title); | |
771 } | |
772 | |
773 if (title.empty()) | |
774 title = base::UTF8ToUTF16(app_name()); | |
775 | |
776 return title; | |
777 } | |
778 | |
779 gfx::Image Panel::GetCurrentPageIcon() const { | |
780 return panel_host_.get() ? panel_host_->GetPageIcon() : gfx::Image(); | |
781 } | |
782 | |
783 void Panel::UpdateTitleBar() { | |
784 native_panel_->UpdatePanelTitleBar(); | |
785 } | |
786 | |
787 void Panel::LoadingStateChanged(bool is_loading) { | |
788 command_updater_.UpdateCommandEnabled(IDC_STOP, is_loading); | |
789 native_panel_->UpdatePanelLoadingAnimations(is_loading); | |
790 UpdateTitleBar(); | |
791 } | |
792 | |
793 void Panel::MoveByInstantly(const gfx::Vector2d& delta_origin) { | |
794 gfx::Rect bounds = GetBounds(); | |
795 bounds.Offset(delta_origin); | |
796 SetPanelBoundsInstantly(bounds); | |
797 } | |
798 | |
799 void Panel::SetWindowCornerStyle(panel::CornerStyle corner_style) { | |
800 native_panel_->SetWindowCornerStyle(corner_style); | |
801 } | |
802 | |
803 void Panel::MinimizeBySystem() { | |
804 native_panel_->MinimizePanelBySystem(); | |
805 } | |
806 | |
807 Panel::Panel(Profile* profile, | |
808 const std::string& app_name, | |
809 const gfx::Size& min_size, | |
810 const gfx::Size& max_size) | |
811 : app_name_(app_name), | |
812 profile_(profile), | |
813 collection_(NULL), | |
814 initialized_(false), | |
815 min_size_(min_size), | |
816 max_size_(max_size), | |
817 max_size_policy_(DEFAULT_MAX_SIZE), | |
818 auto_resizable_(false), | |
819 in_preview_mode_(false), | |
820 native_panel_(NULL), | |
821 attention_mode_(USE_PANEL_ATTENTION), | |
822 expansion_state_(EXPANDED), | |
823 command_updater_(this), | |
824 extension_registry_(extensions::ExtensionRegistry::Get(profile_)), | |
825 image_loader_ptr_factory_(this) { | |
826 } | |
827 | |
828 void Panel::OnImageLoaded(const gfx::Image& image) { | |
829 if (!image.IsEmpty()) { | |
830 app_icon_ = image; | |
831 native_panel_->UpdatePanelTitleBar(); | |
832 } | |
833 | |
834 content::NotificationService::current()->Notify( | |
835 chrome::NOTIFICATION_PANEL_APP_ICON_LOADED, | |
836 content::Source<Panel>(this), | |
837 content::NotificationService::NoDetails()); | |
838 } | |
839 | |
840 void Panel::InitCommandState() { | |
841 // All supported commands whose state isn't set automagically some other way | |
842 // (like Stop during a page load) must have their state initialized here, | |
843 // otherwise they will be forever disabled. | |
844 | |
845 // Navigation commands | |
846 command_updater_.UpdateCommandEnabled(IDC_RELOAD, true); | |
847 command_updater_.UpdateCommandEnabled(IDC_RELOAD_BYPASSING_CACHE, true); | |
848 | |
849 // Window management commands | |
850 command_updater_.UpdateCommandEnabled(IDC_CLOSE_WINDOW, true); | |
851 command_updater_.UpdateCommandEnabled(IDC_EXIT, true); | |
852 | |
853 // Zoom | |
854 command_updater_.UpdateCommandEnabled(IDC_ZOOM_MENU, true); | |
855 command_updater_.UpdateCommandEnabled(IDC_ZOOM_PLUS, true); | |
856 command_updater_.UpdateCommandEnabled(IDC_ZOOM_NORMAL, true); | |
857 command_updater_.UpdateCommandEnabled(IDC_ZOOM_MINUS, true); | |
858 | |
859 // Clipboard | |
860 command_updater_.UpdateCommandEnabled(IDC_COPY, true); | |
861 command_updater_.UpdateCommandEnabled(IDC_CUT, true); | |
862 command_updater_.UpdateCommandEnabled(IDC_PASTE, true); | |
863 | |
864 // DevTools | |
865 command_updater_.UpdateCommandEnabled(IDC_DEV_TOOLS, true); | |
866 command_updater_.UpdateCommandEnabled(IDC_DEV_TOOLS_CONSOLE, true); | |
867 } | |
868 | |
869 void Panel::ConfigureAutoResize(content::WebContents* web_contents) { | |
870 if (!auto_resizable_ || !web_contents) | |
871 return; | |
872 | |
873 // NULL might be returned if the tab has not been added. | |
874 RenderViewHost* render_view_host = web_contents->GetRenderViewHost(); | |
875 if (!render_view_host) | |
876 return; | |
877 | |
878 render_view_host->EnableAutoResize( | |
879 min_size_, | |
880 native_panel_->ContentSizeFromWindowSize(max_size_)); | |
881 } | |
882 | |
883 void Panel::UpdateAppIcon() { | |
884 const extensions::Extension* extension = GetExtension(); | |
885 if (!extension) | |
886 return; | |
887 | |
888 extensions::ImageLoader* loader = extensions::ImageLoader::Get(profile()); | |
889 loader->LoadImageAsync( | |
890 extension, | |
891 extensions::IconsInfo::GetIconResource( | |
892 extension, | |
893 extension_misc::EXTENSION_ICON_SMALL, | |
894 ExtensionIconSet::MATCH_BIGGER), | |
895 gfx::Size(extension_misc::EXTENSION_ICON_SMALL, | |
896 extension_misc::EXTENSION_ICON_SMALL), | |
897 base::Bind(&Panel::OnImageLoaded, | |
898 image_loader_ptr_factory_.GetWeakPtr())); | |
899 } | |
900 | |
901 // static | |
902 void Panel::FormatTitleForDisplay(base::string16* title) { | |
903 size_t current_index = 0; | |
904 size_t match_index; | |
905 while ((match_index = title->find(L'\n', current_index)) != | |
906 base::string16::npos) { | |
907 title->replace(match_index, 1, base::string16()); | |
908 current_index = match_index; | |
909 } | |
910 } | |
OLD | NEW |