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 "base/logging.h" 8 #include "base/strings/string_util.h" 9 #include "chrome/browser/chrome_notification_types.h" 10 #include "chrome/browser/extensions/activity_log/activity_log.h" 11 #include "chrome/browser/extensions/api/declarative/rules_registry_service.h" 12 #include "chrome/browser/extensions/api/declarative_content/content_rules_registry.h" 13 #include "chrome/browser/extensions/crx_installer.h" 14 #include "chrome/browser/extensions/error_console/error_console.h" 15 #include "chrome/browser/extensions/extension_action.h" 16 #include "chrome/browser/extensions/extension_action_manager.h" 17 #include "chrome/browser/extensions/extension_service.h" 18 #include "chrome/browser/extensions/extension_system.h" 19 #include "chrome/browser/extensions/extension_tab_util.h" 20 #include "chrome/browser/extensions/image_loader.h" 21 #include "chrome/browser/extensions/page_action_controller.h" 22 #include "chrome/browser/extensions/script_badge_controller.h" 23 #include "chrome/browser/extensions/script_bubble_controller.h" 24 #include "chrome/browser/extensions/script_executor.h" 25 #include "chrome/browser/extensions/webstore_inline_installer.h" 26 #include "chrome/browser/extensions/webstore_inline_installer_factory.h" 27 #include "chrome/browser/profiles/profile.h" 28 #include "chrome/browser/sessions/session_id.h" 29 #include "chrome/browser/sessions/session_tab_helper.h" 30 #include "chrome/browser/shell_integration.h" 31 #include "chrome/browser/ui/app_list/app_list_service.h" 32 #include "chrome/browser/ui/app_list/app_list_util.h" 33 #include "chrome/browser/ui/browser_commands.h" 34 #include "chrome/browser/ui/browser_dialogs.h" 35 #include "chrome/browser/ui/browser_finder.h" 36 #include "chrome/browser/ui/host_desktop.h" 37 #include "chrome/browser/ui/web_applications/web_app_ui.h" 38 #include "chrome/browser/web_applications/web_app.h" 39 #include "chrome/common/extensions/extension_constants.h" 40 #include "chrome/common/extensions/extension_icon_set.h" 41 #include "chrome/common/extensions/extension_messages.h" 42 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h" 43 #include "chrome/common/extensions/manifest_handlers/icons_handler.h" 44 #include "chrome/common/render_messages.h" 45 #include "chrome/common/url_constants.h" 46 #include "content/public/browser/invalidate_type.h" 47 #include "content/public/browser/navigation_controller.h" 48 #include "content/public/browser/navigation_details.h" 49 #include "content/public/browser/navigation_entry.h" 50 #include "content/public/browser/notification_service.h" 51 #include "content/public/browser/notification_source.h" 52 #include "content/public/browser/notification_types.h" 53 #include "content/public/browser/render_process_host.h" 54 #include "content/public/browser/render_view_host.h" 55 #include "content/public/browser/render_widget_host_view.h" 56 #include "content/public/browser/web_contents.h" 57 #include "content/public/browser/web_contents_view.h" 58 #include "content/public/common/frame_navigate_params.h" 59 #include "extensions/browser/extension_error.h" 60 #include "extensions/common/extension.h" 61 #include "extensions/common/extension_resource.h" 62 #include "extensions/common/extension_urls.h" 63 #include "extensions/common/feature_switch.h" 64 #include "ui/gfx/image/image.h" 65 66 using content::NavigationController; 67 using content::NavigationEntry; 68 using content::RenderViewHost; 69 using content::WebContents; 70 71 DEFINE_WEB_CONTENTS_USER_DATA_KEY(extensions::TabHelper); 72 73 namespace extensions { 74 75 TabHelper::ScriptExecutionObserver::ScriptExecutionObserver( 76 TabHelper* tab_helper) 77 : tab_helper_(tab_helper) { 78 tab_helper_->AddScriptExecutionObserver(this); 79 } 80 81 TabHelper::ScriptExecutionObserver::ScriptExecutionObserver() 82 : tab_helper_(NULL) { 83 } 84 85 TabHelper::ScriptExecutionObserver::~ScriptExecutionObserver() { 86 if (tab_helper_) 87 tab_helper_->RemoveScriptExecutionObserver(this); 88 } 89 90 TabHelper::TabHelper(content::WebContents* web_contents) 91 : content::WebContentsObserver(web_contents), 92 extension_app_(NULL), 93 extension_function_dispatcher_( 94 Profile::FromBrowserContext(web_contents->GetBrowserContext()), this), 95 pending_web_app_action_(NONE), 96 script_executor_(new ScriptExecutor(web_contents, 97 &script_execution_observers_)), 98 image_loader_ptr_factory_(this), 99 webstore_inline_installer_factory_(new WebstoreInlineInstallerFactory()) { 100 // The ActiveTabPermissionManager requires a session ID; ensure this 101 // WebContents has one. 102 SessionTabHelper::CreateForWebContents(web_contents); 103 if (web_contents->GetRenderViewHost()) 104 SetTabId(web_contents->GetRenderViewHost()); 105 active_tab_permission_granter_.reset(new ActiveTabPermissionGranter( 106 web_contents, 107 SessionID::IdForTab(web_contents), 108 Profile::FromBrowserContext(web_contents->GetBrowserContext()))); 109 if (FeatureSwitch::script_badges()->IsEnabled()) { 110 location_bar_controller_.reset( 111 new ScriptBadgeController(web_contents, this)); 112 } else { 113 location_bar_controller_.reset( 114 new PageActionController(web_contents)); 115 } 116 117 if (FeatureSwitch::script_bubble()->IsEnabled()) { 118 script_bubble_controller_.reset( 119 new ScriptBubbleController(web_contents, this)); 120 } 121 122 // If more classes need to listen to global content script activity, then 123 // a separate routing class with an observer interface should be written. 124 profile_ = Profile::FromBrowserContext(web_contents->GetBrowserContext()); 125 126 #if defined(ENABLE_EXTENSIONS) 127 AddScriptExecutionObserver(ActivityLog::GetInstance(profile_)); 128 #endif 129 130 registrar_.Add(this, 131 content::NOTIFICATION_LOAD_STOP, 132 content::Source<NavigationController>( 133 &web_contents->GetController())); 134 135 registrar_.Add(this, 136 chrome::NOTIFICATION_CRX_INSTALLER_DONE, 137 content::Source<CrxInstaller>(NULL)); 138 139 registrar_.Add(this, 140 chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR, 141 content::NotificationService::AllSources()); 142 143 registrar_.Add(this, 144 chrome::NOTIFICATION_EXTENSION_UNLOADED, 145 content::NotificationService::AllSources()); 146 } 147 148 TabHelper::~TabHelper() { 149 #if defined(ENABLE_EXTENSIONS) 150 RemoveScriptExecutionObserver(ActivityLog::GetInstance(profile_)); 151 #endif 152 } 153 154 void TabHelper::CreateApplicationShortcuts() { 155 DCHECK(CanCreateApplicationShortcuts()); 156 NavigationEntry* entry = 157 web_contents()->GetController().GetLastCommittedEntry(); 158 if (!entry) 159 return; 160 161 pending_web_app_action_ = CREATE_SHORTCUT; 162 163 // Start fetching web app info for CreateApplicationShortcut dialog and show 164 // the dialog when the data is available in OnDidGetApplicationInfo. 165 GetApplicationInfo(entry->GetPageID()); 166 } 167 168 void TabHelper::CreateHostedAppFromWebContents() { 169 DCHECK(CanCreateApplicationShortcuts()); 170 NavigationEntry* entry = 171 web_contents()->GetController().GetLastCommittedEntry(); 172 if (!entry) 173 return; 174 175 pending_web_app_action_ = CREATE_HOSTED_APP; 176 177 // Start fetching web app info for CreateApplicationShortcut dialog and show 178 // the dialog when the data is available in OnDidGetApplicationInfo. 179 GetApplicationInfo(entry->GetPageID()); 180 } 181 182 bool TabHelper::CanCreateApplicationShortcuts() const { 183 #if defined(OS_MACOSX) 184 return false; 185 #else 186 return web_app::IsValidUrl(web_contents()->GetURL()) && 187 pending_web_app_action_ == NONE; 188 #endif 189 } 190 191 void TabHelper::SetExtensionApp(const Extension* extension) { 192 DCHECK(!extension || AppLaunchInfo::GetFullLaunchURL(extension).is_valid()); 193 extension_app_ = extension; 194 195 UpdateExtensionAppIcon(extension_app_); 196 197 content::NotificationService::current()->Notify( 198 chrome::NOTIFICATION_TAB_CONTENTS_APPLICATION_EXTENSION_CHANGED, 199 content::Source<TabHelper>(this), 200 content::NotificationService::NoDetails()); 201 } 202 203 void TabHelper::SetExtensionAppById(const std::string& extension_app_id) { 204 const Extension* extension = GetExtension(extension_app_id); 205 if (extension) 206 SetExtensionApp(extension); 207 } 208 209 void TabHelper::SetExtensionAppIconById(const std::string& extension_app_id) { 210 const Extension* extension = GetExtension(extension_app_id); 211 if (extension) 212 UpdateExtensionAppIcon(extension); 213 } 214 215 SkBitmap* TabHelper::GetExtensionAppIcon() { 216 if (extension_app_icon_.empty()) 217 return NULL; 218 219 return &extension_app_icon_; 220 } 221 222 void TabHelper::RenderViewCreated(RenderViewHost* render_view_host) { 223 SetTabId(render_view_host); 224 } 225 226 void TabHelper::DidNavigateMainFrame( 227 const content::LoadCommittedDetails& details, 228 const content::FrameNavigateParams& params) { 229 #if defined(ENABLE_EXTENSIONS) 230 if (ExtensionSystem::Get(profile_)->extension_service() && 231 RulesRegistryService::Get(profile_)) { 232 RulesRegistryService::Get(profile_)->content_rules_registry()-> 233 DidNavigateMainFrame(web_contents(), details, params); 234 } 235 #endif // defined(ENABLE_EXTENSIONS) 236 237 Profile* profile = 238 Profile::FromBrowserContext(web_contents()->GetBrowserContext()); 239 ExtensionService* service = profile->GetExtensionService(); 240 if (!service) 241 return; 242 243 UpdateExtensionAppIcon(service->GetInstalledExtensionByUrl(params.url)); 244 245 if (details.is_in_page) 246 return; 247 248 ExtensionActionManager* extension_action_manager = 249 ExtensionActionManager::Get(profile); 250 for (ExtensionSet::const_iterator it = service->extensions()->begin(); 251 it != service->extensions()->end(); ++it) { 252 ExtensionAction* browser_action = 253 extension_action_manager->GetBrowserAction(*it->get()); 254 if (browser_action) { 255 browser_action->ClearAllValuesForTab(SessionID::IdForTab(web_contents())); 256 content::NotificationService::current()->Notify( 257 chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_UPDATED, 258 content::Source<ExtensionAction>(browser_action), 259 content::NotificationService::NoDetails()); 260 } 261 } 262 } 263 264 bool TabHelper::OnMessageReceived(const IPC::Message& message) { 265 bool handled = true; 266 IPC_BEGIN_MESSAGE_MAP(TabHelper, message) 267 IPC_MESSAGE_HANDLER(ExtensionHostMsg_DidGetApplicationInfo, 268 OnDidGetApplicationInfo) 269 IPC_MESSAGE_HANDLER(ExtensionHostMsg_InlineWebstoreInstall, 270 OnInlineWebstoreInstall) 271 IPC_MESSAGE_HANDLER(ExtensionHostMsg_GetAppInstallState, 272 OnGetAppInstallState); 273 IPC_MESSAGE_HANDLER(ExtensionHostMsg_Request, OnRequest) 274 IPC_MESSAGE_HANDLER(ExtensionHostMsg_ContentScriptsExecuting, 275 OnContentScriptsExecuting) 276 IPC_MESSAGE_HANDLER(ExtensionHostMsg_OnWatchedPageChange, 277 OnWatchedPageChange) 278 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_DetailedConsoleMessageAdded, 279 OnDetailedConsoleMessageAdded) 280 IPC_MESSAGE_UNHANDLED(handled = false) 281 IPC_END_MESSAGE_MAP() 282 return handled; 283 } 284 285 void TabHelper::DidCloneToNewWebContents(WebContents* old_web_contents, 286 WebContents* new_web_contents) { 287 // When the WebContents that this is attached to is cloned, give the new clone 288 // a TabHelper and copy state over. 289 CreateForWebContents(new_web_contents); 290 TabHelper* new_helper = FromWebContents(new_web_contents); 291 292 new_helper->SetExtensionApp(extension_app()); 293 new_helper->extension_app_icon_ = extension_app_icon_; 294 } 295 296 void TabHelper::OnDidGetApplicationInfo(int32 page_id, 297 const WebApplicationInfo& info) { 298 // Android does not implement BrowserWindow. 299 #if !defined(OS_MACOSX) && !defined(OS_ANDROID) 300 web_app_info_ = info; 301 302 NavigationEntry* entry = 303 web_contents()->GetController().GetLastCommittedEntry(); 304 if (!entry || (entry->GetPageID() != page_id)) 305 return; 306 307 switch (pending_web_app_action_) { 308 case CREATE_SHORTCUT: { 309 chrome::ShowCreateWebAppShortcutsDialog( 310 web_contents()->GetView()->GetTopLevelNativeWindow(), 311 web_contents()); 312 break; 313 } 314 case CREATE_HOSTED_APP: { 315 CreateHostedApp(info); 316 break; 317 } 318 case UPDATE_SHORTCUT: { 319 web_app::UpdateShortcutForTabContents(web_contents()); 320 break; 321 } 322 default: 323 NOTREACHED(); 324 break; 325 } 326 327 // The hosted app action will be cleared once the installation completes or 328 // fails. 329 if (pending_web_app_action_ != CREATE_HOSTED_APP) 330 pending_web_app_action_ = NONE; 331 #endif 332 } 333 334 void TabHelper::CreateHostedApp(const WebApplicationInfo& info) { 335 ShellIntegration::ShortcutInfo shortcut_info; 336 web_app::GetShortcutInfoForTab(web_contents(), &shortcut_info); 337 WebApplicationInfo web_app_info; 338 339 web_app_info.is_bookmark_app = true; 340 web_app_info.app_url = shortcut_info.url; 341 web_app_info.title = shortcut_info.title; 342 web_app_info.urls.push_back(web_app_info.app_url); 343 344 // TODO(calamity): this should attempt to download the best icon that it can 345 // from |info.icons| rather than just using the favicon as it scales up badly. 346 // Fix this once |info.icons| gets populated commonly. 347 348 // Get the smallest icon in the icon family (should have only 1). 349 const gfx::Image* icon = shortcut_info.favicon.GetBest(0, 0); 350 SkBitmap bitmap = icon ? icon->AsBitmap() : SkBitmap(); 351 352 if (!icon->IsEmpty()) { 353 WebApplicationInfo::IconInfo icon_info; 354 icon_info.data = bitmap; 355 icon_info.width = icon_info.data.width(); 356 icon_info.height = icon_info.data.height(); 357 web_app_info.icons.push_back(icon_info); 358 } 359 360 ExtensionService* service = profile_->GetExtensionService(); 361 scoped_refptr<extensions::CrxInstaller> installer( 362 extensions::CrxInstaller::CreateSilent(service)); 363 installer->set_error_on_unsupported_requirements(true); 364 installer->InstallWebApp(web_app_info); 365 } 366 367 void TabHelper::OnInlineWebstoreInstall( 368 int install_id, 369 int return_route_id, 370 const std::string& webstore_item_id, 371 const GURL& requestor_url) { 372 WebstoreStandaloneInstaller::Callback callback = 373 base::Bind(&TabHelper::OnInlineInstallComplete, base::Unretained(this), 374 install_id, return_route_id); 375 scoped_refptr<WebstoreInlineInstaller> installer( 376 webstore_inline_installer_factory_->CreateInstaller( 377 web_contents(), 378 webstore_item_id, 379 requestor_url, 380 callback)); 381 installer->BeginInstall(); 382 } 383 384 void TabHelper::OnGetAppInstallState(const GURL& requestor_url, 385 int return_route_id, 386 int callback_id) { 387 Profile* profile = 388 Profile::FromBrowserContext(web_contents()->GetBrowserContext()); 389 ExtensionService* extension_service = profile->GetExtensionService(); 390 const ExtensionSet* extensions = extension_service->extensions(); 391 const ExtensionSet* disabled = extension_service->disabled_extensions(); 392 393 std::string state; 394 if (extensions->GetHostedAppByURL(requestor_url)) 395 state = extension_misc::kAppStateInstalled; 396 else if (disabled->GetHostedAppByURL(requestor_url)) 397 state = extension_misc::kAppStateDisabled; 398 else 399 state = extension_misc::kAppStateNotInstalled; 400 401 Send(new ExtensionMsg_GetAppInstallStateResponse( 402 return_route_id, state, callback_id)); 403 } 404 405 void TabHelper::OnRequest(const ExtensionHostMsg_Request_Params& request) { 406 extension_function_dispatcher_.Dispatch(request, 407 web_contents()->GetRenderViewHost()); 408 } 409 410 void TabHelper::OnContentScriptsExecuting( 411 const ScriptExecutionObserver::ExecutingScriptsMap& executing_scripts_map, 412 int32 on_page_id, 413 const GURL& on_url) { 414 FOR_EACH_OBSERVER(ScriptExecutionObserver, script_execution_observers_, 415 OnScriptsExecuted(web_contents(), 416 executing_scripts_map, 417 on_page_id, 418 on_url)); 419 } 420 421 void TabHelper::OnWatchedPageChange( 422 const std::vector<std::string>& css_selectors) { 423 #if defined(ENABLE_EXTENSIONS) 424 if (ExtensionSystem::Get(profile_)->extension_service() && 425 RulesRegistryService::Get(profile_)) { 426 RulesRegistryService::Get(profile_)->content_rules_registry()->Apply( 427 web_contents(), css_selectors); 428 } 429 #endif // defined(ENABLE_EXTENSIONS) 430 } 431 432 void TabHelper::OnDetailedConsoleMessageAdded( 433 const base::string16& message, 434 const base::string16& source, 435 const StackTrace& stack_trace, 436 int32 severity_level) { 437 if (IsSourceFromAnExtension(source)) { 438 content::RenderViewHost* rvh = web_contents()->GetRenderViewHost(); 439 ErrorConsole::Get(profile_)->ReportError( 440 scoped_ptr<ExtensionError>(new RuntimeError( 441 extension_app_ ? extension_app_->id() : std::string(), 442 profile_->IsOffTheRecord(), 443 source, 444 message, 445 stack_trace, 446 web_contents() ? 447 web_contents()->GetLastCommittedURL() : GURL::EmptyGURL(), 448 static_cast<logging::LogSeverity>(severity_level), 449 rvh->GetRoutingID(), 450 rvh->GetProcess()->GetID()))); 451 } 452 } 453 454 const Extension* TabHelper::GetExtension(const std::string& extension_app_id) { 455 if (extension_app_id.empty()) 456 return NULL; 457 458 Profile* profile = 459 Profile::FromBrowserContext(web_contents()->GetBrowserContext()); 460 ExtensionService* extension_service = profile->GetExtensionService(); 461 if (!extension_service || !extension_service->is_ready()) 462 return NULL; 463 464 const Extension* extension = 465 extension_service->GetExtensionById(extension_app_id, false); 466 return extension; 467 } 468 469 void TabHelper::UpdateExtensionAppIcon(const Extension* extension) { 470 extension_app_icon_.reset(); 471 // Ensure previously enqueued callbacks are ignored. 472 image_loader_ptr_factory_.InvalidateWeakPtrs(); 473 474 // Enqueue OnImageLoaded callback. 475 if (extension) { 476 Profile* profile = 477 Profile::FromBrowserContext(web_contents()->GetBrowserContext()); 478 extensions::ImageLoader* loader = extensions::ImageLoader::Get(profile); 479 loader->LoadImageAsync( 480 extension, 481 IconsInfo::GetIconResource(extension, 482 extension_misc::EXTENSION_ICON_SMALL, 483 ExtensionIconSet::MATCH_BIGGER), 484 gfx::Size(extension_misc::EXTENSION_ICON_SMALL, 485 extension_misc::EXTENSION_ICON_SMALL), 486 base::Bind(&TabHelper::OnImageLoaded, 487 image_loader_ptr_factory_.GetWeakPtr())); 488 } 489 } 490 491 void TabHelper::SetAppIcon(const SkBitmap& app_icon) { 492 extension_app_icon_ = app_icon; 493 web_contents()->NotifyNavigationStateChanged(content::INVALIDATE_TYPE_TITLE); 494 } 495 496 void TabHelper::SetWebstoreInlineInstallerFactoryForTests( 497 WebstoreInlineInstallerFactory* factory) { 498 webstore_inline_installer_factory_.reset(factory); 499 } 500 501 void TabHelper::OnImageLoaded(const gfx::Image& image) { 502 if (!image.IsEmpty()) { 503 extension_app_icon_ = *image.ToSkBitmap(); 504 web_contents()->NotifyNavigationStateChanged(content::INVALIDATE_TYPE_TAB); 505 } 506 } 507 508 WindowController* TabHelper::GetExtensionWindowController() const { 509 return ExtensionTabUtil::GetWindowControllerOfTab(web_contents()); 510 } 511 512 void TabHelper::OnInlineInstallComplete(int install_id, 513 int return_route_id, 514 bool success, 515 const std::string& error) { 516 Send(new ExtensionMsg_InlineWebstoreInstallResponse( 517 return_route_id, install_id, success, success ? std::string() : error)); 518 } 519 520 WebContents* TabHelper::GetAssociatedWebContents() const { 521 return web_contents(); 522 } 523 524 void TabHelper::GetApplicationInfo(int32 page_id) { 525 Send(new ExtensionMsg_GetApplicationInfo(routing_id(), page_id)); 526 } 527 528 void TabHelper::Observe(int type, 529 const content::NotificationSource& source, 530 const content::NotificationDetails& details) { 531 switch (type) { 532 case content::NOTIFICATION_LOAD_STOP: { 533 const NavigationController& controller = 534 *content::Source<NavigationController>(source).ptr(); 535 DCHECK_EQ(controller.GetWebContents(), web_contents()); 536 537 if (pending_web_app_action_ == UPDATE_SHORTCUT) { 538 // Schedule a shortcut update when web application info is available if 539 // last committed entry is not NULL. Last committed entry could be NULL 540 // when an interstitial page is injected (e.g. bad https certificate, 541 // malware site etc). When this happens, we abort the shortcut update. 542 NavigationEntry* entry = controller.GetLastCommittedEntry(); 543 if (entry) 544 GetApplicationInfo(entry->GetPageID()); 545 else 546 pending_web_app_action_ = NONE; 547 } 548 break; 549 } 550 case chrome::NOTIFICATION_CRX_INSTALLER_DONE: { 551 if (pending_web_app_action_ != CREATE_HOSTED_APP) 552 return; 553 554 pending_web_app_action_ = NONE; 555 556 const Extension* extension = 557 content::Details<const Extension>(details).ptr(); 558 if (!extension || !extension->from_bookmark()) 559 return; 560 561 // If enabled, launch the app launcher and highlight the new app. 562 // Otherwise, open the chrome://apps page in a new foreground tab. 563 if (IsAppLauncherEnabled()) { 564 AppListService::Get(chrome::GetHostDesktopTypeForNativeView( 565 web_contents()->GetView()->GetNativeView()))-> 566 ShowForProfile(profile_); 567 568 content::NotificationService::current()->Notify( 569 chrome::NOTIFICATION_APP_INSTALLED_TO_APPLIST, 570 content::Source<Profile>(profile_), 571 content::Details<const std::string>(&extension->id())); 572 return; 573 } 574 575 // Android does not implement browser_finder.cc. 576 #if !defined(OS_ANDROID) 577 Browser* browser = 578 chrome::FindBrowserWithWebContents(web_contents()); 579 if (browser) { 580 browser->OpenURL( 581 content::OpenURLParams(GURL(chrome::kChromeUIAppsURL), 582 content::Referrer(), 583 NEW_FOREGROUND_TAB, 584 content::PAGE_TRANSITION_LINK, 585 false)); 586 } 587 #endif 588 } 589 case chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR: { 590 if (pending_web_app_action_ == CREATE_HOSTED_APP) 591 pending_web_app_action_ = NONE; 592 break; 593 } 594 case chrome::NOTIFICATION_EXTENSION_UNLOADED: { 595 if (script_bubble_controller_) { 596 script_bubble_controller_->OnExtensionUnloaded( 597 content::Details<extensions::UnloadedExtensionInfo>( 598 details)->extension->id()); 599 break; 600 } 601 } 602 } 603 } 604 605 void TabHelper::SetTabId(RenderViewHost* render_view_host) { 606 render_view_host->Send( 607 new ExtensionMsg_SetTabId(render_view_host->GetRoutingID(), 608 SessionID::IdForTab(web_contents()))); 609 } 610 611 } // namespace extensions 612