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