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/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