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