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.
5 #include "chrome/browser/extensions/tab_helper.h"
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"
58 using content::NavigationController;
59 using content::NavigationEntry;
60 using content::RenderViewHost;
61 using content::WebContents;
63 DEFINE_WEB_CONTENTS_USER_DATA_KEY(extensions::TabHelper);
65 namespace extensions {
67 TabHelper::ScriptExecutionObserver::ScriptExecutionObserver(
68 TabHelper* tab_helper)
69 : tab_helper_(tab_helper) {
70 tab_helper_->AddScriptExecutionObserver(this);
73 TabHelper::ScriptExecutionObserver::ScriptExecutionObserver()
77 TabHelper::ScriptExecutionObserver::~ScriptExecutionObserver() {
79 tab_helper_->RemoveScriptExecutionObserver(this);
82 TabHelper::TabHelper(content::WebContents* web_contents)
83 : content::WebContentsObserver(web_contents),
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(
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));
105 location_bar_controller_.reset(
106 new PageActionController(web_contents));
109 if (FeatureSwitch::script_bubble()->IsEnabled()) {
110 script_bubble_controller_.reset(
111 new ScriptBubbleController(web_contents, this));
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());
118 #if defined(ENABLE_EXTENSIONS)
119 AddScriptExecutionObserver(ActivityLog::GetInstance(profile_));
123 content::NOTIFICATION_LOAD_STOP,
124 content::Source<NavigationController>(
125 &web_contents->GetController()));
128 chrome::NOTIFICATION_EXTENSION_UNLOADED,
129 content::NotificationService::AllSources());
132 TabHelper::~TabHelper() {
133 #if defined(ENABLE_EXTENSIONS)
134 RemoveScriptExecutionObserver(ActivityLog::GetInstance(profile_));
138 void TabHelper::CreateApplicationShortcuts() {
139 DCHECK(CanCreateApplicationShortcuts());
140 NavigationEntry* entry =
141 web_contents()->GetController().GetLastCommittedEntry();
145 pending_web_app_action_ = CREATE_SHORTCUT;
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());
152 bool TabHelper::CanCreateApplicationShortcuts() const {
153 #if defined(OS_MACOSX)
156 return web_app::IsValidUrl(web_contents()->GetURL()) &&
157 pending_web_app_action_ == NONE;
161 void TabHelper::SetExtensionApp(const Extension* extension) {
162 DCHECK(!extension || AppLaunchInfo::GetFullLaunchURL(extension).is_valid());
163 extension_app_ = extension;
165 UpdateExtensionAppIcon(extension_app_);
167 content::NotificationService::current()->Notify(
168 chrome::NOTIFICATION_TAB_CONTENTS_APPLICATION_EXTENSION_CHANGED,
169 content::Source<TabHelper>(this),
170 content::NotificationService::NoDetails());
173 void TabHelper::SetExtensionAppById(const std::string& extension_app_id) {
174 const Extension* extension = GetExtension(extension_app_id);
176 SetExtensionApp(extension);
179 void TabHelper::SetExtensionAppIconById(const std::string& extension_app_id) {
180 const Extension* extension = GetExtension(extension_app_id);
182 UpdateExtensionAppIcon(extension);
185 SkBitmap* TabHelper::GetExtensionAppIcon() {
186 if (extension_app_icon_.empty())
189 return &extension_app_icon_;
192 void TabHelper::RenderViewCreated(RenderViewHost* render_view_host) {
193 SetTabId(render_view_host);
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);
205 #endif // defined(ENABLE_EXTENSIONS)
207 if (details.is_in_page)
211 Profile::FromBrowserContext(web_contents()->GetBrowserContext());
212 ExtensionService* service = profile->GetExtensionService();
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());
232 bool TabHelper::OnMessageReceived(const IPC::Message& message) {
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,
246 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_DetailedConsoleMessageAdded,
247 OnDetailedConsoleMessageAdded)
248 IPC_MESSAGE_UNHANDLED(handled = false)
249 IPC_END_MESSAGE_MAP()
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);
260 new_helper->SetExtensionApp(extension_app());
261 new_helper->extension_app_icon_ = extension_app_icon_;
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;
270 NavigationEntry* entry =
271 web_contents()->GetController().GetLastCommittedEntry();
272 if (!entry || (entry->GetPageID() != page_id))
275 switch (pending_web_app_action_) {
276 case CREATE_SHORTCUT: {
277 chrome::ShowCreateWebAppShortcutsDialog(
278 web_contents()->GetView()->GetTopLevelNativeWindow(),
282 case UPDATE_SHORTCUT: {
283 web_app::UpdateShortcutForTabContents(web_contents());
291 pending_web_app_action_ = NONE;
295 void TabHelper::OnInlineWebstoreInstall(
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(
309 installer->BeginInstall();
312 void TabHelper::OnGetAppInstallState(const GURL& requestor_url,
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();
322 if (extensions->GetHostedAppByURL(requestor_url))
323 state = extension_misc::kAppStateInstalled;
324 else if (disabled->GetHostedAppByURL(requestor_url))
325 state = extension_misc::kAppStateDisabled;
327 state = extension_misc::kAppStateNotInstalled;
329 Send(new ExtensionMsg_GetAppInstallStateResponse(
330 return_route_id, state, callback_id));
333 void TabHelper::OnRequest(const ExtensionHostMsg_Request_Params& request) {
334 extension_function_dispatcher_.Dispatch(request,
335 web_contents()->GetRenderViewHost());
338 void TabHelper::OnContentScriptsExecuting(
339 const ScriptExecutionObserver::ExecutingScriptsMap& executing_scripts_map,
341 const GURL& on_url) {
342 FOR_EACH_OBSERVER(ScriptExecutionObserver, script_execution_observers_,
343 OnScriptsExecuted(web_contents(),
344 executing_scripts_map,
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);
357 #endif // defined(ENABLE_EXTENSIONS)
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(),
375 web_contents()->GetLastCommittedURL() : GURL::EmptyGURL(),
376 static_cast<logging::LogSeverity>(severity_level),
378 rvh->GetProcess()->GetID())));
382 const Extension* TabHelper::GetExtension(const std::string& extension_app_id) {
383 if (extension_app_id.empty())
387 Profile::FromBrowserContext(web_contents()->GetBrowserContext());
388 ExtensionService* extension_service = profile->GetExtensionService();
389 if (!extension_service || !extension_service->is_ready())
392 const Extension* extension =
393 extension_service->GetExtensionById(extension_app_id, false);
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();
402 // Enqueue OnImageLoaded callback.
405 Profile::FromBrowserContext(web_contents()->GetBrowserContext());
406 extensions::ImageLoader* loader = extensions::ImageLoader::Get(profile);
407 loader->LoadImageAsync(
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()));
419 void TabHelper::SetAppIcon(const SkBitmap& app_icon) {
420 extension_app_icon_ = app_icon;
421 web_contents()->NotifyNavigationStateChanged(content::INVALIDATE_TYPE_TITLE);
424 void TabHelper::SetWebstoreInlineInstallerFactoryForTests(
425 WebstoreInlineInstallerFactory* factory) {
426 webstore_inline_installer_factory_.reset(factory);
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);
436 WindowController* TabHelper::GetExtensionWindowController() const {
437 return ExtensionTabUtil::GetWindowControllerOfTab(web_contents());
440 void TabHelper::OnInlineInstallComplete(int install_id,
443 const std::string& error) {
444 Send(new ExtensionMsg_InlineWebstoreInstallResponse(
445 return_route_id, install_id, success, success ? std::string() : error));
448 WebContents* TabHelper::GetAssociatedWebContents() const {
449 return web_contents();
452 void TabHelper::GetApplicationInfo(int32 page_id) {
453 Send(new ExtensionMsg_GetApplicationInfo(routing_id(), page_id));
456 void TabHelper::Observe(int type,
457 const content::NotificationSource& source,
458 const content::NotificationDetails& details) {
460 case content::NOTIFICATION_LOAD_STOP: {
461 const NavigationController& controller =
462 *content::Source<NavigationController>(source).ptr();
463 DCHECK_EQ(controller.GetWebContents(), web_contents());
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();
472 GetApplicationInfo(entry->GetPageID());
474 pending_web_app_action_ = NONE;
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());
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())));
496 } // namespace extensions