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:
Jeremy Apthorp 2018-04-05 16:13:24 -07:00 коммит произвёл Charles Kerr
Родитель 4316949a1d
Коммит f0d08f4da1
14 изменённых файлов: 186 добавлений и 22 удалений

Просмотреть файл

@ -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", &params.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)
})
})
})
})