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