Implement Extension request throttling for network service.

This change introduces the ExtensionURLLoaderThrottle which
exists in the renderer and replaces ExtensionRequestLimitingThrottle
when the network service is both enabled and disabled.

Also re-enabled two extension throttle tests to speculatively
fix issue 836188. Hopefully now that the throttling is done in
the renderer they will no longer be flaky.

Bug: 784576,836188
Cq-Include-Trybots: luci.chromium.try:linux_mojo;master.tryserver.chromium.linux:linux_mojo
Change-Id: Ib0c18f7b3fb17f4fbe47d79bf2f5bd7f1ebd6e06
Reviewed-on: https://chromium-review.googlesource.com/914589
Reviewed-by: Devlin <[email protected]>
Reviewed-by: Matt Menke <[email protected]>
Reviewed-by: Changwan Ryu <[email protected]>
Reviewed-by: Scott Violet <[email protected]>
Cr-Commit-Position: refs/heads/master@{#577168}
diff --git a/extensions/browser/BUILD.gn b/extensions/browser/BUILD.gn
index 69a3342..a6a1539 100644
--- a/extensions/browser/BUILD.gn
+++ b/extensions/browser/BUILD.gn
@@ -169,19 +169,12 @@
     "extension_registry_factory.cc",
     "extension_registry_factory.h",
     "extension_registry_observer.h",
-    "extension_request_limiting_throttle.cc",
-    "extension_request_limiting_throttle.h",
     "extension_service_worker_message_filter.cc",
     "extension_service_worker_message_filter.h",
     "extension_system.cc",
     "extension_system.h",
     "extension_system_provider.cc",
     "extension_system_provider.h",
-    "extension_throttle_entry.cc",
-    "extension_throttle_entry.h",
-    "extension_throttle_entry_interface.h",
-    "extension_throttle_manager.cc",
-    "extension_throttle_manager.h",
     "extension_user_script_loader.cc",
     "extension_user_script_loader.h",
     "extension_util.cc",
@@ -571,10 +564,6 @@
     "extension_pref_value_map_unittest.cc",
     "extension_registrar_unittest.cc",
     "extension_registry_unittest.cc",
-    "extension_throttle_simulation_unittest.cc",
-    "extension_throttle_test_support.cc",
-    "extension_throttle_test_support.h",
-    "extension_throttle_unittest.cc",
     "file_highlighter_unittest.cc",
     "file_reader_unittest.cc",
     "image_loader_unittest.cc",
diff --git a/extensions/browser/extension_request_limiting_throttle.cc b/extensions/browser/extension_request_limiting_throttle.cc
deleted file mode 100644
index 2e21348..0000000
--- a/extensions/browser/extension_request_limiting_throttle.cc
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "extensions/browser/extension_request_limiting_throttle.h"
-
-#include "base/logging.h"
-#include "extensions/browser/extension_throttle_entry.h"
-#include "extensions/browser/extension_throttle_manager.h"
-#include "net/base/net_errors.h"
-#include "net/url_request/redirect_info.h"
-#include "net/url_request/url_request.h"
-
-namespace extensions {
-
-ExtensionRequestLimitingThrottle::ExtensionRequestLimitingThrottle(
-    const net::URLRequest* request,
-    ExtensionThrottleManager* manager)
-    : request_(request), manager_(manager) {
-  DCHECK(manager_);
-}
-
-ExtensionRequestLimitingThrottle::~ExtensionRequestLimitingThrottle() {
-}
-
-void ExtensionRequestLimitingThrottle::WillStartRequest(bool* defer) {
-  throttling_entry_ = manager_->RegisterRequestUrl(request_->url());
-  if (throttling_entry_->ShouldRejectRequest(*request_))
-    CancelWithError(net::ERR_TEMPORARILY_THROTTLED);
-}
-
-void ExtensionRequestLimitingThrottle::WillRedirectRequest(
-    const net::RedirectInfo& redirect_info,
-    bool* defer) {
-  DCHECK_EQ(manager_->GetIdFromUrl(request_->url()),
-            throttling_entry_->GetURLIdForDebugging());
-
-  throttling_entry_->UpdateWithResponse(redirect_info.status_code);
-
-  throttling_entry_ = manager_->RegisterRequestUrl(redirect_info.new_url);
-  if (throttling_entry_->ShouldRejectRequest(*request_))
-    CancelWithError(net::ERR_TEMPORARILY_THROTTLED);
-}
-
-void ExtensionRequestLimitingThrottle::WillProcessResponse(bool* defer) {
-  DCHECK_EQ(manager_->GetIdFromUrl(request_->url()),
-            throttling_entry_->GetURLIdForDebugging());
-
-  if (!request_->was_cached())
-    throttling_entry_->UpdateWithResponse(request_->GetResponseCode());
-}
-
-const char* ExtensionRequestLimitingThrottle::GetNameForLogging() const {
-  return "ExtensionRequestLimitingThrottle";
-}
-
-}  // namespace extensions
diff --git a/extensions/browser/extension_request_limiting_throttle.h b/extensions/browser/extension_request_limiting_throttle.h
deleted file mode 100644
index bd9dd4c..0000000
--- a/extensions/browser/extension_request_limiting_throttle.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef EXTENSIONS_BROWSER_EXTENSION_REQUEST_LIMITING_THROTTLE_H_
-#define EXTENSIONS_BROWSER_EXTENSION_REQUEST_LIMITING_THROTTLE_H_
-
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "content/public/browser/resource_throttle.h"
-
-namespace net {
-struct RedirectInfo;
-class URLRequest;
-}
-
-namespace extensions {
-
-class ExtensionThrottleEntryInterface;
-class ExtensionThrottleManager;
-
-// This class monitors requests issued by extensions and throttles the request
-// if there are too many requests made within a short time to urls with the same
-// scheme, host, port and path. For the exact criteria for throttling, please
-// also see extension_throttle_manager.cc.
-class ExtensionRequestLimitingThrottle : public content::ResourceThrottle {
- public:
-  ExtensionRequestLimitingThrottle(const net::URLRequest* request,
-                                   ExtensionThrottleManager* manager);
-  ~ExtensionRequestLimitingThrottle() override;
-
-  // content::ResourceThrottle implementation (called on IO thread):
-  void WillStartRequest(bool* defer) override;
-  void WillRedirectRequest(const net::RedirectInfo& redirect_info,
-                           bool* defer) override;
-  void WillProcessResponse(bool* defer) override;
-
-  const char* GetNameForLogging() const override;
-
- private:
-  const net::URLRequest* request_;
-  ExtensionThrottleManager* manager_;
-
-  // This is used to supervise traffic and enforce exponential back-off.
-  scoped_refptr<ExtensionThrottleEntryInterface> throttling_entry_;
-
-  DISALLOW_COPY_AND_ASSIGN(ExtensionRequestLimitingThrottle);
-};
-
-}  // namespace extensions
-
-#endif  // EXTENSIONS_BROWSER_EXTENSION_REQUEST_LIMITING_THROTTLE_H_
diff --git a/extensions/common/switches.cc b/extensions/common/switches.cc
index d6d4b0f..b7f529d 100644
--- a/extensions/common/switches.cc
+++ b/extensions/common/switches.cc
@@ -41,6 +41,11 @@
 // Enables extensions to hide bookmarks UI elements.
 const char kEnableOverrideBookmarksUI[] = "enable-override-bookmarks-ui";
 
+// Disable the net::URLRequestThrottlerManager functionality for
+// requests originating from extensions.
+const char kDisableExtensionsHttpThrottling[] =
+    "disable-extensions-http-throttling";
+
 // Enables tab for desktop sharing.
 const char kDisableTabForDesktopShare[] = "disable-tab-for-desktop-share";
 
@@ -72,6 +77,10 @@
 const char kPromptForExternalExtensions[] = "prompt-for-external-extensions";
 #endif
 
+// Set the parameters for ExtensionURLLoaderThrottleBrowserTest.
+const char kSetExtensionThrottleTestParams[] =
+    "set-extension-throttle-test-params";
+
 // Makes component extensions appear in chrome://settings/extensions.
 const char kShowComponentExtensionOptions[] =
     "show-component-extension-options";
diff --git a/extensions/common/switches.h b/extensions/common/switches.h
index cd01383..28eb9e2 100644
--- a/extensions/common/switches.h
+++ b/extensions/common/switches.h
@@ -14,6 +14,7 @@
 extern const char kAllowHTTPBackgroundPage[];
 extern const char kAllowLegacyExtensionManifests[];
 extern const char kDisableDesktopCaptureAudio[];
+extern const char kDisableExtensionsHttpThrottling[];
 extern const char kDisableTabForDesktopShare[];
 extern const char kEmbeddedExtensionOptions[];
 extern const char kEnableEmbeddedExtensionOptions[];
@@ -29,6 +30,7 @@
 #if defined(CHROMIUM_BUILD)
 extern const char kPromptForExternalExtensions[];
 #endif
+extern const char kSetExtensionThrottleTestParams[];
 extern const char kShowComponentExtensionOptions[];
 extern const char kTraceAppSource[];
 extern const char kWhitelistedExtensionID[];
diff --git a/extensions/renderer/BUILD.gn b/extensions/renderer/BUILD.gn
index 3bc1060..0870ec25 100644
--- a/extensions/renderer/BUILD.gn
+++ b/extensions/renderer/BUILD.gn
@@ -111,6 +111,13 @@
     "extension_js_runner.h",
     "extension_port.cc",
     "extension_port.h",
+    "extension_throttle_entry.cc",
+    "extension_throttle_entry.h",
+    "extension_throttle_entry_interface.h",
+    "extension_throttle_manager.cc",
+    "extension_throttle_manager.h",
+    "extension_url_loader_throttle.cc",
+    "extension_url_loader_throttle.h",
     "extensions_render_frame_observer.cc",
     "extensions_render_frame_observer.h",
     "extensions_renderer_client.cc",
@@ -419,6 +426,10 @@
     "bindings/event_emitter_unittest.cc",
     "bindings/exception_handler_unittest.cc",
     "event_unittest.cc",
+    "extension_throttle_simulation_unittest.cc",
+    "extension_throttle_test_support.cc",
+    "extension_throttle_test_support.h",
+    "extension_throttle_unittest.cc",
     "feature_cache_unittest.cc",
     "gc_callback_unittest.cc",
     "gin_port_unittest.cc",
diff --git a/extensions/renderer/DEPS b/extensions/renderer/DEPS
index d11f4ab..61d8e6e 100644
--- a/extensions/renderer/DEPS
+++ b/extensions/renderer/DEPS
@@ -4,6 +4,11 @@
   "+content/public/renderer",
 
   "+gin",
+  "+net/base/backoff_entry.h",
+
+  # For net::LOAD_MAYBE_USER_GESTURE. Will be removed as
+  # per crbug.com/516495
+  "+net/base/load_flags.h",
 
   "+third_party/skia/include/core",
   "+third_party/cld_3",
@@ -31,4 +36,8 @@
     # //chrome). See https://crbug.com/773004.
     "+chrome/test/base/chrome_render_view_test.h",
   ],
