| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/extensions/navigation_observer.h" | 5 #include "chrome/browser/extensions/navigation_observer.h" |
| 6 | 6 |
| 7 #include "base/memory/ptr_util.h" | 7 #include "base/memory/ptr_util.h" |
| 8 #include "chrome/browser/extensions/extension_service.h" | 8 #include "chrome/browser/extensions/extension_service.h" |
| 9 #include "chrome/browser/profiles/profile.h" | 9 #include "chrome/browser/profiles/profile.h" |
| 10 #include "content/public/browser/navigation_controller.h" | 10 #include "content/public/browser/navigation_controller.h" |
| 11 #include "content/public/browser/navigation_entry.h" | 11 #include "content/public/browser/navigation_entry.h" |
| 12 #include "content/public/browser/notification_service.h" | 12 #include "content/public/browser/notification_service.h" |
| 13 #include "content/public/browser/notification_types.h" | 13 #include "content/public/browser/notification_types.h" |
| 14 #include "extensions/browser/extension_prefs.h" | 14 #include "extensions/browser/extension_prefs.h" |
| 15 #include "extensions/browser/extension_registry.h" | 15 #include "extensions/browser/extension_registry.h" |
| 16 #include "extensions/browser/extension_system.h" | 16 #include "extensions/browser/extension_system.h" |
| 17 | 17 |
| 18 using content::NavigationController; | 18 using content::NavigationController; |
| 19 using content::NavigationEntry; | 19 using content::NavigationEntry; |
| 20 | 20 |
| 21 namespace extensions { | 21 namespace extensions { |
| 22 | 22 |
| 23 namespace { |
| 24 // Whether to repeatedly prompt for the same extension id. |
| 25 bool g_repeat_prompting = false; |
| 26 } |
| 27 |
| 23 NavigationObserver::NavigationObserver(Profile* profile) | 28 NavigationObserver::NavigationObserver(Profile* profile) |
| 24 : profile_(profile), | 29 : profile_(profile), |
| 25 extension_registry_observer_(this), | 30 extension_registry_observer_(this), |
| 26 weak_factory_(this) { | 31 weak_factory_(this) { |
| 27 RegisterForNotifications(); | 32 RegisterForNotifications(); |
| 28 extension_registry_observer_.Add(ExtensionRegistry::Get(profile)); | 33 extension_registry_observer_.Add(ExtensionRegistry::Get(profile)); |
| 29 } | 34 } |
| 30 | 35 |
| 31 NavigationObserver::~NavigationObserver() {} | 36 NavigationObserver::~NavigationObserver() {} |
| 32 | 37 |
| 33 void NavigationObserver::Observe(int type, | 38 void NavigationObserver::Observe(int type, |
| 34 const content::NotificationSource& source, | 39 const content::NotificationSource& source, |
| 35 const content::NotificationDetails& details) { | 40 const content::NotificationDetails& details) { |
| 36 DCHECK_EQ(content::NOTIFICATION_NAV_ENTRY_COMMITTED, type); | 41 DCHECK_EQ(content::NOTIFICATION_NAV_ENTRY_COMMITTED, type); |
| 37 | 42 |
| 38 NavigationController* controller = | 43 NavigationController* controller = |
| 39 content::Source<NavigationController>(source).ptr(); | 44 content::Source<NavigationController>(source).ptr(); |
| 40 if (!profile_->IsSameProfile( | 45 if (!profile_->IsSameProfile( |
| 41 Profile::FromBrowserContext(controller->GetBrowserContext()))) | 46 Profile::FromBrowserContext(controller->GetBrowserContext()))) |
| 42 return; | 47 return; |
| 43 | 48 |
| 44 PromptToEnableExtensionIfNecessary(controller); | 49 PromptToEnableExtensionIfNecessary(controller); |
| 45 } | 50 } |
| 46 | 51 |
| 52 // static |
| 53 void NavigationObserver::SetAllowedRepeatedPromptingForTesting(bool allowed) { |
| 54 g_repeat_prompting = allowed; |
| 55 } |
| 56 |
| 47 void NavigationObserver::RegisterForNotifications() { | 57 void NavigationObserver::RegisterForNotifications() { |
| 48 registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED, | 58 registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED, |
| 49 content::NotificationService::AllSources()); | 59 content::NotificationService::AllSources()); |
| 50 } | 60 } |
| 51 | 61 |
| 52 void NavigationObserver::PromptToEnableExtensionIfNecessary( | 62 void NavigationObserver::PromptToEnableExtensionIfNecessary( |
| 53 NavigationController* nav_controller) { | 63 NavigationController* nav_controller) { |
| 54 // Bail out if we're already running a prompt. | 64 // Bail out if we're already running a prompt. |
| 55 if (!in_progress_prompt_extension_id_.empty()) | 65 if (!in_progress_prompt_extension_id_.empty()) |
| 56 return; | 66 return; |
| 57 | 67 |
| 58 NavigationEntry* nav_entry = nav_controller->GetVisibleEntry(); | 68 NavigationEntry* nav_entry = nav_controller->GetVisibleEntry(); |
| 59 if (!nav_entry) | 69 if (!nav_entry) |
| 60 return; | 70 return; |
| 61 | 71 |
| 62 ExtensionRegistry* registry = extensions::ExtensionRegistry::Get(profile_); | 72 const GURL& url = nav_entry->GetURL(); |
| 63 const Extension* extension = | 73 // NOTE: We only consider chrome-extension:// urls, and deliberately don't |
| 64 registry->disabled_extensions().GetExtensionOrAppByURL( | 74 // consider hosted app urls. This is because it's really annoying to visit the |
| 65 nav_entry->GetURL()); | 75 // site associated with a hosted app (like calendar.google.com or |
| 76 // drive.google.com) and have it repeatedly prompt you to re-enable an item. |
| 77 // Visiting a chrome-extension:// url is a much stronger signal, and, without |
| 78 // the item enabled, we won't show anything. |
| 79 // TODO(devlin): While true, I still wonder how useful this is. We should get |
| 80 // metrics. |
| 81 if (!url.SchemeIs(kExtensionScheme)) |
| 82 return; |
| 83 |
| 84 const Extension* extension = ExtensionRegistry::Get(profile_) |
| 85 ->disabled_extensions() |
| 86 .GetExtensionOrAppByURL(url); |
| 66 if (!extension) | 87 if (!extension) |
| 67 return; | 88 return; |
| 68 | 89 |
| 69 // Try not to repeatedly prompt the user about the same extension. | 90 // Try not to repeatedly prompt the user about the same extension. |
| 70 if (prompted_extensions_.find(extension->id()) != prompted_extensions_.end()) | 91 if (!prompted_extensions_.insert(extension->id()).second && |
| 92 !g_repeat_prompting) { |
| 71 return; | 93 return; |
| 72 prompted_extensions_.insert(extension->id()); | 94 } |
| 73 | 95 |
| 74 ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(profile_); | 96 ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(profile_); |
| 97 // TODO(devlin): Why do we only consider extensions that escalate permissions? |
| 98 // Maybe because it's the only one we have a good prompt for? |
| 75 if (extension_prefs->DidExtensionEscalatePermissions(extension->id())) { | 99 if (extension_prefs->DidExtensionEscalatePermissions(extension->id())) { |
| 76 // Keep track of the extension id and nav controller we're prompting for. | 100 // Keep track of the extension id and nav controller we're prompting for. |
| 77 // These must be reset in OnInstallPromptDone. | 101 // These must be reset in OnInstallPromptDone. |
| 78 in_progress_prompt_extension_id_ = extension->id(); | 102 in_progress_prompt_extension_id_ = extension->id(); |
| 79 in_progress_prompt_navigation_controller_ = nav_controller; | 103 in_progress_prompt_navigation_controller_ = nav_controller; |
| 80 | 104 |
| 81 extension_install_prompt_.reset( | 105 extension_install_prompt_.reset( |
| 82 new ExtensionInstallPrompt(nav_controller->GetWebContents())); | 106 new ExtensionInstallPrompt(nav_controller->GetWebContents())); |
| 83 ExtensionInstallPrompt::PromptType type = | 107 ExtensionInstallPrompt::PromptType type = |
| 84 ExtensionInstallPrompt::GetReEnablePromptTypeForExtension(profile_, | 108 ExtensionInstallPrompt::GetReEnablePromptTypeForExtension(profile_, |
| (...skipping 21 matching lines...) Expand all Loading... |
| 106 | 130 |
| 107 if (result == ExtensionInstallPrompt::Result::ACCEPTED) { | 131 if (result == ExtensionInstallPrompt::Result::ACCEPTED) { |
| 108 NavigationController* nav_controller = | 132 NavigationController* nav_controller = |
| 109 in_progress_prompt_navigation_controller_; | 133 in_progress_prompt_navigation_controller_; |
| 110 CHECK(nav_controller); | 134 CHECK(nav_controller); |
| 111 | 135 |
| 112 // Grant permissions, re-enable the extension, and then reload the tab. | 136 // Grant permissions, re-enable the extension, and then reload the tab. |
| 113 extension_service->GrantPermissionsAndEnableExtension(extension); | 137 extension_service->GrantPermissionsAndEnableExtension(extension); |
| 114 nav_controller->Reload(content::ReloadType::NORMAL, true); | 138 nav_controller->Reload(content::ReloadType::NORMAL, true); |
| 115 } else { | 139 } else { |
| 140 // TODO(devlin): These metrics aren't very useful, since they're lumped in |
| 141 // with the same for re-enabling/canceling when the extension first gets |
| 142 // disabled, which is likely significantly more common (though impossible to |
| 143 // tell). We need to separate these. |
| 116 std::string histogram_name = | 144 std::string histogram_name = |
| 117 result == ExtensionInstallPrompt::Result::USER_CANCELED | 145 result == ExtensionInstallPrompt::Result::USER_CANCELED |
| 118 ? "ReEnableCancel" | 146 ? "ReEnableCancel" |
| 119 : "ReEnableAbort"; | 147 : "ReEnableAbort"; |
| 120 ExtensionService::RecordPermissionMessagesHistogram(extension, | 148 ExtensionService::RecordPermissionMessagesHistogram(extension, |
| 121 histogram_name.c_str()); | 149 histogram_name.c_str()); |
| 122 } | 150 } |
| 123 | 151 |
| 124 in_progress_prompt_extension_id_ = std::string(); | 152 in_progress_prompt_extension_id_ = std::string(); |
| 125 in_progress_prompt_navigation_controller_ = nullptr; | 153 in_progress_prompt_navigation_controller_ = nullptr; |
| 126 extension_install_prompt_.reset(); | 154 extension_install_prompt_.reset(); |
| 127 } | 155 } |
| 128 | 156 |
| 129 void NavigationObserver::OnExtensionUninstalled( | 157 void NavigationObserver::OnExtensionUninstalled( |
| 130 content::BrowserContext* browser_context, | 158 content::BrowserContext* browser_context, |
| 131 const Extension* extension, | 159 const Extension* extension, |
| 132 UninstallReason reason) { | 160 UninstallReason reason) { |
| 133 if (in_progress_prompt_extension_id_.empty() || | 161 if (in_progress_prompt_extension_id_.empty() || |
| 134 in_progress_prompt_extension_id_ != extension->id()) { | 162 in_progress_prompt_extension_id_ != extension->id()) { |
| 135 return; | 163 return; |
| 136 } | 164 } |
| 137 | 165 |
| 138 in_progress_prompt_extension_id_ = std::string(); | 166 in_progress_prompt_extension_id_ = std::string(); |
| 139 in_progress_prompt_navigation_controller_ = nullptr; | 167 in_progress_prompt_navigation_controller_ = nullptr; |
| 140 extension_install_prompt_.reset(); | 168 extension_install_prompt_.reset(); |
| 141 } | 169 } |
| 142 | 170 |
| 143 } // namespace extensions | 171 } // namespace extensions |
| OLD | NEW |