Create a URLRequestJobFactory to replace the URLRequest globals.

URLRequest::Interceptor and URLRequest::ProtocolFactory are globally registered. This causes a variety of problems. This provides a method for replacing them.

It used to be the case that we used net::URLRequest::IsHandledProtocol()/net::URLRequest::IsHandledURL() to see if the request would be handled by Chrome, or deferred to an external protocol handler. This required that URLRequest be aware of all protocol handlers.

We instead provide ProfileIOData::IsHandledProtocol(), which checks to see if there are any Chrome registered protocol handlers, and if not, checks the default ones in net::URLRequest.

Note this doesn't work for custom handlers (registerProtocolHandler) because they are dynamic and profile-specific. We would have to add a member function to ProfileIOData and not use a global. This is problematic since we check ProfileIOData::IsHandledProtocol in the RenderViewContextMenu, which runs on the UI thread, whereas ProfileIOData lives on the IO thread. RenderViewContextMenu is using also using it synchronously, which makes it a pain to support.

So, we don't support custom handlers in ProfileIOData::IsHandledProtocol(). This means that "save as" won't work for custom handlers. Seems ok for now.

This also fixes the multiprofile/incognito bugs where if a profile registers a custom handler, and then a different profile / an incognito profile registers the same custom handler and then unregisters it, which globally unregisters it, so the original profile is now broken. By removing the use of the globals, we fix this.

Also fixes a bunch of style guide violations in the ProtocolHandler/ProtocolHandlerRegistry code.

This changelist replaces two existing URLRequest::ProtocolFactory uses: chrome-extension/user-script and custom handlers.

Also improve the tests in ResourceDispatcherHost so we don't have to do as many NULL checks. Change the MockResourceContext to create a TestURLRequestContext.

BUG=81979
TEST=none


Review URL: http://codereview.chromium.org/6960006

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@85376 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/autocomplete/autocomplete.cc b/chrome/browser/autocomplete/autocomplete.cc
index 2de0cfea..5d241dc 100644
--- a/chrome/browser/autocomplete/autocomplete.cc
+++ b/chrome/browser/autocomplete/autocomplete.cc
@@ -27,6 +27,7 @@
 #include "chrome/browser/net/url_fixer_upper.h"
 #include "chrome/browser/prefs/pref_service.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_io_data.h"
 #include "chrome/browser/ui/webui/history_ui.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
