tab_helper.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
1ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com// Use of this source code is governed by a BSD-style license that can be 3ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com// found in the LICENSE file. 4ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com 5ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com#include "chrome/browser/extensions/tab_helper.h" 6ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com 7ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com#include "chrome/browser/extensions/activity_log.h" 8ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com#include "chrome/browser/extensions/api/declarative/rules_registry_service.h" 98a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "chrome/browser/extensions/api/declarative_content/content_rules_registry.h" 108a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "chrome/browser/extensions/crx_installer.h" 118a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "chrome/browser/extensions/extension_action.h" 128a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "chrome/browser/extensions/extension_action_manager.h" 138a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "chrome/browser/extensions/extension_service.h" 148a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "chrome/browser/extensions/extension_system.h" 158a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "chrome/browser/extensions/extension_tab_util.h" 168a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "chrome/browser/extensions/image_loader.h" 178a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "chrome/browser/extensions/page_action_controller.h" 188a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "chrome/browser/extensions/script_badge_controller.h" 198a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "chrome/browser/extensions/script_bubble_controller.h" 208a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "chrome/browser/extensions/script_executor.h" 21b6e161937bc890f0aa12ac5e27415d4d260ea6e0junov@chromium.org#include "chrome/browser/extensions/webstore_inline_installer.h" 228a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "chrome/browser/profiles/profile.h" 238a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "chrome/browser/sessions/session_id.h" 248a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "chrome/browser/sessions/session_tab_helper.h" 258a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "chrome/browser/ui/browser_dialogs.h" 268a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "chrome/browser/ui/web_applications/web_app_ui.h" 278a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "chrome/browser/web_applications/web_app.h" 287c2f27d788fff9dbf66a6d52753e47f786a313c0reed@google.com#include "chrome/common/chrome_notification_types.h" 297c2f27d788fff9dbf66a6d52753e47f786a313c0reed@google.com#include "chrome/common/extensions/extension.h" 308a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "chrome/common/extensions/extension_constants.h" 318a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "chrome/common/extensions/extension_icon_set.h" 328a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "chrome/common/extensions/extension_messages.h" 337c2f27d788fff9dbf66a6d52753e47f786a313c0reed@google.com#include "chrome/common/extensions/feature_switch.h" 348a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "chrome/common/extensions/manifest_handlers/icons_handler.h" 358a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "content/public/browser/invalidate_type.h" 368a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "content/public/browser/navigation_controller.h" 378a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "content/public/browser/navigation_details.h" 388a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "content/public/browser/navigation_entry.h" 398a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "content/public/browser/notification_service.h" 408a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "content/public/browser/notification_source.h" 418a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "content/public/browser/notification_types.h" 428a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "content/public/browser/render_process_host.h" 438a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "content/public/browser/render_view_host.h" 448a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "content/public/browser/render_widget_host_view.h" 458a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "content/public/browser/web_contents.h" 468a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "content/public/browser/web_contents_view.h" 478a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "extensions/common/extension_resource.h" 488a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "ui/gfx/image/image.h" 497c2f27d788fff9dbf66a6d52753e47f786a313c0reed@google.com 508a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comusing content::NavigationController; 518a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comusing content::NavigationEntry; 528a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comusing content::RenderViewHost; 538a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comusing content::WebContents; 54 55DEFINE_WEB_CONTENTS_USER_DATA_KEY(extensions::TabHelper); 56 57namespace { 58 59const char kPermissionError[] = "permission_error"; 60 61} // namespace 62 63namespace extensions { 64 65TabHelper::ScriptExecutionObserver::ScriptExecutionObserver( 66 TabHelper* tab_helper) 67 : tab_helper_(tab_helper) { 68 tab_helper_->AddScriptExecutionObserver(this); 69} 70 71TabHelper::ScriptExecutionObserver::ScriptExecutionObserver() 72 : tab_helper_(NULL) { 73} 74 75TabHelper::ScriptExecutionObserver::~ScriptExecutionObserver() { 76 if (tab_helper_) 77 tab_helper_->RemoveScriptExecutionObserver(this); 78} 79 80TabHelper::TabHelper(content::WebContents* web_contents) 81 : content::WebContentsObserver(web_contents), 82 extension_app_(NULL), 83 extension_function_dispatcher_( 84 Profile::FromBrowserContext(web_contents->GetBrowserContext()), this), 85 pending_web_app_action_(NONE), 86 script_executor_(new ScriptExecutor(web_contents, 87 &script_execution_observers_)), 88 rules_registry_service_( 89 ExtensionSystem::Get( 90 Profile::FromBrowserContext(web_contents->GetBrowserContext()))-> 91 rules_registry_service()), 92 image_loader_ptr_factory_(this) { 93 // The ActiveTabPermissionManager requires a session ID; ensure this 94 // WebContents has one. 95 SessionTabHelper::CreateForWebContents(web_contents); 96 active_tab_permission_granter_.reset(new ActiveTabPermissionGranter( 97 web_contents, 98 SessionID::IdForTab(web_contents), 99 Profile::FromBrowserContext(web_contents->GetBrowserContext()))); 100 if (FeatureSwitch::script_badges()->IsEnabled()) { 101 location_bar_controller_.reset( 102 new ScriptBadgeController(web_contents, this)); 103 } else { 104 location_bar_controller_.reset( 105 new PageActionController(web_contents)); 106 } 107 108 if (FeatureSwitch::script_bubble()->IsEnabled()) { 109 script_bubble_controller_.reset( 110 new ScriptBubbleController(web_contents, this)); 111 } 112 113 114 // If more classes need to listen to global content script activity, then 115 // a separate routing class with an observer interface should be written. 116 profile_ = Profile::FromBrowserContext(web_contents->GetBrowserContext()); 117 AddScriptExecutionObserver(ActivityLog::GetInstance(profile_)); 118 119 registrar_.Add(this, 120 content::NOTIFICATION_LOAD_STOP, 121 content::Source<NavigationController>( 122 &web_contents->GetController())); 123 124 registrar_.Add(this, 125 chrome::NOTIFICATION_EXTENSION_UNLOADED, 126 content::NotificationService::AllSources()); 127} 128 129TabHelper::~TabHelper() { 130 RemoveScriptExecutionObserver(ActivityLog::GetInstance(profile_)); 131} 132 133void TabHelper::CreateApplicationShortcuts() { 134 DCHECK(CanCreateApplicationShortcuts()); 135 NavigationEntry* entry = 136 web_contents()->GetController().GetLastCommittedEntry(); 137 if (!entry) 138 return; 139 140 pending_web_app_action_ = CREATE_SHORTCUT; 141 142 // Start fetching web app info for CreateApplicationShortcut dialog and show 143 // the dialog when the data is available in OnDidGetApplicationInfo. 144 GetApplicationInfo(entry->GetPageID()); 145} 146 147bool TabHelper::CanCreateApplicationShortcuts() const { 148#if defined(OS_MACOSX) 149 return false; 150#else 151 return web_app::IsValidUrl(web_contents()->GetURL()) && 152 pending_web_app_action_ == NONE; 153#endif 154} 155 156void TabHelper::SetExtensionApp(const Extension* extension) { 157 DCHECK(!extension || extension->GetFullLaunchURL().is_valid()); 158 extension_app_ = extension; 159 160 UpdateExtensionAppIcon(extension_app_); 161 162 content::NotificationService::current()->Notify( 163 chrome::NOTIFICATION_TAB_CONTENTS_APPLICATION_EXTENSION_CHANGED, 164 content::Source<TabHelper>(this), 165 content::NotificationService::NoDetails()); 166} 167 168void TabHelper::SetExtensionAppById(const std::string& extension_app_id) { 169 const Extension* extension = GetExtension(extension_app_id); 170 if (extension) 171 SetExtensionApp(extension); 172} 173 174void TabHelper::SetExtensionAppIconById(const std::string& extension_app_id) { 175 const Extension* extension = GetExtension(extension_app_id); 176 if (extension) 177 UpdateExtensionAppIcon(extension); 178} 179 180SkBitmap* TabHelper::GetExtensionAppIcon() { 181 if (extension_app_icon_.empty()) 182 return NULL; 183 184 return &extension_app_icon_; 185} 186 187void TabHelper::RenderViewCreated(RenderViewHost* render_view_host) { 188 render_view_host->Send( 189 new ExtensionMsg_SetTabId(render_view_host->GetRoutingID(), 190 SessionID::IdForTab(web_contents()))); 191} 192 193void TabHelper::DidNavigateMainFrame( 194 const content::LoadCommittedDetails& details, 195 const content::FrameNavigateParams& params) { 196#if defined(ENABLE_EXTENSIONS) 197 if (rules_registry_service_) { 198 rules_registry_service_->content_rules_registry()->DidNavigateMainFrame( 199 web_contents(), details, params); 200 } 201#endif // defined(ENABLE_EXTENSIONS) 202 203 if (details.is_in_page) 204 return; 205 206 Profile* profile = 207 Profile::FromBrowserContext(web_contents()->GetBrowserContext()); 208 ExtensionService* service = profile->GetExtensionService(); 209 if (!service) 210 return; 211 212 ExtensionActionManager* extension_action_manager = 213 ExtensionActionManager::Get(profile); 214 for (ExtensionSet::const_iterator it = service->extensions()->begin(); 215 it != service->extensions()->end(); ++it) { 216 ExtensionAction* browser_action = 217 extension_action_manager->GetBrowserAction(**it); 218 if (browser_action) { 219 browser_action->ClearAllValuesForTab(SessionID::IdForTab(web_contents())); 220 content::NotificationService::current()->Notify( 221 chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_UPDATED, 222 content::Source<ExtensionAction>(browser_action), 223 content::NotificationService::NoDetails()); 224 } 225 } 226} 227 228bool TabHelper::OnMessageReceived(const IPC::Message& message) { 229 bool handled = true; 230 IPC_BEGIN_MESSAGE_MAP(TabHelper, message) 231 IPC_MESSAGE_HANDLER(ExtensionHostMsg_DidGetApplicationInfo, 232 OnDidGetApplicationInfo) 233 IPC_MESSAGE_HANDLER(ExtensionHostMsg_InlineWebstoreInstall, 234 OnInlineWebstoreInstall) 235 IPC_MESSAGE_HANDLER(ExtensionHostMsg_GetAppInstallState, 236 OnGetAppInstallState); 237 IPC_MESSAGE_HANDLER(ExtensionHostMsg_Request, OnRequest) 238 IPC_MESSAGE_HANDLER(ExtensionHostMsg_ContentScriptsExecuting, 239 OnContentScriptsExecuting) 240 IPC_MESSAGE_HANDLER(ExtensionHostMsg_OnWatchedPageChange, 241 OnWatchedPageChange) 242 IPC_MESSAGE_UNHANDLED(handled = false) 243 IPC_END_MESSAGE_MAP() 244 return handled; 245} 246 247void TabHelper::DidCloneToNewWebContents(WebContents* old_web_contents, 248 WebContents* new_web_contents) { 249 // When the WebContents that this is attached to is cloned, give the new clone 250 // a TabHelper and copy state over. 251 CreateForWebContents(new_web_contents); 252 TabHelper* new_helper = FromWebContents(new_web_contents); 253 254 new_helper->SetExtensionApp(extension_app()); 255 new_helper->extension_app_icon_ = extension_app_icon_; 256} 257 258 259void TabHelper::OnDidGetApplicationInfo(int32 page_id, 260 const WebApplicationInfo& info) { 261 // Android does not implement BrowserWindow. 262#if !defined(OS_MACOSX) && !defined(OS_ANDROID) 263 web_app_info_ = info; 264 265 NavigationEntry* entry = 266 web_contents()->GetController().GetLastCommittedEntry(); 267 if (!entry || (entry->GetPageID() != page_id)) 268 return; 269 270 switch (pending_web_app_action_) { 271 case CREATE_SHORTCUT: { 272 chrome::ShowCreateWebAppShortcutsDialog( 273 web_contents()->GetView()->GetTopLevelNativeWindow(), 274 web_contents()); 275 break; 276 } 277 case UPDATE_SHORTCUT: { 278 web_app::UpdateShortcutForTabContents(web_contents()); 279 break; 280 } 281 default: 282 NOTREACHED(); 283 break; 284 } 285 286 pending_web_app_action_ = NONE; 287#endif 288} 289 290void TabHelper::OnInlineWebstoreInstall( 291 int install_id, 292 int return_route_id, 293 const std::string& webstore_item_id, 294 const GURL& requestor_url) { 295 WebstoreStandaloneInstaller::Callback callback = 296 base::Bind(&TabHelper::OnInlineInstallComplete, base::Unretained(this), 297 install_id, return_route_id); 298 scoped_refptr<WebstoreInlineInstaller> installer( 299 new WebstoreInlineInstaller( 300 web_contents(), 301 webstore_item_id, 302 requestor_url, 303 callback)); 304 installer->BeginInstall(); 305} 306 307void TabHelper::OnGetAppInstallState(const GURL& requestor_url, 308 int return_route_id, 309 int callback_id) { 310 Profile* profile = 311 Profile::FromBrowserContext(web_contents()->GetBrowserContext()); 312 ExtensionService* extension_service = profile->GetExtensionService(); 313 const ExtensionSet* extensions = extension_service->extensions(); 314 const ExtensionSet* disabled = extension_service->disabled_extensions(); 315 316 ExtensionURLInfo url(requestor_url); 317 std::string state; 318 if (extensions->GetHostedAppByURL(url)) 319 state = extension_misc::kAppStateInstalled; 320 else if (disabled->GetHostedAppByURL(url)) 321 state = extension_misc::kAppStateDisabled; 322 else 323 state = extension_misc::kAppStateNotInstalled; 324 325 Send(new ExtensionMsg_GetAppInstallStateResponse( 326 return_route_id, state, callback_id)); 327} 328 329void TabHelper::OnRequest(const ExtensionHostMsg_Request_Params& request) { 330 extension_function_dispatcher_.Dispatch(request, 331 web_contents()->GetRenderViewHost()); 332} 333 334void TabHelper::OnContentScriptsExecuting( 335 const ScriptExecutionObserver::ExecutingScriptsMap& executing_scripts_map, 336 int32 on_page_id, 337 const GURL& on_url) { 338 FOR_EACH_OBSERVER(ScriptExecutionObserver, script_execution_observers_, 339 OnScriptsExecuted(web_contents(), 340 executing_scripts_map, 341 on_page_id, 342 on_url)); 343} 344 345void TabHelper::OnWatchedPageChange( 346 const std::vector<std::string>& css_selectors) { 347#if defined(ENABLE_EXTENSIONS) 348 if (rules_registry_service_) { 349 rules_registry_service_->content_rules_registry()->Apply( 350 web_contents(), css_selectors); 351 } 352#endif // defined(ENABLE_EXTENSIONS) 353} 354 355const Extension* TabHelper::GetExtension(const std::string& extension_app_id) { 356 if (extension_app_id.empty()) 357 return NULL; 358 359 Profile* profile = 360 Profile::FromBrowserContext(web_contents()->GetBrowserContext()); 361 ExtensionService* extension_service = profile->GetExtensionService(); 362 if (!extension_service || !extension_service->is_ready()) 363 return NULL; 364 365 const Extension* extension = 366 extension_service->GetExtensionById(extension_app_id, false); 367 return extension; 368} 369 370void TabHelper::UpdateExtensionAppIcon(const Extension* extension) { 371 extension_app_icon_.reset(); 372 // Ensure previously enqueued callbacks are ignored. 373 image_loader_ptr_factory_.InvalidateWeakPtrs(); 374 375 // Enqueue OnImageLoaded callback. 376 if (extension) { 377 Profile* profile = 378 Profile::FromBrowserContext(web_contents()->GetBrowserContext()); 379 extensions::ImageLoader* loader = extensions::ImageLoader::Get(profile); 380 loader->LoadImageAsync( 381 extension, 382 IconsInfo::GetIconResource(extension, 383 extension_misc::EXTENSION_ICON_SMALLISH, 384 ExtensionIconSet::MATCH_EXACTLY), 385 gfx::Size(extension_misc::EXTENSION_ICON_SMALLISH, 386 extension_misc::EXTENSION_ICON_SMALLISH), 387 base::Bind(&TabHelper::OnImageLoaded, 388 image_loader_ptr_factory_.GetWeakPtr())); 389 } 390} 391 392void TabHelper::SetAppIcon(const SkBitmap& app_icon) { 393 extension_app_icon_ = app_icon; 394 web_contents()->NotifyNavigationStateChanged(content::INVALIDATE_TYPE_TITLE); 395} 396 397void TabHelper::OnImageLoaded(const gfx::Image& image) { 398 if (!image.IsEmpty()) { 399 extension_app_icon_ = *image.ToSkBitmap(); 400 web_contents()->NotifyNavigationStateChanged(content::INVALIDATE_TYPE_TAB); 401 } 402} 403 404WindowController* TabHelper::GetExtensionWindowController() const { 405 return ExtensionTabUtil::GetWindowControllerOfTab(web_contents()); 406} 407 408void TabHelper::OnInlineInstallComplete(int install_id, 409 int return_route_id, 410 bool success, 411 const std::string& error) { 412 if (success) { 413 Send(new ExtensionMsg_InlineWebstoreInstallResponse( 414 return_route_id, install_id, true, std::string())); 415 } else { 416 Send(new ExtensionMsg_InlineWebstoreInstallResponse( 417 return_route_id, install_id, false, error)); 418 } 419} 420 421WebContents* TabHelper::GetAssociatedWebContents() const { 422 return web_contents(); 423} 424 425void TabHelper::GetApplicationInfo(int32 page_id) { 426 Send(new ExtensionMsg_GetApplicationInfo(routing_id(), page_id)); 427} 428 429void TabHelper::Observe(int type, 430 const content::NotificationSource& source, 431 const content::NotificationDetails& details) { 432 switch (type) { 433 case content::NOTIFICATION_LOAD_STOP: { 434 const NavigationController& controller = 435 *content::Source<NavigationController>(source).ptr(); 436 DCHECK_EQ(controller.GetWebContents(), web_contents()); 437 438 if (pending_web_app_action_ == UPDATE_SHORTCUT) { 439 // Schedule a shortcut update when web application info is available if 440 // last committed entry is not NULL. Last committed entry could be NULL 441 // when an interstitial page is injected (e.g. bad https certificate, 442 // malware site etc). When this happens, we abort the shortcut update. 443 NavigationEntry* entry = controller.GetLastCommittedEntry(); 444 if (entry) 445 GetApplicationInfo(entry->GetPageID()); 446 else 447 pending_web_app_action_ = NONE; 448 } 449 break; 450 } 451 452 case chrome::NOTIFICATION_EXTENSION_UNLOADED: { 453 if (script_bubble_controller_) { 454 script_bubble_controller_->OnExtensionUnloaded( 455 content::Details<extensions::UnloadedExtensionInfo>( 456 details)->extension->id()); 457 break; 458 } 459 } 460 } 461} 462 463} // namespace extensions 464