Upstream version 10.39.225.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   // Start fetching web app info for CreateApplicationShortcut dialog and show
123   // the dialog when the data is available in OnDidGetApplicationInfo.
124   GetApplicationInfo(CREATE_SHORTCUT);
125 }
126
127 void TabHelper::CreateHostedAppFromWebContents() {
128   DCHECK(CanCreateBookmarkApp());
129   // Start fetching web app info for CreateApplicationShortcut dialog and show
130   // the dialog when the data is available in OnDidGetApplicationInfo.
131   GetApplicationInfo(CREATE_HOSTED_APP);
132 }
133
134 bool TabHelper::CanCreateApplicationShortcuts() const {
135 #if defined(OS_MACOSX)
136   return false;
137 #else
138   return web_app::IsValidUrl(web_contents()->GetURL()) &&
139       pending_web_app_action_ == NONE;
140 #endif
141 }
142
143 bool TabHelper::CanCreateBookmarkApp() const {
144 #if defined(OS_MACOSX)
145   return false;
146 #else
147   return IsValidBookmarkAppUrl(web_contents()->GetURL()) &&
148          pending_web_app_action_ == NONE;
149 #endif
150 }
151
152 void TabHelper::AddScriptExecutionObserver(ScriptExecutionObserver* observer) {
153   script_execution_observers_.AddObserver(observer);
154 }
155
156 void TabHelper::RemoveScriptExecutionObserver(
157     ScriptExecutionObserver* observer) {
158   script_execution_observers_.RemoveObserver(observer);
159 }
160
161 void TabHelper::SetExtensionApp(const Extension* extension) {
162   DCHECK(!extension || AppLaunchInfo::GetFullLaunchURL(extension).is_valid());
163   if (extension_app_ == extension)
164     return;
165
166   extension_app_ = extension;
167
168   UpdateExtensionAppIcon(extension_app_);
169
170   content::NotificationService::current()->Notify(
171       chrome::NOTIFICATION_TAB_CONTENTS_APPLICATION_EXTENSION_CHANGED,
172       content::Source<TabHelper>(this),
173       content::NotificationService::NoDetails());
174 }
175
176 void TabHelper::SetExtensionAppById(const std::string& extension_app_id) {
177   const Extension* extension = GetExtension(extension_app_id);
178   if (extension)
179     SetExtensionApp(extension);
180 }
181
182 void TabHelper::SetExtensionAppIconById(const std::string& extension_app_id) {
183   const Extension* extension = GetExtension(extension_app_id);
184   if (extension)
185     UpdateExtensionAppIcon(extension);
186 }
187
188 SkBitmap* TabHelper::GetExtensionAppIcon() {
189   if (extension_app_icon_.empty())
190     return NULL;
191
192   return &extension_app_icon_;
193 }
194
195 void TabHelper::FinishCreateBookmarkApp(
196     const Extension* extension,
197     const WebApplicationInfo& web_app_info) {
198   pending_web_app_action_ = NONE;
199
200   // There was an error with downloading the icons or installing the app.
201   if (!extension)
202     return;
203
204 #if defined(OS_CHROMEOS)
205   ChromeLauncherController::instance()->PinAppWithID(extension->id());
206 #endif
207
208   Browser* browser = chrome::FindBrowserWithWebContents(web_contents());
209   if (browser) {
210     browser->window()->ShowBookmarkAppBubble(web_app_info, extension->id());
211   }
212 }
213
214 void TabHelper::RenderViewCreated(RenderViewHost* render_view_host) {
215   SetTabId(render_view_host);
216 }
217
218 void TabHelper::DidNavigateMainFrame(
219     const content::LoadCommittedDetails& details,
220     const content::FrameNavigateParams& params) {
221   if (ExtensionSystem::Get(profile_)->extension_service() &&
222       RulesRegistryService::Get(profile_)) {
223     RulesRegistryService::Get(profile_)->content_rules_registry()->
224         DidNavigateMainFrame(web_contents(), details, params);
225   }
226
227   content::BrowserContext* context = web_contents()->GetBrowserContext();
228   ExtensionRegistry* registry = ExtensionRegistry::Get(context);
229   const ExtensionSet& enabled_extensions = registry->enabled_extensions();
230
231   if (util::IsStreamlinedHostedAppsEnabled()) {
232     Browser* browser = chrome::FindBrowserWithWebContents(web_contents());
233     if (browser && browser->is_app()) {
234       SetExtensionApp(registry->GetExtensionById(
235           web_app::GetExtensionIdFromApplicationName(browser->app_name()),
236           ExtensionRegistry::EVERYTHING));
237     } else {
238       UpdateExtensionAppIcon(
239           enabled_extensions.GetExtensionOrAppByURL(params.url));
240     }
241   } else {
242     UpdateExtensionAppIcon(
243         enabled_extensions.GetExtensionOrAppByURL(params.url));
244   }
245
246   if (!details.is_in_page)
247     ExtensionActionAPI::Get(context)->ClearAllValuesForTab(web_contents());
248 }
249
250 bool TabHelper::OnMessageReceived(const IPC::Message& message) {
251   bool handled = true;
252   IPC_BEGIN_MESSAGE_MAP(TabHelper, message)
253     IPC_MESSAGE_HANDLER(ChromeViewHostMsg_DidGetWebApplicationInfo,
254                         OnDidGetWebApplicationInfo)
255     IPC_MESSAGE_HANDLER(ExtensionHostMsg_InlineWebstoreInstall,
256                         OnInlineWebstoreInstall)
257     IPC_MESSAGE_HANDLER(ExtensionHostMsg_GetAppInstallState,
258                         OnGetAppInstallState);
259     IPC_MESSAGE_HANDLER(ExtensionHostMsg_Request, OnRequest)
260     IPC_MESSAGE_HANDLER(ExtensionHostMsg_ContentScriptsExecuting,
261                         OnContentScriptsExecuting)
262     IPC_MESSAGE_HANDLER(ExtensionHostMsg_OnWatchedPageChange,
263                         OnWatchedPageChange)
264     IPC_MESSAGE_UNHANDLED(handled = false)
265   IPC_END_MESSAGE_MAP()
266   return handled;
267 }
268
269 bool TabHelper::OnMessageReceived(const IPC::Message& message,
270                                   content::RenderFrameHost* render_frame_host) {
271   bool handled = true;
272   IPC_BEGIN_MESSAGE_MAP(TabHelper, message)
273     IPC_MESSAGE_HANDLER(ExtensionHostMsg_DetailedConsoleMessageAdded,
274                         OnDetailedConsoleMessageAdded)
275     IPC_MESSAGE_UNHANDLED(handled = false)
276   IPC_END_MESSAGE_MAP()
277   return handled;
278 }
279
280 void TabHelper::DidCloneToNewWebContents(WebContents* old_web_contents,
281                                          WebContents* new_web_contents) {
282   // When the WebContents that this is attached to is cloned, give the new clone
283   // a TabHelper and copy state over.
284   CreateForWebContents(new_web_contents);
285   TabHelper* new_helper = FromWebContents(new_web_contents);
286
287   new_helper->SetExtensionApp(extension_app());
288   new_helper->extension_app_icon_ = extension_app_icon_;
289 }
290
291 void TabHelper::OnDidGetWebApplicationInfo(const WebApplicationInfo& info) {
292 #if !defined(OS_MACOSX)
293   web_app_info_ = info;
294
295   NavigationEntry* entry =
296       web_contents()->GetController().GetLastCommittedEntry();
297   if (!entry || last_committed_page_id_ != entry->GetPageID())
298     return;
299   last_committed_page_id_ = -1;
300
301   switch (pending_web_app_action_) {
302     case CREATE_SHORTCUT: {
303       chrome::ShowCreateWebAppShortcutsDialog(
304           web_contents()->GetTopLevelNativeWindow(),
305           web_contents());
306       break;
307     }
308     case CREATE_HOSTED_APP: {
309       if (web_app_info_.app_url.is_empty())
310         web_app_info_.app_url = web_contents()->GetURL();
311
312       if (web_app_info_.title.empty())
313         web_app_info_.title = web_contents()->GetTitle();
314       if (web_app_info_.title.empty())
315         web_app_info_.title = base::UTF8ToUTF16(web_app_info_.app_url.spec());
316
317       bookmark_app_helper_.reset(new BookmarkAppHelper(
318           ExtensionSystem::Get(profile_)->extension_service(),
319           web_app_info_, web_contents()));
320       bookmark_app_helper_->Create(base::Bind(
321           &TabHelper::FinishCreateBookmarkApp, base::Unretained(this)));
322       break;
323     }
324     case UPDATE_SHORTCUT: {
325       web_app::UpdateShortcutForTabContents(web_contents());
326       break;
327     }
328     default:
329       NOTREACHED();
330       break;
331   }
332
333   // The hosted app action will be cleared once the installation completes or
334   // fails.
335   if (pending_web_app_action_ != CREATE_HOSTED_APP)
336     pending_web_app_action_ = NONE;
337 #endif
338 }
339
340 void TabHelper::OnInlineWebstoreInstall(int install_id,
341                                         int return_route_id,
342                                         const std::string& webstore_item_id,
343                                         const GURL& requestor_url,
344                                         int listeners_mask) {
345   // Check that the listener is reasonable. We should never get anything other
346   // than an install stage listener, a download listener, or both.
347   if ((listeners_mask & ~(api::webstore::INSTALL_STAGE_LISTENER |
348                           api::webstore::DOWNLOAD_PROGRESS_LISTENER)) != 0) {
349     NOTREACHED();
350     return;
351   }
352   // Inform the Webstore API that an inline install is happening, in case the
353   // page requested status updates.
354   Profile* profile =
355       Profile::FromBrowserContext(web_contents()->GetBrowserContext());
356   WebstoreAPI::Get(profile)->OnInlineInstallStart(
357       return_route_id, this, webstore_item_id, listeners_mask);
358
359   WebstoreStandaloneInstaller::Callback callback =
360       base::Bind(&TabHelper::OnInlineInstallComplete,
361                  base::Unretained(this),
362                  install_id,
363                  return_route_id);
364   scoped_refptr<WebstoreInlineInstaller> installer(
365       webstore_inline_installer_factory_->CreateInstaller(
366           web_contents(),
367           webstore_item_id,
368           requestor_url,
369           callback));
370   installer->BeginInstall();
371 }
372
373 void TabHelper::OnGetAppInstallState(const GURL& requestor_url,
374                                      int return_route_id,
375                                      int callback_id) {
376   ExtensionRegistry* registry =
377       ExtensionRegistry::Get(web_contents()->GetBrowserContext());
378   const ExtensionSet& extensions = registry->enabled_extensions();
379   const ExtensionSet& disabled_extensions = registry->disabled_extensions();
380
381   std::string state;
382   if (extensions.GetHostedAppByURL(requestor_url))
383     state = extension_misc::kAppStateInstalled;
384   else if (disabled_extensions.GetHostedAppByURL(requestor_url))
385     state = extension_misc::kAppStateDisabled;
386   else
387     state = extension_misc::kAppStateNotInstalled;
388
389   Send(new ExtensionMsg_GetAppInstallStateResponse(
390       return_route_id, state, callback_id));
391 }
392
393 void TabHelper::OnRequest(const ExtensionHostMsg_Request_Params& request) {
394   extension_function_dispatcher_.Dispatch(request,
395                                           web_contents()->GetRenderViewHost());
396 }
397
398 void TabHelper::OnContentScriptsExecuting(
399     const ScriptExecutionObserver::ExecutingScriptsMap& executing_scripts_map,
400     const GURL& on_url) {
401   FOR_EACH_OBSERVER(
402       ScriptExecutionObserver,
403       script_execution_observers_,
404       OnScriptsExecuted(web_contents(), executing_scripts_map, on_url));
405 }
406
407 void TabHelper::OnWatchedPageChange(
408     const std::vector<std::string>& css_selectors) {
409   if (ExtensionSystem::Get(profile_)->extension_service() &&
410       RulesRegistryService::Get(profile_)) {
411     RulesRegistryService::Get(profile_)->content_rules_registry()->Apply(
412         web_contents(), css_selectors);
413   }
414 }
415
416 void TabHelper::OnDetailedConsoleMessageAdded(
417     const base::string16& message,
418     const base::string16& source,
419     const StackTrace& stack_trace,
420     int32 severity_level) {
421   if (IsSourceFromAnExtension(source)) {
422     content::RenderViewHost* rvh = web_contents()->GetRenderViewHost();
423     ErrorConsole::Get(profile_)->ReportError(
424         scoped_ptr<ExtensionError>(new RuntimeError(
425             extension_app_ ? extension_app_->id() : std::string(),
426             profile_->IsOffTheRecord(),
427             source,
428             message,
429             stack_trace,
430             web_contents() ?
431                 web_contents()->GetLastCommittedURL() : GURL::EmptyGURL(),
432             static_cast<logging::LogSeverity>(severity_level),
433             rvh->GetRoutingID(),
434             rvh->GetProcess()->GetID())));
435   }
436 }
437
438 const Extension* TabHelper::GetExtension(const std::string& extension_app_id) {
439   if (extension_app_id.empty())
440     return NULL;
441
442   content::BrowserContext* context = web_contents()->GetBrowserContext();
443   return ExtensionRegistry::Get(context)->enabled_extensions().GetByID(
444       extension_app_id);
445 }
446
447 void TabHelper::UpdateExtensionAppIcon(const Extension* extension) {
448   extension_app_icon_.reset();
449   // Ensure previously enqueued callbacks are ignored.
450   image_loader_ptr_factory_.InvalidateWeakPtrs();
451
452   // Enqueue OnImageLoaded callback.
453   if (extension) {
454     Profile* profile =
455         Profile::FromBrowserContext(web_contents()->GetBrowserContext());
456     ImageLoader* loader = ImageLoader::Get(profile);
457     loader->LoadImageAsync(
458         extension,
459         IconsInfo::GetIconResource(extension,
460                                    extension_misc::EXTENSION_ICON_SMALL,
461                                    ExtensionIconSet::MATCH_BIGGER),
462         gfx::Size(extension_misc::EXTENSION_ICON_SMALL,
463                   extension_misc::EXTENSION_ICON_SMALL),
464         base::Bind(&TabHelper::OnImageLoaded,
465                    image_loader_ptr_factory_.GetWeakPtr()));
466   }
467 }
468
469 void TabHelper::SetAppIcon(const SkBitmap& app_icon) {
470   extension_app_icon_ = app_icon;
471   web_contents()->NotifyNavigationStateChanged(content::INVALIDATE_TYPE_TITLE);
472 }
473
474 void TabHelper::SetWebstoreInlineInstallerFactoryForTests(
475     WebstoreInlineInstallerFactory* factory) {
476   webstore_inline_installer_factory_.reset(factory);
477 }
478
479 void TabHelper::OnImageLoaded(const gfx::Image& image) {
480   if (!image.IsEmpty()) {
481     extension_app_icon_ = *image.ToSkBitmap();
482     web_contents()->NotifyNavigationStateChanged(content::INVALIDATE_TYPE_TAB);
483   }
484 }
485
486 WindowController* TabHelper::GetExtensionWindowController() const  {
487   return ExtensionTabUtil::GetWindowControllerOfTab(web_contents());
488 }
489
490 void TabHelper::OnInlineInstallComplete(int install_id,
491                                         int return_route_id,
492                                         bool success,
493                                         const std::string& error,
494                                         webstore_install::Result result) {
495   Send(new ExtensionMsg_InlineWebstoreInstallResponse(
496       return_route_id,
497       install_id,
498       success,
499       success ? std::string() : error,
500       result));
501 }
502
503 WebContents* TabHelper::GetAssociatedWebContents() const {
504   return web_contents();
505 }
506
507 void TabHelper::GetApplicationInfo(WebAppAction action) {
508   NavigationEntry* entry =
509       web_contents()->GetController().GetLastCommittedEntry();
510   if (!entry)
511     return;
512
513   pending_web_app_action_ = action;
514   last_committed_page_id_ = entry->GetPageID();
515
516   Send(new ChromeViewMsg_GetWebApplicationInfo(routing_id()));
517 }
518
519 void TabHelper::Observe(int type,
520                         const content::NotificationSource& source,
521                         const content::NotificationDetails& details) {
522   DCHECK_EQ(content::NOTIFICATION_LOAD_STOP, type);
523   const NavigationController& controller =
524       *content::Source<NavigationController>(source).ptr();
525   DCHECK_EQ(controller.GetWebContents(), web_contents());
526
527   if (update_shortcut_on_load_complete_) {
528     update_shortcut_on_load_complete_ = false;
529     // Schedule a shortcut update when web application info is available if
530     // last committed entry is not NULL. Last committed entry could be NULL
531     // when an interstitial page is injected (e.g. bad https certificate,
532     // malware site etc). When this happens, we abort the shortcut update.
533     if (controller.GetLastCommittedEntry())
534       GetApplicationInfo(UPDATE_SHORTCUT);
535   }
536 }
537
538 void TabHelper::SetTabId(RenderViewHost* render_view_host) {
539   render_view_host->Send(
540       new ExtensionMsg_SetTabId(render_view_host->GetRoutingID(),
541                                 SessionTabHelper::IdForTab(web_contents())));
542 }
543
544 }  // namespace extensions