tab_helper.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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/activity_log/activity_log.h"
13#include "chrome/browser/extensions/api/declarative/rules_registry_service.h"
14#include "chrome/browser/extensions/api/declarative_content/content_rules_registry.h"
15#include "chrome/browser/extensions/crx_installer.h"
16#include "chrome/browser/extensions/error_console/error_console.h"
17#include "chrome/browser/extensions/extension_action.h"
18#include "chrome/browser/extensions/extension_action_manager.h"
19#include "chrome/browser/extensions/extension_service.h"
20#include "chrome/browser/extensions/extension_tab_util.h"
21#include "chrome/browser/extensions/favicon_downloader.h"
22#include "chrome/browser/extensions/image_loader.h"
23#include "chrome/browser/extensions/page_action_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/ui/web_applications/web_app_ui.h"
37#include "chrome/browser/web_applications/web_app.h"
38#include "chrome/common/chrome_switches.h"
39#include "chrome/common/extensions/chrome_extension_messages.h"
40#include "chrome/common/extensions/extension_constants.h"
41#include "chrome/common/extensions/extension_icon_set.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/browser/extension_registry.h"
61#include "extensions/browser/extension_system.h"
62#include "extensions/common/extension.h"
63#include "extensions/common/extension_messages.h"
64#include "extensions/common/extension_resource.h"
65#include "extensions/common/extension_urls.h"
66#include "extensions/common/feature_switch.h"
67#include "skia/ext/image_operations.h"
68#include "skia/ext/platform_canvas.h"
69#include "ui/gfx/color_analysis.h"
70#include "ui/gfx/image/image.h"
71
72#if defined(OS_CHROMEOS)
73#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
74#endif
75
76using content::NavigationController;
77using content::NavigationEntry;
78using content::RenderViewHost;
79using content::WebContents;
80
81DEFINE_WEB_CONTENTS_USER_DATA_KEY(extensions::TabHelper);
82
83namespace extensions {
84
85TabHelper::ScriptExecutionObserver::ScriptExecutionObserver(
86    TabHelper* tab_helper)
87    : tab_helper_(tab_helper) {
88  tab_helper_->AddScriptExecutionObserver(this);
89}
90
91TabHelper::ScriptExecutionObserver::ScriptExecutionObserver()
92    : tab_helper_(NULL) {
93}
94
95TabHelper::ScriptExecutionObserver::~ScriptExecutionObserver() {
96  if (tab_helper_)
97    tab_helper_->RemoveScriptExecutionObserver(this);
98}
99
100// static
101std::map<int, SkBitmap> TabHelper::ConstrainBitmapsToSizes(
102    const std::vector<SkBitmap>& bitmaps,
103    const std::set<int>& sizes) {
104  std::map<int, SkBitmap> output_bitmaps;
105  std::map<int, SkBitmap> ordered_bitmaps;
106  for (std::vector<SkBitmap>::const_iterator it = bitmaps.begin();
107       it != bitmaps.end(); ++it) {
108    DCHECK(it->width() == it->height());
109    ordered_bitmaps[it->width()] = *it;
110  }
111
112  std::set<int>::const_iterator sizes_it = sizes.begin();
113  std::map<int, SkBitmap>::const_iterator bitmaps_it = ordered_bitmaps.begin();
114  while (sizes_it != sizes.end() && bitmaps_it != ordered_bitmaps.end()) {
115    int size = *sizes_it;
116    // Find the closest not-smaller bitmap.
117    bitmaps_it = ordered_bitmaps.lower_bound(size);
118    ++sizes_it;
119    // Ensure the bitmap is valid and smaller than the next allowed size.
120    if (bitmaps_it != ordered_bitmaps.end() &&
121        (sizes_it == sizes.end() || bitmaps_it->second.width() < *sizes_it)) {
122      // Resize the bitmap if it does not exactly match the desired size.
123      output_bitmaps[size] = bitmaps_it->second.width() == size
124          ? bitmaps_it->second
125          : skia::ImageOperations::Resize(
126                bitmaps_it->second, skia::ImageOperations::RESIZE_LANCZOS3,
127                size, size);
128    }
129  }
130  return output_bitmaps;
131}
132
133// static
134void TabHelper::GenerateContainerIcon(std::map<int, SkBitmap>* bitmaps,
135                                      int output_size) {
136  std::map<int, SkBitmap>::const_iterator it =
137      bitmaps->lower_bound(output_size);
138  // Do nothing if there is no icon smaller than the desired size or there is
139  // already an icon of |output_size|.
140  if (it == bitmaps->begin() || bitmaps->count(output_size))
141    return;
142
143  --it;
144  // This is the biggest icon smaller than |output_size|.
145  const SkBitmap& base_icon = it->second;
146
147  const size_t kBorderRadius = 5;
148  const size_t kColorStripHeight = 3;
149  const SkColor kBorderColor = 0xFFD5D5D5;
150  const SkColor kBackgroundColor = 0xFFFFFFFF;
151
152  // Create a separate canvas for the color strip.
153  SkBitmap color_strip_bitmap;
154  color_strip_bitmap.allocN32Pixels(output_size, output_size);
155  SkCanvas color_strip_canvas(color_strip_bitmap);
156  color_strip_canvas.clear(SK_ColorTRANSPARENT);
157
158  // Draw a rounded rect of the |base_icon|'s dominant color.
159  SkPaint color_strip_paint;
160  color_strip_paint.setFlags(SkPaint::kAntiAlias_Flag);
161  color_strip_paint.setColor(
162      color_utils::CalculateKMeanColorOfBitmap(base_icon));
163  color_strip_canvas.drawRoundRect(SkRect::MakeWH(output_size, output_size),
164                                   kBorderRadius,
165                                   kBorderRadius,
166                                   color_strip_paint);
167
168  // Erase the top of the rounded rect to leave a color strip.
169  SkPaint clear_paint;
170  clear_paint.setColor(SK_ColorTRANSPARENT);
171  clear_paint.setXfermodeMode(SkXfermode::kSrc_Mode);
172  color_strip_canvas.drawRect(
173      SkRect::MakeWH(output_size, output_size - kColorStripHeight),
174      clear_paint);
175
176  // Draw each element to an output canvas.
177  SkBitmap generated_icon;
178  generated_icon.allocN32Pixels(output_size, output_size);
179  SkCanvas generated_icon_canvas(generated_icon);
180  generated_icon_canvas.clear(SK_ColorTRANSPARENT);
181
182  // Draw the background.
183  SkPaint background_paint;
184  background_paint.setColor(kBackgroundColor);
185  background_paint.setFlags(SkPaint::kAntiAlias_Flag);
186  generated_icon_canvas.drawRoundRect(SkRect::MakeWH(output_size, output_size),
187                                      kBorderRadius,
188                                      kBorderRadius,
189                                      background_paint);
190
191  // Draw the color strip.
192  generated_icon_canvas.drawBitmap(color_strip_bitmap, 0, 0);
193
194  // Draw the border.
195  SkPaint border_paint;
196  border_paint.setColor(kBorderColor);
197  border_paint.setStyle(SkPaint::kStroke_Style);
198  border_paint.setFlags(SkPaint::kAntiAlias_Flag);
199  generated_icon_canvas.drawRoundRect(SkRect::MakeWH(output_size, output_size),
200                                      kBorderRadius,
201                                      kBorderRadius,
202                                      border_paint);
203
204  // Draw the centered base icon to the output canvas.
205  generated_icon_canvas.drawBitmap(base_icon,
206                                   (output_size - base_icon.width()) / 2,
207                                   (output_size - base_icon.height()) / 2);
208
209  generated_icon.deepCopyTo(&(*bitmaps)[output_size]);
210}
211
212TabHelper::TabHelper(content::WebContents* web_contents)
213    : content::WebContentsObserver(web_contents),
214      extension_app_(NULL),
215      extension_function_dispatcher_(
216          Profile::FromBrowserContext(web_contents->GetBrowserContext()), this),
217      pending_web_app_action_(NONE),
218      script_executor_(new ScriptExecutor(web_contents,
219                                          &script_execution_observers_)),
220      location_bar_controller_(new PageActionController(web_contents)),
221      image_loader_ptr_factory_(this),
222      webstore_inline_installer_factory_(new WebstoreInlineInstallerFactory()) {
223  // The ActiveTabPermissionManager requires a session ID; ensure this
224  // WebContents has one.
225  SessionTabHelper::CreateForWebContents(web_contents);
226  if (web_contents->GetRenderViewHost())
227    SetTabId(web_contents->GetRenderViewHost());
228  active_tab_permission_granter_.reset(new ActiveTabPermissionGranter(
229      web_contents,
230      SessionID::IdForTab(web_contents),
231      Profile::FromBrowserContext(web_contents->GetBrowserContext())));
232
233  // If more classes need to listen to global content script activity, then
234  // a separate routing class with an observer interface should be written.
235  profile_ = Profile::FromBrowserContext(web_contents->GetBrowserContext());
236
237#if defined(ENABLE_EXTENSIONS)
238  AddScriptExecutionObserver(ActivityLog::GetInstance(profile_));
239#endif
240
241  registrar_.Add(this,
242                 content::NOTIFICATION_LOAD_STOP,
243                 content::Source<NavigationController>(
244                     &web_contents->GetController()));
245
246  registrar_.Add(this,
247                 chrome::NOTIFICATION_CRX_INSTALLER_DONE,
248                 content::Source<CrxInstaller>(NULL));
249
250  registrar_.Add(this,
251                 chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR,
252                 content::NotificationService::AllSources());
253}
254
255TabHelper::~TabHelper() {
256#if defined(ENABLE_EXTENSIONS)
257  RemoveScriptExecutionObserver(ActivityLog::GetInstance(profile_));
258#endif
259}
260
261void TabHelper::CreateApplicationShortcuts() {
262  DCHECK(CanCreateApplicationShortcuts());
263  NavigationEntry* entry =
264      web_contents()->GetController().GetLastCommittedEntry();
265  if (!entry)
266    return;
267
268  pending_web_app_action_ = CREATE_SHORTCUT;
269
270  // Start fetching web app info for CreateApplicationShortcut dialog and show
271  // the dialog when the data is available in OnDidGetApplicationInfo.
272  GetApplicationInfo(entry->GetPageID());
273}
274
275void TabHelper::CreateHostedAppFromWebContents() {
276  DCHECK(CanCreateApplicationShortcuts());
277  NavigationEntry* entry =
278      web_contents()->GetController().GetLastCommittedEntry();
279  if (!entry)
280    return;
281
282  pending_web_app_action_ = CREATE_HOSTED_APP;
283
284  // Start fetching web app info for CreateApplicationShortcut dialog and show
285  // the dialog when the data is available in OnDidGetApplicationInfo.
286  GetApplicationInfo(entry->GetPageID());
287}
288
289bool TabHelper::CanCreateApplicationShortcuts() const {
290#if defined(OS_MACOSX)
291  return false;
292#else
293  return web_app::IsValidUrl(web_contents()->GetURL()) &&
294      pending_web_app_action_ == NONE;
295#endif
296}
297
298void TabHelper::SetExtensionApp(const Extension* extension) {
299  DCHECK(!extension || AppLaunchInfo::GetFullLaunchURL(extension).is_valid());
300  if (extension_app_ == extension)
301    return;
302
303  extension_app_ = extension;
304
305  UpdateExtensionAppIcon(extension_app_);
306
307  content::NotificationService::current()->Notify(
308      chrome::NOTIFICATION_TAB_CONTENTS_APPLICATION_EXTENSION_CHANGED,
309      content::Source<TabHelper>(this),
310      content::NotificationService::NoDetails());
311}
312
313void TabHelper::SetExtensionAppById(const std::string& extension_app_id) {
314  const Extension* extension = GetExtension(extension_app_id);
315  if (extension)
316    SetExtensionApp(extension);
317}
318
319void TabHelper::SetExtensionAppIconById(const std::string& extension_app_id) {
320  const Extension* extension = GetExtension(extension_app_id);
321  if (extension)
322    UpdateExtensionAppIcon(extension);
323}
324
325SkBitmap* TabHelper::GetExtensionAppIcon() {
326  if (extension_app_icon_.empty())
327    return NULL;
328
329  return &extension_app_icon_;
330}
331
332void TabHelper::RenderViewCreated(RenderViewHost* render_view_host) {
333  SetTabId(render_view_host);
334}
335
336void TabHelper::DidNavigateMainFrame(
337    const content::LoadCommittedDetails& details,
338    const content::FrameNavigateParams& params) {
339#if defined(ENABLE_EXTENSIONS)
340  if (ExtensionSystem::Get(profile_)->extension_service() &&
341      RulesRegistryService::Get(profile_)) {
342    RulesRegistryService::Get(profile_)->content_rules_registry()->
343        DidNavigateMainFrame(web_contents(), details, params);
344  }
345#endif  // defined(ENABLE_EXTENSIONS)
346
347  Profile* profile =
348      Profile::FromBrowserContext(web_contents()->GetBrowserContext());
349  ExtensionService* service = profile->GetExtensionService();
350  if (!service)
351    return;
352
353  if (CommandLine::ForCurrentProcess()->HasSwitch(
354          switches::kEnableStreamlinedHostedApps)) {
355#if !defined(OS_ANDROID)
356    Browser* browser = chrome::FindBrowserWithWebContents(web_contents());
357    if (browser && browser->is_app()) {
358      SetExtensionApp(service->GetInstalledExtension(
359          web_app::GetExtensionIdFromApplicationName(browser->app_name())));
360    } else {
361      UpdateExtensionAppIcon(service->GetInstalledExtensionByUrl(params.url));
362    }
363#endif
364  } else {
365    UpdateExtensionAppIcon(service->GetInstalledExtensionByUrl(params.url));
366  }
367
368  if (details.is_in_page)
369    return;
370
371  ExtensionActionManager* extension_action_manager =
372      ExtensionActionManager::Get(profile);
373  for (ExtensionSet::const_iterator it = service->extensions()->begin();
374       it != service->extensions()->end(); ++it) {
375    ExtensionAction* browser_action =
376        extension_action_manager->GetBrowserAction(*it->get());
377    if (browser_action) {
378      browser_action->ClearAllValuesForTab(SessionID::IdForTab(web_contents()));
379      content::NotificationService::current()->Notify(
380          chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_UPDATED,
381          content::Source<ExtensionAction>(browser_action),
382          content::NotificationService::NoDetails());
383    }
384  }
385}
386
387bool TabHelper::OnMessageReceived(const IPC::Message& message) {
388  bool handled = true;
389  IPC_BEGIN_MESSAGE_MAP(TabHelper, message)
390    IPC_MESSAGE_HANDLER(ChromeExtensionHostMsg_DidGetApplicationInfo,
391                        OnDidGetApplicationInfo)
392    IPC_MESSAGE_HANDLER(ExtensionHostMsg_InlineWebstoreInstall,
393                        OnInlineWebstoreInstall)
394    IPC_MESSAGE_HANDLER(ExtensionHostMsg_GetAppInstallState,
395                        OnGetAppInstallState);
396    IPC_MESSAGE_HANDLER(ExtensionHostMsg_Request, OnRequest)
397    IPC_MESSAGE_HANDLER(ExtensionHostMsg_ContentScriptsExecuting,
398                        OnContentScriptsExecuting)
399    IPC_MESSAGE_HANDLER(ExtensionHostMsg_OnWatchedPageChange,
400                        OnWatchedPageChange)
401    IPC_MESSAGE_HANDLER(ChromeViewHostMsg_DetailedConsoleMessageAdded,
402                        OnDetailedConsoleMessageAdded)
403    IPC_MESSAGE_UNHANDLED(handled = false)
404  IPC_END_MESSAGE_MAP()
405  return handled;
406}
407
408void TabHelper::CreateHostedApp() {
409  // Add urls from the WebApplicationInfo.
410  std::vector<GURL> web_app_info_icon_urls;
411  for (std::vector<WebApplicationInfo::IconInfo>::const_iterator it =
412           web_app_info_.icons.begin();
413       it != web_app_info_.icons.end(); ++it) {
414    if (it->url.is_valid())
415      web_app_info_icon_urls.push_back(it->url);
416  }
417
418  favicon_downloader_.reset(
419      new FaviconDownloader(web_contents(),
420                            web_app_info_icon_urls,
421                            base::Bind(&TabHelper::FinishCreateHostedApp,
422                                       base::Unretained(this))));
423  favicon_downloader_->Start();
424}
425
426// TODO(calamity): Move hosted app generation into its own file.
427void TabHelper::FinishCreateHostedApp(
428    bool success,
429    const std::map<GURL, std::vector<SkBitmap> >& bitmaps) {
430  // The tab has navigated away during the icon download. Cancel the hosted app
431  // creation.
432  if (!success) {
433    favicon_downloader_.reset();
434    return;
435  }
436
437  if (web_app_info_.app_url.is_empty())
438    web_app_info_.app_url = web_contents()->GetURL();
439
440  if (web_app_info_.title.empty())
441    web_app_info_.title = web_contents()->GetTitle();
442  if (web_app_info_.title.empty())
443    web_app_info_.title = base::UTF8ToUTF16(web_app_info_.app_url.spec());
444
445  // Add the downloaded icons. Extensions only allow certain icon sizes. First
446  // populate icons that match the allowed sizes exactly and then downscale
447  // remaining icons to the closest allowed size that doesn't yet have an icon.
448  std::set<int> allowed_sizes(
449      extension_misc::kExtensionIconSizes,
450      extension_misc::kExtensionIconSizes +
451          extension_misc::kNumExtensionIconSizes);
452  std::vector<SkBitmap> downloaded_icons;
453  for (FaviconDownloader::FaviconMap::const_iterator map_it = bitmaps.begin();
454       map_it != bitmaps.end(); ++map_it) {
455    for (std::vector<SkBitmap>::const_iterator bitmap_it =
456             map_it->second.begin();
457         bitmap_it != map_it->second.end(); ++bitmap_it) {
458      if (bitmap_it->empty() || bitmap_it->width() != bitmap_it->height())
459        continue;
460
461      downloaded_icons.push_back(*bitmap_it);
462    }
463  }
464
465  // If there are icons that don't match the accepted icon sizes, find the
466  // closest bigger icon to the accepted sizes and resize the icon to it. An
467  // icon will be resized and used for at most one size.
468  std::map<int, SkBitmap> resized_bitmaps(
469      TabHelper::ConstrainBitmapsToSizes(downloaded_icons,
470                                         allowed_sizes));
471
472  // Generate container icons from smaller icons.
473  const int kIconSizesToGenerate[] = {
474    extension_misc::EXTENSION_ICON_SMALL,
475    extension_misc::EXTENSION_ICON_MEDIUM,
476  };
477  const std::set<int> generate_sizes(
478      kIconSizesToGenerate,
479      kIconSizesToGenerate + arraysize(kIconSizesToGenerate));
480
481  // Only generate icons if larger icons don't exist. This means the app
482  // launcher and the taskbar will do their best downsizing large icons and
483  // these container icons are only generated as a last resort against upscaling
484  // a smaller icon.
485  if (resized_bitmaps.lower_bound(*generate_sizes.rbegin()) ==
486      resized_bitmaps.end()) {
487    // Generate these from biggest to smallest so we don't end up with
488    // concentric container icons.
489    for (std::set<int>::const_reverse_iterator it = generate_sizes.rbegin();
490         it != generate_sizes.rend(); ++it) {
491      TabHelper::GenerateContainerIcon(&resized_bitmaps, *it);
492    }
493  }
494
495  // Populate a the icon data into the WebApplicationInfo we are using to
496  // install the bookmark app.
497  for (std::map<int, SkBitmap>::const_iterator resized_bitmaps_it =
498           resized_bitmaps.begin();
499       resized_bitmaps_it != resized_bitmaps.end(); ++resized_bitmaps_it) {
500    WebApplicationInfo::IconInfo icon_info;
501    icon_info.data = resized_bitmaps_it->second;
502    icon_info.width = icon_info.data.width();
503    icon_info.height = icon_info.data.height();
504    web_app_info_.icons.push_back(icon_info);
505  }
506
507  // Install the app.
508  Profile* profile =
509      Profile::FromBrowserContext(web_contents()->GetBrowserContext());
510  scoped_refptr<extensions::CrxInstaller> installer(
511      extensions::CrxInstaller::CreateSilent(profile->GetExtensionService()));
512  installer->set_error_on_unsupported_requirements(true);
513  installer->InstallWebApp(web_app_info_);
514  favicon_downloader_.reset();
515}
516
517void TabHelper::DidCloneToNewWebContents(WebContents* old_web_contents,
518                                         WebContents* new_web_contents) {
519  // When the WebContents that this is attached to is cloned, give the new clone
520  // a TabHelper and copy state over.
521  CreateForWebContents(new_web_contents);
522  TabHelper* new_helper = FromWebContents(new_web_contents);
523
524  new_helper->SetExtensionApp(extension_app());
525  new_helper->extension_app_icon_ = extension_app_icon_;
526}
527
528void TabHelper::OnDidGetApplicationInfo(int32 page_id,
529                                        const WebApplicationInfo& info) {
530  // Android does not implement BrowserWindow.
531#if !defined(OS_MACOSX) && !defined(OS_ANDROID)
532  web_app_info_ = info;
533
534  NavigationEntry* entry =
535      web_contents()->GetController().GetLastCommittedEntry();
536  if (!entry || (entry->GetPageID() != page_id))
537    return;
538
539  switch (pending_web_app_action_) {
540    case CREATE_SHORTCUT: {
541      chrome::ShowCreateWebAppShortcutsDialog(
542          web_contents()->GetView()->GetTopLevelNativeWindow(),
543          web_contents());
544      break;
545    }
546    case CREATE_HOSTED_APP: {
547      CreateHostedApp();
548      break;
549    }
550    case UPDATE_SHORTCUT: {
551      web_app::UpdateShortcutForTabContents(web_contents());
552      break;
553    }
554    default:
555      NOTREACHED();
556      break;
557  }
558
559  // The hosted app action will be cleared once the installation completes or
560  // fails.
561  if (pending_web_app_action_ != CREATE_HOSTED_APP)
562    pending_web_app_action_ = NONE;
563#endif
564}
565
566void TabHelper::OnInlineWebstoreInstall(
567    int install_id,
568    int return_route_id,
569    const std::string& webstore_item_id,
570    const GURL& requestor_url) {
571  WebstoreStandaloneInstaller::Callback callback =
572      base::Bind(&TabHelper::OnInlineInstallComplete, base::Unretained(this),
573                 install_id, return_route_id);
574  scoped_refptr<WebstoreInlineInstaller> installer(
575      webstore_inline_installer_factory_->CreateInstaller(
576          web_contents(),
577          webstore_item_id,
578          requestor_url,
579          callback));
580  installer->BeginInstall();
581}
582
583void TabHelper::OnGetAppInstallState(const GURL& requestor_url,
584                                     int return_route_id,
585                                     int callback_id) {
586  ExtensionRegistry* registry =
587      ExtensionRegistry::Get(web_contents()->GetBrowserContext());
588  const ExtensionSet& extensions = registry->enabled_extensions();
589  const ExtensionSet& disabled_extensions = registry->disabled_extensions();
590
591  std::string state;
592  if (extensions.GetHostedAppByURL(requestor_url))
593    state = extension_misc::kAppStateInstalled;
594  else if (disabled_extensions.GetHostedAppByURL(requestor_url))
595    state = extension_misc::kAppStateDisabled;
596  else
597    state = extension_misc::kAppStateNotInstalled;
598
599  Send(new ExtensionMsg_GetAppInstallStateResponse(
600      return_route_id, state, callback_id));
601}
602
603void TabHelper::OnRequest(const ExtensionHostMsg_Request_Params& request) {
604  extension_function_dispatcher_.Dispatch(request,
605                                          web_contents()->GetRenderViewHost());
606}
607
608void TabHelper::OnContentScriptsExecuting(
609    const ScriptExecutionObserver::ExecutingScriptsMap& executing_scripts_map,
610    int32 on_page_id,
611    const GURL& on_url) {
612  FOR_EACH_OBSERVER(ScriptExecutionObserver, script_execution_observers_,
613                    OnScriptsExecuted(web_contents(),
614                                      executing_scripts_map,
615                                      on_page_id,
616                                      on_url));
617}
618
619void TabHelper::OnWatchedPageChange(
620    const std::vector<std::string>& css_selectors) {
621#if defined(ENABLE_EXTENSIONS)
622  if (ExtensionSystem::Get(profile_)->extension_service() &&
623      RulesRegistryService::Get(profile_)) {
624    RulesRegistryService::Get(profile_)->content_rules_registry()->Apply(
625        web_contents(), css_selectors);
626  }
627#endif  // defined(ENABLE_EXTENSIONS)
628}
629
630void TabHelper::OnDetailedConsoleMessageAdded(
631    const base::string16& message,
632    const base::string16& source,
633    const StackTrace& stack_trace,
634    int32 severity_level) {
635  if (IsSourceFromAnExtension(source)) {
636    content::RenderViewHost* rvh = web_contents()->GetRenderViewHost();
637    ErrorConsole::Get(profile_)->ReportError(
638        scoped_ptr<ExtensionError>(new RuntimeError(
639            extension_app_ ? extension_app_->id() : std::string(),
640            profile_->IsOffTheRecord(),
641            source,
642            message,
643            stack_trace,
644            web_contents() ?
645                web_contents()->GetLastCommittedURL() : GURL::EmptyGURL(),
646            static_cast<logging::LogSeverity>(severity_level),
647            rvh->GetRoutingID(),
648            rvh->GetProcess()->GetID())));
649  }
650}
651
652const Extension* TabHelper::GetExtension(const std::string& extension_app_id) {
653  if (extension_app_id.empty())
654    return NULL;
655
656  Profile* profile =
657      Profile::FromBrowserContext(web_contents()->GetBrowserContext());
658  ExtensionService* extension_service = profile->GetExtensionService();
659  if (!extension_service || !extension_service->is_ready())
660    return NULL;
661
662  const Extension* extension =
663      extension_service->GetExtensionById(extension_app_id, false);
664  return extension;
665}
666
667void TabHelper::UpdateExtensionAppIcon(const Extension* extension) {
668  extension_app_icon_.reset();
669  // Ensure previously enqueued callbacks are ignored.
670  image_loader_ptr_factory_.InvalidateWeakPtrs();
671
672  // Enqueue OnImageLoaded callback.
673  if (extension) {
674    Profile* profile =
675        Profile::FromBrowserContext(web_contents()->GetBrowserContext());
676    extensions::ImageLoader* loader = extensions::ImageLoader::Get(profile);
677    loader->LoadImageAsync(
678        extension,
679        IconsInfo::GetIconResource(extension,
680                                   extension_misc::EXTENSION_ICON_SMALL,
681                                   ExtensionIconSet::MATCH_BIGGER),
682        gfx::Size(extension_misc::EXTENSION_ICON_SMALL,
683                  extension_misc::EXTENSION_ICON_SMALL),
684        base::Bind(&TabHelper::OnImageLoaded,
685                   image_loader_ptr_factory_.GetWeakPtr()));
686  }
687}
688
689void TabHelper::SetAppIcon(const SkBitmap& app_icon) {
690  extension_app_icon_ = app_icon;
691  web_contents()->NotifyNavigationStateChanged(content::INVALIDATE_TYPE_TITLE);
692}
693
694void TabHelper::SetWebstoreInlineInstallerFactoryForTests(
695    WebstoreInlineInstallerFactory* factory) {
696  webstore_inline_installer_factory_.reset(factory);
697}
698
699void TabHelper::OnImageLoaded(const gfx::Image& image) {
700  if (!image.IsEmpty()) {
701    extension_app_icon_ = *image.ToSkBitmap();
702    web_contents()->NotifyNavigationStateChanged(content::INVALIDATE_TYPE_TAB);
703  }
704}
705
706WindowController* TabHelper::GetExtensionWindowController() const  {
707  return ExtensionTabUtil::GetWindowControllerOfTab(web_contents());
708}
709
710void TabHelper::OnInlineInstallComplete(int install_id,
711                                        int return_route_id,
712                                        bool success,
713                                        const std::string& error) {
714  Send(new ExtensionMsg_InlineWebstoreInstallResponse(
715      return_route_id, install_id, success, success ? std::string() : error));
716}
717
718WebContents* TabHelper::GetAssociatedWebContents() const {
719  return web_contents();
720}
721
722void TabHelper::GetApplicationInfo(int32 page_id) {
723  Send(new ChromeExtensionMsg_GetApplicationInfo(routing_id(), page_id));
724}
725
726void TabHelper::Observe(int type,
727                        const content::NotificationSource& source,
728                        const content::NotificationDetails& details) {
729  switch (type) {
730    case content::NOTIFICATION_LOAD_STOP: {
731      const NavigationController& controller =
732          *content::Source<NavigationController>(source).ptr();
733      DCHECK_EQ(controller.GetWebContents(), web_contents());
734
735      if (pending_web_app_action_ == UPDATE_SHORTCUT) {
736        // Schedule a shortcut update when web application info is available if
737        // last committed entry is not NULL. Last committed entry could be NULL
738        // when an interstitial page is injected (e.g. bad https certificate,
739        // malware site etc). When this happens, we abort the shortcut update.
740        NavigationEntry* entry = controller.GetLastCommittedEntry();
741        if (entry)
742          GetApplicationInfo(entry->GetPageID());
743        else
744          pending_web_app_action_ = NONE;
745      }
746      break;
747    }
748    case chrome::NOTIFICATION_CRX_INSTALLER_DONE: {
749      if (pending_web_app_action_ != CREATE_HOSTED_APP)
750        return;
751
752      pending_web_app_action_ = NONE;
753
754      const Extension* extension =
755          content::Details<const Extension>(details).ptr();
756      if (!extension ||
757          AppLaunchInfo::GetLaunchWebURL(extension) != web_app_info_.app_url)
758        return;
759
760#if defined(OS_CHROMEOS)
761      ChromeLauncherController::instance()->PinAppWithID(extension->id());
762#endif
763
764      // Android does not implement browser_finder.cc.
765#if !defined(OS_ANDROID)
766      Browser* browser =
767          chrome::FindBrowserWithWebContents(web_contents());
768      if (browser) {
769        browser->window()->ShowBookmarkAppBubble(web_app_info_,
770                                                 extension->id());
771      }
772#endif
773    }
774    case chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR: {
775      if (pending_web_app_action_ == CREATE_HOSTED_APP)
776        pending_web_app_action_ = NONE;
777      break;
778    }
779  }
780}
781
782void TabHelper::SetTabId(RenderViewHost* render_view_host) {
783  render_view_host->Send(
784      new ExtensionMsg_SetTabId(render_view_host->GetRoutingID(),
785                                SessionID::IdForTab(web_contents())));
786}
787
788}  // namespace extensions
789