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