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