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