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