Home | History | Annotate | Download | only in extensions
      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