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_host.h"
6
7#include "base/bind.h"
8#include "base/logging.h"
9#include "base/message_loop/message_loop.h"
10#include "chrome/browser/chrome_notification_types.h"
11#include "chrome/browser/chrome_page_zoom.h"
12#include "chrome/browser/extensions/chrome_extension_web_contents_observer.h"
13#include "chrome/browser/extensions/window_controller.h"
14#include "chrome/browser/favicon/favicon_tab_helper.h"
15#include "chrome/browser/profiles/profile.h"
16#include "chrome/browser/sessions/session_tab_helper.h"
17#include "chrome/browser/ui/browser_navigator.h"
18#include "chrome/browser/ui/panels/panel.h"
19#include "chrome/browser/ui/prefs/prefs_tab_helper.h"
20#include "chrome/browser/ui/zoom/zoom_controller.h"
21#include "content/public/browser/invalidate_type.h"
22#include "content/public/browser/navigation_controller.h"
23#include "content/public/browser/notification_service.h"
24#include "content/public/browser/notification_source.h"
25#include "content/public/browser/notification_types.h"
26#include "content/public/browser/render_view_host.h"
27#include "content/public/browser/site_instance.h"
28#include "content/public/browser/user_metrics.h"
29#include "content/public/browser/web_contents.h"
30#include "extensions/browser/view_type_utils.h"
31#include "extensions/common/extension_messages.h"
32#include "ipc/ipc_message.h"
33#include "ipc/ipc_message_macros.h"
34#include "ui/gfx/image/image.h"
35#include "ui/gfx/rect.h"
36
37using base::UserMetricsAction;
38
39PanelHost::PanelHost(Panel* panel, Profile* profile)
40    : panel_(panel),
41      profile_(profile),
42      extension_function_dispatcher_(profile, this),
43      weak_factory_(this) {
44}
45
46PanelHost::~PanelHost() {
47}
48
49void PanelHost::Init(const GURL& url) {
50  if (url.is_empty())
51    return;
52
53  content::WebContents::CreateParams create_params(
54      profile_, content::SiteInstance::CreateForURL(profile_, url));
55  web_contents_.reset(content::WebContents::Create(create_params));
56  extensions::SetViewType(web_contents_.get(), extensions::VIEW_TYPE_PANEL);
57  web_contents_->SetDelegate(this);
58  // web_contents_ may be passed to chrome_page_zoom::Zoom(), so it needs
59  // a ZoomController.
60  ZoomController::CreateForWebContents(web_contents_.get());
61  content::WebContentsObserver::Observe(web_contents_.get());
62
63  // Needed to give the web contents a Tab ID. Extension APIs
64  // expect web contents to have a Tab ID.
65  SessionTabHelper::CreateForWebContents(web_contents_.get());
66  SessionTabHelper::FromWebContents(web_contents_.get())->SetWindowID(
67      panel_->session_id());
68
69  FaviconTabHelper::CreateForWebContents(web_contents_.get());
70  PrefsTabHelper::CreateForWebContents(web_contents_.get());
71  extensions::ChromeExtensionWebContentsObserver::CreateForWebContents(
72      web_contents_.get());
73
74  web_contents_->GetController().LoadURL(
75      url, content::Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
76}
77
78void PanelHost::DestroyWebContents() {
79  // Cannot do a web_contents_.reset() because web_contents_.get() will
80  // still return the pointer when we CHECK in WebContentsDestroyed (or if
81  // we get called back in the middle of web contents destruction, which
82  // WebView might do when it detects the web contents is destroyed).
83  content::WebContents* contents = web_contents_.release();
84  delete contents;
85}
86
87gfx::Image PanelHost::GetPageIcon() const {
88  if (!web_contents_.get())
89    return gfx::Image();
90
91  FaviconTabHelper* favicon_tab_helper =
92      FaviconTabHelper::FromWebContents(web_contents_.get());
93  CHECK(favicon_tab_helper);
94  return favicon_tab_helper->GetFavicon();
95}
96
97content::WebContents* PanelHost::OpenURLFromTab(
98    content::WebContents* source,
99    const content::OpenURLParams& params) {
100  // These dispositions aren't really navigations.
101  if (params.disposition == SUPPRESS_OPEN ||
102      params.disposition == SAVE_TO_DISK ||
103      params.disposition == IGNORE_ACTION)
104    return NULL;
105
106  // Only allow clicks on links.
107  if (params.transition != ui::PAGE_TRANSITION_LINK)
108    return NULL;
109
110  // Force all links to open in a new tab.
111  chrome::NavigateParams navigate_params(profile_,
112                                         params.url,
113                                         params.transition);
114  switch (params.disposition) {
115    case NEW_BACKGROUND_TAB:
116    case NEW_WINDOW:
117    case OFF_THE_RECORD:
118      navigate_params.disposition = params.disposition;
119      break;
120    default:
121      navigate_params.disposition = NEW_FOREGROUND_TAB;
122      break;
123  }
124  chrome::Navigate(&navigate_params);
125  return navigate_params.target_contents;
126}
127
128void PanelHost::NavigationStateChanged(const content::WebContents* source,
129                                       content::InvalidateTypes changed_flags) {
130  // Only need to update the title if the title changed while not loading,
131  // because the title is also updated when loading state changes.
132  if ((changed_flags & content::INVALIDATE_TYPE_TAB) ||
133      ((changed_flags & content::INVALIDATE_TYPE_TITLE) &&
134       !source->IsLoading()))
135    panel_->UpdateTitleBar();
136}
137
138void PanelHost::AddNewContents(content::WebContents* source,
139                               content::WebContents* new_contents,
140                               WindowOpenDisposition disposition,
141                               const gfx::Rect& initial_pos,
142                               bool user_gesture,
143                               bool* was_blocked) {
144  chrome::NavigateParams navigate_params(profile_, new_contents->GetURL(),
145                                         ui::PAGE_TRANSITION_LINK);
146  navigate_params.target_contents = new_contents;
147
148  // Force all links to open in a new tab, even if they were trying to open a
149  // window.
150  navigate_params.disposition =
151      disposition == NEW_BACKGROUND_TAB ? disposition : NEW_FOREGROUND_TAB;
152
153  navigate_params.window_bounds = initial_pos;
154  navigate_params.user_gesture = user_gesture;
155  navigate_params.extension_app_id = panel_->extension_id();
156  chrome::Navigate(&navigate_params);
157}
158
159void PanelHost::ActivateContents(content::WebContents* contents) {
160  panel_->Activate();
161}
162
163void PanelHost::DeactivateContents(content::WebContents* contents) {
164  panel_->Deactivate();
165}
166
167void PanelHost::LoadingStateChanged(content::WebContents* source,
168    bool to_different_document) {
169  bool is_loading = source->IsLoading() && to_different_document;
170  panel_->LoadingStateChanged(is_loading);
171}
172
173void PanelHost::CloseContents(content::WebContents* source) {
174  panel_->Close();
175}
176
177void PanelHost::MoveContents(content::WebContents* source,
178                             const gfx::Rect& pos) {
179  panel_->SetBounds(pos);
180}
181
182bool PanelHost::IsPopupOrPanel(const content::WebContents* source) const {
183  return true;
184}
185
186void PanelHost::ContentsZoomChange(bool zoom_in) {
187  Zoom(zoom_in ? content::PAGE_ZOOM_IN : content::PAGE_ZOOM_OUT);
188}
189
190void PanelHost::HandleKeyboardEvent(
191    content::WebContents* source,
192    const content::NativeWebKeyboardEvent& event) {
193  return panel_->HandleKeyboardEvent(event);
194}
195
196void PanelHost::WebContentsFocused(content::WebContents* contents) {
197  panel_->WebContentsFocused(contents);
198}
199
200void PanelHost::ResizeDueToAutoResize(content::WebContents* web_contents,
201                                      const gfx::Size& new_size) {
202  panel_->OnContentsAutoResized(new_size);
203}
204
205void PanelHost::RenderViewCreated(content::RenderViewHost* render_view_host) {
206  extensions::WindowController* window = GetExtensionWindowController();
207  render_view_host->Send(new ExtensionMsg_UpdateBrowserWindowId(
208      render_view_host->GetRoutingID(), window->GetWindowId()));
209}
210
211void PanelHost::RenderProcessGone(base::TerminationStatus status) {
212  CloseContents(web_contents_.get());
213}
214
215void PanelHost::WebContentsDestroyed() {
216  // Web contents should only be destroyed by us.
217  CHECK(!web_contents_.get());
218
219  // Close the panel after we return to the message loop (not immediately,
220  // otherwise, it may destroy this object before the stack has a chance
221  // to cleanly unwind.)
222  base::MessageLoop::current()->PostTask(
223      FROM_HERE,
224      base::Bind(&PanelHost::ClosePanel, weak_factory_.GetWeakPtr()));
225}
226
227void PanelHost::ClosePanel() {
228  panel_->Close();
229}
230
231bool PanelHost::OnMessageReceived(const IPC::Message& message) {
232  bool handled = true;
233  IPC_BEGIN_MESSAGE_MAP(PanelHost, message)
234    IPC_MESSAGE_HANDLER(ExtensionHostMsg_Request, OnRequest)
235    IPC_MESSAGE_UNHANDLED(handled = false)
236  IPC_END_MESSAGE_MAP()
237  return handled;
238}
239
240void PanelHost::OnRequest(const ExtensionHostMsg_Request_Params& params) {
241  if (!web_contents_.get())
242    return;
243
244  extension_function_dispatcher_.Dispatch(params,
245                                          web_contents_->GetRenderViewHost());
246}
247
248extensions::WindowController* PanelHost::GetExtensionWindowController() const {
249  return panel_->extension_window_controller();
250}
251
252content::WebContents* PanelHost::GetAssociatedWebContents() const {
253  return web_contents_.get();
254}
255
256void PanelHost::Reload() {
257  content::RecordAction(UserMetricsAction("Reload"));
258  web_contents_->GetController().Reload(true);
259}
260
261void PanelHost::ReloadIgnoringCache() {
262  content::RecordAction(UserMetricsAction("ReloadIgnoringCache"));
263  web_contents_->GetController().ReloadIgnoringCache(true);
264}
265
266void PanelHost::StopLoading() {
267  content::RecordAction(UserMetricsAction("Stop"));
268  web_contents_->Stop();
269}
270
271void PanelHost::Zoom(content::PageZoom zoom) {
272  chrome_page_zoom::Zoom(web_contents_.get(), zoom);
273}
274