+  "extension_throttle_manager.cc": [
+    # For net::IsLocalhost.
+    "+net/base/url_util.h",
+  ],
 }
diff --git a/extensions/browser/extension_throttle_entry.cc b/extensions/renderer/extension_throttle_entry.cc
similarity index 94%
rename from extensions/browser/extension_throttle_entry.cc
rename to extensions/renderer/extension_throttle_entry.cc
index 877669a..3eab2ef 100644
--- a/extensions/browser/extension_throttle_entry.cc
+++ b/extensions/renderer/extension_throttle_entry.cc
@@ -2,19 +2,19 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "extensions/browser/extension_throttle_entry.h"
+#include "extensions/renderer/extension_throttle_entry.h"
 
+#include <algorithm>
 #include <cmath>
+#include <memory>
 #include <utility>
 
 #include "base/logging.h"
 #include "base/rand_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/values.h"
-#include "extensions/browser/extension_throttle_manager.h"
+#include "extensions/renderer/extension_throttle_manager.h"
 #include "net/base/load_flags.h"
-#include "net/url_request/url_request.h"
-#include "net/url_request/url_request_context.h"
 
 namespace extensions {
 
@@ -47,8 +47,7 @@
 ExtensionThrottleEntry::ExtensionThrottleEntry(
     ExtensionThrottleManager* manager,
     const std::string& url_id)
-    : ExtensionThrottleEntry(manager, url_id, false) {
-}
+    : ExtensionThrottleEntry(manager, url_id, false) {}
 
 ExtensionThrottleEntry::ExtensionThrottleEntry(
     ExtensionThrottleManager* manager,
@@ -127,11 +126,11 @@
   manager_ = NULL;
 }
 
-bool ExtensionThrottleEntry::ShouldRejectRequest(
-    const net::URLRequest& request) const {
+bool ExtensionThrottleEntry::ShouldRejectRequest(int request_load_flags) const {
   bool reject_request = false;
-  if (!is_backoff_disabled_ && (ignore_user_gesture_load_flag_for_tests_ ||
-                                !ExplicitUserRequest(request.load_flags())) &&
+  if (!is_backoff_disabled_ &&
+      (ignore_user_gesture_load_flag_for_tests_ ||
+       !ExplicitUserRequest(request_load_flags)) &&
       GetBackoffEntry()->ShouldRejectRequest()) {
     reject_request = true;
   }
@@ -209,8 +208,7 @@
   return url_id_;
 }
 
-ExtensionThrottleEntry::~ExtensionThrottleEntry() {
-}
+ExtensionThrottleEntry::~ExtensionThrottleEntry() {}
 
 void ExtensionThrottleEntry::Initialize() {
   sliding_window_release_time_ = base::TimeTicks::Now();
diff --git a/extensions/browser/extension_throttle_entry.h b/extensions/renderer/extension_throttle_entry.h
similarity index 94%
rename from extensions/browser/extension_throttle_entry.h
rename to extensions/renderer/extension_throttle_entry.h
index e54e6c6..78e80f4 100644
--- a/extensions/browser/extension_throttle_entry.h
+++ b/extensions/renderer/extension_throttle_entry.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef EXTENSIONS_BROWSER_EXTENSION_THROTTLE_ENTRY_H_
-#define EXTENSIONS_BROWSER_EXTENSION_THROTTLE_ENTRY_H_
+#ifndef EXTENSIONS_RENDERER_EXTENSION_THROTTLE_ENTRY_H_
+#define EXTENSIONS_RENDERER_EXTENSION_THROTTLE_ENTRY_H_
 
 #include <stdint.h>
 
@@ -12,7 +12,7 @@
 #include "base/containers/queue.h"
 #include "base/macros.h"
 #include "base/time/time.h"
-#include "extensions/browser/extension_throttle_entry_interface.h"
+#include "extensions/renderer/extension_throttle_entry_interface.h"
 #include "net/base/backoff_entry.h"
 
 namespace extensions {
@@ -86,7 +86,7 @@
   void DetachManager();
 
   // Implementation of ExtensionThrottleEntryInterface.
-  bool ShouldRejectRequest(const net::URLRequest& request) const override;
+  bool ShouldRejectRequest(int request_load_flags) const override;
   int64_t ReserveSendingTimeForNextRequest(
       const base::TimeTicks& earliest_time) override;
   base::TimeTicks GetExponentialBackoffReleaseTime() const override;
@@ -161,4 +161,4 @@
 
 }  // namespace extensions
 
-#endif  // EXTENSIONS_BROWSER_EXTENSION_THROTTLE_ENTRY_H_
+#endif  // EXTENSIONS_RENDERER_EXTENSION_THROTTLE_ENTRY_H_
diff --git a/extensions/browser/extension_throttle_entry_interface.h b/extensions/renderer/extension_throttle_entry_interface.h
similarity index 83%
rename from extensions/browser/extension_throttle_entry_interface.h
rename to extensions/renderer/extension_throttle_entry_interface.h
index 0b52a037..eb301295 100644
--- a/extensions/browser/extension_throttle_entry_interface.h
+++ b/extensions/renderer/extension_throttle_entry_interface.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef EXTENSIONS_BROWSER_EXTENSION_THROTTLE_ENTRY_INTERFACE_H_
-#define EXTENSIONS_BROWSER_EXTENSION_THROTTLE_ENTRY_INTERFACE_H_
+#ifndef EXTENSIONS_RENDERER_EXTENSION_THROTTLE_ENTRY_INTERFACE_H_
+#define EXTENSIONS_RENDERER_EXTENSION_THROTTLE_ENTRY_INTERFACE_H_
 
 #include <stdint.h>
 
@@ -12,11 +12,6 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/time/time.h"
-#include "net/base/net_export.h"
-
-namespace net {
-class URLRequest;
-}  // namespace net
 
 namespace extensions {
 
@@ -27,13 +22,15 @@
   ExtensionThrottleEntryInterface() {}
 
   // Returns true when we have encountered server errors and are doing
-  // exponential back-off, unless the request has load flags that mean
-  // it is likely to be user-initiated, or the NetworkDelegate returns
+  // exponential back-off, unless |request_load_flags| indicates the
+  // request is likely to be user-initiated, or the NetworkDelegate returns
   // false for |CanThrottleRequest(request)|.
   //
   // URLRequestHttpJob checks this method prior to every request; it
   // cancels requests if this method returns true.
-  virtual bool ShouldRejectRequest(const net::URLRequest& request) const = 0;
+  //
+  // Note: See load_flags.h for more detail on |request_load_flags|.
+  virtual bool ShouldRejectRequest(int request_load_flags) const = 0;
 
   // Calculates a recommended sending time for the next request and reserves it.
   // The sending time is not earlier than the current exponential back-off
@@ -75,4 +72,4 @@
 
 }  // namespace extensions
 
-#endif  // EXTENSIONS_BROWSER_EXTENSION_THROTTLE_ENTRY_INTERFACE_H_
+#endif  // EXTENSIONS_RENDERER_EXTENSION_THROTTLE_ENTRY_INTERFACE_H_
diff --git a/extensions/browser/extension_throttle_manager.cc b/extensions/renderer/extension_throttle_manager.cc
similarity index 72%
rename from extensions/browser/extension_throttle_manager.cc
rename to extensions/renderer/extension_throttle_manager.cc
index 36ab677..deba3fa 100644
--- a/extensions/browser/extension_throttle_manager.cc
+++ b/extensions/renderer/extension_throttle_manager.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "extensions/browser/extension_throttle_manager.h"
+#include "extensions/renderer/extension_throttle_manager.h"
 
 #include <utility>
 
@@ -10,13 +10,11 @@
 #include "base/metrics/field_trial.h"
 #include "base/metrics/histogram.h"
 #include "base/strings/string_util.h"
-#include "extensions/browser/extension_request_limiting_throttle.h"
 #include "extensions/common/constants.h"
+#include "extensions/renderer/extension_url_loader_throttle.h"
 #include "net/base/url_util.h"
-#include "net/log/net_log.h"
-#include "net/log/net_log_event_type.h"
-#include "net/log/net_log_source_type.h"
-#include "net/url_request/url_request.h"
+#include "third_party/blink/public/platform/web_url.h"
+#include "third_party/blink/public/platform/web_url_request.h"
 
 namespace extensions {
 
@@ -25,20 +23,15 @@
 
 ExtensionThrottleManager::ExtensionThrottleManager()
     : requests_since_last_gc_(0),
-      registered_from_thread_(base::kInvalidThreadId),
       ignore_user_gesture_load_flag_for_tests_(false) {
   url_id_replacements_.ClearPassword();
   url_id_replacements_.ClearUsername();
   url_id_replacements_.ClearQuery();
   url_id_replacements_.ClearRef();
-
-  net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
 }
 
 ExtensionThrottleManager::~ExtensionThrottleManager() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
-
+  base::AutoLock auto_lock(lock_);
   // Since the manager object might conceivably go away before the
   // entries, detach the entries' back-pointer to the manager.
   UrlEntryMap::iterator i = url_entries_.begin();
@@ -53,18 +46,17 @@
   url_entries_.clear();
 }
 
-std::unique_ptr<content::ResourceThrottle>
-ExtensionThrottleManager::MaybeCreateThrottle(const net::URLRequest* request) {
-  if (request->site_for_cookies().scheme() != extensions::kExtensionScheme) {
+std::unique_ptr<content::URLLoaderThrottle>
+ExtensionThrottleManager::MaybeCreateURLLoaderThrottle(
+    const blink::WebURLRequest& request) {
+  if (!request.SiteForCookies().ProtocolIs(extensions::kExtensionScheme))
     return nullptr;
-  }
-  return std::make_unique<extensions::ExtensionRequestLimitingThrottle>(request,
-                                                                        this);
+  return std::make_unique<ExtensionURLLoaderThrottle>(this);
 }
 
 scoped_refptr<ExtensionThrottleEntryInterface>
 ExtensionThrottleManager::RegisterRequestUrl(const GURL& url) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  // Internal function, no locking.
 
   // Normalize the url.
   std::string url_id = GetIdFromUrl(url);
@@ -108,14 +100,49 @@
   return entry;
 }
 
+bool ExtensionThrottleManager::ShouldRejectRequest(const GURL& request_url,
+                                                   int request_load_flags) {
+  base::AutoLock auto_lock(lock_);
+  return RegisterRequestUrl(request_url)
+      ->ShouldRejectRequest(request_load_flags);
+}
+
+bool ExtensionThrottleManager::ShouldRejectRedirect(
+    const GURL& request_url,
+    int request_load_flags,
+    const net::RedirectInfo& redirect_info) {
+  {
+    base::AutoLock auto_lock(lock_);
+    const std::string url_id = GetIdFromUrl(request_url);
+    scoped_refptr<ExtensionThrottleEntry>& entry = url_entries_[url_id];
+    DCHECK(entry);
+    entry->UpdateWithResponse(redirect_info.status_code);
+  }
+  return ShouldRejectRequest(redirect_info.new_url, request_load_flags);
+}
+
+void ExtensionThrottleManager::WillProcessResponse(
+    const GURL& response_url,
+    const network::ResourceResponseHead& response_head) {
+  if (response_head.network_accessed) {
+    base::AutoLock auto_lock(lock_);
+    const std::string url_id = GetIdFromUrl(response_url);
+    scoped_refptr<ExtensionThrottleEntry>& entry = url_entries_[url_id];
+    DCHECK(entry);
+    entry->UpdateWithResponse(response_head.headers->response_code());
+  }
+}
+
 void ExtensionThrottleManager::SetBackoffPolicyForTests(
     std::unique_ptr<net::BackoffEntry::Policy> policy) {
+  base::AutoLock auto_lock(lock_);
   backoff_policy_for_tests_ = std::move(policy);
 }
 
 void ExtensionThrottleManager::OverrideEntryForTests(
     const GURL& url,
     ExtensionThrottleEntry* entry) {
+  base::AutoLock auto_lock(lock_);
   // Normalize the url.
   std::string url_id = GetIdFromUrl(url);
 
@@ -126,6 +153,7 @@
 }
 
 void ExtensionThrottleManager::EraseEntryForTests(const GURL& url) {
+  base::AutoLock auto_lock(lock_);
   // Normalize the url.
   std::string url_id = GetIdFromUrl(url);
   url_entries_.erase(url_id);
@@ -133,11 +161,11 @@
 
 void ExtensionThrottleManager::SetIgnoreUserGestureLoadFlagForTests(
     bool ignore_user_gesture_load_flag_for_tests) {
+  base::AutoLock auto_lock(lock_);
   ignore_user_gesture_load_flag_for_tests_ = true;
 }
 
-void ExtensionThrottleManager::OnNetworkChanged(
-    net::NetworkChangeNotifier::ConnectionType type) {
+void ExtensionThrottleManager::SetOnline(bool is_online) {
   // When we switch from online to offline or change IP addresses, we
   // clear all back-off history. This is a precaution in case the change in
   // online state now lets us communicate without error with servers that
@@ -149,6 +177,7 @@
   // to will live until those requests end, and these entries may be
   // inconsistent with new entries for the same URLs, but since what we
   // want is a clean slate for the new connection type, this is OK.
+  base::AutoLock auto_lock(lock_);
   url_entries_.clear();
   requests_since_last_gc_ = 0;
 }
diff --git a/extensions/browser/extension_throttle_manager.h b/extensions/renderer/extension_throttle_manager.h
similarity index 67%
rename from extensions/browser/extension_throttle_manager.h
rename to extensions/renderer/extension_throttle_manager.h
index 640ca7f..d465508 100644
--- a/extensions/browser/extension_throttle_manager.h
+++ b/extensions/renderer/extension_throttle_manager.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef EXTENSIONS_BROWSER_EXTENSION_THROTTLE_MANAGER_H_
-#define EXTENSIONS_BROWSER_EXTENSION_THROTTLE_MANAGER_H_
+#ifndef EXTENSIONS_RENDERER_EXTENSION_THROTTLE_MANAGER_H_
+#define EXTENSIONS_RENDERER_EXTENSION_THROTTLE_MANAGER_H_
 
 #include <map>
 #include <memory>
@@ -11,39 +11,89 @@
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
-#include "base/sequence_checker.h"
-#include "base/threading/platform_thread.h"
-#include "extensions/browser/extension_throttle_entry.h"
-#include "net/base/backoff_entry.h"
-#include "net/base/net_export.h"
-#include "net/base/network_change_notifier.h"
+#include "base/synchronization/lock.h"
+#include "extensions/renderer/extension_throttle_entry.h"
 #include "url/gurl.h"
 
+namespace blink {
+class WebURLRequest;
+}  // namespace blink
+
 namespace content {
-class ResourceThrottle;
-}
+class URLLoaderThrottle;
+}  // namespace content
+
+namespace net {
+struct RedirectInfo;
+}  // namespace net
+
+namespace network {
+struct ResourceResponseHead;
+}  // namespace network
 
 namespace extensions {
 
-// Class that registers URL request throttler entries for URLs being accessed
-// in order to supervise traffic. URL requests for HTTP contents should
-// register their URLs in this manager on each request.
+// This is a thread safe class that registers URL request throttler entries for
+// URLs being accessed in order to supervise traffic. URL requests for HTTP
+// contents should register their URLs in this manager on each request.
 //
 // ExtensionThrottleManager maintains a map of URL IDs to URL request
 // throttler entries. It creates URL request throttler entries when new URLs
 // are registered, and does garbage collection from time to time in order to
 // clean out outdated entries. URL ID consists of lowercased scheme, host, port
 // and path. All URLs converted to the same ID will share the same entry.
-class ExtensionThrottleManager
-    : public net::NetworkChangeNotifier::NetworkChangeObserver {
+class ExtensionThrottleManager {
  public:
   ExtensionThrottleManager();
-  ~ExtensionThrottleManager() override;
+  virtual ~ExtensionThrottleManager();
 
-  // Creates a content::ResourceThrottle for |request| to prevent extensions
-  // from requesting a URL too often, if such a throttle is needed.
-  std::unique_ptr<content::ResourceThrottle> MaybeCreateThrottle(
-      const net::URLRequest* request);
+  // Creates a throttle which uses this class to prevent extensions from
+  // requesting a URL too often, if such a throttle is needed.
+  std::unique_ptr<content::URLLoaderThrottle> MaybeCreateURLLoaderThrottle(
+      const blink::WebURLRequest& request);
+
+  // Determine if a request to |request_url| with the given |request_load_flags|
+  // (see net/base/load_flags.h) should be rejected.
+  bool ShouldRejectRequest(const GURL& request_url, int request_load_flags);
+
+  // Determine if a redirect from the original |request_url| with the original
+  // |request_load_flags| (see net/base/load_flags.h) should be allowed to be
+  // redirected as specified by |redirect_info|.
+  bool ShouldRejectRedirect(const GURL& request_url,
+                            int request_load_flags,
+                            const net::RedirectInfo& redirect_info);
+
+  // Must be called when the |response_head| for a request has been received.
+  void WillProcessResponse(const GURL& response_url,
+                           const network::ResourceResponseHead& response_head);
+
+  // Set the network status online state as specified in |is_online|.
+  void SetOnline(bool is_online);
+
+  void SetBackoffPolicyForTests(
+      std::unique_ptr<net::BackoffEntry::Policy> policy);
+
+  // Registers a new entry in this service and overrides the existing entry (if
+  // any) for the URL. The service will hold a reference to the entry.
+  // It is only used by unit tests.
+  void OverrideEntryForTests(const GURL& url, ExtensionThrottleEntry* entry);
+
+  // Sets whether to ignore net::LOAD_MAYBE_USER_GESTURE of the request for
+  // testing. Otherwise, requests will not be throttled when they may have been
+  // throttled in response to a recent user gesture, though they're still
+  // counted for the purpose of throttling other requests.
+  void SetIgnoreUserGestureLoadFlagForTests(
+      bool ignore_user_gesture_load_flag_for_tests);
+
+  int GetNumberOfEntriesForTests() const { return url_entries_.size(); }
+
+ protected:
+  // Method that allows us to transform a URL into an ID that can be used in our
+  // map. Resulting IDs will be lowercase and consist of the scheme, host, port
+  // and path (without query string, fragment, etc.).
+  // If the URL is invalid, the invalid spec will be returned, without any
+  // transformation.
+  std::string GetIdFromUrl(const GURL& url) const;
 
   // TODO(xunjieli): Remove this method and replace with
   // ShouldRejectRequest(request) and UpdateWithResponse(request, status_code),
@@ -57,54 +107,25 @@
   scoped_refptr<ExtensionThrottleEntryInterface> RegisterRequestUrl(
       const GURL& url);
 
-  void SetBackoffPolicyForTests(
-      std::unique_ptr<net::BackoffEntry::Policy> policy);
+  // Method that does the actual work of garbage collecting.
+  void GarbageCollectEntries();
 
-  // Registers a new entry in this service and overrides the existing entry (if
-  // any) for the URL. The service will hold a reference to the entry.
-  // It is only used by unit tests.
-  void OverrideEntryForTests(const GURL& url, ExtensionThrottleEntry* entry);
-
+ private:
   // Explicitly erases an entry.
   // This is useful to remove those entries which have got infinite lifetime and
   // thus won't be garbage collected.
   // It is only used by unit tests.
   void EraseEntryForTests(const GURL& url);
 
-  // Sets whether to ignore net::LOAD_MAYBE_USER_GESTURE of the request for
-  // testing. Otherwise, requests will not be throttled when they may have been
-  // throttled in response to a recent user gesture, though they're still
-  // counted for the purpose of throttling other requests.
-  void SetIgnoreUserGestureLoadFlagForTests(
-      bool ignore_user_gesture_load_flag_for_tests);
-
   // Whether throttling is enabled or not.
   void set_enforce_throttling(bool enforce);
   bool enforce_throttling();
 
-  // NetworkChangeObserver interface.
-  void OnNetworkChanged(
-      net::NetworkChangeNotifier::ConnectionType type) override;
-
-  // Method that allows us to transform a URL into an ID that can be used in our
-  // map. Resulting IDs will be lowercase and consist of the scheme, host, port
-  // and path (without query string, fragment, etc.).
-  // If the URL is invalid, the invalid spec will be returned, without any
-  // transformation.
-  std::string GetIdFromUrl(const GURL& url) const;
-
   // Method that ensures the map gets cleaned from time to time. The period at
   // which garbage collecting happens is adjustable with the
   // kRequestBetweenCollecting constant.
   void GarbageCollectEntriesIfNecessary();
 
-  // Method that does the actual work of garbage collecting.
-  void GarbageCollectEntries();
-
-  // Used by tests.
-  int GetNumberOfEntriesForTests() const { return url_entries_.size(); }
-
- private:
   // From each URL we generate an ID composed of the scheme, host, port and path
   // that allows us to uniquely map an entry to it.
   typedef std::map<std::string, scoped_refptr<ExtensionThrottleEntry>>
@@ -126,19 +147,17 @@
   // Valid after construction.
   GURL::Replacements url_id_replacements_;
 
-  // Valid once we've registered for network notifications.
-  base::PlatformThreadId registered_from_thread_;
-
   bool ignore_user_gesture_load_flag_for_tests_;
 
   // This is NULL when it is not set for tests.
   std::unique_ptr<net::BackoffEntry::Policy> backoff_policy_for_tests_;
 
-  SEQUENCE_CHECKER(sequence_checker_);
+  // Used to synchronize all public methods.
+  base::Lock lock_;
 
   DISALLOW_COPY_AND_ASSIGN(ExtensionThrottleManager);
 };
 
 }  // namespace extensions
 
-#endif  // EXTENSIONS_BROWSER_EXTENSION_THROTTLE_MANAGER_H_
+#endif  // EXTENSIONS_RENDERER_EXTENSION_THROTTLE_MANAGER_H_
diff --git a/extensions/browser/extension_throttle_simulation_unittest.cc b/extensions/renderer/extension_throttle_simulation_unittest.cc
similarity index 95%
rename from extensions/browser/extension_throttle_simulation_unittest.cc
rename to extensions/renderer/extension_throttle_simulation_unittest.cc
index 55ddf9a..1eff508 100644
--- a/extensions/browser/extension_throttle_simulation_unittest.cc
+++ b/extensions/renderer/extension_throttle_simulation_unittest.cc
@@ -25,21 +25,14 @@
 #include "base/message_loop/message_loop.h"
 #include "base/rand_util.h"
 #include "base/time/time.h"
-#include "extensions/browser/extension_throttle_manager.h"
-#include "extensions/browser/extension_throttle_test_support.h"
-#include "net/base/request_priority.h"
-#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
-#include "net/url_request/url_request.h"
-#include "net/url_request/url_request_context.h"
-#include "net/url_request/url_request_test_util.h"
+#include "extensions/renderer/extension_throttle_manager.h"
+#include "extensions/renderer/extension_throttle_test_support.h"
+#include "net/base/load_flags.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using base::TimeDelta;
 using base::TimeTicks;
 using net::BackoffEntry;
-using net::TestURLRequestContext;
-using net::URLRequest;
-using net::URLRequestContext;
 
 namespace extensions {
 namespace {
@@ -131,11 +124,7 @@
         num_overloaded_ticks_remaining_(0),
         num_current_tick_queries_(0),
         num_overloaded_ticks_(0),
-        max_experienced_queries_per_tick_(0),
-        mock_request_(context_.CreateRequest(GURL(),
-                                             net::DEFAULT_PRIORITY,
-                                             nullptr,
-                                             TRAFFIC_ANNOTATION_FOR_TESTS)) {}
+        max_experienced_queries_per_tick_(0) {}
 
   void SetDowntime(const TimeTicks& start_time, const TimeDelta& duration) {
     start_downtime_ = start_time;
@@ -196,8 +185,6 @@
     return max_experienced_queries_per_tick_;
   }
 
-  const URLRequest& mock_request() const { return *mock_request_; }
-
   std::string VisualizeASCII(int terminal_width) {
     // Account for | characters we place at left of graph.
     terminal_width -= 1;
@@ -282,8 +269,6 @@
     return output;
   }
 
-  const URLRequestContext& context() const { return context_; }
-
  private:
   TimeTicks now_;
   TimeTicks start_downtime_;  // Can be 0 to say "no downtime".
@@ -296,9 +281,6 @@
   int max_experienced_queries_per_tick_;
   std::vector<int> requests_per_tick_;
 
-  TestURLRequestContext context_;
-  std::unique_ptr<URLRequest> mock_request_;
-
   DISALLOW_COPY_AND_ASSIGN(Server);
 };
 
@@ -426,7 +408,7 @@
 
     if (throttler_entry_->ImplGetTimeNow() - time_of_last_attempt_ >
         effective_delay) {
-      if (!throttler_entry_->ShouldRejectRequest(server_->mock_request())) {
+      if (!throttler_entry_->ShouldRejectRequest(net::LOAD_NORMAL)) {
         int status_code = server_->HandleRequest();
         throttler_entry_->UpdateWithResponse(status_code);
 
diff --git a/extensions/browser/extension_throttle_test_support.cc b/extensions/renderer/extension_throttle_test_support.cc
similarity index 86%
rename from extensions/browser/extension_throttle_test_support.cc
rename to extensions/renderer/extension_throttle_test_support.cc
index 75433b4..feba4a35 100644
--- a/extensions/browser/extension_throttle_test_support.cc
+++ b/extensions/renderer/extension_throttle_test_support.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "extensions/browser/extension_throttle_test_support.h"
+#include "extensions/renderer/extension_throttle_test_support.h"
 
 namespace extensions {
 
diff --git a/extensions/browser/extension_throttle_test_support.h b/extensions/renderer/extension_throttle_test_support.h
similarity index 78%
rename from extensions/browser/extension_throttle_test_support.h
rename to extensions/renderer/extension_throttle_test_support.h
index 9ab5dfe3..e7c1f9640 100644
--- a/extensions/browser/extension_throttle_test_support.h
+++ b/extensions/renderer/extension_throttle_test_support.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef EXTENSIONS_BROWSER_EXTENSION_THROTTLE_TEST_SUPPORT_H_
-#define EXTENSIONS_BROWSER_EXTENSION_THROTTLE_TEST_SUPPORT_H_
+#ifndef EXTENSIONS_RENDERER_EXTENSION_THROTTLE_TEST_SUPPORT_H_
+#define EXTENSIONS_RENDERER_EXTENSION_THROTTLE_TEST_SUPPORT_H_
 
 #include <string>
 
@@ -30,4 +30,4 @@
 
 }  // namespace extensions
 
-#endif  // EXTENSIONS_BROWSER_EXTENSION_THROTTLE_TEST_SUPPORT_H_
+#endif  // EXTENSIONS_RENDERER_EXTENSION_THROTTLE_TEST_SUPPORT_H_
diff --git a/extensions/browser/extension_throttle_unittest.cc b/extensions/renderer/extension_throttle_unittest.cc
similarity index 79%
rename from extensions/browser/extension_throttle_unittest.cc
rename to extensions/renderer/extension_throttle_unittest.cc
index 260f9aa..aa9e1aa 100644
--- a/extensions/browser/extension_throttle_unittest.cc
+++ b/extensions/renderer/extension_throttle_unittest.cc
@@ -5,32 +5,18 @@
 #include <memory>
 
 #include "base/macros.h"
-#include "base/message_loop/message_loop.h"
-#include "base/pickle.h"
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
-#include "base/strings/stringprintf.h"
 #include "base/time/time.h"
-#include "extensions/browser/extension_throttle_entry.h"
-#include "extensions/browser/extension_throttle_manager.h"
-#include "extensions/browser/extension_throttle_test_support.h"
+#include "extensions/renderer/extension_throttle_entry.h"
+#include "extensions/renderer/extension_throttle_manager.h"
+#include "extensions/renderer/extension_throttle_test_support.h"
 #include "net/base/load_flags.h"
-#include "net/base/request_priority.h"
-#include "net/base/test_completion_callback.h"
-#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
-#include "net/url_request/url_request.h"
-#include "net/url_request/url_request_context.h"
-#include "net/url_request/url_request_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using base::TimeDelta;
 using base::TimeTicks;
 using net::BackoffEntry;
-using net::NetworkChangeNotifier;
-using net::TestNetworkDelegate;
-using net::TestURLRequestContext;
-using net::URLRequest;
-using net::URLRequestContext;
 
 namespace extensions {
 
@@ -111,12 +97,18 @@
  public:
   MockExtensionThrottleManager() : create_entry_index_(0) {}
 
-  // Method to process the URL using ExtensionThrottleManager protected
-  // method.
-  std::string DoGetUrlIdFromUrl(const GURL& url) { return GetIdFromUrl(url); }
+  std::string GetIdFromUrl(const GURL& url) const {
+    return ExtensionThrottleManager::GetIdFromUrl(url);
+  }
 
-  // Method to use the garbage collecting method of ExtensionThrottleManager.
-  void DoGarbageCollectEntries() { GarbageCollectEntries(); }
+  scoped_refptr<ExtensionThrottleEntryInterface> RegisterRequestUrl(
+      const GURL& url) {
+    return ExtensionThrottleManager::RegisterRequestUrl(url);
+  }
+
+  void GarbageCollectEntries() {
+    ExtensionThrottleManager::GarbageCollectEntries();
+  }
 
   // Returns the number of entries in the map.
   int GetNumberOfEntries() const { return GetNumberOfEntriesForTests(); }
@@ -167,26 +159,16 @@
 
 class ExtensionThrottleEntryTest : public testing::Test {
  protected:
-  ExtensionThrottleEntryTest()
-      : request_(context_.CreateRequest(GURL(),
-                                        net::DEFAULT_PRIORITY,
-                                        nullptr,
-                                        TRAFFIC_ANNOTATION_FOR_TESTS)) {}
+  ExtensionThrottleEntryTest() = default;
 
   void SetUp() override;
 
   TimeTicks now_;
   MockExtensionThrottleManager manager_;  // Dummy object, not used.
   scoped_refptr<MockExtensionThrottleEntry> entry_;
-  base::MessageLoopForIO message_loop_;
-
-  TestURLRequestContext context_;
-  std::unique_ptr<URLRequest> request_;
 };
 
 void ExtensionThrottleEntryTest::SetUp() {
-  request_->SetLoadFlags(0);
-
   now_ = TimeTicks::Now();
   entry_ = new MockExtensionThrottleEntry(&manager_);
   entry_->ResetToBlank(now_);
@@ -196,25 +178,24 @@
   entry_->set_exponential_backoff_release_time(entry_->ImplGetTimeNow() +
                                                TimeDelta::FromMilliseconds(1));
 
-  EXPECT_TRUE(entry_->ShouldRejectRequest(*request_));
+  EXPECT_TRUE(entry_->ShouldRejectRequest(net::LOAD_NORMAL));
 }
 
 TEST_F(ExtensionThrottleEntryTest, InterfaceDuringExponentialBackoff) {
   entry_->set_exponential_backoff_release_time(entry_->ImplGetTimeNow() +
                                                TimeDelta::FromMilliseconds(1));
-  EXPECT_TRUE(entry_->ShouldRejectRequest(*request_));
+  EXPECT_TRUE(entry_->ShouldRejectRequest(net::LOAD_NORMAL));
 
   // Also end-to-end test the load flags exceptions.
-  request_->SetLoadFlags(net::LOAD_MAYBE_USER_GESTURE);
-  EXPECT_FALSE(entry_->ShouldRejectRequest(*request_));
+  EXPECT_FALSE(entry_->ShouldRejectRequest(net::LOAD_MAYBE_USER_GESTURE));
 }
 
 TEST_F(ExtensionThrottleEntryTest, InterfaceNotDuringExponentialBackoff) {
   entry_->set_exponential_backoff_release_time(entry_->ImplGetTimeNow());
-  EXPECT_FALSE(entry_->ShouldRejectRequest(*request_));
+  EXPECT_FALSE(entry_->ShouldRejectRequest(net::LOAD_NORMAL));
   entry_->set_exponential_backoff_release_time(entry_->ImplGetTimeNow() -
                                                TimeDelta::FromMilliseconds(1));
-  EXPECT_FALSE(entry_->ShouldRejectRequest(*request_));
+  EXPECT_FALSE(entry_->ShouldRejectRequest(net::LOAD_NORMAL));
 }
 
 TEST_F(ExtensionThrottleEntryTest, InterfaceUpdateFailure) {
@@ -324,23 +305,7 @@
       ~net::LOAD_MAYBE_USER_GESTURE));
 }
 
-class ExtensionThrottleManagerTest : public testing::Test {
- protected:
-  ExtensionThrottleManagerTest()
-      : request_(context_.CreateRequest(GURL(),
-                                        net::DEFAULT_PRIORITY,
-                                        nullptr,
-                                        TRAFFIC_ANNOTATION_FOR_TESTS)) {}
-
-  void SetUp() override { request_->SetLoadFlags(0); }
-
-  base::MessageLoopForIO message_loop_;
-  // context_ must be declared before request_.
-  TestURLRequestContext context_;
-  std::unique_ptr<URLRequest> request_;
-};
-
-TEST_F(ExtensionThrottleManagerTest, IsUrlStandardised) {
+TEST(ExtensionThrottleManagerTest, IsUrlStandardised) {
   MockExtensionThrottleManager manager;
   GurlAndString test_values[] = {
       GurlAndString(GURL("http://www.example.com"),
@@ -362,30 +327,30 @@
                     std::string("http://www.example.com:1234/"), __LINE__)};
 
   for (unsigned int i = 0; i < base::size(test_values); ++i) {
-    std::string temp = manager.DoGetUrlIdFromUrl(test_values[i].url);
-    EXPECT_EQ(temp, test_values[i].result) << "Test case #" << i << " line "
-                                           << test_values[i].line << " failed";
+    std::string temp = manager.GetIdFromUrl(test_values[i].url);
+    EXPECT_EQ(temp, test_values[i].result)
+        << "Test case #" << i << " line " << test_values[i].line << " failed";
   }
 }
 
-TEST_F(ExtensionThrottleManagerTest, AreEntriesBeingCollected) {
+TEST(ExtensionThrottleManagerTest, AreEntriesBeingCollected) {
   MockExtensionThrottleManager manager;
 
   manager.CreateEntry(true);  // true = Entry is outdated.
   manager.CreateEntry(true);
   manager.CreateEntry(true);
-  manager.DoGarbageCollectEntries();
+  manager.GarbageCollectEntries();
   EXPECT_EQ(0, manager.GetNumberOfEntries());
 
   manager.CreateEntry(false);
   manager.CreateEntry(false);
   manager.CreateEntry(false);
   manager.CreateEntry(true);
-  manager.DoGarbageCollectEntries();
+  manager.GarbageCollectEntries();
   EXPECT_EQ(3, manager.GetNumberOfEntries());
 }
 
-TEST_F(ExtensionThrottleManagerTest, IsHostBeingRegistered) {
+TEST(ExtensionThrottleManagerTest, IsHostBeingRegistered) {
   MockExtensionThrottleManager manager;
 
   manager.RegisterRequestUrl(GURL("http://www.example.com/"));
@@ -397,16 +362,16 @@
   EXPECT_EQ(3, manager.GetNumberOfEntries());
 }
 
-TEST_F(ExtensionThrottleManagerTest, LocalHostOptedOut) {
+TEST(ExtensionThrottleManagerTest, LocalHostOptedOut) {
   MockExtensionThrottleManager manager;
   // A localhost entry should always be opted out.
   scoped_refptr<ExtensionThrottleEntryInterface> localhost_entry =
       manager.RegisterRequestUrl(GURL("http://localhost/hello"));
-  EXPECT_FALSE(localhost_entry->ShouldRejectRequest((*request_)));
+  EXPECT_FALSE(localhost_entry->ShouldRejectRequest(net::LOAD_NORMAL));
   for (int i = 0; i < 10; ++i) {
     localhost_entry->UpdateWithResponse(503);
   }
-  EXPECT_FALSE(localhost_entry->ShouldRejectRequest((*request_)));
+  EXPECT_FALSE(localhost_entry->ShouldRejectRequest(net::LOAD_NORMAL));
 
   // We're not mocking out GetTimeNow() in this scenario
   // so add a 100 ms buffer to avoid flakiness (that should always
@@ -416,7 +381,7 @@
             localhost_entry->GetExponentialBackoffReleaseTime());
 }
 
-TEST_F(ExtensionThrottleManagerTest, ClearOnNetworkChange) {
+TEST(ExtensionThrottleManagerTest, ClearOnNetworkChange) {
   for (int i = 0; i < 2; ++i) {
     MockExtensionThrottleManager manager;
     scoped_refptr<ExtensionThrottleEntryInterface> entry_before =
@@ -424,14 +389,14 @@
     for (int j = 0; j < 10; ++j) {
       entry_before->UpdateWithResponse(503);
     }
-    EXPECT_TRUE(entry_before->ShouldRejectRequest(*request_));
+    EXPECT_TRUE(entry_before->ShouldRejectRequest(net::LOAD_NORMAL));
 
     switch (i) {
       case 0:
-        manager.OnNetworkChanged(NetworkChangeNotifier::CONNECTION_UNKNOWN);
+        manager.SetOnline(/*is_online=*/true);
         break;
       case 1:
-        manager.OnNetworkChanged(NetworkChangeNotifier::CONNECTION_NONE);
+        manager.SetOnline(/*is_online=*/false);
         break;
       default:
         FAIL();
@@ -439,7 +404,7 @@
 
     scoped_refptr<ExtensionThrottleEntryInterface> entry_after =
         manager.RegisterRequestUrl(GURL("http://www.example.com/"));
-    EXPECT_FALSE(entry_after->ShouldRejectRequest(*request_));
+    EXPECT_FALSE(entry_after->ShouldRejectRequest(net::LOAD_NORMAL));
   }
 }
 
diff --git a/extensions/renderer/extension_url_loader_throttle.cc b/extensions/renderer/extension_url_loader_throttle.cc
new file mode 100644
index 0000000..cd8ec20
--- /dev/null
+++ b/extensions/renderer/extension_url_loader_throttle.cc
@@ -0,0 +1,53 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/renderer/extension_url_loader_throttle.h"
+
+#include "extensions/renderer/extension_throttle_manager.h"
+
+namespace extensions {
+
+namespace {
+
+const char kCancelReason[] = "ExtensionURLLoaderThrottle";
+
+}  // anonymous namespace
+
+ExtensionURLLoaderThrottle::ExtensionURLLoaderThrottle(
+    ExtensionThrottleManager* manager)
+    : manager_(manager) {
+  DCHECK(manager_);
+}
+
+ExtensionURLLoaderThrottle::~ExtensionURLLoaderThrottle() = default;
+
+void ExtensionURLLoaderThrottle::WillStartRequest(
+    network::ResourceRequest* request,
+    bool* defer) {
+  start_request_url_ = request->url;
+  request_load_flags_ = request->load_flags;
+  if (manager_->ShouldRejectRequest(start_request_url_, request_load_flags_))
+    delegate_->CancelWithError(net::ERR_TEMPORARILY_THROTTLED, kCancelReason);
+}
+
+void ExtensionURLLoaderThrottle::WillRedirectRequest(
+    const net::RedirectInfo& redirect_info,
+    const network::ResourceResponseHead& response_head,
+    bool* defer,
+    std::vector<std::string>* to_be_removed_request_headers) {
+  if (manager_->ShouldRejectRedirect(start_request_url_, request_load_flags_,
+                                     redirect_info))
+    delegate_->CancelWithError(net::ERR_TEMPORARILY_THROTTLED, kCancelReason);
+}
+
+void ExtensionURLLoaderThrottle::WillProcessResponse(
+    const GURL& response_url,
+    const network::ResourceResponseHead& response_head,
+    bool* defer) {
+  manager_->WillProcessResponse(response_url, response_head);
+}
+
+void ExtensionURLLoaderThrottle::DetachFromCurrentSequence() {}
+
+}  // namespace extensions
diff --git a/extensions/renderer/extension_url_loader_throttle.h b/extensions/renderer/extension_url_loader_throttle.h
new file mode 100644
index 0000000..d8e6330
--- /dev/null
+++ b/extensions/renderer/extension_url_loader_throttle.h
@@ -0,0 +1,53 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXTENSIONS_RENDERER_EXTENSION_URL_LOADER_THROTTLE_H_
+#define EXTENSIONS_RENDERER_EXTENSION_URL_LOADER_THROTTLE_H_
+
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "content/public/common/url_loader_throttle.h"
+
+namespace extensions {
+
+class ExtensionThrottleManager;
+
+// This class monitors requests issued by extensions and throttles the request
+// if there are too many requests made within a short time to urls with the same
+// scheme, host, port and path. For the exact criteria for throttling, please
+// also see extension_throttle_manager.cc.
+class ExtensionURLLoaderThrottle : public content::URLLoaderThrottle {
+ public:
+  explicit ExtensionURLLoaderThrottle(ExtensionThrottleManager* manager);
+
+  ~ExtensionURLLoaderThrottle() override;
+
+  // content::URLLoaderThrottle:
+  void WillStartRequest(network::ResourceRequest* request,
+                        bool* defer) override;
+  void WillRedirectRequest(
+      const net::RedirectInfo& redirect_info,
+      const network::ResourceResponseHead& response_head,
+      bool* defer,
+      std::vector<std::string>* to_be_removed_request_headers) override;
+  void WillProcessResponse(const GURL& response_url,
+                           const network::ResourceResponseHead& response_head,
+                           bool* defer) override;
+
+ private:
+  // content::URLLoaderThrottle:
+  void DetachFromCurrentSequence() override;
+
+  ExtensionThrottleManager* manager_ = nullptr;
+  int request_load_flags_ = 0;
+  GURL start_request_url_;
+
+  DISALLOW_COPY_AND_ASSIGN(ExtensionURLLoaderThrottle);
+};
+
+}  // namespace extensions
+
+#endif  // EXTENSIONS_RENDERER_EXTENSION_URL_LOADER_THROTTLE_H_