- add sources.
[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 "chrome/browser/chrome_notification_types.h"
10 #include "chrome/browser/extensions/activity_log/activity_log.h"
11 #include "chrome/browser/extensions/api/declarative/rules_registry_service.h"
12 #include "chrome/browser/extensions/api/declarative_content/content_rules_registry.h"
13 #include "chrome/browser/extensions/crx_installer.h"
14 #include "chrome/browser/extensions/error_console/error_console.h"
15 #include "chrome/browser/extensions/extension_action.h"
16 #include "chrome/browser/extensions/extension_action_manager.h"
17 #include "chrome/browser/extensions/extension_service.h"
18 #include "chrome/browser/extensions/extension_system.h"
19 #include "chrome/browser/extensions/extension_tab_util.h"
20 #include "chrome/browser/extensions/image_loader.h"
21 #include "chrome/browser/extensions/page_action_controller.h"
22 #include "chrome/browser/extensions/script_badge_controller.h"
23 #include "chrome/browser/extensions/script_bubble_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/ui/browser_dialogs.h"
31 #include "chrome/browser/ui/web_applications/web_app_ui.h"
32 #include "chrome/browser/web_applications/web_app.h"
33 #include "chrome/common/extensions/extension.h"
34 #include "chrome/common/extensions/extension_constants.h"
35 #include "chrome/common/extensions/extension_icon_set.h"
36 #include "chrome/common/extensions/extension_messages.h"
37 #include "chrome/common/extensions/feature_switch.h"
38 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
39 #include "chrome/common/extensions/manifest_handlers/icons_handler.h"
40 #include "chrome/common/render_messages.h"
41 #include "content/public/browser/invalidate_type.h"
42 #include "content/public/browser/navigation_controller.h"
43 #include "content/public/browser/navigation_details.h"
44 #include "content/public/browser/navigation_entry.h"
45 #include "content/public/browser/notification_service.h"
46 #include "content/public/browser/notification_source.h"
47 #include "content/public/browser/notification_types.h"
48 #include "content/public/browser/render_process_host.h"
49 #include "content/public/browser/render_view_host.h"
50 #include "content/public/browser/render_widget_host_view.h"
51 #include "content/public/browser/web_contents.h"
52 #include "content/public/browser/web_contents_view.h"
53 #include "extensions/browser/extension_error.h"
54 #include "extensions/common/extension_resource.h"
55 #include "extensions/common/extension_urls.h"
56 #include "ui/gfx/image/image.h"
57
58 using content::NavigationController;
59 using content::NavigationEntry;
60 using content::RenderViewHost;
61 using content::WebContents;
62
63 DEFINE_WEB_CONTENTS_USER_DATA_KEY(extensions::TabHelper);
64
65 namespace extensions {
66
67 TabHelper::ScriptExecutionObserver::ScriptExecutionObserver(
68     TabHelper* tab_helper)
69     : tab_helper_(tab_helper) {
70   tab_helper_->AddScriptExecutionObserver(this);
71 }
72
73 TabHelper::ScriptExecutionObserver::ScriptExecutionObserver()
74     : tab_helper_(NULL) {
75 }
76
77 TabHelper::ScriptExecutionObserver::~ScriptExecutionObserver() {
78   if (tab_helper_)
79     tab_helper_->RemoveScriptExecutionObserver(this);
80 }
81
82 TabHelper::TabHelper(content::WebContents* web_contents)
83     : content::WebContentsObserver(web_contents),
84       extension_app_(NULL),
85       extension_function_dispatcher_(
86           Profile::FromBrowserContext(web_contents->GetBrowserContext()), this),
87       pending_web_app_action_(NONE),
88       script_executor_(new ScriptExecutor(web_contents,
89                                           &script_execution_observers_)),
90       image_loader_ptr_factory_(this),
91       webstore_inline_installer_factory_(new WebstoreInlineInstallerFactory()) {
92   // The ActiveTabPermissionManager requires a session ID; ensure this
93   // WebContents has one.
94   SessionTabHelper::CreateForWebContents(web_contents);
95   if (web_contents->GetRenderViewHost())
96     SetTabId(web_contents->GetRenderViewHost());
97   active_tab_permission_granter_.reset(new ActiveTabPermissionGranter(
98       web_contents,
99       SessionID::IdForTab(web_contents),
100       Profile::FromBrowserContext(web_contents->GetBrowserContext())));
101   if (FeatureSwitch::script_badges()->IsEnabled()) {
102     location_bar_controller_.reset(
103         new ScriptBadgeController(web_contents, this));
104   } else {
105     location_bar_controller_.reset(
106         new PageActionController(web_contents));
107   }
108
109   if (FeatureSwitch::script_bubble()->IsEnabled()) {
110     script_bubble_controller_.reset(
111         new ScriptBubbleController(web_contents, this));
112   }
113
114   // If more classes need to listen to global content script activity, then
115   // a separate routing class with an observer interface should be written.
116   profile_ = Profile::FromBrowserContext(web_contents->GetBrowserContext());
117
118 #if defined(ENABLE_EXTENSIONS)
119   AddScriptExecutionObserver(ActivityLog::GetInstance(profile_));
120 #endif
121
122   registrar_.Add(this,
123                  content::NOTIFICATION_LOAD_STOP,
124                  content::Source<NavigationController>(
125                      &web_contents->GetController()));
126
127   registrar_.Add(this,
128                  chrome::NOTIFICATION_EXTENSION_UNLOADED,
129                  content::NotificationService::AllSources());
130 }
131
132 TabHelper::~TabHelper() {
133 #if defined(ENABLE_EXTENSIONS)
134   RemoveScriptExecutionObserver(ActivityLog::GetInstance(profile_));
135 #endif
136 }
137
138 void TabHelper::CreateApplicationShortcuts() {
139   DCHECK(CanCreateApplicationShortcuts());
140   NavigationEntry* entry =
141       web_contents()->GetController().GetLastCommittedEntry();
142   if (!entry)
143     return;
144
145   pending_web_app_action_ = CREATE_SHORTCUT;
146
147   // Start fetching web app info for CreateApplicationShortcut dialog and show
148   // the dialog when the data is available in OnDidGetApplicationInfo.
149   GetApplicationInfo(entry->GetPageID());
150 }
151
152 bool TabHelper::CanCreateApplicationShortcuts() const {
153 #if defined(OS_MACOSX)
154   return false;
155 #else
156   return web_app::IsValidUrl(web_contents()->GetURL()) &&
157       pending_web_app_action_ == NONE;
158 #endif
159 }
160
161 void TabHelper::SetExtensionApp(const Extension* extension) {
162   DCHECK(!extension || AppLaunchInfo::GetFullLaunchURL(extension).is_valid());
163   extension_app_ = extension;
164
165   UpdateExtensionAppIcon(extension_app_);
166
167   content::NotificationService::current()->Notify(
168       chrome::NOTIFICATION_TAB_CONTENTS_APPLICATION_EXTENSION_CHANGED,
169       content::Source<TabHelper>(this),
170       content::NotificationService::NoDetails());
171 }
172
173 void TabHelper::SetExtensionAppById(const std::string& extension_app_id) {
174   const Extension* extension = GetExtension(extension_app_id);
175   if (extension)
176     SetExtensionApp(extension);
177 }
178
179 void TabHelper::SetExtensionAppIconById(const std::string& extension_app_id) {
180   const Extension* extension = GetExtension(extension_app_id);
181   if (extension)
182     UpdateExtensionAppIcon(extension);
183 }
184
185 SkBitmap* TabHelper::GetExtensionAppIcon() {
186   if (extension_app_icon_.empty())
187     return NULL;
188
189   return &extension_app_icon_;
190 }
191
192 void TabHelper::RenderViewCreated(RenderViewHost* render_view_host) {
193   SetTabId(render_view_host);
194 }
195
196 void TabHelper::DidNavigateMainFrame(
197     const content::LoadCommittedDetails& details,
198     const content::FrameNavigateParams& params) {
199 #if defined(ENABLE_EXTENSIONS)
200   if (ExtensionSystem::Get(profile_)->extension_service() &&
201       RulesRegistryService::Get(profile_)) {
202     RulesRegistryService::Get(profile_)->content_rules_registry()->
203         DidNavigateMainFrame(web_contents(), details, params);
204   }
205 #endif  // defined(ENABLE_EXTENSIONS)
206
207   if (details.is_in_page)
208     return;
209
210   Profile* profile =
211       Profile::FromBrowserContext(web_contents()->GetBrowserContext());
212   ExtensionService* service = profile->GetExtensionService();
213   if (!service)
214     return;
215
216   ExtensionActionManager* extension_action_manager =
217       ExtensionActionManager::Get(profile);
218   for (ExtensionSet::const_iterator it = service->extensions()->begin();
219        it != service->extensions()->end(); ++it) {
220     ExtensionAction* browser_action =
221         extension_action_manager->GetBrowserAction(*it->get());
222     if (browser_action) {
223       browser_action->ClearAllValuesForTab(SessionID::IdForTab(web_contents()));
224       content::NotificationService::current()->Notify(
225           chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_UPDATED,
226           content::Source<ExtensionAction>(browser_action),
227           content::NotificationService::NoDetails());
228     }
229   }
230 }
231
232 bool TabHelper::OnMessageReceived(const IPC::Message& message) {
233   bool handled = true;
234   IPC_BEGIN_MESSAGE_MAP(TabHelper, message)
235     IPC_MESSAGE_HANDLER(ExtensionHostMsg_DidGetApplicationInfo,
236                         OnDidGetApplicationInfo)
237     IPC_MESSAGE_HANDLER(ExtensionHostMsg_InlineWebstoreInstall,
238                         OnInlineWebstoreInstall)
239     IPC_MESSAGE_HANDLER(ExtensionHostMsg_GetAppInstallState,
240                         OnGetAppInstallState);
241     IPC_MESSAGE_HANDLER(ExtensionHostMsg_Request, OnRequest)
242     IPC_MESSAGE_HANDLER(ExtensionHostMsg_ContentScriptsExecuting,
243                         OnContentScriptsExecuting)
244     IPC_MESSAGE_HANDLER(ExtensionHostMsg_OnWatchedPageChange,
245                         OnWatchedPageChange)
246     IPC_MESSAGE_HANDLER(ChromeViewHostMsg_DetailedConsoleMessageAdded,
247                         OnDetailedConsoleMessageAdded)
248     IPC_MESSAGE_UNHANDLED(handled = false)
249   IPC_END_MESSAGE_MAP()
250   return handled;
251 }
252
253 void TabHelper::DidCloneToNewWebContents(WebContents* old_web_contents,
254                                          WebContents* new_web_contents) {
255   // When the WebContents that this is attached to is cloned, give the new clone
256   // a TabHelper and copy state over.
257   CreateForWebContents(new_web_contents);
258   TabHelper* new_helper = FromWebContents(new_web_contents);
259
260   new_helper->SetExtensionApp(extension_app());
261   new_helper->extension_app_icon_ = extension_app_icon_;
262 }
263
264 void TabHelper::OnDidGetApplicationInfo(int32 page_id,
265                                         const WebApplicationInfo& info) {
266   // Android does not implement BrowserWindow.
267 #if !defined(OS_MACOSX) && !defined(OS_ANDROID)
268   web_app_info_ = info;
269
270   NavigationEntry* entry =
271       web_contents()->GetController().GetLastCommittedEntry();
272   if (!entry || (entry->GetPageID() != page_id))
273     return;
274
275   switch (pending_web_app_action_) {
276     case CREATE_SHORTCUT: {
277       chrome::ShowCreateWebAppShortcutsDialog(
278           web_contents()->GetView()->GetTopLevelNativeWindow(),
279           web_contents());
280       break;
281     }
282     case UPDATE_SHORTCUT: {
283       web_app::UpdateShortcutForTabContents(web_contents());
284       break;
285     }
286     default:
287       NOTREACHED();
288       break;
289   }
290
291   pending_web_app_action_ = NONE;
292 #endif
293 }
294
295 void TabHelper::OnInlineWebstoreInstall(
296     int install_id,
297     int return_route_id,
298     const std::string& webstore_item_id,
299     const GURL& requestor_url) {
300   WebstoreStandaloneInstaller::Callback callback =
301       base::Bind(&TabHelper::OnInlineInstallComplete, base::Unretained(this),
302                  install_id, return_route_id);
303   scoped_refptr<WebstoreInlineInstaller> installer(
304       webstore_inline_installer_factory_->CreateInstaller(
305           web_contents(),
306           webstore_item_id,
307           requestor_url,
308           callback));
309   installer->BeginInstall();
310 }
311
312 void TabHelper::OnGetAppInstallState(const GURL& requestor_url,
313                                      int return_route_id,
314                                      int callback_id) {
315   Profile* profile =
316       Profile::FromBrowserContext(web_contents()->GetBrowserContext());
317   ExtensionService* extension_service = profile->GetExtensionService();
318   const ExtensionSet* extensions = extension_service->extensions();
319   const ExtensionSet* disabled = extension_service->disabled_extensions();
320
321   std::string state;
322   if (extensions->GetHostedAppByURL(requestor_url))
323     state = extension_misc::kAppStateInstalled;
324   else if (disabled->GetHostedAppByURL(requestor_url))
325     state = extension_misc::kAppStateDisabled;
326   else
327     state = extension_misc::kAppStateNotInstalled;
328
329   Send(new ExtensionMsg_GetAppInstallStateResponse(
330       return_route_id, state, callback_id));
331 }
332
333 void TabHelper::OnRequest(const ExtensionHostMsg_Request_Params& request) {
334   extension_function_dispatcher_.Dispatch(request,
335                                           web_contents()->GetRenderViewHost());
336 }
337
338 void TabHelper::OnContentScriptsExecuting(
339     const ScriptExecutionObserver::ExecutingScriptsMap& executing_scripts_map,
340     int32 on_page_id,
341     const GURL& on_url) {
342   FOR_EACH_OBSERVER(ScriptExecutionObserver, script_execution_observers_,
343                     OnScriptsExecuted(web_contents(),
344                                       executing_scripts_map,
345                                       on_page_id,
346                                       on_url));
347 }
348
349 void TabHelper::OnWatchedPageChange(
350     const std::vector<std::string>& css_selectors) {
351 #if defined(ENABLE_EXTENSIONS)
352   if (ExtensionSystem::Get(profile_)->extension_service() &&
353       RulesRegistryService::Get(profile_)) {
354     RulesRegistryService::Get(profile_)->content_rules_registry()->Apply(
355         web_contents(), css_selectors);
356   }
357 #endif  // defined(ENABLE_EXTENSIONS)
358 }
359
360 void TabHelper::OnDetailedConsoleMessageAdded(
361     const base::string16& message,
362     const base::string16& source,
363     const StackTrace& stack_trace,
364     int32 severity_level) {
365   if (IsSourceFromAnExtension(source)) {
366     content::RenderViewHost* rvh = web_contents()->GetRenderViewHost();
367     ErrorConsole::Get(profile_)->ReportError(
368         scoped_ptr<ExtensionError>(new RuntimeError(
369             extension_app_ ? extension_app_->id() : EmptyString(),
370             profile_->IsOffTheRecord(),
371             source,
372             message,
373             stack_trace,
374             web_contents() ?
375                 web_contents()->GetLastCommittedURL() : GURL::EmptyGURL(),
376             static_cast<logging::LogSeverity>(severity_level),
377             rvh->GetRoutingID(),
378             rvh->GetProcess()->GetID())));
379   }
380 }
381
382 const Extension* TabHelper::GetExtension(const std::string& extension_app_id) {
383   if (extension_app_id.empty())
384     return NULL;
385
386   Profile* profile =
387       Profile::FromBrowserContext(web_contents()->GetBrowserContext());
388   ExtensionService* extension_service = profile->GetExtensionService();
389   if (!extension_service || !extension_service->is_ready())
390     return NULL;
391
392   const Extension* extension =
393       extension_service->GetExtensionById(extension_app_id, false);
394   return extension;
395 }
396
397 void TabHelper::UpdateExtensionAppIcon(const Extension* extension) {
398   extension_app_icon_.reset();
399   // Ensure previously enqueued callbacks are ignored.
400   image_loader_ptr_factory_.InvalidateWeakPtrs();
401
402   // Enqueue OnImageLoaded callback.
403   if (extension) {
404     Profile* profile =
405         Profile::FromBrowserContext(web_contents()->GetBrowserContext());
406     extensions::ImageLoader* loader = extensions::ImageLoader::Get(profile);
407     loader->LoadImageAsync(
408         extension,
409         IconsInfo::GetIconResource(extension,
410                                    extension_misc::EXTENSION_ICON_SMALLISH,
411                                    ExtensionIconSet::MATCH_EXACTLY),
412         gfx::Size(extension_misc::EXTENSION_ICON_SMALLISH,
413                   extension_misc::EXTENSION_ICON_SMALLISH),
414         base::Bind(&TabHelper::OnImageLoaded,
415                    image_loader_ptr_factory_.GetWeakPtr()));
416   }
417 }
418
419 void TabHelper::SetAppIcon(const SkBitmap& app_icon) {
420   extension_app_icon_ = app_icon;
421   web_contents()->NotifyNavigationStateChanged(content::INVALIDATE_TYPE_TITLE);
422 }
423
424 void TabHelper::SetWebstoreInlineInstallerFactoryForTests(
425     WebstoreInlineInstallerFactory* factory) {
426   webstore_inline_installer_factory_.reset(factory);
427 }
428
429 void TabHelper::OnImageLoaded(const gfx::Image& image) {
430   if (!image.IsEmpty()) {
431     extension_app_icon_ = *image.ToSkBitmap();
432     web_contents()->NotifyNavigationStateChanged(content::INVALIDATE_TYPE_TAB);
433   }
434 }
435
436 WindowController* TabHelper::GetExtensionWindowController() const  {
437   return ExtensionTabUtil::GetWindowControllerOfTab(web_contents());
438 }
439
440 void TabHelper::OnInlineInstallComplete(int install_id,
441                                         int return_route_id,
442                                         bool success,
443                                         const std::string& error) {
444   Send(new ExtensionMsg_InlineWebstoreInstallResponse(
445       return_route_id, install_id, success, success ? std::string() : error));
446 }
447
448 WebContents* TabHelper::GetAssociatedWebContents() const {
449   return web_contents();
450 }
451
452 void TabHelper::GetApplicationInfo(int32 page_id) {
453   Send(new ExtensionMsg_GetApplicationInfo(routing_id(), page_id));
454 }
455
456 void TabHelper::Observe(int type,
457                         const content::NotificationSource& source,
458                         const content::NotificationDetails& details) {
459   switch (type) {
460     case content::NOTIFICATION_LOAD_STOP: {
461       const NavigationController& controller =
462           *content::Source<NavigationController>(source).ptr();
463       DCHECK_EQ(controller.GetWebContents(), web_contents());
464
465       if (pending_web_app_action_ == UPDATE_SHORTCUT) {
466         // Schedule a shortcut update when web application info is available if
467         // last committed entry is not NULL. Last committed entry could be NULL
468         // when an interstitial page is injected (e.g. bad https certificate,
469         // malware site etc). When this happens, we abort the shortcut update.
470         NavigationEntry* entry = controller.GetLastCommittedEntry();
471         if (entry)
472           GetApplicationInfo(entry->GetPageID());
473         else
474           pending_web_app_action_ = NONE;
475       }
476       break;
477     }
478
479     case chrome::NOTIFICATION_EXTENSION_UNLOADED: {
480       if (script_bubble_controller_) {
481         script_bubble_controller_->OnExtensionUnloaded(
482             content::Details<extensions::UnloadedExtensionInfo>(
483                 details)->extension->id());
484         break;
485       }
486     }
487   }
488 }
489
490 void TabHelper::SetTabId(RenderViewHost* render_view_host) {
491   render_view_host->Send(
492       new ExtensionMsg_SetTabId(render_view_host->GetRoutingID(),
493                                 SessionID::IdForTab(web_contents())));
494 }
495
496 }  // namespace extensions