summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorcbentzel@chromium.org <cbentzel@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-10-11 15:36:45 +0000
committercbentzel@chromium.org <cbentzel@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-10-11 15:36:45 +0000
commit90449abd576c6153650b7201f68da897f05b8717 (patch)
treec7e55ce93c20b3d9031ce8fa94268714b02cdd19
parentd7b175e8762301a1bfca9d17681111bbf5bf5c0a (diff)
downloadchromium_src-90449abd576c6153650b7201f68da897f05b8717.zip
chromium_src-90449abd576c6153650b7201f68da897f05b8717.tar.gz
chromium_src-90449abd576c6153650b7201f68da897f05b8717.tar.bz2
webRequest.onAuthRequired listeners can provide authentication credentials.
onAuthRequired listeners that specify "blocking" in the extraInfoSpec can return authentication credentials [username and password] in the BlockingResponse. If these are provided, Chrome will use these credentials rather than showing a login prompt for the user. If "blocking" is not specified, or an authCredentials object is not present in the BlockingResponse, then a login prompt will be displayed. Warning: If the authentication credentials are invalid, the extension may still present credentials for subsequent challenges. This could lead to infinite loops of bad credentials being entered without user intervention. BUG=32056 TEST=Write an extension which does a blocking onAuthRequired and provides correct credentials, validate that it works. Review URL: http://codereview.chromium.org/8015004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@104896 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/extensions/extension_webrequest_api.cc116
-rw-r--r--chrome/browser/extensions/extension_webrequest_api.h29
-rw-r--r--chrome/browser/extensions/extension_webrequest_api_constants.cc3
-rw-r--r--chrome/browser/extensions/extension_webrequest_api_constants.h3
-rw-r--r--chrome/browser/extensions/extension_webrequest_apitest.cc11
-rw-r--r--chrome/browser/net/chrome_network_delegate.cc6
-rw-r--r--chrome/common/extensions/api/extension_api.json20
-rw-r--r--chrome/common/extensions/docs/experimental.webRequest.html280
-rw-r--r--chrome/test/data/extensions/api_test/webrequest/framework.js2
-rw-r--r--chrome/test/data/extensions/api_test/webrequest/test_api.html2
-rw-r--r--chrome/test/data/extensions/api_test/webrequest/test_auth_required.html323
-rw-r--r--chrome/test/data/extensions/api_test/webrequest/test_complex.html80
-rw-r--r--net/base/net_log_event_type_list.h8
13 files changed, 772 insertions, 111 deletions
diff --git a/chrome/browser/extensions/extension_webrequest_api.cc b/chrome/browser/extensions/extension_webrequest_api.cc
index 208554a..a225112 100644
--- a/chrome/browser/extensions/extension_webrequest_api.cc
+++ b/chrome/browser/extensions/extension_webrequest_api.cc
@@ -338,6 +338,15 @@ struct ExtensionWebRequestEventRouter::BlockedRequest {
// OnHeadersReceived.
scoped_refptr<net::HttpResponseHeaders>* override_response_headers;
+ // If non-empty, this contains the auth credentials that may be filled in.
+ // Only valid for OnAuthRequired.
+ net::AuthCredentials* auth_credentials;
+
+ // The callback to invoke for auth. If |auth_callback.is_null()| is false,
+ // |callback| must be NULL.
+ // Only valid for OnAuthRequired.
+ net::NetworkDelegate::AuthCallback auth_callback;
+
// Time the request was paused. Used for logging purposes.
base::Time blocking_time;
@@ -352,7 +361,8 @@ struct ExtensionWebRequestEventRouter::BlockedRequest {
callback(NULL),
new_url(NULL),
request_headers(NULL),
- override_response_headers(NULL) {}
+ override_response_headers(NULL),
+ auth_credentials(NULL) {}
};
bool ExtensionWebRequestEventRouter::RequestFilter::InitFromValue(
@@ -636,23 +646,28 @@ int ExtensionWebRequestEventRouter::OnHeadersReceived(
return net::OK;
}
-void ExtensionWebRequestEventRouter::OnAuthRequired(
+net::NetworkDelegate::AuthRequiredResponse
+ExtensionWebRequestEventRouter::OnAuthRequired(
void* profile,
ExtensionInfoMap* extension_info_map,
net::URLRequest* request,
- const net::AuthChallengeInfo& auth_info) {
+ const net::AuthChallengeInfo& auth_info,
+ const net::NetworkDelegate::AuthCallback& callback,
+ net::AuthCredentials* credentials) {
+ // No profile means that this is for authentication challenges in the
+ // system context. Skip in that case.
if (!profile)
- return;
+ return net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_NO_ACTION;
if (!HasWebRequestScheme(request->url()))
- return;
+ return net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_NO_ACTION;
int extra_info_spec = 0;
std::vector<const EventListener*> listeners =
GetMatchingListeners(profile, extension_info_map,
keys::kOnAuthRequired, request, &extra_info_spec);
if (listeners.empty())
- return;
+ return net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_NO_ACTION;
ListValue args;
DictionaryValue* dict = new DictionaryValue();
@@ -673,7 +688,14 @@ void ExtensionWebRequestEventRouter::OnAuthRequired(
}
args.Append(dict);
- DispatchEvent(profile, request, listeners, args);
+ if (DispatchEvent(profile, request, listeners, args)) {
+ blocked_requests_[request->identifier()].event = kOnAuthRequired;
+ blocked_requests_[request->identifier()].auth_callback = callback;
+ blocked_requests_[request->identifier()].auth_credentials = credentials;
+ blocked_requests_[request->identifier()].net_log = &request->net_log();
+ return net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_IO_PENDING;
+ }
+ return net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_NO_ACTION;
}
void ExtensionWebRequestEventRouter::OnBeforeRedirect(
@@ -1147,6 +1169,9 @@ linked_ptr<ExtensionWebRequestEventRouter::EventResponseDelta>
}
}
+ if (blocked_request->event == kOnAuthRequired)
+ result->auth_credentials.swap(response->auth_credentials);
+
return result;
}
@@ -1191,7 +1216,6 @@ void ExtensionWebRequestEventRouter::MergeOnBeforeSendHeadersResponses(
// We assume here that the deltas are sorted in decreasing extension
// precedence (i.e. decreasing extension installation time).
for (delta = deltas.begin(); delta != deltas.end(); ++delta) {
-
if ((*delta)->modified_request_headers.IsEmpty() &&
(*delta)->deleted_request_headers.empty()) {
continue;
@@ -1314,6 +1338,37 @@ void ExtensionWebRequestEventRouter::MergeOnHeadersReceivedResponses(
}
}
+bool ExtensionWebRequestEventRouter::MergeOnAuthRequiredResponses(
+ BlockedRequest* request,
+ std::list<std::string>* conflicting_extensions) const {
+ CHECK(request);
+ CHECK(request->auth_credentials);
+ bool credentials_set = false;
+
+ const EventResponseDeltas& deltas = request->response_deltas;
+ for (EventResponseDeltas::const_iterator delta = deltas.begin();
+ delta != deltas.end();
+ ++delta) {
+ if (!(*delta)->auth_credentials.get())
+ continue;
+ if (credentials_set) {
+ conflicting_extensions->push_back((*delta)->extension_id);
+ request->net_log->AddEvent(
+ net::NetLog::TYPE_CHROME_EXTENSION_IGNORED_DUE_TO_CONFLICT,
+ make_scoped_refptr(
+ new NetLogExtensionIdParameter((*delta)->extension_id)));
+ } else {
+ request->net_log->AddEvent(
+ net::NetLog::TYPE_CHROME_EXTENSION_PROVIDE_AUTH_CREDENTIALS,
+ make_scoped_refptr(
+ new NetLogExtensionIdParameter((*delta)->extension_id)));
+ *request->auth_credentials = *(*delta)->auth_credentials;
+ credentials_set = true;
+ }
+ }
+ return credentials_set;
+}
+
void ExtensionWebRequestEventRouter::DecrementBlockCount(
void* profile,
const std::string& extension_id,
@@ -1344,14 +1399,14 @@ void ExtensionWebRequestEventRouter::DecrementBlockCount(
if (num_handlers_blocking == 0) {
request_time_tracker_->IncrementTotalBlockTime(request_id, block_time);
- EventResponseDeltas::iterator i;
EventResponseDeltas& deltas = blocked_request.response_deltas;
-
bool canceled = false;
- for (i = deltas.begin(); i != deltas.end(); ++i) {
+ bool credentials_set = false;
+ for (EventResponseDeltas::const_iterator i = deltas.begin();
+ i != deltas.end(); ++i) {
if ((*i)->cancel) {
- canceled = true;
- blocked_request.net_log->AddEvent(
+ canceled = true;
+ blocked_request.net_log->AddEvent(
net::NetLog::TYPE_CHROME_EXTENSION_ABORTED_REQUEST,
make_scoped_refptr(
new NetLogExtensionIdParameter((*i)->extension_id)));
@@ -1364,7 +1419,8 @@ void ExtensionWebRequestEventRouter::DecrementBlockCount(
if (blocked_request.event == kOnBeforeRequest) {
CHECK(blocked_request.callback);
- MergeOnBeforeRequestResponses(&blocked_request, &conflicting_extensions);
+ MergeOnBeforeRequestResponses(&blocked_request,
+ &conflicting_extensions);
} else if (blocked_request.event == kOnBeforeSendHeaders) {
CHECK(blocked_request.callback);
MergeOnBeforeSendHeadersResponses(&blocked_request,
@@ -1373,6 +1429,12 @@ void ExtensionWebRequestEventRouter::DecrementBlockCount(
CHECK(blocked_request.callback);
MergeOnHeadersReceivedResponses(&blocked_request,
&conflicting_extensions);
+ } else if (blocked_request.event == kOnAuthRequired) {
+ CHECK(!blocked_request.callback);
+ CHECK(!blocked_request.auth_callback.is_null());
+ credentials_set = MergeOnAuthRequiredResponses(
+ &blocked_request,
+ &conflicting_extensions);
} else {
NOTREACHED();
}
@@ -1403,6 +1465,18 @@ void ExtensionWebRequestEventRouter::DecrementBlockCount(
// might trigger the next event.
blocked_requests_.erase(request_id);
callback->Run(rv);
+ } else if (!blocked_request.auth_callback.is_null()) {
+ net::NetworkDelegate::AuthRequiredResponse response =
+ net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_NO_ACTION;
+ if (canceled) {
+ response = net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_CANCEL_AUTH;
+ } else if (credentials_set) {
+ response = net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_SET_AUTH;
+ }
+ net::NetworkDelegate::AuthCallback callback =
+ blocked_request.auth_callback;
+ blocked_requests_.erase(request_id);
+ callback.Run(response);
} else {
blocked_requests_.erase(request_id);
}
@@ -1575,6 +1649,20 @@ bool WebRequestEventHandled::RunImpl() {
response_headers_string += '\n';
response->response_headers_string.swap(response_headers_string);
}
+
+ if (value->HasKey(keys::kAuthCredentialsKey)) {
+ DictionaryValue* credentials_value = NULL;
+ EXTENSION_FUNCTION_VALIDATE(value->GetDictionary(
+ keys::kAuthCredentialsKey,
+ &credentials_value));
+ response->auth_credentials.reset(new net::AuthCredentials());
+ EXTENSION_FUNCTION_VALIDATE(
+ credentials_value->GetString(keys::kUsernameKey,
+ &response->auth_credentials->username));
+ EXTENSION_FUNCTION_VALIDATE(
+ credentials_value->GetString(keys::kPasswordKey,
+ &response->auth_credentials->password));
+ }
}
ExtensionWebRequestEventRouter::GetInstance()->OnEventHandled(
diff --git a/chrome/browser/extensions/extension_webrequest_api.h b/chrome/browser/extensions/extension_webrequest_api.h
index 268a4c2..5196818 100644
--- a/chrome/browser/extensions/extension_webrequest_api.h
+++ b/chrome/browser/extensions/extension_webrequest_api.h
@@ -18,6 +18,7 @@
#include "chrome/common/extensions/url_pattern_set.h"
#include "ipc/ipc_message.h"
#include "net/base/completion_callback.h"
+#include "net/base/network_delegate.h"
#include "net/http/http_request_headers.h"
#include "webkit/glue/resource_type.h"
@@ -32,6 +33,7 @@ class StringValue;
}
namespace net {
+class AuthCredentials;
class AuthChallengeInfo;
class HostPortPair;
class HttpRequestHeaders;
@@ -104,6 +106,7 @@ class ExtensionWebRequestEventRouter {
scoped_ptr<net::HttpRequestHeaders> request_headers;
// Contains all header lines after the status line, lines are \n separated.
std::string response_headers_string;
+ scoped_ptr<net::AuthCredentials> auth_credentials;
EventResponse(const std::string& extension_id,
const base::Time& extension_install_time);
@@ -135,6 +138,9 @@ class ExtensionWebRequestEventRouter {
// Complete set of response headers that will replace the original ones.
scoped_refptr<net::HttpResponseHeaders> new_response_headers;
+ // Authentication Credentials to use.
+ scoped_ptr<net::AuthCredentials> auth_credentials;
+
EventResponseDelta(const std::string& extension_id,
const base::Time& extension_install_time);
~EventResponseDelta();
@@ -192,11 +198,18 @@ class ExtensionWebRequestEventRouter {
net::HttpResponseHeaders* original_response_headers,
scoped_refptr<net::HttpResponseHeaders>* override_response_headers);
- // Dispatches the onAuthRequired event.
- void OnAuthRequired(void* profile,
+ // Dispatches the OnAuthRequired event to any extensions whose filters match
+ // the given request. If the listener is not registered as "blocking", then
+ // AUTH_REQUIRED_RESPONSE_OK is returned. Otherwise,
+ // AUTH_REQUIRED_RESPONSE_IO_PENDING is returned and |callback| will be
+ // invoked later.
+ net::NetworkDelegate::AuthRequiredResponse OnAuthRequired(
+ void* profile,
ExtensionInfoMap* extension_info_map,
net::URLRequest* request,
- const net::AuthChallengeInfo& auth_info);
+ const net::AuthChallengeInfo& auth_info,
+ const net::NetworkDelegate::AuthCallback& callback,
+ net::AuthCredentials* credentials);
// Dispatches the onBeforeRedirect event. This is fired for HTTP(s) requests
// only.
@@ -347,6 +360,16 @@ class ExtensionWebRequestEventRouter {
BlockedRequest* request,
std::list<std::string>* conflicting_extensions) const;
+ // Merge the responses of blocked onAuthRequired handlers. The first
+ // registered listener that supplies authentication credentials in a response,
+ // if any, will have its authentication credentials used. |request| must be
+ // non-NULL, and contain |deltas| that are sorted in decreasing order of
+ // precedence.
+ // Returns whether authentication credentials are set.
+ bool MergeOnAuthRequiredResponses(
+ BlockedRequest* request,
+ std::list<std::string>* conflicting_extensions) const;
+
// A map for each profile that maps an event name to a set of extensions that
// are listening to that event.
ListenerMap listeners_;
diff --git a/chrome/browser/extensions/extension_webrequest_api_constants.cc b/chrome/browser/extensions/extension_webrequest_api_constants.cc
index 63a980a..82e5fb7 100644
--- a/chrome/browser/extensions/extension_webrequest_api_constants.cc
+++ b/chrome/browser/extensions/extension_webrequest_api_constants.cc
@@ -29,6 +29,9 @@ const char kHeaderValueKey[] = "value";
const char kIsProxyKey[] = "isProxy";
const char kSchemeKey[] = "scheme";
const char kRealmKey[] = "realm";
+const char kAuthCredentialsKey[] = "authCredentials";
+const char kUsernameKey[] = "username";
+const char kPasswordKey[] = "password";
const char kOnBeforeRedirect[] = "experimental.webRequest.onBeforeRedirect";
const char kOnBeforeRequest[] = "experimental.webRequest.onBeforeRequest";
diff --git a/chrome/browser/extensions/extension_webrequest_api_constants.h b/chrome/browser/extensions/extension_webrequest_api_constants.h
index 4e3318f..e58d17e 100644
--- a/chrome/browser/extensions/extension_webrequest_api_constants.h
+++ b/chrome/browser/extensions/extension_webrequest_api_constants.h
@@ -35,6 +35,9 @@ extern const char kHeaderValueKey[];
extern const char kIsProxyKey[];
extern const char kSchemeKey[];
extern const char kRealmKey[];
+extern const char kAuthCredentialsKey[];
+extern const char kUsernameKey[];
+extern const char kPasswordKey[];
// Events.
extern const char kOnAuthRequired[];
diff --git a/chrome/browser/extensions/extension_webrequest_apitest.cc b/chrome/browser/extensions/extension_webrequest_apitest.cc
index 5ca1208..204468b 100644
--- a/chrome/browser/extensions/extension_webrequest_apitest.cc
+++ b/chrome/browser/extensions/extension_webrequest_apitest.cc
@@ -69,10 +69,17 @@ IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest, WebRequestComplex) {
CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kEnableExperimentalExtensionApis);
- // Needed for the auth tests.
+ ASSERT_TRUE(RunExtensionSubtest("webrequest", "test_complex.html")) <<
+ message_;
+}
+
+IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest, WebRequestAuthRequired) {
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kEnableExperimentalExtensionApis);
+
CancelLoginDialog login_dialog_helper;
- ASSERT_TRUE(RunExtensionSubtest("webrequest", "test_complex.html")) <<
+ ASSERT_TRUE(RunExtensionSubtest("webrequest", "test_auth_required.html")) <<
message_;
}
diff --git a/chrome/browser/net/chrome_network_delegate.cc b/chrome/browser/net/chrome_network_delegate.cc
index db96460..b6a41b7 100644
--- a/chrome/browser/net/chrome_network_delegate.cc
+++ b/chrome/browser/net/chrome_network_delegate.cc
@@ -176,7 +176,7 @@ ChromeNetworkDelegate::OnAuthRequired(
const net::AuthChallengeInfo& auth_info,
const AuthCallback& callback,
net::AuthCredentials* credentials) {
- ExtensionWebRequestEventRouter::GetInstance()->OnAuthRequired(
- profile_, extension_info_map_.get(), request, auth_info);
- return net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_NO_ACTION;
+ return ExtensionWebRequestEventRouter::GetInstance()->OnAuthRequired(
+ profile_, extension_info_map_.get(), request, auth_info,
+ callback, credentials);
}
diff --git a/chrome/common/extensions/api/extension_api.json b/chrome/common/extensions/api/extension_api.json
index d8e7299..7388936 100644
--- a/chrome/common/extensions/api/extension_api.json
+++ b/chrome/common/extensions/api/extension_api.json
@@ -5822,6 +5822,15 @@
"$ref": "HttpHeaders",
"optional": true,
"description": "Only used as a response to the onHeadersReceived event. If set, the server is assumed to have responsed with these response headers instead. Only return <code>responseHeaders</code> if you really want to modify the headers in order to limit the number of conflicts (only one extension may modify <code>responseHeaders</code> for each request)."
+ },
+ "authCredentials": {
+ "type": "object",
+ "description": "Only used as a response to the onAuthRequired event. If set, the request is made using the supplied credentials.",
+ "optional": true,
+ "properties": {
+ "username": {"type": "string"},
+ "password": {"type": "string"}
+ }
}
}
}
@@ -6054,7 +6063,7 @@
{
"name": "onAuthRequired",
"type": "function",
- "description": "Fired when an authentication failure was received. Depending on whether the user provides credentials, the request is either reissued or cancelled.",
+ "description": "Fired when an authentication failure was received. The listener has three options: it can provide authentication credentials, it can cancel the request and display the error page, or it can take no action on the challenge. If bad user credentials are provided, this may be called multiple times for the same request.",
"parameters": [
{
"type": "object",
@@ -6090,10 +6099,15 @@
"description": "Array of extra information that should be passed to the listener function.",
"items": {
"type": "string",
- "enum": ["responseHeaders"]
+ "enum": ["responseHeaders", "blocking"]
}
}
- ]
+ ],
+ "returns": {
+ "$ref": "BlockingResponse",
+ "description": "If \"blocking\" is specified in the \"extraInfoSpec\" parameter, the event listener should return an object of this type.",
+ "optional": true
+ }
},
{
"name": "onResponseStarted",
diff --git a/chrome/common/extensions/docs/experimental.webRequest.html b/chrome/common/extensions/docs/experimental.webRequest.html
index 09f9dcd..23650dd 100644
--- a/chrome/common/extensions/docs/experimental.webRequest.html
+++ b/chrome/common/extensions/docs/experimental.webRequest.html
@@ -961,7 +961,7 @@ chrome.windows.onRemoved.addListener(
<div class="description">
<p class="todo" style="display: none; ">Undocumented.</p>
- <p>Fired when an authentication failure was received. Depending on whether the user provides credentials, the request is either reissued or cancelled.</p>
+ <p>Fired when an authentication failure was received. The listener has three options: it can provide authentication credentials, it can cancel the request and display the error page, or it can take no action on the challenge. If bad user credentials are provided, this may be called multiple times for the same request.</p>
<!-- LISTENER PARAMETERS -->
<div>
@@ -2159,7 +2159,7 @@ chrome.windows.onRemoved.addListener(
array of <span><span></span></span>
</span>
<span>string</span>
- <span>["responseHeaders"]</span>
+ <span>["responseHeaders", "blocking"]</span>
</span>
</span></span>
</span>
@@ -2217,11 +2217,76 @@ chrome.windows.onRemoved.addListener(
</div>
<!-- LISTENER RETURN VALUE -->
- <h4 style="display: none; ">Listener returns</h4>
+ <h4>Listener returns</h4>
<dl>
- <div style="display: none; ">
+ <div>
<div>
- </div>
+ <dt>
+ <var style="display: none; ">paramName</var>
+ <em>
+
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional">optional</span>
+ <span class="enum" style="display: none; ">enumerated</span>
+ <span id="typeTemplate">
+ <span>
+ <a href="experimental.webRequest.html#type-BlockingResponse">BlockingResponse</a>
+ </span>
+ <span style="display: none; ">
+ <span>
+ array of <span><span></span></span>
+ </span>
+ <span>paramType</span>
+ <span></span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo" style="display: none; ">
+ Undocumented.
+ </dd>
+ <dd>If "blocking" is specified in the "extraInfoSpec" parameter, the event listener should return an object of this type.</dd>
+ <dd style="display: none; ">
+ This parameter was added in version
+ <b><span></span></b>.
+ You must omit this parameter in earlier versions,
+ and you may omit it in any version. If you require this
+ parameter, the manifest key
+ <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a>
+ can ensure that your extension won't be run in an earlier browser version.
+ </dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+
+ <!-- OBJECT METHODS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ <!-- OBJECT EVENT FIELDS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ <!-- FUNCTION PARAMETERS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ </div>
</div>
</dl>
@@ -10534,6 +10599,211 @@ chrome.windows.onRemoved.addListener(
</dd>
</div>
+ </div><div>
+ <div>
+ <dt>
+ <var>authCredentials</var>
+ <em>
+
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional">optional</span>
+ <span class="enum" style="display: none; ">enumerated</span>
+ <span id="typeTemplate">
+ <span style="display: none; ">
+ <a> Type</a>
+ </span>
+ <span>
+ <span style="display: none; ">
+ array of <span><span></span></span>
+ </span>
+ <span>object</span>
+ <span style="display: none; "></span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo" style="display: none; ">
+ Undocumented.
+ </dd>
+ <dd>Only used as a response to the onAuthRequired event. If set, the request is made using the supplied credentials.</dd>
+ <dd style="display: none; ">
+ This parameter was added in version
+ <b><span></span></b>.
+ You must omit this parameter in earlier versions,
+ and you may omit it in any version. If you require this
+ parameter, the manifest key
+ <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a>
+ can ensure that your extension won't be run in an earlier browser version.
+ </dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd>
+ <dl>
+ <div>
+ <div>
+ <dt>
+ <var>username</var>
+ <em>
+
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional" style="display: none; ">optional</span>
+ <span class="enum" style="display: none; ">enumerated</span>
+ <span id="typeTemplate">
+ <span style="display: none; ">
+ <a> Type</a>
+ </span>
+ <span>
+ <span style="display: none; ">
+ array of <span><span></span></span>
+ </span>
+ <span>string</span>
+ <span style="display: none; "></span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo">
+ Undocumented.
+ </dd>
+ <dd style="display: none; ">
+ Description of this parameter from the json schema.
+ </dd>
+ <dd style="display: none; ">
+ This parameter was added in version
+ <b><span></span></b>.
+ You must omit this parameter in earlier versions,
+ and you may omit it in any version. If you require this
+ parameter, the manifest key
+ <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a>
+ can ensure that your extension won't be run in an earlier browser version.
+ </dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+
+ <!-- OBJECT METHODS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ <!-- OBJECT EVENT FIELDS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ <!-- FUNCTION PARAMETERS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ </div>
+ </div><div>
+ <div>
+ <dt>
+ <var>password</var>
+ <em>
+
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional" style="display: none; ">optional</span>
+ <span class="enum" style="display: none; ">enumerated</span>
+ <span id="typeTemplate">
+ <span style="display: none; ">
+ <a> Type</a>
+ </span>
+ <span>
+ <span style="display: none; ">
+ array of <span><span></span></span>
+ </span>
+ <span>string</span>
+ <span style="display: none; "></span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo">
+ Undocumented.
+ </dd>
+ <dd style="display: none; ">
+ Description of this parameter from the json schema.
+ </dd>
+ <dd style="display: none; ">
+ This parameter was added in version
+ <b><span></span></b>.
+ You must omit this parameter in earlier versions,
+ and you may omit it in any version. If you require this
+ parameter, the manifest key
+ <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a>
+ can ensure that your extension won't be run in an earlier browser version.
+ </dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+
+ <!-- OBJECT METHODS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ <!-- OBJECT EVENT FIELDS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ <!-- FUNCTION PARAMETERS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ </div>
+ </div>
+ </dl>
+ </dd>
+
+ <!-- OBJECT METHODS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ <!-- OBJECT EVENT FIELDS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ <!-- FUNCTION PARAMETERS -->
+ <dd style="display: none; ">
+ <div></div>
+ </dd>
+
+ </div>
</div>
</dl>
</dd>
diff --git a/chrome/test/data/extensions/api_test/webrequest/framework.js b/chrome/test/data/extensions/api_test/webrequest/framework.js
index 635c2e3..939e63e 100644
--- a/chrome/test/data/extensions/api_test/webrequest/framework.js
+++ b/chrome/test/data/extensions/api_test/webrequest/framework.js
@@ -221,7 +221,7 @@ function initListeners(filter, extraInfoSpec) {
chrome.experimental.webRequest.onAuthRequired.addListener(
function(details) {
return captureEvent("onAuthRequired", details);
- }, filter, intersect(extraInfoSpec, ["responseHeaders"]));
+ }, filter, intersect(extraInfoSpec, ["blocking", "responseHeaders"]));
chrome.experimental.webRequest.onResponseStarted.addListener(
function(details) {
return captureEvent("onResponseStarted", details);
diff --git a/chrome/test/data/extensions/api_test/webrequest/test_api.html b/chrome/test/data/extensions/api_test/webrequest/test_api.html
index c43cea1..ea05e20 100644
--- a/chrome/test/data/extensions/api_test/webrequest/test_api.html
+++ b/chrome/test/data/extensions/api_test/webrequest/test_api.html
@@ -17,6 +17,8 @@ chrome.test.runTests([
function(details) {});
chrome.experimental.webRequest.onErrorOccurred.addListener(
function(details) {});
+ chrome.experimental.webRequest.onAuthRequired.addListener(
+ function(details) {});
chrome.test.succeed();
},
diff --git a/chrome/test/data/extensions/api_test/webrequest/test_auth_required.html b/chrome/test/data/extensions/api_test/webrequest/test_auth_required.html
new file mode 100644
index 0000000..3836a02
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/webrequest/test_auth_required.html
@@ -0,0 +1,323 @@
+<script src="framework.js">
+</script>
+<script>
+function getURLAuthRequired() {
+ return getServerURL('auth-basic');
+}
+
+runTests([
+ // onAuthRequired is not a blocking function in this variant.
+ function authRequiredNonBlocking() {
+ expect(
+ [ // events
+ { label: "onBeforeRequest",
+ event: "onBeforeRequest",
+ details: {
+ url: getURLAuthRequired(),
+ frameUrl: getURLAuthRequired()
+ }
+ },
+ { label: "onBeforeSendHeaders",
+ event: "onBeforeSendHeaders",
+ details: {
+ url: getURLAuthRequired(),
+ // Note: no requestHeaders because we don't ask for them.
+ },
+ },
+ { label: "onSendHeaders",
+ event: "onSendHeaders",
+ details: {
+ url: getURLAuthRequired(),
+ }
+ },
+ { label: "onHeadersReceived",
+ event: "onHeadersReceived",
+ details: {
+ url: getURLAuthRequired(),
+ responseHeadersExist: true,
+ statusLine: "HTTP/1.0 401 Unauthorized"
+ }
+ },
+ { label: "onAuthRequired",
+ event: "onAuthRequired",
+ details: {
+ url: getURLAuthRequired(),
+ isProxy: false,
+ scheme: "basic",
+ realm: "testrealm",
+ challenger: {host: testServer, port: testServerPort},
+ responseHeadersExist: true,
+ statusLine: "HTTP/1.0 401 Unauthorized",
+ }
+ },
+ { label: "onResponseStarted",
+ event: "onResponseStarted",
+ details: {
+ url: getURLAuthRequired(),
+ fromCache: false,
+ statusCode: 401,
+ ip: "127.0.0.1",
+ responseHeadersExist: true,
+ statusLine: "HTTP/1.0 401 Unauthorized",
+ }
+ },
+ { label: "onCompleted",
+ event: "onCompleted",
+ details: {
+ url: getURLAuthRequired(),
+ fromCache: false,
+ statusCode: 401,
+ ip: "127.0.0.1",
+ responseHeadersExist: true,
+ statusLine: "HTTP/1.0 401 Unauthorized",
+ }
+ },
+ ],
+ [ // event order
+ ["onBeforeRequest", "onBeforeSendHeaders", "onSendHeaders",
+ "onHeadersReceived", "onAuthRequired", "onResponseStarted",
+ "onCompleted"]
+ ],
+ {}, ["responseHeaders"]);
+ navigateAndWait(getURLAuthRequired());
+ },
+
+ // onAuthRequired is a blocking function but takes no action in this variant.
+ function authRequiredNoAction() {
+ expect(
+ [ // events
+ { label: "onBeforeRequest",
+ event: "onBeforeRequest",
+ details: {
+ url: getURLAuthRequired(),
+ frameUrl: getURLAuthRequired()
+ }
+ },
+ { label: "onBeforeSendHeaders",
+ event: "onBeforeSendHeaders",
+ details: {
+ url: getURLAuthRequired(),
+ // Note: no requestHeaders because we don't ask for them.
+ },
+ },
+ { label: "onSendHeaders",
+ event: "onSendHeaders",
+ details: {
+ url: getURLAuthRequired(),
+ }
+ },
+ { label: "onHeadersReceived",
+ event: "onHeadersReceived",
+ details: {
+ url: getURLAuthRequired(),
+ responseHeadersExist: true,
+ statusLine: "HTTP/1.0 401 Unauthorized"
+ }
+ },
+ { label: "onAuthRequired",
+ event: "onAuthRequired",
+ details: {
+ url: getURLAuthRequired(),
+ isProxy: false,
+ scheme: "basic",
+ realm: "testrealm",
+ challenger: {host: testServer, port: testServerPort},
+ responseHeadersExist: true,
+ statusLine: "HTTP/1.0 401 Unauthorized",
+ }
+ },
+ { label: "onResponseStarted",
+ event: "onResponseStarted",
+ details: {
+ url: getURLAuthRequired(),
+ fromCache: false,
+ statusCode: 401,
+ ip: "127.0.0.1",
+ responseHeadersExist: true,
+ statusLine: "HTTP/1.0 401 Unauthorized",
+ }
+ },
+ { label: "onCompleted",
+ event: "onCompleted",
+ details: {
+ url: getURLAuthRequired(),
+ fromCache: false,
+ statusCode: 401,
+ ip: "127.0.0.1",
+ responseHeadersExist: true,
+ statusLine: "HTTP/1.0 401 Unauthorized",
+ }
+ },
+ ],
+ [ // event order
+ ["onBeforeRequest", "onBeforeSendHeaders", "onSendHeaders",
+ "onHeadersReceived", "onAuthRequired", "onResponseStarted",
+ "onCompleted"]
+ ],
+ {}, ["blocking", "responseHeaders"]);
+ navigateAndWait(getURLAuthRequired());
+ },
+
+ // onAuthRequired is a blocking function that cancels the auth attempt.
+ function authRequiredCancelAuth() {
+ expect(
+ [ // events
+ { label: "onBeforeRequest",
+ event: "onBeforeRequest",
+ details: {
+ url: getURLAuthRequired(),
+ frameUrl: getURLAuthRequired()
+ },
+ retval: {}
+ },
+ { label: "onBeforeSendHeaders",
+ event: "onBeforeSendHeaders",
+ details: {
+ url: getURLAuthRequired(),
+ // Note: no requestHeaders because we don't ask for them.
+ },
+ retval: {}
+ },
+ { label: "onSendHeaders",
+ event: "onSendHeaders",
+ details: {
+ url: getURLAuthRequired(),
+ }
+ },
+ { label: "onHeadersReceived",
+ event: "onHeadersReceived",
+ details: {
+ url: getURLAuthRequired(),
+ responseHeadersExist: true,
+ statusLine: "HTTP/1.0 401 Unauthorized"
+ }
+ },
+ { label: "onAuthRequired",
+ event: "onAuthRequired",
+ details: {
+ url: getURLAuthRequired(),
+ isProxy: false,
+ scheme: "basic",
+ realm: "testrealm",
+ challenger: {host: testServer, port: testServerPort},
+ responseHeadersExist: true,
+ statusLine: "HTTP/1.0 401 Unauthorized",
+ },
+ retval: {cancel: true}
+ },
+ { label: "onResponseStarted",
+ event: "onResponseStarted",
+ details: {
+ url: getURLAuthRequired(),
+ fromCache: false,
+ statusCode: 401,
+ ip: "127.0.0.1",
+ responseHeadersExist: true,
+ statusLine: "HTTP/1.0 401 Unauthorized",
+ }
+ },
+ { label: "onCompleted",
+ event: "onCompleted",
+ details: {
+ url: getURLAuthRequired(),
+ fromCache: false,
+ statusCode: 401,
+ ip: "127.0.0.1",
+ responseHeadersExist: true,
+ statusLine: "HTTP/1.0 401 Unauthorized",
+ }
+ },
+ ],
+ [ // event order
+ ["onBeforeRequest", "onBeforeSendHeaders", "onSendHeaders",
+ "onHeadersReceived", "onAuthRequired", "onResponseStarted",
+ "onCompleted"]
+ ],
+ {},
+ ["responseHeaders", "blocking"]);
+ navigateAndWait(getURLAuthRequired());
+ },
+
+ // onAuthRequired is a blocking function that sets authentication credentials.
+ // This needs to be done last, otherwise the authentication
+ // credentials will be cached and the other tests will fail.
+ function authRequiredSetAuth() {
+ expect(
+ [ // events
+ { label: "onBeforeRequest",
+ event: "onBeforeRequest",
+ details: {
+ url: getURLAuthRequired(),
+ frameUrl: getURLAuthRequired()
+ },
+ retval: {}
+ },
+ { label: "onBeforeSendHeaders",
+ event: "onBeforeSendHeaders",
+ details: {
+ url: getURLAuthRequired(),
+ // Note: no requestHeaders because we don't ask for them.
+ },
+ retval: {}
+ },
+ { label: "onSendHeaders",
+ event: "onSendHeaders",
+ details: {
+ url: getURLAuthRequired(),
+ }
+ },
+ { label: "onHeadersReceived",
+ event: "onHeadersReceived",
+ details: {
+ url: getURLAuthRequired(),
+ responseHeadersExist: true,
+ statusLine: "HTTP/1.0 401 Unauthorized"
+ }
+ },
+ { label: "onAuthRequired",
+ event: "onAuthRequired",
+ details: {
+ url: getURLAuthRequired(),
+ isProxy: false,
+ scheme: "basic",
+ realm: "testrealm",
+ challenger: {host: testServer, port: testServerPort},
+ responseHeadersExist: true,
+ statusLine: "HTTP/1.0 401 Unauthorized",
+ },
+ retval: {authCredentials: {username: "foo", password: "secret"}}
+ },
+ { label: "onResponseStarted",
+ event: "onResponseStarted",
+ details: {
+ url: getURLAuthRequired(),
+ fromCache: false,
+ statusCode: 200,
+ ip: "127.0.0.1",
+ responseHeadersExist: true,
+ statusLine: "HTTP/1.1 200 OK",
+ }
+ },
+ { label: "onCompleted",
+ event: "onCompleted",
+ details: {
+ url: getURLAuthRequired(),
+ fromCache: false,
+ statusCode: 200,
+ ip: "127.0.0.1",
+ responseHeadersExist: true,
+ statusLine: "HTTP/1.1 200 OK",
+ }
+ },
+ ],
+ [ // event order
+ ["onBeforeRequest", "onBeforeSendHeaders", "onSendHeaders",
+ "onHeadersReceived", "onAuthRequired", "onResponseStarted",
+ "onCompleted"]
+ ],
+ {},
+ ["responseHeaders", "blocking"]);
+ navigateAndWait(getURLAuthRequired());
+ },
+]);
+</script>
diff --git a/chrome/test/data/extensions/api_test/webrequest/test_complex.html b/chrome/test/data/extensions/api_test/webrequest/test_complex.html
index 8cd7019..4628576 100644
--- a/chrome/test/data/extensions/api_test/webrequest/test_complex.html
+++ b/chrome/test/data/extensions/api_test/webrequest/test_complex.html
@@ -1,10 +1,6 @@
<script src="framework.js">
</script>
<script>
-// Constants as functions, not to be called until after runTests.
-function getURLAuthRequired() {
- return getServerURL('auth-basic');
-}
function getURLHttpXHR() {
return getServerURL('files/extensions/api_test/webrequest/xhr/a.html');
}
@@ -203,82 +199,6 @@ runTests([
});
},
- // Loads a testserver page that requires authentication.
- function authRequired() {
- expect(
- [ // events
- { label: "onBeforeRequest",
- event: "onBeforeRequest",
- details: {
- url: getURLAuthRequired(),
- frameUrl: getURLAuthRequired()
- }
- },
- { label: "onBeforeSendHeaders",
- event: "onBeforeSendHeaders",
- details: {
- url: getURLAuthRequired(),
- // Note: no requestHeaders because we don't ask for them.
- },
- },
- { label: "onSendHeaders",
- event: "onSendHeaders",
- details: {
- url: getURLAuthRequired(),
- }
- },
- { label: "onHeadersReceived",
- event: "onHeadersReceived",
- details: {
- url: getURLAuthRequired(),
- responseHeadersExist: true,
- statusLine: "HTTP/1.0 401 Unauthorized",
- }
- },
- { label: "onAuthRequired",
- event: "onAuthRequired",
- details: {
- url: getURLAuthRequired(),
- isProxy: false,
- scheme: "basic",
- realm: "testrealm",
- challenger: {host: testServer, port: testServerPort},
- responseHeadersExist: true,
- statusLine: "HTTP/1.0 401 Unauthorized",
- }
- },
- { label: "onResponseStarted",
- event: "onResponseStarted",
- details: {
- url: getURLAuthRequired(),
- fromCache: false,
- statusCode: 401,
- ip: "127.0.0.1",
- responseHeadersExist: true,
- statusLine: "HTTP/1.0 401 Unauthorized",
- }
- },
- { label: "onCompleted",
- event: "onCompleted",
- details: {
- url: getURLAuthRequired(),
- fromCache: false,
- statusCode: 401,
- ip: "127.0.0.1",
- responseHeadersExist: true,
- statusLine: "HTTP/1.0 401 Unauthorized",
- }
- },
- ],
- [ // event order
- ["onBeforeRequest", "onBeforeSendHeaders", "onSendHeaders",
- "onHeadersReceived", "onAuthRequired", "onResponseStarted",
- "onCompleted"]
- ],
- {}, ["responseHeaders"]);
- navigateAndWait(getURLAuthRequired());
- },
-
// Navigates to a page to generates an XHR.
function xhrLoad() {
expect(
diff --git a/net/base/net_log_event_type_list.h b/net/base/net_log_event_type_list.h
index 1369790..b03bc27 100644
--- a/net/base/net_log_event_type_list.h
+++ b/net/base/net_log_event_type_list.h
@@ -1207,6 +1207,14 @@ EVENT_TYPE(CHROME_EXTENSION_MODIFIED_HEADERS)
// }
EVENT_TYPE(CHROME_EXTENSION_IGNORED_DUE_TO_CONFLICT)
+// This event is created when a Chrome extension provides authentication
+// credentials.
+//
+// {
+// "extension_id": <Extension ID that provides credentials>
+// }
+EVENT_TYPE(CHROME_EXTENSION_PROVIDE_AUTH_CREDENTIALS)
+
// ------------------------------------------------------------------------
// HostBlacklistManager
// ------------------------------------------------------------------------