@@ -159,7 +160,7 @@
       !LowerCaseEqualsASCII(parsed_scheme, chrome::kHttpScheme) &&
       !LowerCaseEqualsASCII(parsed_scheme, chrome::kHttpsScheme)) {
     // See if we know how to handle the URL internally.
-    if (net::URLRequest::IsHandledProtocol(UTF16ToASCII(parsed_scheme)))
+    if (ProfileIOData::IsHandledProtocol(UTF16ToASCII(parsed_scheme)))
       return URL;
 
     // There are also some schemes that we convert to other things before they
diff --git a/chrome/browser/browser_main.cc b/chrome/browser/browser_main.cc
index f80a813..f71ab40 100644
--- a/chrome/browser/browser_main.cc
+++ b/chrome/browser/browser_main.cc
@@ -1690,10 +1690,7 @@
   // Configure modules that need access to resources.
   net::NetModule::SetResourceProvider(chrome_common_net::NetResourceProvider);
 
-  // Register our global network handler for chrome:// and
-  // chrome-extension:// URLs.
   ChromeURLDataManagerBackend::Register();
-  RegisterExtensionProtocols();
   RegisterMetadataURLRequestHandler();
   RegisterBlobURLRequestJobFactory();
   RegisterFileSystemURLRequestJobFactory();
diff --git a/chrome/browser/custom_handlers/protocol_handler.cc b/chrome/browser/custom_handlers/protocol_handler.cc
index e9ed3cb..1b2bec1 100644
--- a/chrome/browser/custom_handlers/protocol_handler.cc
+++ b/chrome/browser/custom_handlers/protocol_handler.cc
@@ -11,34 +11,36 @@
 ProtocolHandler::ProtocolHandler(const std::string& protocol,
                                  const GURL& url,
                                  const string16& title)
-  : protocol_(protocol),
-    url_(url),
-    title_(title) {
+    : protocol_(protocol),
+      url_(url),
+      title_(title) {
 }
 
 ProtocolHandler ProtocolHandler::CreateProtocolHandler(
     const std::string& protocol,
     const GURL& url,
     const string16& title) {
-  std::string lower_protocol(protocol);
-  lower_protocol = StringToLowerASCII(protocol);
+  std::string lower_protocol = StringToLowerASCII(protocol);
   return ProtocolHandler(lower_protocol, url, title);
 }
 
 ProtocolHandler::ProtocolHandler() {
 }
 
-const ProtocolHandler ProtocolHandler::kEmpty;
-
 bool ProtocolHandler::IsValidDict(const DictionaryValue* value) {
   return value->HasKey("protocol") && value->HasKey("url") &&
     value->HasKey("title");
 }
 
+const ProtocolHandler& ProtocolHandler::EmptyProtocolHandler() {
+  static const ProtocolHandler* const kEmpty = new ProtocolHandler();
+  return *kEmpty;
+}
+
 ProtocolHandler ProtocolHandler::CreateProtocolHandler(
     const DictionaryValue* value) {
   if (!IsValidDict(value)) {
-    return kEmpty;
+    return EmptyProtocolHandler();
   }
   std::string protocol, url;
   string16 title;
@@ -48,14 +50,14 @@
   return ProtocolHandler::CreateProtocolHandler(protocol, GURL(url), title);
 }
 
-GURL ProtocolHandler::TranslateUrl(const GURL& url) {
+GURL ProtocolHandler::TranslateUrl(const GURL& url) const {
   std::string translatedUrlSpec(url_.spec());
   ReplaceSubstringsAfterOffset(&translatedUrlSpec, 0, "%s",
       EscapeQueryParamValue(url.spec(), true));
   return GURL(translatedUrlSpec);
 }
 
-DictionaryValue* ProtocolHandler::Encode() {
+DictionaryValue* ProtocolHandler::Encode() const {
   DictionaryValue* d = new DictionaryValue();
   d->Set("protocol", Value::CreateStringValue(protocol_));
   d->Set("url", Value::CreateStringValue(url_.spec()));
@@ -63,9 +65,8 @@
   return d;
 }
 
-bool ProtocolHandler::operator==(const ProtocolHandler &other) const {
+bool ProtocolHandler::operator==(const ProtocolHandler& other) const {
   return protocol_ == other.protocol_ &&
     url_ == other.url_ &&
     title_ == other.title_;
 }
-
diff --git a/chrome/browser/custom_handlers/protocol_handler.h b/chrome/browser/custom_handlers/protocol_handler.h
index 36387b5..e145222 100644
--- a/chrome/browser/custom_handlers/protocol_handler.h
+++ b/chrome/browser/custom_handlers/protocol_handler.h
@@ -29,23 +29,21 @@
   static bool IsValidDict(const DictionaryValue* value);
 
   // Canonical empty ProtocolHandler.
-  static const ProtocolHandler kEmpty;
-
-  explicit ProtocolHandler();
+  static const ProtocolHandler& EmptyProtocolHandler();
 
   // Interpolates the given URL into the URL template of this handler.
-  GURL TranslateUrl(const GURL& url);
+  GURL TranslateUrl(const GURL& url) const;
 
   // Encodes this protocol handler as a DictionaryValue. The caller is
   // responsible for deleting the returned value.
-  DictionaryValue* Encode();
+  DictionaryValue* Encode() const;
 
-  std::string protocol() const { return protocol_; }
-  GURL url() const { return url_;}
-  string16 title() const { return title_; }
+  const std::string& protocol() const { return protocol_; }
+  const GURL& url() const { return url_;}
+  const string16& title() const { return title_; }
 
   bool IsEmpty() const {
-    return protocol_ == "";
+    return protocol_.empty();
   }
 
   bool operator==(const ProtocolHandler &other) const;
@@ -54,10 +52,11 @@
   ProtocolHandler(const std::string& protocol,
                   const GURL& url,
                   const string16& title);
+  ProtocolHandler();
+
   std::string protocol_;
   GURL url_;
   string16 title_;
 };
 
 #endif  // CHROME_BROWSER_CUSTOM_HANDLERS_PROTOCOL_HANDLER_H_
-
diff --git a/chrome/browser/custom_handlers/protocol_handler_registry.cc b/chrome/browser/custom_handlers/protocol_handler_registry.cc
index 7edb280..b4991e2 100644
--- a/chrome/browser/custom_handlers/protocol_handler_registry.cc
+++ b/chrome/browser/custom_handlers/protocol_handler_registry.cc
@@ -4,11 +4,14 @@
 
 #include "chrome/browser/custom_handlers/protocol_handler_registry.h"
 
+#include <utility>
+
 #include "base/memory/scoped_ptr.h"
 #include "chrome/browser/custom_handlers/protocol_handler.h"
 #include "chrome/browser/custom_handlers/register_protocol_handler_infobar_delegate.h"
 #include "chrome/browser/net/chrome_url_request_context.h"
 #include "chrome/browser/prefs/pref_service.h"
+#include "chrome/browser/profiles/profile_io_data.h"
 #include "chrome/common/pref_names.h"
 #include "content/browser/child_process_security_policy.h"
 #include "net/base/network_delegate.h"
@@ -19,16 +22,16 @@
 
 ProtocolHandlerRegistry::ProtocolHandlerRegistry(Profile* profile,
     Delegate* delegate)
-  : profile_(profile),
-    delegate_(delegate),
-    enabled_(true) {
+    : profile_(profile),
+      delegate_(delegate),
+      enabled_(true) {
 }
 
 ProtocolHandlerRegistry::~ProtocolHandlerRegistry() {
 }
 
-ProtocolHandlerList& ProtocolHandlerRegistry::GetHandlerListFor(
-    const std::string& scheme) {
+ProtocolHandlerRegistry::ProtocolHandlerList&
+ProtocolHandlerRegistry::GetHandlerListFor(const std::string& scheme) {
   ProtocolHandlerMultiMap::iterator p = protocol_handlers_.find(scheme);
   if (p == protocol_handlers_.end()) {
     protocol_handlers_[scheme] = ProtocolHandlerList();
@@ -60,7 +63,7 @@
   }
   enabled_ = true;
   for (ProtocolHandlerMultiMap::const_iterator p = protocol_handlers_.begin();
-      p != protocol_handlers_.end(); p++) {
+       p != protocol_handlers_.end(); ++p) {
     delegate_->RegisterExternalHandler(p->first);
   }
 }
@@ -71,13 +74,13 @@
   }
   enabled_ = false;
   for (ProtocolHandlerMultiMap::const_iterator p = protocol_handlers_.begin();
-      p != protocol_handlers_.end(); p++) {
+       p != protocol_handlers_.end(); ++p) {
     delegate_->DeregisterExternalHandler(p->first);
   }
 }
 
 std::vector<const DictionaryValue*>
-ProtocolHandlerRegistry::GetHandlersFromPref(const char* pref_name) {
+ProtocolHandlerRegistry::GetHandlersFromPref(const char* pref_name) const {
   std::vector<const DictionaryValue*> result;
   PrefService* prefs = profile_->GetPrefs();
   if (!prefs->HasPrefPath(pref_name)) {
@@ -86,7 +89,7 @@
 
   const ListValue* handlers = prefs->GetList(pref_name);
   if (handlers) {
-    for (size_t i = 0; i < handlers->GetSize(); i++) {
+    for (size_t i = 0; i < handlers->GetSize(); ++i) {
       DictionaryValue* dict;
       handlers->GetDictionary(i, &dict);
       if (ProtocolHandler::IsValidDict(dict)) {
@@ -107,7 +110,7 @@
     GetHandlersFromPref(prefs::kRegisteredProtocolHandlers);
   for (std::vector<const DictionaryValue*>::const_iterator p =
       registered_handlers.begin();
-      p != registered_handlers.end(); p++) {
+      p != registered_handlers.end(); ++p) {
     ProtocolHandler handler = ProtocolHandler::CreateProtocolHandler(*p);
     RegisterProtocolHandler(handler);
     bool is_default = false;
@@ -118,8 +121,8 @@
   std::vector<const DictionaryValue*> ignored_handlers =
     GetHandlersFromPref(prefs::kIgnoredProtocolHandlers);
   for (std::vector<const DictionaryValue*>::const_iterator p =
-      ignored_handlers.begin();
-      p != ignored_handlers.end(); p++) {
+       ignored_handlers.begin();
+       p != ignored_handlers.end(); ++p) {
     IgnoreProtocolHandler(ProtocolHandler::CreateProtocolHandler(*p));
   }
   is_loading_ = false;
@@ -157,7 +160,7 @@
 void ProtocolHandlerRegistry::GetHandledProtocols(
     std::vector<std::string>* output) const {
   ProtocolHandlerMultiMap::const_iterator p;
-  for (p = protocol_handlers_.begin(); p != protocol_handlers_.end(); p++) {
+  for (p = protocol_handlers_.begin(); p != protocol_handlers_.end(); ++p) {
     output->push_back(p->first);
   }
 }
@@ -165,7 +168,7 @@
 void ProtocolHandlerRegistry::RemoveIgnoredHandler(
     const ProtocolHandler& handler) {
   for (ProtocolHandlerList::iterator p = ignored_protocol_handlers_.begin();
-      p != ignored_protocol_handlers_.end(); p++) {
+       p != ignored_protocol_handlers_.end(); ++p) {
     if (handler == *p) {
       ignored_protocol_handlers_.erase(p);
       break;
@@ -181,7 +184,7 @@
 bool ProtocolHandlerRegistry::IsIgnored(const ProtocolHandler& handler) const {
   ProtocolHandlerList::const_iterator i;
   for (i = ignored_protocol_handlers_.begin();
-      i != ignored_protocol_handlers_.end(); i++) {
+      i != ignored_protocol_handlers_.end(); ++i) {
     if (*i == handler) {
       return true;
     }
@@ -202,17 +205,12 @@
     handlers.erase(p);
   }
 
-  if (default_handlers_[handler.protocol()] == handler) {
-    default_handlers_.erase(handler.protocol());
+  ProtocolHandlerMap::iterator it = default_handlers_.find(handler.protocol());
+  if (it != default_handlers_.end() && it->second == handler) {
+    default_handlers_.erase(it);
   }
 }
 
-net::URLRequestJob* ProtocolHandlerRegistry::Factory(net::URLRequest* request,
-    const std::string& scheme) {
-  return request->context()->network_delegate()->MaybeCreateURLRequestJob(
-      request);
-}
-
 net::URLRequestJob* ProtocolHandlerRegistry::MaybeCreateJob(
     net::URLRequest* request) const {
   ProtocolHandler handler = GetHandlerFor(request->url().scheme());
@@ -234,9 +232,9 @@
   ListValue* protocol_handlers = new ListValue();
 
   for (ProtocolHandlerMultiMap::iterator i = protocol_handlers_.begin();
-      i != protocol_handlers_.end(); ++i) {
+       i != protocol_handlers_.end(); ++i) {
     for (ProtocolHandlerList::iterator j = i->second.begin();
-        j != i->second.end(); j++) {
+         j != i->second.end(); ++j) {
       DictionaryValue* encoded = j->Encode();
       if (IsDefault(*j)) {
         encoded->Set("default", Value::CreateBooleanValue(true));
@@ -251,7 +249,7 @@
   ListValue* handlers = new ListValue();
 
   for (ProtocolHandlerList::iterator i = ignored_protocol_handlers_.begin();
-      i != ignored_protocol_handlers_.end(); ++i) {
+       i != ignored_protocol_handlers_.end(); ++i) {
     handlers->Append(i->Encode());
   }
   return handlers;
@@ -273,17 +271,18 @@
   Save();
 }
 
-void ProtocolHandlerRegistry::RegisterPrefs(PrefService* prefService) {
-  prefService->RegisterListPref(prefs::kRegisteredProtocolHandlers,
-                                PrefService::UNSYNCABLE_PREF);
-  prefService->RegisterListPref(prefs::kIgnoredProtocolHandlers,
-                                PrefService::UNSYNCABLE_PREF);
-  prefService->RegisterBooleanPref(prefs::kCustomHandlersEnabled, true,
-                                   PrefService::UNSYNCABLE_PREF);
+void ProtocolHandlerRegistry::RegisterPrefs(PrefService* pref_service) {
+  pref_service->RegisterListPref(prefs::kRegisteredProtocolHandlers,
+                                 PrefService::UNSYNCABLE_PREF);
+  pref_service->RegisterListPref(prefs::kIgnoredProtocolHandlers,
+                                 PrefService::UNSYNCABLE_PREF);
+  pref_service->RegisterBooleanPref(prefs::kCustomHandlersEnabled, true,
+                                    PrefService::UNSYNCABLE_PREF);
 }
 
 void ProtocolHandlerRegistry::SetDefault(const ProtocolHandler& handler) {
-  default_handlers_[handler.protocol()] = handler;
+  default_handlers_.erase(handler.protocol());
+  default_handlers_.insert(std::make_pair(handler.protocol(), handler));
   Save();
 }
 
@@ -307,7 +306,7 @@
   if (q != protocol_handlers_.end() && q->second.size() == 1) {
     return q->second[0];
   }
-  return ProtocolHandler::kEmpty;
+  return ProtocolHandler::EmptyProtocolHandler();
 }
 
 bool ProtocolHandlerRegistry::HasDefault(
@@ -331,16 +330,13 @@
   if (!policy->IsWebSafeScheme(protocol)) {
     policy->RegisterWebSafeScheme(protocol);
   }
-  net::URLRequest::RegisterProtocolFactory(protocol,
-      &ProtocolHandlerRegistry::Factory);
 }
 
 void ProtocolHandlerRegistry::Delegate::DeregisterExternalHandler(
     const std::string& protocol) {
-  net::URLRequest::RegisterProtocolFactory(protocol, NULL);
 }
 
 bool ProtocolHandlerRegistry::Delegate::IsExternalHandlerRegistered(
     const std::string& protocol) {
-  return net::URLRequest::IsHandledProtocol(protocol);
+  return ProfileIOData::IsHandledProtocol(protocol);
 }
diff --git a/chrome/browser/custom_handlers/protocol_handler_registry.h b/chrome/browser/custom_handlers/protocol_handler_registry.h
index 8ce29eed..eaeb0c6 100644
--- a/chrome/browser/custom_handlers/protocol_handler_registry.h
+++ b/chrome/browser/custom_handlers/protocol_handler_registry.h
@@ -18,10 +18,6 @@
 #include "net/url_request/url_request.h"
 #include "net/url_request/url_request_job.h"
 
-typedef std::map<std::string, ProtocolHandler> ProtocolHandlerMap;
-typedef std::vector<ProtocolHandler> ProtocolHandlerList;
-typedef std::map<std::string, ProtocolHandlerList> ProtocolHandlerMultiMap;
-
 // This is where handlers for protocols registered with
 // navigator.registerProtocolHandler() are registered. Each Profile owns an
 // instance of this class, which is initialized on browser start through
@@ -31,7 +27,16 @@
 class ProtocolHandlerRegistry
     : public base::RefCountedThreadSafe<ProtocolHandlerRegistry> {
  public:
-  class Delegate;
+  // TODO(koz): Refactor this to eliminate the unnecessary virtuals. All that
+  // should be needed is a way to ensure that the list of websafe protocols is
+  // updated.
+  class Delegate {
+   public:
+    virtual ~Delegate();
+    virtual void RegisterExternalHandler(const std::string& protocol);
+    virtual void DeregisterExternalHandler(const std::string& protocol);
+    virtual bool IsExternalHandlerRegistered(const std::string& protocol);
+  };
 
   ProtocolHandlerRegistry(Profile* profile, Delegate* delegate);
   ~ProtocolHandlerRegistry();
@@ -94,11 +99,6 @@
   // Causes the given protocol handler to not be ignored anymore.
   void RemoveIgnoredHandler(const ProtocolHandler& handler);
 
-  // URLRequestFactory for use with URLRequest::RegisterProtocolFactory().
-  // Redirects any URLRequests for which there is a matching protocol handler.
-  static net::URLRequestJob* Factory(net::URLRequest* request,
-                                     const std::string& scheme);
-
   // Registers the preferences that we store registered protocol handlers in.
   static void RegisterPrefs(PrefService* prefService);
 
@@ -114,20 +114,15 @@
   // will not handle requests.
   void Disable();
 
-  bool enabled() { return enabled_; }
-
-  class Delegate {
-   public:
-    virtual ~Delegate();
-    virtual void RegisterExternalHandler(const std::string& protocol);
-    virtual void DeregisterExternalHandler(const std::string& protocol);
-    virtual bool IsExternalHandlerRegistered(const std::string& protocol);
-  };
+  bool enabled() const { return enabled_; }
 
  private:
-
   friend class base::RefCountedThreadSafe<ProtocolHandlerRegistry>;
 
+  typedef std::map<std::string, ProtocolHandler> ProtocolHandlerMap;
+  typedef std::vector<ProtocolHandler> ProtocolHandlerList;
+  typedef std::map<std::string, ProtocolHandlerList> ProtocolHandlerMultiMap;
+
   // Returns a JSON list of protocol handlers. The caller is responsible for
   // deleting this Value.
   Value* EncodeRegisteredHandlers();
@@ -142,7 +137,7 @@
   // Get the DictionaryValues stored under the given pref name that are valid
   // ProtocolHandler values.
   std::vector<const DictionaryValue*> GetHandlersFromPref(
-      const char* pref_name);
+      const char* pref_name) const;
 
   // Ignores future requests to register the given protocol handler.
   void IgnoreProtocolHandler(const ProtocolHandler& handler);
@@ -180,4 +175,3 @@
   DISALLOW_COPY_AND_ASSIGN(ProtocolHandlerRegistry);
 };
 #endif  // CHROME_BROWSER_CUSTOM_HANDLERS_PROTOCOL_HANDLER_REGISTRY_H_
-
diff --git a/chrome/browser/custom_handlers/protocol_handler_registry_unittest.cc b/chrome/browser/custom_handlers/protocol_handler_registry_unittest.cc
index 982d4517..5613be7 100644
--- a/chrome/browser/custom_handlers/protocol_handler_registry_unittest.cc
+++ b/chrome/browser/custom_handlers/protocol_handler_registry_unittest.cc
@@ -42,7 +42,10 @@
 
 
 class ProtocolHandlerRegistryTest : public testing::Test {
- public:
+ protected:
+  ProtocolHandlerRegistryTest()
+      : test_protocol_handler_(CreateProtocolHandler("test", "test")) {}
+
   FakeDelegate* delegate() const { return delegate_; }
   TestingProfile* profile() const { return profile_.get(); }
   PrefService* pref_service() const { return profile_->GetPrefs(); }
@@ -70,9 +73,7 @@
     registry_->Load();
   }
 
- private:
   virtual void SetUp() {
-    test_protocol_handler_ = CreateProtocolHandler("test", "test");
     profile_.reset(new TestingProfile());
     profile_->SetPrefService(new TestingPrefService());
     delegate_ = new FakeDelegate();
diff --git a/chrome/browser/extensions/extension_protocols.cc b/chrome/browser/extensions/extension_protocols.cc
index 10bc072..d7eac2f8 100644
--- a/chrome/browser/extensions/extension_protocols.cc
+++ b/chrome/browser/extensions/extension_protocols.cc
@@ -119,8 +119,8 @@
 // TODO(aa): This should be moved into ExtensionResourceRequestPolicy, but we
 // first need to find a way to get CanLoadInIncognito state into the renderers.
 bool AllowExtensionResourceLoad(net::URLRequest* request,
-                                ChromeURLRequestContext* context,
-                                const std::string& scheme) {
+                                bool is_incognito,
+                                ExtensionInfoMap* extension_info_map) {
   const ResourceDispatcherHostRequestInfo* info =
       ResourceDispatcherHost::InfoForRequest(request);
 
@@ -135,10 +135,9 @@
   // Don't allow toplevel navigations to extension resources in incognito mode.
   // This is because an extension must run in a single process, and an
   // incognito tab prevents that.
-  if (context->is_incognito() &&
+  if (is_incognito &&
       info->resource_type() == ResourceType::MAIN_FRAME &&
-      !context->extension_info_map()->
-          ExtensionCanLoadInIncognito(request->url().host())) {
+      !extension_info_map->ExtensionCanLoadInIncognito(request->url().host())) {
     LOG(ERROR) << "Denying load of " << request->url().spec() << " from "
                << "incognito tab.";
     return false;
@@ -147,29 +146,42 @@
   return true;
 }
 
-}  // namespace
+class ExtensionProtocolHandler
+    : public net::URLRequestJobFactory::ProtocolHandler {
+ public:
+  ExtensionProtocolHandler(bool is_incognito,
+                           ExtensionInfoMap* extension_info_map)
+      : is_incognito_(is_incognito),
+        extension_info_map_(extension_info_map) {}
 
-// Factory registered with net::URLRequest to create URLRequestJobs for
-// extension:// URLs.
-static net::URLRequestJob* CreateExtensionURLRequestJob(
-    net::URLRequest* request,
-    const std::string& scheme) {
-  ChromeURLRequestContext* context =
-      static_cast<ChromeURLRequestContext*>(request->context());
+  virtual ~ExtensionProtocolHandler() {}
 
+  virtual net::URLRequestJob* MaybeCreateJob(
+      net::URLRequest* request) const OVERRIDE;
+
+ private:
+  const bool is_incognito_;
+  ExtensionInfoMap* const extension_info_map_;
+  DISALLOW_COPY_AND_ASSIGN(ExtensionProtocolHandler);
+};
+
+// Creates URLRequestJobs for extension:// URLs.
+net::URLRequestJob*
+ExtensionProtocolHandler::MaybeCreateJob(net::URLRequest* request) const {
   // TODO(mpcomplete): better error code.
-  if (!AllowExtensionResourceLoad(request, context, scheme)) {
+  if (!AllowExtensionResourceLoad(
+           request, is_incognito_, extension_info_map_)) {
     LOG(ERROR) << "disallowed in extension protocols";
     return new net::URLRequestErrorJob(request, net::ERR_ADDRESS_UNREACHABLE);
   }
 
   // chrome-extension://extension-id/resource/path.js
   const std::string& extension_id = request->url().host();
-  FilePath directory_path = context->extension_info_map()->
+  FilePath directory_path = extension_info_map_->
       GetPathForExtension(extension_id);
   if (directory_path.value().empty()) {
-    if (context->extension_info_map()->URLIsForExtensionIcon(request->url()))
-      directory_path = context->extension_info_map()->
+    if (extension_info_map_->URLIsForExtensionIcon(request->url()))
+      directory_path = extension_info_map_->
           GetPathForDisabledExtension(extension_id);
     if (directory_path.value().empty()) {
       LOG(WARNING) << "Failed to GetPathForExtension: " << extension_id;
@@ -177,7 +189,7 @@
     }
   }
 
-  const std::string& content_security_policy = context->extension_info_map()->
+  const std::string& content_security_policy = extension_info_map_->
       GetContentSecurityPolicyForExtension(extension_id);
 
   FilePath resources_path;
@@ -221,26 +233,47 @@
                                     content_security_policy);
 }
 
+class UserScriptProtocolHandler
+    : public net::URLRequestJobFactory::ProtocolHandler {
+ public:
+  UserScriptProtocolHandler(const FilePath& user_script_dir_path,
+                            ExtensionInfoMap* extension_info_map)
+      : user_script_dir_path_(user_script_dir_path),
+        extension_info_map_(extension_info_map) {}
+
+  virtual ~UserScriptProtocolHandler() {}
+
+  virtual net::URLRequestJob* MaybeCreateJob(
+      net::URLRequest* request) const OVERRIDE;
+
+ private:
+  const FilePath user_script_dir_path_;
+  ExtensionInfoMap* const extension_info_map_;
+};
+
 // Factory registered with net::URLRequest to create URLRequestJobs for
 // chrome-user-script:/ URLs.
-static net::URLRequestJob* CreateUserScriptURLRequestJob(
-    net::URLRequest* request,
-    const std::string& scheme) {
-  ChromeURLRequestContext* context =
-      static_cast<ChromeURLRequestContext*>(request->context());
-
+net::URLRequestJob* UserScriptProtocolHandler::MaybeCreateJob(
+    net::URLRequest* request) const {
   // chrome-user-script:/user-script-name.user.js
-  FilePath directory_path = context->user_script_dir_path();
-
-  ExtensionResource resource(request->url().host(), directory_path,
+  ExtensionResource resource(
+      request->url().host(), user_script_dir_path_,
       extension_file_util::ExtensionURLToRelativeFilePath(request->url()));
 
   return new net::URLRequestFileJob(request, resource.GetFilePath());
 }
 
-void RegisterExtensionProtocols() {
-  net::URLRequest::RegisterProtocolFactory(chrome::kExtensionScheme,
-                                           &CreateExtensionURLRequestJob);
-  net::URLRequest::RegisterProtocolFactory(chrome::kUserScriptScheme,
-                                           &CreateUserScriptURLRequestJob);
+}  // namespace
+
+net::URLRequestJobFactory::ProtocolHandler* CreateExtensionProtocolHandler(
+    bool is_incognito,
+    ExtensionInfoMap* extension_info_map) {
+  return new ExtensionProtocolHandler(is_incognito, extension_info_map);
+}
+
+net::URLRequestJobFactory::ProtocolHandler* CreateUserScriptProtocolHandler(
+    const FilePath& user_script_dir_path,
+    ExtensionInfoMap* extension_info_map) {
+  return new UserScriptProtocolHandler(
+      user_script_dir_path, extension_info_map);
 }
diff --git a/chrome/browser/extensions/extension_protocols.h b/chrome/browser/extensions/extension_protocols.h
index b8bc4f1..06ff2de 100644
--- a/chrome/browser/extensions/extension_protocols.h
+++ b/chrome/browser/extensions/extension_protocols.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
 
@@ -6,7 +6,19 @@
 #define CHROME_BROWSER_EXTENSIONS_EXTENSION_PROTOCOLS_H_
 #pragma once
 
-// Registers support for the extension URL scheme.
-void RegisterExtensionProtocols();
+#include "net/url_request/url_request_job_factory.h"
+
+class ExtensionInfoMap;
+class FilePath;
+
+// Creates the handlers for the chrome-extension:// scheme.
+net::URLRequestJobFactory::ProtocolHandler* CreateExtensionProtocolHandler(
+    bool is_incognito,
+    ExtensionInfoMap* extension_info_map);
+
+// Creates the handlers for the chrome-user-script:// scheme.
+net::URLRequestJobFactory::ProtocolHandler* CreateUserScriptProtocolHandler(
+    const FilePath& user_script_dir_path,
+    ExtensionInfoMap* extension_info_map);
 
 #endif  // CHROME_BROWSER_EXTENSIONS_EXTENSION_PROTOCOLS_H_
diff --git a/chrome/browser/extensions/extension_webrequest_api_unittest.cc b/chrome/browser/extensions/extension_webrequest_api_unittest.cc
index 5207079..e2ace67 100644
--- a/chrome/browser/extensions/extension_webrequest_api_unittest.cc
+++ b/chrome/browser/extensions/extension_webrequest_api_unittest.cc
@@ -71,7 +71,7 @@
         prefs::kEnableReferrers, profile_.GetTestingPrefService(), NULL);
     network_delegate_.reset(new ChromeNetworkDelegate(
         event_router_.get(), profile_.GetRuntimeId(),
-        &enable_referrers_, NULL));
+        &enable_referrers_));
     context_ = new TestURLRequestContext();
     context_->set_network_delegate(network_delegate_.get());
   }
diff --git a/chrome/browser/io_thread.cc b/chrome/browser/io_thread.cc
index 0bc6ee8..8aa6e01e 100644
--- a/chrome/browser/io_thread.cc
+++ b/chrome/browser/io_thread.cc
@@ -419,8 +419,7 @@
   globals_->system_network_delegate.reset(new ChromeNetworkDelegate(
       extension_event_router_forwarder_,
       Profile::kInvalidProfileId,
-      &system_enable_referrers_,
-      NULL));
+      &system_enable_referrers_));
   globals_->host_resolver.reset(
       CreateGlobalHostResolver(net_log_));
   globals_->cert_verifier.reset(new net::CertVerifier);
diff --git a/chrome/browser/net/chrome_network_delegate.cc b/chrome/browser/net/chrome_network_delegate.cc
index ee3afad..e2238282 100644
--- a/chrome/browser/net/chrome_network_delegate.cc
+++ b/chrome/browser/net/chrome_network_delegate.cc
@@ -5,7 +5,6 @@
 #include "chrome/browser/net/chrome_network_delegate.h"
 
 #include "base/logging.h"
-#include "chrome/browser/custom_handlers/protocol_handler_registry.h"
 #include "chrome/browser/extensions/extension_event_router_forwarder.h"
 #include "chrome/browser/extensions/extension_proxy_api.h"
 #include "chrome/browser/extensions/extension_webrequest_api.h"
@@ -41,12 +40,10 @@
 ChromeNetworkDelegate::ChromeNetworkDelegate(
     ExtensionEventRouterForwarder* event_router,
     ProfileId profile_id,
-    BooleanPrefMember* enable_referrers,
-    ProtocolHandlerRegistry* protocol_handler_registry)
+    BooleanPrefMember* enable_referrers)
     : event_router_(event_router),
       profile_id_(profile_id),
-      enable_referrers_(enable_referrers),
-      protocol_handler_registry_(protocol_handler_registry) {
+      enable_referrers_(enable_referrers) {
   DCHECK(event_router);
   DCHECK(enable_referrers);
 }
@@ -127,13 +124,6 @@
       profile_id_, request_id);
 }
 
-net::URLRequestJob* ChromeNetworkDelegate::OnMaybeCreateURLRequestJob(
-      net::URLRequest* request) {
-  if (!protocol_handler_registry_)
-    return NULL;
-  return protocol_handler_registry_->MaybeCreateJob(request);
-}
-
 void ChromeNetworkDelegate::OnPACScriptError(int line_number,
                                              const string16& error) {
   ExtensionProxyEventRouter::GetInstance()->OnPACScriptError(
diff --git a/chrome/browser/net/chrome_network_delegate.h b/chrome/browser/net/chrome_network_delegate.h
index aa556de..ad6fd3c 100644
--- a/chrome/browser/net/chrome_network_delegate.h
+++ b/chrome/browser/net/chrome_network_delegate.h
@@ -7,13 +7,13 @@
 #pragma once
 
 #include "base/basictypes.h"
+#include "base/compiler_specific.h"
 #include "base/memory/ref_counted.h"
 #include "chrome/browser/profiles/profile.h"
 #include "net/base/network_delegate.h"
 
 class ExtensionEventRouterForwarder;
 template<class T> class PrefMember;
-class ProtocolHandlerRegistry;
 
 typedef PrefMember<bool> BooleanPrefMember;
 
@@ -29,22 +29,22 @@
   ChromeNetworkDelegate(
       ExtensionEventRouterForwarder* event_router,
       ProfileId profile_id,
-      BooleanPrefMember* enable_referrers,
-      ProtocolHandlerRegistry* protocol_handler_registry);
+      BooleanPrefMember* enable_referrers);
   virtual ~ChromeNetworkDelegate();
 
   // Binds |enable_referrers| to |pref_service| and moves it to the IO thread.
   // This method should be called on the UI thread.
   static void InitializeReferrersEnabled(BooleanPrefMember* enable_referrers,
                                          PrefService* pref_service);
+
  private:
   // NetworkDelegate methods:
   virtual int OnBeforeURLRequest(net::URLRequest* request,
                                  net::CompletionCallback* callback,
-                                 GURL* new_url);
+                                 GURL* new_url) OVERRIDE;
   virtual int OnBeforeSendHeaders(uint64 request_id,
                                   net::CompletionCallback* callback,
-                                  net::HttpRequestHeaders* headers);
+                                  net::HttpRequestHeaders* headers) OVERRIDE;
   virtual void OnRequestSent(uint64 request_id,
                              const net::HostPortPair& socket_address,
                              const net::HttpRequestHeaders& headers);
@@ -54,8 +54,6 @@
   virtual void OnCompleted(net::URLRequest* request);
   virtual void OnURLRequestDestroyed(net::URLRequest* request);
   virtual void OnHttpTransactionDestroyed(uint64 request_id);
-  virtual net::URLRequestJob* OnMaybeCreateURLRequestJob(
-      net::URLRequest* request);
   virtual void OnPACScriptError(int line_number, const string16& error);
 
   scoped_refptr<ExtensionEventRouterForwarder> event_router_;
@@ -63,7 +61,7 @@
 
   // Weak, owned by our owner.
   BooleanPrefMember* enable_referrers_;
-  scoped_refptr<ProtocolHandlerRegistry> protocol_handler_registry_;
+
   DISALLOW_COPY_AND_ASSIGN(ChromeNetworkDelegate);
 };
 
diff --git a/chrome/browser/profiles/off_the_record_profile_io_data.cc b/chrome/browser/profiles/off_the_record_profile_io_data.cc
index 644dbfe1..1d100608 100644
--- a/chrome/browser/profiles/off_the_record_profile_io_data.cc
+++ b/chrome/browser/profiles/off_the_record_profile_io_data.cc
@@ -178,6 +178,9 @@
   main_context->set_http_transaction_factory(cache);
   main_context->set_ftp_transaction_factory(
       new net::FtpNetworkLayer(main_context->host_resolver()));
+
+  main_context->set_job_factory(job_factory());
+  extensions_context->set_job_factory(job_factory());
 }
 
 scoped_refptr<ProfileIOData::RequestContext>
diff --git a/chrome/browser/profiles/profile_impl_io_data.cc b/chrome/browser/profiles/profile_impl_io_data.cc
index 33bd8b8..a1afa801 100644
--- a/chrome/browser/profiles/profile_impl_io_data.cc
+++ b/chrome/browser/profiles/profile_impl_io_data.cc
@@ -272,8 +272,7 @@
 
   main_context->set_cookie_store(cookie_store);
   media_request_context_->set_cookie_store(cookie_store);
-  extensions_context->set_cookie_store(
-      extensions_cookie_store);
+  extensions_context->set_cookie_store(extensions_cookie_store);
 
   main_http_factory_.reset(main_cache);
   media_http_factory_.reset(media_cache);
@@ -283,6 +282,10 @@
   main_context->set_ftp_transaction_factory(
       new net::FtpNetworkLayer(io_thread_globals->host_resolver.get()));
 
+  main_context->set_job_factory(job_factory());
+  media_request_context_->set_job_factory(job_factory());
+  extensions_context->set_job_factory(job_factory());
+
   lazy_params_.reset();
 }
 
diff --git a/chrome/browser/profiles/profile_io_data.cc b/chrome/browser/profiles/profile_io_data.cc
index c34684d..49bc4867 100644
--- a/chrome/browser/profiles/profile_io_data.cc
+++ b/chrome/browser/profiles/profile_io_data.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/content_settings/host_content_settings_map.h"
 #include "chrome/browser/custom_handlers/protocol_handler_registry.h"
+#include "chrome/browser/extensions/extension_protocols.h"
 #include "chrome/browser/extensions/user_script_master.h"
 #include "chrome/browser/io_thread.h"
 #include "chrome/browser/net/chrome_cookie_notification_details.h"
@@ -28,6 +29,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
 #include "content/browser/browser_thread.h"
 #include "content/browser/host_zoom_map.h"
 #include "content/browser/resource_context.h"
@@ -36,6 +38,7 @@
 #include "net/proxy/proxy_config_service_fixed.h"
 #include "net/proxy/proxy_script_fetcher_impl.h"
 #include "net/proxy/proxy_service.h"
+#include "net/url_request/url_request.h"
 #include "webkit/database/database_tracker.h"
 #include "webkit/quota/quota_manager.h"
 
@@ -129,6 +132,38 @@
   scoped_refptr<ProfileGetter> profile_getter_;
 };
 
+class ProtocolHandlerRegistryInterceptor
+    : public net::URLRequestJobFactory::Interceptor {
+ public:
+  explicit ProtocolHandlerRegistryInterceptor(
+      ProtocolHandlerRegistry* protocol_handler_registry)
+      : protocol_handler_registry_(protocol_handler_registry) {
+    DCHECK(protocol_handler_registry_);
+  }
+
+  virtual ~ProtocolHandlerRegistryInterceptor() {}
+
+  virtual net::URLRequestJob* MaybeIntercept(
+      net::URLRequest* request) const OVERRIDE {
+    return protocol_handler_registry_->MaybeCreateJob(request);
+  }
+
+  virtual net::URLRequestJob* MaybeInterceptRedirect(
+      const GURL& url, net::URLRequest* request) const OVERRIDE {
+    return NULL;
+  }
+
+  virtual net::URLRequestJob* MaybeInterceptResponse(
+      net::URLRequest* request) const OVERRIDE {
+    return NULL;
+  }
+
+ private:
+  const scoped_refptr<ProtocolHandlerRegistry> protocol_handler_registry_;
+
+  DISALLOW_COPY_AND_ASSIGN(ProtocolHandlerRegistryInterceptor);
+};
+
 }  // namespace
 
 void ProfileIOData::InitializeProfileParams(Profile* profile) {
@@ -221,6 +256,29 @@
     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
 }
 
+// static
+bool ProfileIOData::IsHandledProtocol(const std::string& scheme) {
+  DCHECK_EQ(scheme, StringToLowerASCII(scheme));
+  static const char* const kProtocolList[] = {
+    chrome::kExtensionScheme,
+    chrome::kUserScriptScheme,
+  };
+  for (size_t i = 0; i < arraysize(kProtocolList); ++i) {
+    if (scheme == kProtocolList[i])
+      return true;
+  }
+  return net::URLRequest::IsHandledProtocol(scheme);
+}
+
+bool ProfileIOData::IsHandledURL(const GURL& url) {
+  if (!url.is_valid()) {
+    // We handle error cases.
+    return true;
+  }
+
+  return IsHandledProtocol(url.scheme());
+}
+
 scoped_refptr<ChromeURLRequestContext>
 ProfileIOData::GetMainRequestContext() const {
   LazyInitialize();
@@ -298,8 +356,7 @@
   network_delegate_.reset(new ChromeNetworkDelegate(
         io_thread_globals->extension_event_router_forwarder.get(),
         profile_params_->profile_id,
-        &enable_referrers_,
-        profile_params_->protocol_handler_registry));
+        &enable_referrers_));
 
   dns_cert_checker_.reset(
       CreateDnsCertProvenanceChecker(io_thread_globals->dnsrr_resolver.get(),
@@ -312,6 +369,25 @@
           profile_params_->proxy_config_service.release(),
           command_line));
 
+  // NOTE(willchan): Keep these protocol handlers in sync with
+  // ProfileIOData::IsHandledProtocol().
+  job_factory_.reset(new net::URLRequestJobFactory);
+  if (profile_params_->protocol_handler_registry) {
+    job_factory_->AddInterceptor(
+        new ProtocolHandlerRegistryInterceptor(
+            profile_params_->protocol_handler_registry));
+  }
+  bool set_protocol = job_factory_->SetProtocolHandler(
+      chrome::kExtensionScheme,
+      CreateExtensionProtocolHandler(profile_params_->is_incognito,
+                                     profile_params_->extension_info_map));
+  DCHECK(set_protocol);
+  set_protocol = job_factory_->SetProtocolHandler(
+      chrome::kUserScriptScheme,
+      CreateUserScriptProtocolHandler(profile_params_->user_script_dir_path,
+                                      profile_params_->extension_info_map));
+  DCHECK(set_protocol);
+
   // Take ownership over these parameters.
   database_tracker_ = profile_params_->database_tracker;
   appcache_service_ = profile_params_->appcache_service;
diff --git a/chrome/browser/profiles/profile_io_data.h b/chrome/browser/profiles/profile_io_data.h
index ba33f3c8..cfa0bfe 100644
--- a/chrome/browser/profiles/profile_io_data.h
+++ b/chrome/browser/profiles/profile_io_data.h
@@ -64,6 +64,14 @@
 // accessor.
 class ProfileIOData : public base::RefCountedThreadSafe<ProfileIOData> {
  public:
+  // Returns true if |scheme| is handled in Chrome, or by default handlers in
+  // net::URLRequest.
+  static bool IsHandledProtocol(const std::string& scheme);
+
+  // Returns true if |url| is handled in Chrome, or by default handlers in
+  // net::URLRequest.
+  static bool IsHandledURL(const GURL& url);
+
   // These should only be called at most once each. Ownership is reversed when
   // they get called, from ProfileIOData owning ChromeURLRequestContext to vice
   // versa.
@@ -162,6 +170,10 @@
     return proxy_service_.get();
   }
 
+  net::URLRequestJobFactory* job_factory() const {
+    return job_factory_.get();
+  }
+
   ChromeURLRequestContext* main_request_context() const {
     return main_request_context_;
   }
@@ -219,6 +231,7 @@
   mutable scoped_ptr<net::NetworkDelegate> network_delegate_;
   mutable scoped_ptr<net::DnsCertProvenanceChecker> dns_cert_checker_;
   mutable scoped_ptr<net::ProxyService> proxy_service_;
+  mutable scoped_ptr<net::URLRequestJobFactory> job_factory_;
 
   // Pointed to by ResourceContext.
   mutable scoped_refptr<webkit_database::DatabaseTracker> database_tracker_;
diff --git a/chrome/browser/safe_browsing/malware_details_unittest.cc b/chrome/browser/safe_browsing/malware_details_unittest.cc
index faca178..cc98020 100644
--- a/chrome/browser/safe_browsing/malware_details_unittest.cc
+++ b/chrome/browser/safe_browsing/malware_details_unittest.cc
@@ -100,11 +100,12 @@
   entry->Close();
 }
 
-void FillCache(net::URLRequestContext* context) {
+void FillCache(net::URLRequestContextGetter* context_getter) {
   TestCompletionCallback cb;
   disk_cache::Backend* cache;
   int rv =
-      context->http_transaction_factory()->GetCache()->GetBackend(&cache, &cb);
+      context_getter->GetURLRequestContext()->http_transaction_factory()->
+      GetCache()-> GetBackend(&cache, &cb);
   ASSERT_EQ(net::OK, cb.GetResult(rv));
 
   std::string empty;
@@ -497,10 +498,13 @@
 
   profile()->CreateRequestContext();
   scoped_refptr<MalwareDetailsWrap> report = new MalwareDetailsWrap(
-      sb_service_.get(), contents(), resource
-      , profile()->GetRequestContext());
+      sb_service_.get(), contents(), resource, profile()->GetRequestContext());
 
-  FillCache(profile()->GetRequestContext()->GetURLRequestContext());
+  BrowserThread::PostTask(
+      BrowserThread::IO, FROM_HERE,
+      NewRunnableFunction(
+          &FillCache,
+          make_scoped_refptr(profile()->GetRequestContext())));
 
   // The cache collection starts after the IPC from the DOM is fired.
   std::vector<SafeBrowsingHostMsg_MalwareDOMDetails_Node> params;
diff --git a/chrome/browser/tab_contents/render_view_context_menu.cc b/chrome/browser/tab_contents/render_view_context_menu.cc
index 191d5640..d7d5633 100644
--- a/chrome/browser/tab_contents/render_view_context_menu.cc
+++ b/chrome/browser/tab_contents/render_view_context_menu.cc
@@ -33,6 +33,7 @@
 #include "chrome/browser/prefs/pref_service.h"
 #include "chrome/browser/printing/print_preview_tab_controller.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_io_data.h"
 #include "chrome/browser/search_engines/template_url.h"
 #include "chrome/browser/search_engines/template_url_model.h"
 #include "chrome/browser/spellcheck_host.h"
@@ -57,7 +58,6 @@
 #include "content/common/content_restriction.h"
 #include "grit/generated_resources.h"
 #include "net/base/escape.h"
-#include "net/url_request/url_request.h"
 #include "third_party/WebKit/Source/WebKit/chromium/public/WebContextMenuData.h"
 #include "third_party/WebKit/Source/WebKit/chromium/public/WebMediaPlayerAction.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -945,7 +945,7 @@
         return false;
 
       return params_.link_url.is_valid() &&
-             net::URLRequest::IsHandledURL(params_.link_url);
+          ProfileIOData::IsHandledProtocol(params_.link_url.scheme());
     }
 
     case IDC_CONTENT_CONTEXT_SAVEIMAGEAS: {
@@ -956,7 +956,7 @@
         return false;
 
       return params_.src_url.is_valid() &&
-             net::URLRequest::IsHandledURL(params_.src_url);
+          ProfileIOData::IsHandledProtocol(params_.src_url.scheme());
     }
 
     case IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB:
@@ -1007,7 +1007,7 @@
       return (params_.media_flags &
               WebContextMenuData::MediaCanSave) &&
              params_.src_url.is_valid() &&
-             net::URLRequest::IsHandledURL(params_.src_url);
+             ProfileIOData::IsHandledProtocol(params_.src_url.scheme());
     }
 
     case IDC_CONTENT_CONTEXT_OPENAVNEWTAB:
diff --git a/chrome/browser/ui/browser_init.cc b/chrome/browser/ui/browser_init.cc
index 3a72032e..9ae505f 100644
--- a/chrome/browser/ui/browser_init.cc
+++ b/chrome/browser/ui/browser_init.cc
@@ -36,6 +36,7 @@
 #include "chrome/browser/printing/cloud_print/cloud_print_proxy_service.h"
 #include "chrome/browser/printing/print_dialog_cloud.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_io_data.h"
 #include "chrome/browser/search_engines/template_url.h"
 #include "chrome/browser/search_engines/template_url_model.h"
 #include "chrome/browser/sessions/session_restore.h"
@@ -70,7 +71,6 @@
 #include "grit/theme_resources.h"
 #include "grit/theme_resources_standard.h"
 #include "net/base/net_util.h"
-#include "net/url_request/url_request.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "webkit/glue/webkit_glue.h"
@@ -991,7 +991,8 @@
     // This avoids us getting into an infinite loop asking ourselves to open
     // a URL, should the handler be (incorrectly) configured to be us. Anyone
     // asking us to open such a URL should really ask the handler directly.
-    if (!process_startup && !net::URLRequest::IsHandledURL(tabs[i].url))
+    if (!process_startup &&
+        !ProfileIOData::IsHandledURL(tabs[i].url))
       continue;
 
     int add_types = first_tab ? TabStripModel::ADD_ACTIVE :
diff --git a/content/browser/mock_resource_context.cc b/content/browser/mock_resource_context.cc
index 5e5e976..50be13d 100644
--- a/content/browser/mock_resource_context.cc
+++ b/content/browser/mock_resource_context.cc
@@ -5,6 +5,7 @@
 #include "content/browser/mock_resource_context.h"
 
 #include "base/lazy_instance.h"
+#include "net/url_request/url_request_test_util.h"
 
 namespace content {
 
@@ -15,7 +16,10 @@
   return g_mock_resource_context.Get();
 }
 
-MockResourceContext::MockResourceContext() {}
+MockResourceContext::MockResourceContext()
+    : test_request_context_(new TestURLRequestContext) {
+  set_request_context(test_request_context_);
+}
 MockResourceContext::~MockResourceContext() {}
 
 void MockResourceContext::EnsureInitialized() const {}
diff --git a/content/browser/mock_resource_context.h b/content/browser/mock_resource_context.h
index 861a1dc..89a619a 100644
--- a/content/browser/mock_resource_context.h
+++ b/content/browser/mock_resource_context.h
@@ -5,6 +5,8 @@
 #ifndef CONTENT_BROWSER_MOCK_RESOURCE_CONTEXT_H_
 #define CONTENT_BROWSER_MOCK_RESOURCE_CONTEXT_H_
 
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
 #include "content/browser/resource_context.h"
 
 namespace base {
@@ -23,7 +25,9 @@
 
   MockResourceContext();
   ~MockResourceContext();
-  virtual void EnsureInitialized() const;
+  virtual void EnsureInitialized() const OVERRIDE;
+
+  const scoped_refptr<net::URLRequestContext> test_request_context_;
 };
 
 }  // namespace content
diff --git a/content/browser/renderer_host/async_resource_handler.cc b/content/browser/renderer_host/async_resource_handler.cc
index 7c0e621..7a624578 100644
--- a/content/browser/renderer_host/async_resource_handler.cc
+++ b/content/browser/renderer_host/async_resource_handler.cc
@@ -126,8 +126,7 @@
   DevToolsNetLogObserver::PopulateResponseInfo(request, response);
 
   ResourceDispatcherHostRequestInfo* info = rdh_->InfoForRequest(request);
-  if (info->resource_type() == ResourceType::MAIN_FRAME &&
-      request->context()) {
+  if (info->resource_type() == ResourceType::MAIN_FRAME && host_zoom_map_) {
     GURL request_url(request->url());
     filter_->Send(new ViewMsg_SetZoomLevelForLoadingURL(
         info->route_id(),
diff --git a/content/browser/renderer_host/resource_dispatcher_host.cc b/content/browser/renderer_host/resource_dispatcher_host.cc
index aeee6e6d..7e4e492 100644
--- a/content/browser/renderer_host/resource_dispatcher_host.cc
+++ b/content/browser/renderer_host/resource_dispatcher_host.cc
@@ -75,6 +75,7 @@
 #include "net/http/http_response_headers.h"
 #include "net/url_request/url_request.h"
 #include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_job_factory.h"
 #include "webkit/appcache/appcache_interceptor.h"
 #include "webkit/appcache/appcache_interfaces.h"
 #include "webkit/blob/blob_storage_controller.h"
@@ -336,14 +337,18 @@
   }
 }
 
-bool ResourceDispatcherHost::HandleExternalProtocol(int request_id,
-                                                    int child_id,
-                                                    int route_id,
-                                                    const GURL& url,
-                                                    ResourceType::Type type,
-                                                    ResourceHandler* handler) {
-  if (!ResourceType::IsFrame(type) || net::URLRequest::IsHandledURL(url))
+bool ResourceDispatcherHost::HandleExternalProtocol(
+    int request_id,
+    int child_id,
+    int route_id,
+    const GURL& url,
+    ResourceType::Type type,
+    const net::URLRequestJobFactory& job_factory,
+    ResourceHandler* handler) {
+  if (!ResourceType::IsFrame(type) ||
+      job_factory.IsHandledURL(url)) {
     return false;
+  }
 
   BrowserThread::PostTask(
       BrowserThread::UI, FROM_HERE,
@@ -491,6 +496,7 @@
 
   if (HandleExternalProtocol(request_id, child_id, route_id,
                              request_data.url, request_data.resource_type,
+                             *resource_context.request_context()->job_factory(),
                              handler)) {
     return;
   }
@@ -806,7 +812,9 @@
                                                 ResourceType::MAIN_FRAME);
   }
 
-  if (!net::URLRequest::IsHandledURL(url)) {
+  const net::URLRequestContext* request_context = context.request_context();
+
+  if (!request_context->job_factory()->IsHandledURL(url)) {
     VLOG(1) << "Download request for unsupported protocol: "
             << url.possibly_invalid_spec();
     return;
@@ -843,7 +851,9 @@
                                   save_file_manager_.get()));
   request_id_--;
 
-  bool known_proto = net::URLRequest::IsHandledURL(url);
+  const net::URLRequestContext* request_context = context.request_context();
+  bool known_proto =
+      request_context->job_factory()->IsHandledURL(url);
   if (!known_proto) {
     // Since any URLs which have non-standard scheme have been filtered
     // by save manager(see GURL::SchemeIsStandard). This situation
@@ -1095,7 +1105,9 @@
 
   if (HandleExternalProtocol(info->request_id(), info->child_id(),
                              info->route_id(), new_url,
-                             info->resource_type(), info->resource_handler())) {
+                             info->resource_type(),
+                             *request->context()->job_factory(),
+                             info->resource_handler())) {
     // The request is complete so we can remove it.
     RemovePendingRequest(info->child_id(), info->request_id());
     return;
diff --git a/content/browser/renderer_host/resource_dispatcher_host.h b/content/browser/renderer_host/resource_dispatcher_host.h
index 2354a23a..f7a5c64 100644
--- a/content/browser/renderer_host/resource_dispatcher_host.h
+++ b/content/browser/renderer_host/resource_dispatcher_host.h
@@ -49,6 +49,9 @@
 namespace content {
 class ResourceContext;
 }
+namespace net {
+class URLRequestJobFactory;
+}  // namespace net
 
 namespace webkit_blob {
 class DeletableFileReference;
@@ -346,6 +349,7 @@
                               int route_id,
                               const GURL& url,
                               ResourceType::Type resource_type,
+                              const net::URLRequestJobFactory& job_factory,
                               ResourceHandler* handler);
 
   // Checks all pending requests and updates the load states and upload
diff --git a/content/browser/renderer_host/resource_dispatcher_host_unittest.cc b/content/browser/renderer_host/resource_dispatcher_host_unittest.cc
index 562da99..8b81cac 100644
--- a/content/browser/renderer_host/resource_dispatcher_host_unittest.cc
+++ b/content/browser/renderer_host/resource_dispatcher_host_unittest.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "content/browser/renderer_host/resource_dispatcher_host.h"
+
 #include <vector>
 
 #include "base/file_path.h"
@@ -11,7 +13,6 @@
 #include "content/browser/browser_thread.h"
 #include "content/browser/child_process_security_policy.h"
 #include "content/browser/mock_resource_context.h"
-#include "content/browser/renderer_host/resource_dispatcher_host.h"
 #include "content/browser/renderer_host/resource_dispatcher_host_request_info.h"
 #include "content/browser/renderer_host/resource_handler.h"
 #include "content/browser/renderer_host/resource_message_filter.h"
@@ -125,10 +126,17 @@
 class MockURLRequestContextSelector
     : public ResourceMessageFilter::URLRequestContextSelector {
  public:
+  explicit MockURLRequestContextSelector(
+      net::URLRequestContext* request_context)
+      : request_context_(request_context) {}
+
   virtual net::URLRequestContext* GetRequestContext(
       ResourceType::Type request_type) {
-    return NULL;
+    return request_context_;
   }
+
+ private:
+  net::URLRequestContext* const request_context_;
 };
 
 // This class forwards the incoming messages to the ResourceDispatcherHostTest.
@@ -138,11 +146,13 @@
 class ForwardingFilter : public ResourceMessageFilter {
  public:
   explicit ForwardingFilter(IPC::Message::Sender* dest)
-    : ResourceMessageFilter(ChildProcessInfo::GenerateChildProcessUniqueId(),
-                            ChildProcessInfo::RENDER_PROCESS,
-                            &content::MockResourceContext::GetInstance(),
-                            new MockURLRequestContextSelector,
-                            NULL),
+    : ResourceMessageFilter(
+        ChildProcessInfo::GenerateChildProcessUniqueId(),
+        ChildProcessInfo::RENDER_PROCESS,
+        &content::MockResourceContext::GetInstance(),
+        new MockURLRequestContextSelector(
+            content::MockResourceContext::GetInstance().request_context()),
+        NULL),
       dest_(dest) {
     OnChannelConnected(base::GetCurrentProcId());
   }
diff --git a/net/base/network_delegate.cc b/net/base/network_delegate.cc
index d895667..248ef45 100644
--- a/net/base/network_delegate.cc
+++ b/net/base/network_delegate.cc
@@ -64,12 +64,6 @@
   OnHttpTransactionDestroyed(request_id);
 }
 
-URLRequestJob* NetworkDelegate::MaybeCreateURLRequestJob(URLRequest* request) {
-  DCHECK(CalledOnValidThread());
-  DCHECK(request);
-  return OnMaybeCreateURLRequestJob(request);
-}
-
 void NetworkDelegate::NotifyPACScriptError(int line_number,
                                            const string16& error) {
   DCHECK(CalledOnValidThread());
diff --git a/net/base/network_delegate.h b/net/base/network_delegate.h
index ce913e9..977df2f 100644
--- a/net/base/network_delegate.h
+++ b/net/base/network_delegate.h
@@ -52,15 +52,6 @@
   void NotifyCompleted(URLRequest* request);
   void NotifyURLRequestDestroyed(URLRequest* request);
   void NotifyHttpTransactionDestroyed(uint64 request_id);
-
-  // Returns a URLRequestJob that will be used to handle the request if
-  // non-null.
-  // TODO(koz): Currently this is called inside registered ProtocolFactories,
-  // so that we can perform Delegate-dependent request handling from the static
-  // factories, but ultimately it should be called directly from
-  // URLRequestJobManager::CreateJob() as a general override mechanism.
-  URLRequestJob* MaybeCreateURLRequestJob(URLRequest* request);
-
   void NotifyPACScriptError(int line_number, const string16& error);
 
  private:
@@ -110,10 +101,6 @@
   // destroyed.
   virtual void OnHttpTransactionDestroyed(uint64 request_id) = 0;
 
-  // Called before a request is sent and before a URLRequestJob is created to
-  // handle the request.
-  virtual URLRequestJob* OnMaybeCreateURLRequestJob(URLRequest* request) = 0;
-
   // Corresponds to ProxyResolverJSBindings::OnError.
   virtual void OnPACScriptError(int line_number, const string16& error) = 0;
 };
diff --git a/net/net.gyp b/net/net.gyp
index cca149e..f462a61e 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -594,6 +594,8 @@
         'url_request/url_request_http_job.h',
         'url_request/url_request_job.cc',
         'url_request/url_request_job.h',
+        'url_request/url_request_job_factory.cc',
+        'url_request/url_request_job_factory.h',
         'url_request/url_request_job_manager.cc',
         'url_request/url_request_job_manager.h',
         'url_request/url_request_job_tracker.cc',
@@ -954,6 +956,7 @@
         'tools/dump_cache/url_utilities.cc',
         'tools/dump_cache/url_utilities_unittest.cc',
         'udp/udp_socket_unittest.cc',
+        'url_request/url_request_job_factory_unittest.cc',
         'url_request/url_request_job_tracker_unittest.cc',
         'url_request/url_request_throttler_unittest.cc',
         'url_request/url_request_unittest.cc',
diff --git a/net/url_request/url_request_context.cc b/net/url_request/url_request_context.cc
index 0057f2a..c9c232d 100644
--- a/net/url_request/url_request_context.cc
+++ b/net/url_request/url_request_context.cc
@@ -24,7 +24,8 @@
       network_delegate_(NULL),
       transport_security_state_(NULL),
       http_transaction_factory_(NULL),
-      ftp_transaction_factory_(NULL) {
+      ftp_transaction_factory_(NULL),
+      job_factory_(NULL) {
 }
 
 void URLRequestContext::CopyFrom(URLRequestContext* other) {
@@ -47,6 +48,7 @@
   set_referrer_charset(other->referrer_charset());
   set_http_transaction_factory(other->http_transaction_factory());
   set_ftp_transaction_factory(other->ftp_transaction_factory());
+  set_job_factory(other->job_factory());
 }
 
 void URLRequestContext::set_cookie_store(CookieStore* cookie_store) {
diff --git a/net/url_request/url_request_context.h b/net/url_request/url_request_context.h
index bc744594..8749573 100644
--- a/net/url_request/url_request_context.h
+++ b/net/url_request/url_request_context.h
@@ -32,6 +32,7 @@
 class ProxyService;
 class SSLConfigService;
 class URLRequest;
+class URLRequestJobFactory;
 
 // Subclass to provide application-specific context for URLRequest
 // instances. Note that URLRequestContext typically does not provide storage for
@@ -172,6 +173,11 @@
   bool is_main() const { return is_main_; }
   void set_is_main(bool is_main) { is_main_ = is_main; }
 
+  const URLRequestJobFactory* job_factory() const { return job_factory_; }
+  void set_job_factory(const URLRequestJobFactory* job_factory) {
+    job_factory_ = job_factory;
+  }
+
  protected:
   friend class base::RefCountedThreadSafe<URLRequestContext>;
 
@@ -206,9 +212,9 @@
   // used in communication with a server but is used to construct a suggested
   // filename for file download.
   std::string referrer_charset_;
-
   HttpTransactionFactory* http_transaction_factory_;
   FtpTransactionFactory* ftp_transaction_factory_;
+  const URLRequestJobFactory* job_factory_;
 
   // ---------------------------------------------------------------------------
   // Important: When adding any new members below, consider whether they need to
diff --git a/net/url_request/url_request_context_storage.cc b/net/url_request/url_request_context_storage.cc
index d54614a..7531fd7 100644
--- a/net/url_request/url_request_context_storage.cc
+++ b/net/url_request/url_request_context_storage.cc
@@ -16,6 +16,7 @@
 #include "net/http/http_transaction_factory.h"
 #include "net/proxy/proxy_service.h"
 #include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_job_factory.h"
 
 namespace net {
 
@@ -99,4 +100,10 @@
   ftp_transaction_factory_.reset(ftp_transaction_factory);
 }
 
+void URLRequestContextStorage::set_job_factory(
+    URLRequestJobFactory* job_factory) {
+  context_->set_job_factory(job_factory);
+  job_factory_.reset(job_factory);
+}
+
 }  // namespace net
diff --git a/net/url_request/url_request_context_storage.h b/net/url_request/url_request_context_storage.h
index ccb042e..4cdfc43 100644
--- a/net/url_request/url_request_context_storage.h
+++ b/net/url_request/url_request_context_storage.h
@@ -26,6 +26,7 @@
 class SSLConfigService;
 class TransportSecurityState;
 class URLRequestContext;
+class URLRequestJobFactory;
 
 // URLRequestContextStorage is a helper class that provides storage for unowned
 // member variables of URLRequestContext.
@@ -57,6 +58,7 @@
       HttpTransactionFactory* http_transaction_factory);
   void set_ftp_transaction_factory(
       FtpTransactionFactory* ftp_transaction_factory);
+  void set_job_factory(URLRequestJobFactory* job_factory);
 
  private:
   // We use a raw pointer to prevent reference cycles, since
@@ -80,6 +82,7 @@
 
   scoped_ptr<HttpTransactionFactory> http_transaction_factory_;
   scoped_ptr<FtpTransactionFactory> ftp_transaction_factory_;
+  scoped_ptr<URLRequestJobFactory> job_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(URLRequestContextStorage);
 };
diff --git a/net/url_request/url_request_job_factory.cc b/net/url_request/url_request_job_factory.cc
new file mode 100644
index 0000000..78fcb38
--- /dev/null
+++ b/net/url_request/url_request_job_factory.cc
@@ -0,0 +1,93 @@
+// Copyright (c) 2011 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 "net/url_request/url_request_job_factory.h"
+
+#include "base/stl_util-inl.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/load_flags.h"
+#include "net/url_request/url_request_job_manager.h"
+
+namespace net {
+
+URLRequestJobFactory::ProtocolHandler::~ProtocolHandler() {}
+
+URLRequestJobFactory::Interceptor::~Interceptor() {}
+
+URLRequestJobFactory::URLRequestJobFactory() {}
+
+URLRequestJobFactory::~URLRequestJobFactory() {
+  STLDeleteValues(&protocol_handler_map_);
+  STLDeleteElements(&interceptors_);
+}
+
+bool URLRequestJobFactory::SetProtocolHandler(
+    const std::string& scheme,
+    ProtocolHandler* protocol_handler) {
+  DCHECK(CalledOnValidThread());
+
+  if (!protocol_handler) {
+    ProtocolHandlerMap::iterator it = protocol_handler_map_.find(scheme);
+    if (it == protocol_handler_map_.end())
+      return false;
+
+    delete it->second;
+    protocol_handler_map_.erase(it);
+    return true;
+  }
+
+  if (ContainsKey(protocol_handler_map_, scheme))
+    return false;
+  protocol_handler_map_[scheme] = protocol_handler;
+  return true;
+}
+
+void URLRequestJobFactory::AddInterceptor(Interceptor* interceptor) {
+  DCHECK(CalledOnValidThread());
+  CHECK(interceptor);
+
+  interceptors_.push_back(interceptor);
+}
+
+URLRequestJob* URLRequestJobFactory::MaybeCreateJobWithInterceptor(
+    URLRequest* request) const {
+  DCHECK(CalledOnValidThread());
+  URLRequestJob* job = NULL;
+
+  if (!(request->load_flags() & LOAD_DISABLE_INTERCEPT)) {
+    InterceptorList::const_iterator i;
+    for (i = interceptors_.begin(); i != interceptors_.end(); ++i) {
+      job = (*i)->MaybeIntercept(request);
+      if (job)
+        return job;
+    }
+  }
+  return NULL;
+}
+
+URLRequestJob* URLRequestJobFactory::MaybeCreateJobWithProtocolHandler(
+    const std::string& scheme,
+    URLRequest* request) const {
+  DCHECK(CalledOnValidThread());
+  ProtocolHandlerMap::const_iterator it = protocol_handler_map_.find(scheme);
+  if (it == protocol_handler_map_.end())
+    return NULL;
+  return it->second->MaybeCreateJob(request);
+}
+
+bool URLRequestJobFactory::IsHandledProtocol(const std::string& scheme) const {
+  DCHECK(CalledOnValidThread());
+  return ContainsKey(protocol_handler_map_, scheme) ||
+      URLRequestJobManager::GetInstance()->SupportsScheme(scheme);
+}
+
+bool URLRequestJobFactory::IsHandledURL(const GURL& url) const {
+  if (!url.is_valid()) {
+    // We handle error cases.
+    return true;
+  }
+  return IsHandledProtocol(url.scheme());
+}
+
+}  // namespace net
diff --git a/net/url_request/url_request_job_factory.h b/net/url_request/url_request_job_factory.h
new file mode 100644
index 0000000..594883aad
--- /dev/null
+++ b/net/url_request/url_request_job_factory.h
@@ -0,0 +1,96 @@
+// Copyright (c) 2011 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 NET_URL_REQUEST_URL_REQUEST_JOB_FACTORY_H_
+#define NET_URL_REQUEST_URL_REQUEST_JOB_FACTORY_H_
+#pragma once
+
+#include <map>
+#include <string>
+#include <vector>
+#include "base/basictypes.h"
+#include "base/threading/non_thread_safe.h"
+
+class GURL;
+
+namespace net {
+
+class URLRequest;
+class URLRequestJob;
+
+class URLRequestJobFactory : public base::NonThreadSafe {
+ public:
+  class ProtocolHandler {
+   public:
+    virtual ~ProtocolHandler();
+
+    virtual URLRequestJob* MaybeCreateJob(URLRequest* request) const = 0;
+  };
+
+  class Interceptor {
+   public:
+    virtual ~Interceptor();
+
+    // Called for every request made.  Should return a new job to handle the
+    // request if it should be intercepted, or NULL to allow the request to
+    // be handled in the normal manner.
+    virtual URLRequestJob* MaybeIntercept(URLRequest* request) const = 0;
+
+    // Called after having received a redirect response, but prior to the
+    // the request delegate being informed of the redirect. Can return a new
+    // job to replace the existing job if it should be intercepted, or NULL
+    // to allow the normal handling to continue. If a new job is provided,
+    // the delegate never sees the original redirect response, instead the
+    // response produced by the intercept job will be returned.
+    virtual URLRequestJob* MaybeInterceptRedirect(
+        const GURL& location,
+        URLRequest* request) const = 0;
+
+    // Called after having received a final response, but prior to the
+    // the request delegate being informed of the response. This is also
+    // called when there is no server response at all to allow interception
+    // on DNS or network errors. Can return a new job to replace the existing
+    // job if it should be intercepted, or NULL to allow the normal handling to
+    // continue. If a new job is provided, the delegate never sees the original
+    // response, instead the response produced by the intercept job will be
+    // returned.
+    virtual URLRequestJob* MaybeInterceptResponse(
+        URLRequest* request) const = 0;
+  };
+
+  URLRequestJobFactory();
+  ~URLRequestJobFactory();
+
+  // Sets the ProtocolHandler for a scheme. Returns true on success, false on
+  // failure (a ProtocolHandler already exists for |scheme|). On success,
+  // URLRequestJobFactory takes ownership of |protocol_handler|.
+  bool SetProtocolHandler(const std::string& scheme,
+                          ProtocolHandler* protocol_handler);
+
+  // Takes ownership of |interceptor|. Adds it to the end of the Interceptor
+  // list.
+  void AddInterceptor(Interceptor* interceptor);
+
+  URLRequestJob* MaybeCreateJobWithInterceptor(URLRequest* request) const;
+
+  URLRequestJob* MaybeCreateJobWithProtocolHandler(const std::string& scheme,
+                                                   URLRequest* request) const;
+
+  bool IsHandledProtocol(const std::string& scheme) const;
+
+  bool IsHandledURL(const GURL& url) const;
+
+ private:
+  typedef std::map<std::string, ProtocolHandler*> ProtocolHandlerMap;
+  typedef std::vector<Interceptor*> InterceptorList;
+
+  ProtocolHandlerMap protocol_handler_map_;
+  InterceptorList interceptors_;
+
+  DISALLOW_COPY_AND_ASSIGN(URLRequestJobFactory);
+};
+
+}  // namespace net
+
+#endif  // NET_URL_REQUEST_URL_REQUEST_JOB_FACTORY_H_
diff --git a/net/url_request/url_request_job_factory_unittest.cc b/net/url_request/url_request_job_factory_unittest.cc
new file mode 100644
index 0000000..46c952c
--- /dev/null
+++ b/net/url_request/url_request_job_factory_unittest.cc
@@ -0,0 +1,152 @@
+// Copyright (c) 2011 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 "net/url_request/url_request_job_factory.h"
+
+#include "base/task.h"
+#include "net/url_request/url_request_job.h"
+#include "net/url_request/url_request_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+class MockURLRequestJob : public URLRequestJob {
+ public:
+  MockURLRequestJob(URLRequest* request, const URLRequestStatus& status)
+      : URLRequestJob(request),
+        status_(status),
+        ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {}
+
+  virtual void Start() {
+    // Start reading asynchronously so that all error reporting and data
+    // callbacks happen as they would for network requests.
+    MessageLoop::current()->PostTask(
+        FROM_HERE,
+        method_factory_.NewRunnableMethod(&MockURLRequestJob::StartAsync));
+  }
+
+ private:
+  void StartAsync() {
+    SetStatus(status_);
+    NotifyHeadersComplete();
+  }
+
+  URLRequestStatus status_;
+  ScopedRunnableMethodFactory<MockURLRequestJob> method_factory_;
+};
+
+class DummyProtocolHandler : public URLRequestJobFactory::ProtocolHandler {
+ public:
+  virtual URLRequestJob* MaybeCreateJob(URLRequest* request) const {
+    return new MockURLRequestJob(
+        request, URLRequestStatus(URLRequestStatus::SUCCESS, OK));
+  }
+};
+
+class DummyInterceptor : public URLRequestJobFactory::Interceptor {
+ public:
+  virtual URLRequestJob* MaybeIntercept(URLRequest* request) const {
+    return new MockURLRequestJob(
+        request,
+        URLRequestStatus(URLRequestStatus::FAILED, ERR_FAILED));
+  }
+
+  virtual URLRequestJob* MaybeInterceptRedirect(
+      const GURL& /* location */,
+      URLRequest* /* request */) const {
+    return NULL;
+  }
+
+  virtual URLRequestJob* MaybeInterceptResponse(
+      URLRequest* /* request */) const {
+    return NULL;
+  }
+};
+
+TEST(URLRequestJobFactoryTest, NoProtocolHandler) {
+  TestDelegate delegate;
+  scoped_refptr<URLRequestContext> request_context(new TestURLRequestContext);
+  TestURLRequest request(GURL("foo://bar"), &delegate);
+  request.set_context(request_context);
+  request.Start();
+
+  MessageLoop::current()->Run();
+  EXPECT_EQ(URLRequestStatus::FAILED, request.status().status());
+  EXPECT_EQ(ERR_UNKNOWN_URL_SCHEME, request.status().os_error());
+}
+
+TEST(URLRequestJobFactoryTest, BasicProtocolHandler) {
+  TestDelegate delegate;
+  scoped_refptr<URLRequestContext> request_context(new TestURLRequestContext);
+  URLRequestJobFactory job_factory;
+  request_context->set_job_factory(&job_factory);
+  job_factory.SetProtocolHandler("foo", new DummyProtocolHandler);
+  TestURLRequest request(GURL("foo://bar"), &delegate);
+  request.set_context(request_context);
+  request.Start();
+
+  MessageLoop::current()->Run();
+  EXPECT_EQ(URLRequestStatus::SUCCESS, request.status().status());
+  EXPECT_EQ(OK, request.status().os_error());
+}
+
+TEST(URLRequestJobFactoryTest, DeleteProtocolHandler) {
+  scoped_refptr<URLRequestContext> request_context(new TestURLRequestContext);
+  URLRequestJobFactory job_factory;
+  request_context->set_job_factory(&job_factory);
+  job_factory.SetProtocolHandler("foo", new DummyProtocolHandler);
+  job_factory.SetProtocolHandler("foo", NULL);
+}
+
+TEST(URLRequestJobFactoryTest, BasicInterceptor) {
+  TestDelegate delegate;
+  scoped_refptr<URLRequestContext> request_context(new TestURLRequestContext);
+  URLRequestJobFactory job_factory;
+  request_context->set_job_factory(&job_factory);
+  job_factory.AddInterceptor(new DummyInterceptor);
+  TestURLRequest request(GURL("http://bar"), &delegate);
+  request.set_context(request_context);
+  request.Start();
+
+  MessageLoop::current()->Run();
+  EXPECT_EQ(URLRequestStatus::FAILED, request.status().status());
+  EXPECT_EQ(ERR_FAILED, request.status().os_error());
+}
+
+TEST(URLRequestJobFactoryTest, InterceptorNeedsValidSchemeStill) {
+  TestDelegate delegate;
+  scoped_refptr<URLRequestContext> request_context(new TestURLRequestContext);
+  URLRequestJobFactory job_factory;
+  request_context->set_job_factory(&job_factory);
+  job_factory.AddInterceptor(new DummyInterceptor);
+  TestURLRequest request(GURL("foo://bar"), &delegate);
+  request.set_context(request_context);
+  request.Start();
+
+  MessageLoop::current()->Run();
+  EXPECT_EQ(URLRequestStatus::FAILED, request.status().status());
+  EXPECT_EQ(ERR_UNKNOWN_URL_SCHEME, request.status().os_error());
+}
+
+TEST(URLRequestJobFactoryTest, InterceptorOverridesProtocolHandler) {
+  TestDelegate delegate;
+  scoped_refptr<URLRequestContext> request_context(new TestURLRequestContext);
+  URLRequestJobFactory job_factory;
+  request_context->set_job_factory(&job_factory);
+  job_factory.SetProtocolHandler("foo", new DummyProtocolHandler);
+  job_factory.AddInterceptor(new DummyInterceptor);
+  TestURLRequest request(GURL("foo://bar"), &delegate);
+  request.set_context(request_context);
+  request.Start();
+
+  MessageLoop::current()->Run();
+  EXPECT_EQ(URLRequestStatus::FAILED, request.status().status());
+  EXPECT_EQ(ERR_FAILED, request.status().os_error());
+}
+
+}  // namespace
+
+}  // namespace net
diff --git a/net/url_request/url_request_job_manager.cc b/net/url_request/url_request_job_manager.cc
index 06891b4..dc6af79 100644
--- a/net/url_request/url_request_job_manager.cc
+++ b/net/url_request/url_request_job_manager.cc
@@ -11,12 +11,15 @@
 #include "base/string_util.h"
 #include "net/base/load_flags.h"
 #include "net/base/net_errors.h"
+#include "net/base/network_delegate.h"
 #include "net/url_request/url_request_about_job.h"
+#include "net/url_request/url_request_context.h"
 #include "net/url_request/url_request_data_job.h"
 #include "net/url_request/url_request_error_job.h"
 #include "net/url_request/url_request_file_job.h"
 #include "net/url_request/url_request_ftp_job.h"
 #include "net/url_request/url_request_http_job.h"
+#include "net/url_request/url_request_job_factory.h"
 
 namespace net {
 
@@ -55,15 +58,33 @@
     return new URLRequestErrorJob(request, ERR_INVALID_URL);
 
   // We do this here to avoid asking interceptors about unsupported schemes.
+  const URLRequestJobFactory* job_factory = NULL;
+  if (request->context())
+    job_factory = request->context()->job_factory();
+
   const std::string& scheme = request->url().scheme();  // already lowercase
-  if (!SupportsScheme(scheme))
+  if (job_factory) {
+    if (!job_factory->IsHandledProtocol(scheme)) {
+      return new URLRequestErrorJob(request, ERR_UNKNOWN_URL_SCHEME);
+    }
+  } else if (!SupportsScheme(scheme)) {
     return new URLRequestErrorJob(request, ERR_UNKNOWN_URL_SCHEME);
+  }
 
   // THREAD-SAFETY NOTICE:
   //   We do not need to acquire the lock here since we are only reading our
   //   data structures.  They should only be modified on the current thread.
 
   // See if the request should be intercepted.
+  //
+
+  if (job_factory) {
+    URLRequestJob* job = job_factory->MaybeCreateJobWithInterceptor(request);
+    if (job)
+      return job;
+  }
+
+  // TODO(willchan): Remove this in favor of URLRequestJobFactory::Interceptor.
   if (!(request->load_flags() & LOAD_DISABLE_INTERCEPT)) {
     InterceptorList::const_iterator i;
     for (i = interceptors_.begin(); i != interceptors_.end(); ++i) {
@@ -73,6 +94,15 @@
     }
   }
 
+  if (job_factory) {
+    URLRequestJob* job =
+        job_factory->MaybeCreateJobWithProtocolHandler(scheme, request);
+    if (job)
+      return job;
+  }
+
+  // TODO(willchan): Remove this in favor of
+  // URLRequestJobFactory::ProtocolHandler.
   // See if the request should be handled by a registered protocol factory.
   // If the registered factory returns null, then we want to fall-back to the
   // built-in protocol factory.
diff --git a/net/url_request/url_request_test_util.cc b/net/url_request/url_request_test_util.cc
index 680f12dc..fff1d8ba4 100644
--- a/net/url_request/url_request_test_util.cc
+++ b/net/url_request/url_request_test_util.cc
@@ -10,6 +10,7 @@
 #include "base/threading/thread.h"
 #include "net/base/host_port_pair.h"
 #include "net/http/http_network_session.h"
+#include "net/url_request/url_request_job_factory.h"
 
 TestURLRequestContext::TestURLRequestContext()
     : ALLOW_THIS_IN_INITIALIZER_LIST(context_storage_(this)) {
@@ -67,6 +68,7 @@
   context_storage_.set_cookie_store(new net::CookieMonster(NULL, NULL));
   set_accept_language("en-us,fr");
   set_accept_charset("iso-8859-1,*,utf-8");
+  context_storage_.set_job_factory(new net::URLRequestJobFactory);
 }