Revert of Add button to add new FSP services to Files app. (patchset #8 id:140001...
[chromium-blink-merge.git] / chrome / browser / extensions / tab_helper.cc
blobd5c48c99584df3385389c5c3370864c361e07148
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 "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/host_desktop.h"
30 #include "chrome/browser/web_applications/web_app.h"
31 #include "chrome/common/extensions/chrome_extension_messages.h"
32 #include "chrome/common/extensions/extension_constants.h"
33 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
34 #include "chrome/common/render_messages.h"
35 #include "chrome/common/url_constants.h"
36 #include "content/public/browser/invalidate_type.h"
37 #include "content/public/browser/navigation_controller.h"
38 #include "content/public/browser/navigation_details.h"
39 #include "content/public/browser/navigation_entry.h"
40 #include "content/public/browser/notification_service.h"
41 #include "content/public/browser/notification_source.h"
42 #include "content/public/browser/notification_types.h"
43 #include "content/public/browser/render_process_host.h"
44 #include "content/public/browser/render_view_host.h"
45 #include "content/public/browser/web_contents.h"
46 #include "content/public/common/frame_navigate_params.h"
47 #include "extensions/browser/api/declarative/rules_registry_service.h"
48 #include "extensions/browser/extension_error.h"
49 #include "extensions/browser/extension_prefs.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"
62 #if defined(OS_WIN)
63 #include "chrome/browser/web_applications/web_app_win.h"
64 #endif
66 using content::NavigationController;
67 using content::NavigationEntry;
68 using content::RenderViewHost;
69 using content::WebContents;
71 DEFINE_WEB_CONTENTS_USER_DATA_KEY(extensions::TabHelper);
73 namespace extensions {
75 TabHelper::TabHelper(content::WebContents* web_contents)
76 : content::WebContentsObserver(web_contents),
77 extension_app_(NULL),
78 extension_function_dispatcher_(
79 Profile::FromBrowserContext(web_contents->GetBrowserContext()),
80 this),
81 pending_web_app_action_(NONE),
82 last_committed_nav_entry_unique_id_(0),
83 update_shortcut_on_load_complete_(false),
84 script_executor_(
85 new ScriptExecutor(web_contents, &script_execution_observers_)),
86 location_bar_controller_(new LocationBarController(web_contents)),
87 active_script_controller_(new ActiveScriptController(web_contents)),
88 webstore_inline_installer_factory_(new WebstoreInlineInstallerFactory()),
89 image_loader_ptr_factory_(this),
90 weak_ptr_factory_(this) {
91 // The ActiveTabPermissionManager requires a session ID; ensure this
92 // WebContents has one.
93 SessionTabHelper::CreateForWebContents(web_contents);
94 if (web_contents->GetRenderViewHost())
95 SetTabId(web_contents->GetRenderViewHost());
96 active_tab_permission_granter_.reset(new ActiveTabPermissionGranter(
97 web_contents,
98 SessionTabHelper::IdForTab(web_contents),
99 Profile::FromBrowserContext(web_contents->GetBrowserContext())));
101 // If more classes need to listen to global content script activity, then
102 // a separate routing class with an observer interface should be written.
103 profile_ = Profile::FromBrowserContext(web_contents->GetBrowserContext());
105 AddScriptExecutionObserver(ActivityLog::GetInstance(profile_));
107 registrar_.Add(this,
108 content::NOTIFICATION_LOAD_STOP,
109 content::Source<NavigationController>(
110 &web_contents->GetController()));
113 TabHelper::~TabHelper() {
114 RemoveScriptExecutionObserver(ActivityLog::GetInstance(profile_));
117 void TabHelper::CreateApplicationShortcuts() {
118 DCHECK(CanCreateApplicationShortcuts());
119 if (pending_web_app_action_ != NONE)
120 return;
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);
127 void TabHelper::CreateHostedAppFromWebContents() {
128 DCHECK(CanCreateBookmarkApp());
129 if (pending_web_app_action_ != NONE)
130 return;
132 // Start fetching web app info for CreateApplicationShortcut dialog and show
133 // the dialog when the data is available in OnDidGetApplicationInfo.
134 GetApplicationInfo(CREATE_HOSTED_APP);
137 bool TabHelper::CanCreateApplicationShortcuts() const {
138 #if defined(OS_MACOSX)
139 return false;
140 #else
141 return web_app::IsValidUrl(web_contents()->GetURL());
142 #endif
145 bool TabHelper::CanCreateBookmarkApp() const {
146 return IsValidBookmarkAppUrl(web_contents()->GetURL());
149 void TabHelper::AddScriptExecutionObserver(ScriptExecutionObserver* observer) {
150 script_execution_observers_.AddObserver(observer);
153 void TabHelper::RemoveScriptExecutionObserver(
154 ScriptExecutionObserver* observer) {
155 script_execution_observers_.RemoveObserver(observer);
158 void TabHelper::SetExtensionApp(const Extension* extension) {
159 DCHECK(!extension || AppLaunchInfo::GetFullLaunchURL(extension).is_valid());
160 if (extension_app_ == extension)
161 return;
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);
175 if (extension)
176 SetExtensionApp(extension);
179 void TabHelper::SetExtensionAppIconById(const std::string& extension_app_id) {
180 const Extension* extension = GetExtension(extension_app_id);
181 if (extension)
182 UpdateExtensionAppIcon(extension);
185 SkBitmap* TabHelper::GetExtensionAppIcon() {
186 if (extension_app_icon_.empty())
187 return NULL;
189 return &extension_app_icon_;
192 void TabHelper::FinishCreateBookmarkApp(
193 const Extension* extension,
194 const WebApplicationInfo& web_app_info) {
195 pending_web_app_action_ = NONE;
198 void TabHelper::RenderViewCreated(RenderViewHost* render_view_host) {
199 SetTabId(render_view_host);
202 void TabHelper::DidNavigateMainFrame(
203 const content::LoadCommittedDetails& details,
204 const content::FrameNavigateParams& params) {
205 if (ExtensionSystem::Get(profile_)->extension_service() &&
206 RulesRegistryService::Get(profile_)) {
207 RulesRegistryService::Get(profile_)->content_rules_registry()->
208 DidNavigateMainFrame(web_contents(), details, params);
211 content::BrowserContext* context = web_contents()->GetBrowserContext();
212 ExtensionRegistry* registry = ExtensionRegistry::Get(context);
213 const ExtensionSet& enabled_extensions = registry->enabled_extensions();
215 if (util::IsNewBookmarkAppsEnabled()) {
216 Browser* browser = chrome::FindBrowserWithWebContents(web_contents());
217 if (browser && browser->is_app()) {
218 const Extension* extension = registry->GetExtensionById(
219 web_app::GetExtensionIdFromApplicationName(browser->app_name()),
220 ExtensionRegistry::EVERYTHING);
221 if (extension && AppLaunchInfo::GetFullLaunchURL(extension).is_valid())
222 SetExtensionApp(extension);
223 } else {
224 UpdateExtensionAppIcon(
225 enabled_extensions.GetExtensionOrAppByURL(params.url));
227 } else {
228 UpdateExtensionAppIcon(
229 enabled_extensions.GetExtensionOrAppByURL(params.url));
232 if (!details.is_in_page)
233 ExtensionActionAPI::Get(context)->ClearAllValuesForTab(web_contents());
236 bool TabHelper::OnMessageReceived(const IPC::Message& message) {
237 bool handled = true;
238 IPC_BEGIN_MESSAGE_MAP(TabHelper, message)
239 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_DidGetWebApplicationInfo,
240 OnDidGetWebApplicationInfo)
241 IPC_MESSAGE_HANDLER(ExtensionHostMsg_InlineWebstoreInstall,
242 OnInlineWebstoreInstall)
243 IPC_MESSAGE_HANDLER(ExtensionHostMsg_GetAppInstallState,
244 OnGetAppInstallState);
245 IPC_MESSAGE_HANDLER(ExtensionHostMsg_Request, OnRequest)
246 IPC_MESSAGE_HANDLER(ExtensionHostMsg_ContentScriptsExecuting,
247 OnContentScriptsExecuting)
248 IPC_MESSAGE_HANDLER(ExtensionHostMsg_OnWatchedPageChange,
249 OnWatchedPageChange)
250 IPC_MESSAGE_UNHANDLED(handled = false)
251 IPC_END_MESSAGE_MAP()
252 return handled;
255 bool TabHelper::OnMessageReceived(const IPC::Message& message,
256 content::RenderFrameHost* render_frame_host) {
257 bool handled = true;
258 IPC_BEGIN_MESSAGE_MAP(TabHelper, message)
259 IPC_MESSAGE_HANDLER(ExtensionHostMsg_DetailedConsoleMessageAdded,
260 OnDetailedConsoleMessageAdded)
261 IPC_MESSAGE_UNHANDLED(handled = false)
262 IPC_END_MESSAGE_MAP()
263 return handled;
266 void TabHelper::DidCloneToNewWebContents(WebContents* old_web_contents,
267 WebContents* new_web_contents) {
268 // When the WebContents that this is attached to is cloned, give the new clone
269 // a TabHelper and copy state over.
270 CreateForWebContents(new_web_contents);
271 TabHelper* new_helper = FromWebContents(new_web_contents);
273 new_helper->SetExtensionApp(extension_app());
274 new_helper->extension_app_icon_ = extension_app_icon_;
277 void TabHelper::OnDidGetWebApplicationInfo(const WebApplicationInfo& info) {
278 web_app_info_ = info;
280 NavigationEntry* entry =
281 web_contents()->GetController().GetLastCommittedEntry();
282 if (!entry || last_committed_nav_entry_unique_id_ != entry->GetUniqueID())
283 return;
284 last_committed_nav_entry_unique_id_ = 0;
286 switch (pending_web_app_action_) {
287 #if !defined(OS_MACOSX)
288 case CREATE_SHORTCUT: {
289 chrome::ShowCreateWebAppShortcutsDialog(
290 web_contents()->GetTopLevelNativeWindow(),
291 web_contents());
292 break;
294 #endif
295 case CREATE_HOSTED_APP: {
296 if (web_app_info_.app_url.is_empty())
297 web_app_info_.app_url = web_contents()->GetURL();
299 if (web_app_info_.title.empty())
300 web_app_info_.title = web_contents()->GetTitle();
301 if (web_app_info_.title.empty())
302 web_app_info_.title = base::UTF8ToUTF16(web_app_info_.app_url.spec());
304 bookmark_app_helper_.reset(
305 new BookmarkAppHelper(profile_, web_app_info_, web_contents()));
306 bookmark_app_helper_->Create(base::Bind(
307 &TabHelper::FinishCreateBookmarkApp, weak_ptr_factory_.GetWeakPtr()));
308 break;
310 case UPDATE_SHORTCUT: {
311 web_app::UpdateShortcutForTabContents(web_contents());
312 break;
314 default:
315 NOTREACHED();
316 break;
319 // The hosted app action will be cleared once the installation completes or
320 // fails.
321 if (pending_web_app_action_ != CREATE_HOSTED_APP)
322 pending_web_app_action_ = NONE;
325 void TabHelper::OnInlineWebstoreInstall(int install_id,
326 int return_route_id,
327 const std::string& webstore_item_id,
328 const GURL& requestor_url,
329 int listeners_mask) {
330 // Check that the listener is reasonable. We should never get anything other
331 // than an install stage listener, a download listener, or both.
332 if ((listeners_mask & ~(api::webstore::INSTALL_STAGE_LISTENER |
333 api::webstore::DOWNLOAD_PROGRESS_LISTENER)) != 0 ||
334 requestor_url.is_empty()) {
335 NOTREACHED();
336 return;
338 // Inform the Webstore API that an inline install is happening, in case the
339 // page requested status updates.
340 Profile* profile =
341 Profile::FromBrowserContext(web_contents()->GetBrowserContext());
343 ExtensionRegistry* registry = ExtensionRegistry::Get(profile);
344 if (registry->disabled_extensions().Contains(webstore_item_id) &&
345 (ExtensionPrefs::Get(profile)->GetDisableReasons(webstore_item_id) &
346 Extension::DISABLE_PERMISSIONS_INCREASE) != 0) {
347 // The extension was disabled due to permissions increase. Prompt for
348 // re-enable.
349 // TODO(devlin): We should also prompt for re-enable for other reasons,
350 // like user-disabled.
351 // For clarity, explicitly end any prior reenable process.
352 extension_reenabler_.reset();
353 extension_reenabler_ = ExtensionReenabler::PromptForReenable(
354 registry->disabled_extensions().GetByID(webstore_item_id),
355 profile,
356 web_contents(),
357 requestor_url,
358 base::Bind(&TabHelper::OnReenableComplete,
359 weak_ptr_factory_.GetWeakPtr(),
360 install_id,
361 return_route_id));
362 } else {
363 // TODO(devlin): We should adddress the case of the extension already
364 // being installed and enabled.
365 WebstoreAPI::Get(profile)->OnInlineInstallStart(
366 return_route_id, this, webstore_item_id, listeners_mask);
368 WebstoreStandaloneInstaller::Callback callback =
369 base::Bind(&TabHelper::OnInlineInstallComplete,
370 base::Unretained(this),
371 install_id,
372 return_route_id);
373 scoped_refptr<WebstoreInlineInstaller> installer(
374 webstore_inline_installer_factory_->CreateInstaller(
375 web_contents(),
376 webstore_item_id,
377 requestor_url,
378 callback));
379 installer->BeginInstall();
383 void TabHelper::OnGetAppInstallState(const GURL& requestor_url,
384 int return_route_id,
385 int callback_id) {
386 ExtensionRegistry* registry =
387 ExtensionRegistry::Get(web_contents()->GetBrowserContext());
388 const ExtensionSet& extensions = registry->enabled_extensions();
389 const ExtensionSet& disabled_extensions = registry->disabled_extensions();
391 std::string state;
392 if (extensions.GetHostedAppByURL(requestor_url))
393 state = extension_misc::kAppStateInstalled;
394 else if (disabled_extensions.GetHostedAppByURL(requestor_url))
395 state = extension_misc::kAppStateDisabled;
396 else
397 state = extension_misc::kAppStateNotInstalled;
399 Send(new ExtensionMsg_GetAppInstallStateResponse(
400 return_route_id, state, callback_id));
403 void TabHelper::OnRequest(const ExtensionHostMsg_Request_Params& request) {
404 extension_function_dispatcher_.Dispatch(request,
405 web_contents()->GetRenderViewHost());
408 void TabHelper::OnContentScriptsExecuting(
409 const ScriptExecutionObserver::ExecutingScriptsMap& executing_scripts_map,
410 const GURL& on_url) {
411 FOR_EACH_OBSERVER(
412 ScriptExecutionObserver,
413 script_execution_observers_,
414 OnScriptsExecuted(web_contents(), executing_scripts_map, on_url));
417 void TabHelper::OnWatchedPageChange(
418 const std::vector<std::string>& css_selectors) {
419 if (ExtensionSystem::Get(profile_)->extension_service() &&
420 RulesRegistryService::Get(profile_)) {
421 RulesRegistryService::Get(profile_)->content_rules_registry()->Apply(
422 web_contents(), css_selectors);
426 void TabHelper::OnDetailedConsoleMessageAdded(
427 const base::string16& message,
428 const base::string16& source,
429 const StackTrace& stack_trace,
430 int32 severity_level) {
431 if (IsSourceFromAnExtension(source)) {
432 content::RenderViewHost* rvh = web_contents()->GetRenderViewHost();
433 ErrorConsole::Get(profile_)->ReportError(
434 scoped_ptr<ExtensionError>(new RuntimeError(
435 extension_app_ ? extension_app_->id() : std::string(),
436 profile_->IsOffTheRecord(),
437 source,
438 message,
439 stack_trace,
440 web_contents() ?
441 web_contents()->GetLastCommittedURL() : GURL::EmptyGURL(),
442 static_cast<logging::LogSeverity>(severity_level),
443 rvh->GetRoutingID(),
444 rvh->GetProcess()->GetID())));
448 const Extension* TabHelper::GetExtension(const std::string& extension_app_id) {
449 if (extension_app_id.empty())
450 return NULL;
452 content::BrowserContext* context = web_contents()->GetBrowserContext();
453 return ExtensionRegistry::Get(context)->enabled_extensions().GetByID(
454 extension_app_id);
457 void TabHelper::UpdateExtensionAppIcon(const Extension* extension) {
458 extension_app_icon_.reset();
459 // Ensure previously enqueued callbacks are ignored.
460 image_loader_ptr_factory_.InvalidateWeakPtrs();
462 // Enqueue OnImageLoaded callback.
463 if (extension) {
464 Profile* profile =
465 Profile::FromBrowserContext(web_contents()->GetBrowserContext());
466 ImageLoader* loader = ImageLoader::Get(profile);
467 loader->LoadImageAsync(
468 extension,
469 IconsInfo::GetIconResource(extension,
470 extension_misc::EXTENSION_ICON_SMALL,
471 ExtensionIconSet::MATCH_BIGGER),
472 gfx::Size(extension_misc::EXTENSION_ICON_SMALL,
473 extension_misc::EXTENSION_ICON_SMALL),
474 base::Bind(&TabHelper::OnImageLoaded,
475 image_loader_ptr_factory_.GetWeakPtr()));
479 void TabHelper::SetAppIcon(const SkBitmap& app_icon) {
480 extension_app_icon_ = app_icon;
481 web_contents()->NotifyNavigationStateChanged(content::INVALIDATE_TYPE_TITLE);
484 void TabHelper::SetWebstoreInlineInstallerFactoryForTests(
485 WebstoreInlineInstallerFactory* factory) {
486 webstore_inline_installer_factory_.reset(factory);
489 void TabHelper::OnImageLoaded(const gfx::Image& image) {
490 if (!image.IsEmpty()) {
491 extension_app_icon_ = *image.ToSkBitmap();
492 web_contents()->NotifyNavigationStateChanged(content::INVALIDATE_TYPE_TAB);
496 WindowController* TabHelper::GetExtensionWindowController() const {
497 return ExtensionTabUtil::GetWindowControllerOfTab(web_contents());
500 void TabHelper::OnReenableComplete(int install_id,
501 int return_route_id,
502 ExtensionReenabler::ReenableResult result) {
503 extension_reenabler_.reset();
504 // Map the re-enable results to webstore-install results.
505 webstore_install::Result webstore_result = webstore_install::SUCCESS;
506 std::string error;
507 switch (result) {
508 case ExtensionReenabler::REENABLE_SUCCESS:
509 break; // already set
510 case ExtensionReenabler::USER_CANCELED:
511 webstore_result = webstore_install::USER_CANCELLED;
512 error = "User canceled install.";
513 break;
514 case ExtensionReenabler::NOT_ALLOWED:
515 webstore_result = webstore_install::NOT_PERMITTED;
516 error = "Install not permitted.";
517 break;
518 case ExtensionReenabler::ABORTED:
519 webstore_result = webstore_install::ABORTED;
520 error = "Aborted due to tab closing.";
521 break;
524 OnInlineInstallComplete(install_id,
525 return_route_id,
526 result == ExtensionReenabler::REENABLE_SUCCESS,
527 error,
528 webstore_result);
531 void TabHelper::OnInlineInstallComplete(int install_id,
532 int return_route_id,
533 bool success,
534 const std::string& error,
535 webstore_install::Result result) {
536 Send(new ExtensionMsg_InlineWebstoreInstallResponse(
537 return_route_id,
538 install_id,
539 success,
540 success ? std::string() : error,
541 result));
544 WebContents* TabHelper::GetAssociatedWebContents() const {
545 return web_contents();
548 void TabHelper::GetApplicationInfo(WebAppAction action) {
549 NavigationEntry* entry =
550 web_contents()->GetController().GetLastCommittedEntry();
551 if (!entry)
552 return;
554 pending_web_app_action_ = action;
555 last_committed_nav_entry_unique_id_ = entry->GetUniqueID();
557 Send(new ChromeViewMsg_GetWebApplicationInfo(routing_id()));
560 void TabHelper::Observe(int type,
561 const content::NotificationSource& source,
562 const content::NotificationDetails& details) {
563 DCHECK_EQ(content::NOTIFICATION_LOAD_STOP, type);
564 const NavigationController& controller =
565 *content::Source<NavigationController>(source).ptr();
566 DCHECK_EQ(controller.GetWebContents(), web_contents());
568 if (update_shortcut_on_load_complete_) {
569 update_shortcut_on_load_complete_ = false;
570 // Schedule a shortcut update when web application info is available if
571 // last committed entry is not NULL. Last committed entry could be NULL
572 // when an interstitial page is injected (e.g. bad https certificate,
573 // malware site etc). When this happens, we abort the shortcut update.
574 if (controller.GetLastCommittedEntry())
575 GetApplicationInfo(UPDATE_SHORTCUT);
579 void TabHelper::SetTabId(RenderViewHost* render_view_host) {
580 render_view_host->Send(
581 new ExtensionMsg_SetTabId(render_view_host->GetRoutingID(),
582 SessionTabHelper::IdForTab(web_contents())));
585 } // namespace extensions