зеркало из https://github.com/electron/electron.git
Propagate referrer to new windows (#12397)
* Propagate referrer to new windows Fixes #9205 * Rearrange -new-window event arguments for backwards-compatibility * Plumb referrer policy through guest-window-manager * Document the Referrer structure and its uses * Add tests for referrer in new windows * Docs nits
This commit is contained in:
Родитель
4316949a1d
Коммит
f0d08f4da1
|
@ -689,8 +689,8 @@ bool App::CanCreateWindow(
|
|||
content::WebContents::FromRenderFrameHost(opener);
|
||||
if (web_contents) {
|
||||
auto api_web_contents = WebContents::CreateFrom(isolate(), web_contents);
|
||||
api_web_contents->OnCreateWindow(target_url, frame_name, disposition,
|
||||
additional_features, body);
|
||||
api_web_contents->OnCreateWindow(target_url, referrer, frame_name,
|
||||
disposition, additional_features, body);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -525,12 +525,14 @@ bool WebContents::DidAddMessageToConsole(content::WebContents* source,
|
|||
|
||||
void WebContents::OnCreateWindow(
|
||||
const GURL& target_url,
|
||||
const content::Referrer& referrer,
|
||||
const std::string& frame_name,
|
||||
WindowOpenDisposition disposition,
|
||||
const std::vector<std::string>& features,
|
||||
const scoped_refptr<content::ResourceRequestBody>& body) {
|
||||
if (type_ == BROWSER_WINDOW || type_ == OFF_SCREEN)
|
||||
Emit("-new-window", target_url, frame_name, disposition, features, body);
|
||||
Emit("-new-window", target_url, frame_name, disposition, features, body,
|
||||
referrer);
|
||||
else
|
||||
Emit("new-window", target_url, frame_name, disposition, features);
|
||||
}
|
||||
|
@ -1082,10 +1084,12 @@ void WebContents::LoadURL(const GURL& url, const mate::Dictionary& options) {
|
|||
|
||||
content::NavigationController::LoadURLParams params(url);
|
||||
|
||||
GURL http_referrer;
|
||||
if (options.Get("httpReferrer", &http_referrer))
|
||||
params.referrer = content::Referrer(http_referrer.GetAsReferrer(),
|
||||
blink::kWebReferrerPolicyDefault);
|
||||
if (!options.Get("httpReferrer", ¶ms.referrer)) {
|
||||
GURL http_referrer;
|
||||
if (options.Get("httpReferrer", &http_referrer))
|
||||
params.referrer = content::Referrer(http_referrer.GetAsReferrer(),
|
||||
blink::kWebReferrerPolicyDefault);
|
||||
}
|
||||
|
||||
std::string user_agent;
|
||||
if (options.Get("userAgent", &user_agent))
|
||||
|
|
|
@ -223,6 +223,7 @@ class WebContents : public mate::TrackableObject<WebContents>,
|
|||
// Create window with the given disposition.
|
||||
void OnCreateWindow(
|
||||
const GURL& target_url,
|
||||
const content::Referrer& referrer,
|
||||
const std::string& frame_name,
|
||||
WindowOpenDisposition disposition,
|
||||
const std::vector<std::string>& features,
|
||||
|
|
|
@ -481,4 +481,56 @@ v8::Local<v8::Value> Converter<blink::WebCache::ResourceTypeStats>::ToV8(
|
|||
return dict.GetHandle();
|
||||
}
|
||||
|
||||
// static
|
||||
v8::Local<v8::Value> Converter<blink::WebReferrerPolicy>::ToV8(
|
||||
v8::Isolate* isolate,
|
||||
const blink::WebReferrerPolicy& in) {
|
||||
switch (in) {
|
||||
case blink::kWebReferrerPolicyDefault:
|
||||
return mate::StringToV8(isolate, "default");
|
||||
case blink::kWebReferrerPolicyAlways:
|
||||
return mate::StringToV8(isolate, "unsafe-url");
|
||||
case blink::kWebReferrerPolicyNoReferrerWhenDowngrade:
|
||||
return mate::StringToV8(isolate, "no-referrer-when-downgrade");
|
||||
case blink::kWebReferrerPolicyNever:
|
||||
return mate::StringToV8(isolate, "no-referrer");
|
||||
case blink::kWebReferrerPolicyOrigin:
|
||||
return mate::StringToV8(isolate, "origin");
|
||||
case blink::kWebReferrerPolicyNoReferrerWhenDowngradeOriginWhenCrossOrigin:
|
||||
return mate::StringToV8(isolate, "strict-origin-when-cross-origin");
|
||||
case blink::kWebReferrerPolicySameOrigin:
|
||||
return mate::StringToV8(isolate, "same-origin");
|
||||
case blink::kWebReferrerPolicyStrictOrigin:
|
||||
return mate::StringToV8(isolate, "strict-origin");
|
||||
default:
|
||||
return mate::StringToV8(isolate, "no-referrer");
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
bool Converter<blink::WebReferrerPolicy>::FromV8(v8::Isolate* isolate,
|
||||
v8::Handle<v8::Value> val, blink::WebReferrerPolicy* out) {
|
||||
std::string policy = base::ToLowerASCII(V8ToString(val));
|
||||
if (policy == "default")
|
||||
*out = blink::kWebReferrerPolicyDefault;
|
||||
else if (policy == "unsafe-url")
|
||||
*out = blink::kWebReferrerPolicyAlways;
|
||||
else if (policy == "no-referrer-when-downgrade")
|
||||
*out = blink::kWebReferrerPolicyNoReferrerWhenDowngrade;
|
||||
else if (policy == "no-referrer")
|
||||
*out = blink::kWebReferrerPolicyNever;
|
||||
else if (policy == "origin")
|
||||
*out = blink::kWebReferrerPolicyOrigin;
|
||||
else if (policy == "strict-origin-when-cross-origin")
|
||||
*out =
|
||||
blink::kWebReferrerPolicyNoReferrerWhenDowngradeOriginWhenCrossOrigin;
|
||||
else if (policy == "same-origin")
|
||||
*out = blink::kWebReferrerPolicySameOrigin;
|
||||
else if (policy == "strict-origin")
|
||||
*out = blink::kWebReferrerPolicyStrictOrigin;
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace mate
|
||||
|
|
|
@ -119,6 +119,14 @@ struct Converter<blink::WebCache::ResourceTypeStats> {
|
|||
const blink::WebCache::ResourceTypeStats& stats);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Converter<blink::WebReferrerPolicy> {
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
const blink::WebReferrerPolicy& in);
|
||||
static bool FromV8(v8::Isolate* isolate, v8::Local<v8::Value> val,
|
||||
blink::WebReferrerPolicy* out);
|
||||
};
|
||||
|
||||
v8::Local<v8::Value> EditFlagsToV8(v8::Isolate* isolate, int editFlags);
|
||||
v8::Local<v8::Value> MediaFlagsToV8(v8::Isolate* isolate, int mediaFlags);
|
||||
|
||||
|
|
|
@ -318,4 +318,31 @@ bool Converter<content::WebContents*>::FromV8(
|
|||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
v8::Local<v8::Value> Converter<content::Referrer>::ToV8(
|
||||
v8::Isolate* isolate, const content::Referrer& val) {
|
||||
mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
|
||||
dict.Set("url", ConvertToV8(isolate, val.url));
|
||||
dict.Set("policy", ConvertToV8(isolate, val.policy));
|
||||
return mate::ConvertToV8(isolate, dict);
|
||||
}
|
||||
|
||||
// static
|
||||
bool Converter<content::Referrer>::FromV8(
|
||||
v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
content::Referrer* out) {
|
||||
mate::Dictionary dict;
|
||||
if (!ConvertFromV8(isolate, val, &dict))
|
||||
return false;
|
||||
|
||||
if (!dict.Get("url", &out->url))
|
||||
return false;
|
||||
|
||||
if (!dict.Get("policy", &out->policy))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace mate
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include "content/public/browser/permission_type.h"
|
||||
#include "content/public/common/menu_item.h"
|
||||
#include "content/public/common/referrer.h"
|
||||
#include "content/public/common/stop_find_action.h"
|
||||
#include "native_mate/converter.h"
|
||||
#include "third_party/WebKit/public/platform/modules/permissions/permission_status.mojom.h"
|
||||
|
@ -71,6 +72,14 @@ struct Converter<content::WebContents*> {
|
|||
content::WebContents** out);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Converter<content::Referrer> {
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
const content::Referrer& val);
|
||||
static bool FromV8(v8::Isolate* isolate, v8::Local<v8::Value> val,
|
||||
content::Referrer* out);
|
||||
};
|
||||
|
||||
} // namespace mate
|
||||
|
||||
#endif // ATOM_COMMON_NATIVE_MATE_CONVERTERS_CONTENT_CONVERTER_H_
|
||||
|
|
|
@ -1148,7 +1148,7 @@ Same as `webContents.capturePage([rect, ]callback)`.
|
|||
|
||||
* `url` String
|
||||
* `options` Object (optional)
|
||||
* `httpReferrer` String (optional) - A HTTP Referrer url.
|
||||
* `httpReferrer` String (optional) - An HTTP Referrer url.
|
||||
* `userAgent` String (optional) - A user agent originating the request.
|
||||
* `extraHeaders` String (optional) - Extra headers separated by "\n"
|
||||
* `postData` ([UploadRawData[]](structures/upload-raw-data.md) | [UploadFile[]](structures/upload-file.md) | [UploadFileSystem[]](structures/upload-file-system.md) | [UploadBlob[]](structures/upload-blob.md)) (optional)
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
# Referrer Object
|
||||
|
||||
* `url` String - HTTP Referrer URL.
|
||||
* `policy` String - Can be `default`, `unsafe-url`,
|
||||
`no-referrer-when-downgrade`, `no-referrer`, `origin`,
|
||||
`strict-origin-when-cross-origin`, `same-origin`, `strict-origin`, or
|
||||
`no-referrer`. See the [Referrer-Policy spec][1] for more details on the
|
||||
meaning of these values.
|
||||
|
||||
[1]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy
|
|
@ -151,6 +151,9 @@ Returns:
|
|||
[`BrowserWindow`](browser-window.md).
|
||||
* `additionalFeatures` String[] - The non-standard features (features not handled
|
||||
by Chromium or Electron) given to `window.open()`.
|
||||
* `referrer` [Referrer](structures/referrer.md) - The referrer that will be
|
||||
passed to the new window. May or may not result in the `Referer` header being
|
||||
sent, depending on the referrer policy.
|
||||
|
||||
Emitted when the page requests to open a new window for a `url`. It could be
|
||||
requested by `window.open` or an external link like `<a target='_blank'>`.
|
||||
|
@ -609,7 +612,7 @@ for windows with *offscreen rendering* enabled.
|
|||
|
||||
* `url` String
|
||||
* `options` Object (optional)
|
||||
* `httpReferrer` String (optional) - A HTTP Referrer url.
|
||||
* `httpReferrer` (String | [Referrer](structures/referrer.md)) (optional) - An HTTP Referrer url.
|
||||
* `userAgent` String (optional) - A user agent originating the request.
|
||||
* `extraHeaders` String (optional) - Extra headers separated by "\n".
|
||||
* `postData` ([UploadRawData[]](structures/upload-raw-data.md) | [UploadFile[]](structures/upload-file.md) | [UploadFileSystem[]](structures/upload-file-system.md) | [UploadBlob[]](structures/upload-blob.md)) (optional)
|
||||
|
|
|
@ -306,7 +306,7 @@ webview.addEventListener('dom-ready', () => {
|
|||
|
||||
* `url` URL
|
||||
* `options` Object (optional)
|
||||
* `httpReferrer` String (optional) - A HTTP Referrer url.
|
||||
* `httpReferrer` String (optional) - An HTTP Referrer url.
|
||||
* `userAgent` String (optional) - A user agent originating the request.
|
||||
* `extraHeaders` String (optional) - Extra headers separated by "\n"
|
||||
* `postData` ([UploadRawData[]](structures/upload-raw-data.md) | [UploadFile[]](structures/upload-file.md) | [UploadFileSystem[]](structures/upload-file-system.md) | [UploadBlob[]](structures/upload-blob.md)) (optional) -
|
||||
|
|
|
@ -19,16 +19,16 @@ BrowserWindow.prototype._init = function () {
|
|||
}
|
||||
|
||||
// Make new windows requested by links behave like "window.open"
|
||||
this.webContents.on('-new-window', (event, url, frameName,
|
||||
disposition, additionalFeatures,
|
||||
postData) => {
|
||||
this.webContents.on('-new-window', (event, url, frameName, disposition,
|
||||
additionalFeatures, postData,
|
||||
referrer) => {
|
||||
const options = {
|
||||
show: true,
|
||||
width: 800,
|
||||
height: 600
|
||||
}
|
||||
ipcMain.emit('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN',
|
||||
event, url, frameName, disposition,
|
||||
event, url, referrer, frameName, disposition,
|
||||
options, additionalFeatures, postData)
|
||||
})
|
||||
|
||||
|
@ -59,8 +59,9 @@ BrowserWindow.prototype._init = function () {
|
|||
height: height || 600,
|
||||
webContents: webContents
|
||||
}
|
||||
const referrer = { url: '', policy: 'default' }
|
||||
ipcMain.emit('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN',
|
||||
event, url, frameName, disposition, options)
|
||||
event, url, referrer, frameName, disposition, options)
|
||||
})
|
||||
|
||||
// window.resizeTo(...)
|
||||
|
|
|
@ -102,7 +102,7 @@ const setupGuest = function (embedder, frameName, guest, options) {
|
|||
}
|
||||
|
||||
// Create a new guest created by |embedder| with |options|.
|
||||
const createGuest = function (embedder, url, frameName, options, postData) {
|
||||
const createGuest = function (embedder, url, referrer, frameName, options, postData) {
|
||||
let guest = frameToGuest.get(frameName)
|
||||
if (frameName && (guest != null)) {
|
||||
guest.loadURL(url)
|
||||
|
@ -137,7 +137,9 @@ const createGuest = function (embedder, url, frameName, options, postData) {
|
|||
//
|
||||
// The above code would not work if a navigation to "about:blank" is done
|
||||
// here, since the window would be cleared of all changes in the next tick.
|
||||
const loadOptions = {}
|
||||
const loadOptions = {
|
||||
httpReferrer: referrer
|
||||
}
|
||||
if (postData != null) {
|
||||
loadOptions.postData = postData
|
||||
loadOptions.extraHeaders = 'content-type: application/x-www-form-urlencoded'
|
||||
|
@ -242,16 +244,17 @@ ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', (event, url, frameName,
|
|||
}
|
||||
}
|
||||
|
||||
const referrer = { url: '', policy: 'default' }
|
||||
ipcMain.emit('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN', event,
|
||||
url, frameName, disposition, options, additionalFeatures)
|
||||
url, referrer, frameName, disposition, options, additionalFeatures)
|
||||
})
|
||||
|
||||
// Routed window.open messages with fully parsed options
|
||||
ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN', function (event, url, frameName,
|
||||
disposition, options,
|
||||
ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN', function (event, url, referrer,
|
||||
frameName, disposition, options,
|
||||
additionalFeatures, postData) {
|
||||
options = mergeBrowserWindowOptions(event.sender, options)
|
||||
event.sender.emit('new-window', event, url, frameName, disposition, options, additionalFeatures)
|
||||
event.sender.emit('new-window', event, url, frameName, disposition, options, additionalFeatures, referrer)
|
||||
const {newGuest} = event
|
||||
if ((event.sender.isGuest() && !event.sender.allowPopups) || event.defaultPrevented) {
|
||||
if (newGuest != null) {
|
||||
|
@ -265,7 +268,7 @@ ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN', function (event
|
|||
event.returnValue = null
|
||||
}
|
||||
} else {
|
||||
event.returnValue = createGuest(event.sender, url, frameName, options, postData)
|
||||
event.returnValue = createGuest(event.sender, url, referrer, frameName, options, postData)
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -708,4 +708,50 @@ describe('webContents module', () => {
|
|||
w.loadURL(`file://${fixtures}/pages/a.html`)
|
||||
})
|
||||
})
|
||||
|
||||
describe('referrer', () => {
|
||||
it('propagates referrer information to new target=_blank windows', (done) => {
|
||||
const server = http.createServer((req, res) => {
|
||||
if (req.url === '/should_have_referrer') {
|
||||
assert.equal(req.headers.referer, 'http://127.0.0.1:' + server.address().port + '/')
|
||||
return done()
|
||||
}
|
||||
res.end('<a id="a" href="/should_have_referrer" target="_blank">link</a>')
|
||||
})
|
||||
server.listen(0, '127.0.0.1', () => {
|
||||
const url = 'http://127.0.0.1:' + server.address().port + '/'
|
||||
w.webContents.once('did-finish-load', () => {
|
||||
w.webContents.once('new-window', (event, newUrl, frameName, disposition, options, features, referrer) => {
|
||||
assert.equal(referrer.url, url)
|
||||
assert.equal(referrer.policy, 'no-referrer-when-downgrade')
|
||||
})
|
||||
w.webContents.executeJavaScript('a.click()')
|
||||
})
|
||||
w.loadURL(url)
|
||||
})
|
||||
})
|
||||
|
||||
// TODO(jeremy): window.open() in a real browser passes the referrer, but
|
||||
// our hacked-up window.open() shim doesn't. It should.
|
||||
xit('propagates referrer information to windows opened with window.open', (done) => {
|
||||
const server = http.createServer((req, res) => {
|
||||
if (req.url === '/should_have_referrer') {
|
||||
assert.equal(req.headers.referer, 'http://127.0.0.1:' + server.address().port + '/')
|
||||
return done()
|
||||
}
|
||||
res.end('')
|
||||
})
|
||||
server.listen(0, '127.0.0.1', () => {
|
||||
const url = 'http://127.0.0.1:' + server.address().port + '/'
|
||||
w.webContents.once('did-finish-load', () => {
|
||||
w.webContents.once('new-window', (event, newUrl, frameName, disposition, options, features, referrer) => {
|
||||
assert.equal(referrer.url, url)
|
||||
assert.equal(referrer.policy, 'no-referrer-when-downgrade')
|
||||
})
|
||||
w.webContents.executeJavaScript('window.open(location.href + "should_have_referrer")')
|
||||
})
|
||||
w.loadURL(url)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
Загрузка…
Ссылка в новой задаче