Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(109)

Side by Side Diff: chrome/browser/extensions/extension_service.cc

Issue 14973007: Auto-install/uninstall shared module dependencies for extensions. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2013 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/extension_service.h" 5 #include "chrome/browser/extensions/extension_service.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <iterator> 8 #include <iterator>
9 #include <set> 9 #include <set>
10 10
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
73 #include "chrome/browser/ui/webui/ntp/thumbnail_source.h" 73 #include "chrome/browser/ui/webui/ntp/thumbnail_source.h"
74 #include "chrome/browser/ui/webui/theme_source.h" 74 #include "chrome/browser/ui/webui/theme_source.h"
75 #include "chrome/common/child_process_logging.h" 75 #include "chrome/common/child_process_logging.h"
76 #include "chrome/common/chrome_notification_types.h" 76 #include "chrome/common/chrome_notification_types.h"
77 #include "chrome/common/chrome_paths.h" 77 #include "chrome/common/chrome_paths.h"
78 #include "chrome/common/chrome_switches.h" 78 #include "chrome/common/chrome_switches.h"
79 #include "chrome/common/chrome_version_info.h" 79 #include "chrome/common/chrome_version_info.h"
80 #include "chrome/common/extensions/api/plugins/plugins_handler.h" 80 #include "chrome/common/extensions/api/plugins/plugins_handler.h"
81 #include "chrome/common/extensions/background_info.h" 81 #include "chrome/common/extensions/background_info.h"
82 #include "chrome/common/extensions/extension.h" 82 #include "chrome/common/extensions/extension.h"
83 #include "chrome/common/extensions/extension_constants.h"
83 #include "chrome/common/extensions/extension_file_util.h" 84 #include "chrome/common/extensions/extension_file_util.h"
84 #include "chrome/common/extensions/extension_manifest_constants.h" 85 #include "chrome/common/extensions/extension_manifest_constants.h"
85 #include "chrome/common/extensions/extension_messages.h" 86 #include "chrome/common/extensions/extension_messages.h"
86 #include "chrome/common/extensions/feature_switch.h" 87 #include "chrome/common/extensions/feature_switch.h"
87 #include "chrome/common/extensions/features/feature.h" 88 #include "chrome/common/extensions/features/feature.h"
88 #include "chrome/common/extensions/incognito_handler.h" 89 #include "chrome/common/extensions/incognito_handler.h"
89 #include "chrome/common/extensions/manifest.h" 90 #include "chrome/common/extensions/manifest.h"
90 #include "chrome/common/extensions/manifest_handlers/app_isolation_info.h" 91 #include "chrome/common/extensions/manifest_handlers/app_isolation_info.h"
92 #include "chrome/common/extensions/manifest_handlers/shared_module_info.h"
91 #include "chrome/common/extensions/manifest_url_handler.h" 93 #include "chrome/common/extensions/manifest_url_handler.h"
92 #include "chrome/common/pref_names.h" 94 #include "chrome/common/pref_names.h"
93 #include "chrome/common/startup_metric_utils.h" 95 #include "chrome/common/startup_metric_utils.h"
94 #include "chrome/common/url_constants.h" 96 #include "chrome/common/url_constants.h"
95 #include "content/public/browser/browser_thread.h" 97 #include "content/public/browser/browser_thread.h"
96 #include "content/public/browser/devtools_agent_host.h" 98 #include "content/public/browser/devtools_agent_host.h"
97 #include "content/public/browser/notification_service.h" 99 #include "content/public/browser/notification_service.h"
98 #include "content/public/browser/notification_types.h" 100 #include "content/public/browser/notification_types.h"
99 #include "content/public/browser/plugin_service.h" 101 #include "content/public/browser/plugin_service.h"
100 #include "content/public/browser/render_process_host.h" 102 #include "content/public/browser/render_process_host.h"
(...skipping 22 matching lines...) Expand all
123 using content::PluginService; 125 using content::PluginService;
124 using extensions::CrxInstaller; 126 using extensions::CrxInstaller;
125 using extensions::Extension; 127 using extensions::Extension;
126 using extensions::ExtensionIdSet; 128 using extensions::ExtensionIdSet;
127 using extensions::ExtensionInfo; 129 using extensions::ExtensionInfo;
128 using extensions::FeatureSwitch; 130 using extensions::FeatureSwitch;
129 using extensions::Manifest; 131 using extensions::Manifest;
130 using extensions::PermissionMessage; 132 using extensions::PermissionMessage;
131 using extensions::PermissionMessages; 133 using extensions::PermissionMessages;
132 using extensions::PermissionSet; 134 using extensions::PermissionSet;
135 using extensions::SharedModuleInfo;
133 using extensions::UnloadedExtensionInfo; 136 using extensions::UnloadedExtensionInfo;
134 137
135 namespace errors = extension_manifest_errors; 138 namespace errors = extension_manifest_errors;
136 139
137 namespace { 140 namespace {
138 141
139 // Histogram values for logging events related to externally installed 142 // Histogram values for logging events related to externally installed
140 // extensions. 143 // extensions.
141 enum ExternalExtensionEvent { 144 enum ExternalExtensionEvent {
142 EXTERNAL_EXTENSION_INSTALLED = 0, 145 EXTERNAL_EXTENSION_INSTALLED = 0,
(...skipping 15 matching lines...) Expand all
158 const char* kNaClPluginMimeType = "application/x-nacl"; 161 const char* kNaClPluginMimeType = "application/x-nacl";
159 162
160 static bool IsSyncableExtension(const Extension& extension) { 163 static bool IsSyncableExtension(const Extension& extension) {
161 return extension.GetSyncType() == Extension::SYNC_TYPE_EXTENSION; 164 return extension.GetSyncType() == Extension::SYNC_TYPE_EXTENSION;
162 } 165 }
163 166
164 static bool IsSyncableApp(const Extension& extension) { 167 static bool IsSyncableApp(const Extension& extension) {
165 return extension.GetSyncType() == Extension::SYNC_TYPE_APP; 168 return extension.GetSyncType() == Extension::SYNC_TYPE_APP;
166 } 169 }
167 170
171 static bool IsSharedModule(const Extension& extension) {
172 return SharedModuleInfo::IsSharedModule(&extension);
173 }
174
168 } // namespace 175 } // namespace
169 176
170 ExtensionService::ExtensionRuntimeData::ExtensionRuntimeData() 177 ExtensionService::ExtensionRuntimeData::ExtensionRuntimeData()
171 : background_page_ready(false), 178 : background_page_ready(false),
172 being_upgraded(false), 179 being_upgraded(false),
173 has_used_webrequest(false) { 180 has_used_webrequest(false) {
174 } 181 }
175 182
176 ExtensionService::ExtensionRuntimeData::~ExtensionRuntimeData() { 183 ExtensionService::ExtensionRuntimeData::~ExtensionRuntimeData() {
177 } 184 }
(...skipping 687 matching lines...) Expand 10 before | Expand all | Expand 10 after
865 sync_change.sync_data().GetDataType() == syncer::APPS) { 872 sync_change.sync_data().GetDataType() == syncer::APPS) {
866 app_sync_bundle_.ProcessDeletion(extension_id, sync_change); 873 app_sync_bundle_.ProcessDeletion(extension_id, sync_change);
867 } else if (extension_sync_bundle_.HasExtensionId(extension_id) && 874 } else if (extension_sync_bundle_.HasExtensionId(extension_id) &&
868 sync_change.sync_data().GetDataType() == syncer::EXTENSIONS) { 875 sync_change.sync_data().GetDataType() == syncer::EXTENSIONS) {
869 extension_sync_bundle_.ProcessDeletion(extension_id, sync_change); 876 extension_sync_bundle_.ProcessDeletion(extension_id, sync_change);
870 } 877 }
871 878
872 delayed_updates_for_idle_.Remove(extension_id); 879 delayed_updates_for_idle_.Remove(extension_id);
873 delayed_installs_.Remove(extension_id); 880 delayed_installs_.Remove(extension_id);
874 881
882 PruneSharedModulesOnUninstall(extension);
883
875 // Track the uninstallation. 884 // Track the uninstallation.
876 UMA_HISTOGRAM_ENUMERATION("Extensions.ExtensionUninstalled", 1, 2); 885 UMA_HISTOGRAM_ENUMERATION("Extensions.ExtensionUninstalled", 1, 2);
877 886
878 return true; 887 return true;
879 } 888 }
880 889
881 bool ExtensionService::IsExtensionEnabled( 890 bool ExtensionService::IsExtensionEnabled(
882 const std::string& extension_id) const { 891 const std::string& extension_id) const {
883 if (extensions_.Contains(extension_id) || 892 if (extensions_.Contains(extension_id) ||
884 terminated_extensions_.Contains(extension_id)) { 893 terminated_extensions_.Contains(extension_id)) {
(...skipping 1136 matching lines...) Expand 10 before | Expand all | Expand 10 after
2021 content::NotificationService::NoDetails()); 2030 content::NotificationService::NoDetails());
2022 } 2031 }
2023 2032
2024 void ExtensionService::OnLoadedInstalledExtensions() { 2033 void ExtensionService::OnLoadedInstalledExtensions() {
2025 if (updater_) 2034 if (updater_)
2026 updater_->Start(); 2035 updater_->Start();
2027 2036
2028 OnBlacklistUpdated(); 2037 OnBlacklistUpdated();
2029 2038
2030 SetReadyAndNotifyListeners(); 2039 SetReadyAndNotifyListeners();
2040
2041 CheckImportsOnAllExtensions();
2031 } 2042 }
2032 2043
2033 void ExtensionService::AddExtension(const Extension* extension) { 2044 void ExtensionService::AddExtension(const Extension* extension) {
2034 // TODO(jstritar): We may be able to get rid of this branch by overriding the 2045 // TODO(jstritar): We may be able to get rid of this branch by overriding the
2035 // default extension state to DISABLED when the --disable-extensions flag 2046 // default extension state to DISABLED when the --disable-extensions flag
2036 // is set (http://crbug.com/29067). 2047 // is set (http://crbug.com/29067).
2037 if (!extensions_enabled() && 2048 if (!extensions_enabled() &&
2038 !extension->is_theme() && 2049 !extension->is_theme() &&
2039 extension->location() != Manifest::COMPONENT && 2050 extension->location() != Manifest::COMPONENT &&
2040 !Manifest::IsExternalLocation(extension->location())) { 2051 !Manifest::IsExternalLocation(extension->location())) {
(...skipping 229 matching lines...) Expand 10 before | Expand all | Expand 10 after
2270 extension_ids.insert(extension->id()); 2281 extension_ids.insert(extension->id());
2271 } 2282 }
2272 2283
2273 child_process_logging::SetActiveExtensions(extension_ids); 2284 child_process_logging::SetActiveExtensions(extension_ids);
2274 } 2285 }
2275 2286
2276 void ExtensionService::ScheduleLaunchOnLoad(const std::string& extension_id) { 2287 void ExtensionService::ScheduleLaunchOnLoad(const std::string& extension_id) {
2277 on_load_events_[extension_id] = EVENT_LAUNCHED; 2288 on_load_events_[extension_id] = EVENT_LAUNCHED;
2278 } 2289 }
2279 2290
2291 bool ExtensionService::CheckImports(const Extension* extension) {
2292 bool import_failed = false;
2293 if (SharedModuleInfo::ImportsModules(extension)) {
2294 const std::vector<SharedModuleInfo::ImportInfo>& imports =
2295 SharedModuleInfo::GetImports(extension);
2296 std::vector<SharedModuleInfo::ImportInfo>::const_iterator i;
2297 for (i = imports.begin(); i != imports.end(); ++i) {
2298 Version version_required(i->minimum_version);
2299 const Extension* imported_module =
2300 GetExtensionById(i->extension_id, true);
2301 if (!imported_module ||
2302 (version_required.IsValid() &&
2303 imported_module->version()->CompareTo(version_required) < 0)) {
2304 import_failed = true;
2305 if (extension->from_webstore()) {
2306 if (pending_extension_manager()->AddFromExtensionImport(
asargent_no_longer_on_chrome 2013/05/17 17:13:49 in the implementation of PendingExtensionManager::
2307 i->extension_id,
2308 extension_urls::GetWebstoreUpdateUrl(),
2309 IsSharedModule)) {
2310 CheckForUpdatesSoon();
2311 }
2312 }
2313 }
2314 if (imported_module &&
2315 !SharedModuleInfo::IsSharedModule(imported_module)) {
2316 import_failed = true;
2317 }
2318 }
2319 }
2320 const std::string& id = extension->id();
2321 if (import_failed) {
2322 extension_prefs_->AddDisableReason(
2323 id, Extension::DISABLE_MISSING_IMPORT);
2324 } else if (extension_prefs_->GetDisableReasons(id) ==
2325 Extension::DISABLE_MISSING_IMPORT) {
2326 EnableExtension(id);
2327 }
2328 return !import_failed;
2329 }
2330
2331 void ExtensionService::CheckImportsOnAllExtensions() {
2332 std::vector<const Extension*> extensions_to_check;
2333 for (ExtensionSet::const_iterator iter = disabled_extensions_.begin();
2334 iter != disabled_extensions_.end(); ++iter) {
2335 extensions_to_check.push_back(*iter);
2336 }
2337 for (ExtensionSet::const_iterator iter = extensions_.begin();
2338 iter != extensions_.end(); ++iter) {
2339 extensions_to_check.push_back(*iter);
2340 }
2341 std::vector<const Extension*>::const_iterator iter;
2342 for (iter = extensions_to_check.begin();
2343 iter != extensions_to_check.end();
2344 ++iter) {
2345 CheckImports(*iter);
2346 }
2347 }
2348
2349 void ExtensionService::PruneSharedModulesOnUninstall(
2350 const Extension* extension) {
2351 if (SharedModuleInfo::ImportsModules(extension)) {
2352 const std::vector<SharedModuleInfo::ImportInfo>& imports =
2353 SharedModuleInfo::GetImports(extension);
2354 std::vector<SharedModuleInfo::ImportInfo>::const_iterator i;
2355 for (i = imports.begin(); i != imports.end(); ++i) {
2356 const Extension* imported_module =
2357 GetExtensionById(i->extension_id, true);
2358 if (!imported_module)
2359 continue;
2360 if (SharedModuleInfo::IsSharedModule(imported_module)) {
2361 bool is_referenced = false;
2362 for (ExtensionSet::const_iterator iter = disabled_extensions_.begin();
2363 iter != disabled_extensions_.end(); ++iter) {
2364 if (SharedModuleInfo::ImportsExtensionById(*iter, i->extension_id)) {
2365 is_referenced = true;
2366 break;
2367 }
2368 }
2369 if (!is_referenced) {
2370 for (ExtensionSet::const_iterator iter = extensions_.begin();
2371 iter != extensions_.end(); ++iter) {
2372 if (SharedModuleInfo::ImportsExtensionById(*iter,
2373 i->extension_id)) {
2374 is_referenced = true;
2375 break;
2376 }
2377 }
2378 }
2379 if (!is_referenced) {
2380 UninstallExtension(i->extension_id, false, NULL);
2381 }
2382 }
2383 }
2384 }
2385 }
2386
2387
2280 void ExtensionService::OnExtensionInstalled( 2388 void ExtensionService::OnExtensionInstalled(
2281 const Extension* extension, 2389 const Extension* extension,
2282 const syncer::StringOrdinal& page_ordinal, 2390 const syncer::StringOrdinal& page_ordinal,
2283 bool has_requirement_errors, 2391 bool has_requirement_errors,
2284 bool wait_for_idle) { 2392 bool wait_for_idle) {
2285 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 2393 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2286 2394
2287 const std::string& id = extension->id(); 2395 const std::string& id = extension->id();
2288 bool initial_enable = ShouldEnableOnInstall(extension); 2396 bool initial_enable = ShouldEnableOnInstall(extension);
2289 const extensions::PendingExtensionInfo* pending_extension_info = NULL; 2397 const extensions::PendingExtensionInfo* pending_extension_info = NULL;
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
2330 id, Extension::DISABLE_UNSUPPORTED_REQUIREMENT); 2438 id, Extension::DISABLE_UNSUPPORTED_REQUIREMENT);
2331 // If the extension was disabled because of unsupported requirements but 2439 // If the extension was disabled because of unsupported requirements but
2332 // now supports all requirements after an update and there are not other 2440 // now supports all requirements after an update and there are not other
2333 // disable reasons, enable it. 2441 // disable reasons, enable it.
2334 } else if (extension_prefs_->GetDisableReasons(id) == 2442 } else if (extension_prefs_->GetDisableReasons(id) ==
2335 Extension::DISABLE_UNSUPPORTED_REQUIREMENT) { 2443 Extension::DISABLE_UNSUPPORTED_REQUIREMENT) {
2336 initial_enable = true; 2444 initial_enable = true;
2337 extension_prefs_->ClearDisableReasons(id); 2445 extension_prefs_->ClearDisableReasons(id);
2338 } 2446 }
2339 2447
2448 if (!CheckImports(extension)) {
2449 initial_enable = false;
2450 }
2451
2340 if (!GetInstalledExtension(extension->id())) { 2452 if (!GetInstalledExtension(extension->id())) {
2341 UMA_HISTOGRAM_ENUMERATION("Extensions.InstallType", 2453 UMA_HISTOGRAM_ENUMERATION("Extensions.InstallType",
2342 extension->GetType(), 100); 2454 extension->GetType(), 100);
2343 UMA_HISTOGRAM_ENUMERATION("Extensions.InstallSource", 2455 UMA_HISTOGRAM_ENUMERATION("Extensions.InstallSource",
2344 extension->location(), Manifest::NUM_LOCATIONS); 2456 extension->location(), Manifest::NUM_LOCATIONS);
2345 RecordPermissionMessagesHistogram( 2457 RecordPermissionMessagesHistogram(
2346 extension, "Extensions.Permissions_Install"); 2458 extension, "Extensions.Permissions_Install");
2347 } else { 2459 } else {
2348 UMA_HISTOGRAM_ENUMERATION("Extensions.UpdateType", 2460 UMA_HISTOGRAM_ENUMERATION("Extensions.UpdateType",
2349 extension->GetType(), 100); 2461 extension->GetType(), 100);
2350 UMA_HISTOGRAM_ENUMERATION("Extensions.UpdateSource", 2462 UMA_HISTOGRAM_ENUMERATION("Extensions.UpdateSource",
2351 extension->location(), Manifest::NUM_LOCATIONS); 2463 extension->location(), Manifest::NUM_LOCATIONS);
2352 } 2464 }
2353 2465
2354 // Certain extension locations are specific enough that we can 2466 // Certain extension locations are specific enough that we can
2355 // auto-acknowledge any extension that came from one of them. 2467 // auto-acknowledge any extension that came from one of them.
2356 if (extension->location() == Manifest::EXTERNAL_POLICY_DOWNLOAD) 2468 if (extension->location() == Manifest::EXTERNAL_POLICY_DOWNLOAD)
2357 AcknowledgeExternalExtension(extension->id()); 2469 AcknowledgeExternalExtension(extension->id());
2358 const Extension::State initial_state = 2470 const Extension::State initial_state =
2359 initial_enable ? Extension::ENABLED : Extension::DISABLED; 2471 initial_enable ? Extension::ENABLED : Extension::DISABLED;
2360 if (ShouldDelayExtensionUpdate(id, wait_for_idle)) { 2472 if (ShouldDelayExtensionUpdate(id, wait_for_idle)) {
Matt Perry 2013/05/16 19:17:55 We have this concept of delaying an extension upda
asargent_no_longer_on_chrome 2013/05/17 17:13:49 I endorse Matt's suggestion here.
2361 extension_prefs_->SetDelayedInstallInfo(extension, initial_state, 2473 extension_prefs_->SetDelayedInstallInfo(extension, initial_state,
2362 page_ordinal); 2474 page_ordinal);
2363 2475
2364 // Transfer ownership of |extension|. 2476 // Transfer ownership of |extension|.
2365 delayed_updates_for_idle_.Insert(extension); 2477 delayed_updates_for_idle_.Insert(extension);
2366 2478
2367 // Notify extension of available update. 2479 // Notify extension of available update.
2368 extensions::RuntimeEventRouter::DispatchOnUpdateAvailableEvent( 2480 extensions::RuntimeEventRouter::DispatchOnUpdateAvailableEvent(
2369 profile_, id, extension->manifest()->value()); 2481 profile_, id, extension->manifest()->value());
2370 2482
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after
2457 2569
2458 // If this is a new external extension that was disabled, alert the user 2570 // If this is a new external extension that was disabled, alert the user
2459 // so he can reenable it. We do this last so that it has already been 2571 // so he can reenable it. We do this last so that it has already been
2460 // added to our list of extensions. 2572 // added to our list of extensions.
2461 if (unacknowledged_external) { 2573 if (unacknowledged_external) {
2462 UpdateExternalExtensionAlert(); 2574 UpdateExternalExtensionAlert();
2463 UMA_HISTOGRAM_ENUMERATION("Extensions.ExternalExtensionEvent", 2575 UMA_HISTOGRAM_ENUMERATION("Extensions.ExternalExtensionEvent",
2464 EXTERNAL_EXTENSION_INSTALLED, 2576 EXTERNAL_EXTENSION_INSTALLED,
2465 EXTERNAL_EXTENSION_BUCKET_BOUNDARY); 2577 EXTERNAL_EXTENSION_BUCKET_BOUNDARY);
2466 } 2578 }
2579
2580 // Check extensions that may be disabled only because this shared module was
2581 // not available.
2582 if (SharedModuleInfo::IsSharedModule(extension)) {
2583 // Build a list of extensions to check because they can't be checked
2584 // within the loop iterating over disabled_extensions_ because contents
2585 // of that set may change as we CheckImports.
2586 std::vector<const Extension*> extensions_to_check;
2587 for (ExtensionSet::const_iterator iter = disabled_extensions_.begin();
2588 iter != disabled_extensions_.end(); ++iter) {
2589 if (SharedModuleInfo::ImportsExtensionById((*iter), extension->id())) {
2590 extensions_to_check.push_back(*iter);
2591 }
2592 }
2593 std::vector<const Extension*>::const_iterator iter;
2594 for (iter = extensions_to_check.begin();
2595 iter != extensions_to_check.end();
2596 ++iter) {
2597 CheckImports(*iter);
2598 }
2599 }
2467 } 2600 }
2468 2601
2469 const Extension* ExtensionService::GetPendingExtensionUpdate( 2602 const Extension* ExtensionService::GetPendingExtensionUpdate(
2470 const std::string& id) const { 2603 const std::string& id) const {
2471 return delayed_updates_for_idle_.GetByID(id); 2604 return delayed_updates_for_idle_.GetByID(id);
2472 } 2605 }
2473 2606
2474 void ExtensionService::TrackTerminatedExtension(const Extension* extension) { 2607 void ExtensionService::TrackTerminatedExtension(const Extension* extension) {
2475 if (!terminated_extensions_.Contains(extension->id())) 2608 if (!terminated_extensions_.Contains(extension->id()))
2476 terminated_extensions_.Insert(make_scoped_refptr(extension)); 2609 terminated_extensions_.Insert(make_scoped_refptr(extension));
(...skipping 630 matching lines...) Expand 10 before | Expand all | Expand 10 after
3107 } 3240 }
3108 3241
3109 void ExtensionService::AddUpdateObserver(extensions::UpdateObserver* observer) { 3242 void ExtensionService::AddUpdateObserver(extensions::UpdateObserver* observer) {
3110 update_observers_.AddObserver(observer); 3243 update_observers_.AddObserver(observer);
3111 } 3244 }
3112 3245
3113 void ExtensionService::RemoveUpdateObserver( 3246 void ExtensionService::RemoveUpdateObserver(
3114 extensions::UpdateObserver* observer) { 3247 extensions::UpdateObserver* observer) {
3115 update_observers_.RemoveObserver(observer); 3248 update_observers_.RemoveObserver(observer);
3116 } 3249 }
OLDNEW
« no previous file with comments | « chrome/browser/extensions/extension_service.h ('k') | chrome/browser/extensions/pending_extension_manager.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698