Merge remote-tracking branch 'origin/master' into chrome58
This commit is contained in:
commit
c6c93211be
25 changed files with 320 additions and 46 deletions
|
@ -75,7 +75,7 @@ void BrowserView::Init(v8::Isolate* isolate,
|
||||||
}
|
}
|
||||||
|
|
||||||
BrowserView::~BrowserView() {
|
BrowserView::~BrowserView() {
|
||||||
api_web_contents_->DestroyWebContents();
|
api_web_contents_->DestroyWebContents(true /* async */);
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
#include "atom/browser/atom_browser_main_parts.h"
|
#include "atom/browser/atom_browser_main_parts.h"
|
||||||
#include "atom/common/native_mate_converters/callback.h"
|
#include "atom/common/native_mate_converters/callback.h"
|
||||||
#include "atom/common/native_mate_converters/value_converter.h"
|
#include "atom/common/native_mate_converters/value_converter.h"
|
||||||
#include "base/json/json_reader.h"
|
|
||||||
#include "base/json/json_writer.h"
|
#include "base/json/json_writer.h"
|
||||||
#include "content/public/browser/devtools_agent_host.h"
|
#include "content/public/browser/devtools_agent_host.h"
|
||||||
#include "content/public/browser/web_contents.h"
|
#include "content/public/browser/web_contents.h"
|
||||||
|
@ -45,12 +44,23 @@ void Debugger::DispatchProtocolMessage(DevToolsAgentHost* agent_host,
|
||||||
const std::string& message) {
|
const std::string& message) {
|
||||||
DCHECK(agent_host == agent_host_.get());
|
DCHECK(agent_host == agent_host_.get());
|
||||||
|
|
||||||
std::unique_ptr<base::Value> parsed_message(base::JSONReader::Read(message));
|
v8::Locker locker(isolate());
|
||||||
if (!parsed_message->IsType(base::Value::Type::DICTIONARY))
|
v8::HandleScope handle_scope(isolate());
|
||||||
return;
|
|
||||||
|
v8::Local<v8::String> local_message =
|
||||||
|
v8::String::NewFromUtf8(isolate(), message.data());
|
||||||
|
v8::MaybeLocal<v8::Value> parsed_message = v8::JSON::Parse(
|
||||||
|
isolate()->GetCurrentContext(), local_message);
|
||||||
|
if (parsed_message.IsEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
|
||||||
|
if (!mate::ConvertFromV8(isolate(), parsed_message.ToLocalChecked(),
|
||||||
|
dict.get())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
base::DictionaryValue* dict =
|
|
||||||
static_cast<base::DictionaryValue*>(parsed_message.get());
|
|
||||||
int id;
|
int id;
|
||||||
if (!dict->GetInteger("id", &id)) {
|
if (!dict->GetInteger("id", &id)) {
|
||||||
std::string method;
|
std::string method;
|
||||||
|
|
|
@ -418,15 +418,28 @@ WebContents::~WebContents() {
|
||||||
guest_delegate_->Destroy();
|
guest_delegate_->Destroy();
|
||||||
|
|
||||||
RenderViewDeleted(web_contents()->GetRenderViewHost());
|
RenderViewDeleted(web_contents()->GetRenderViewHost());
|
||||||
DestroyWebContents();
|
|
||||||
|
if (type_ == WEB_VIEW) {
|
||||||
|
DestroyWebContents(false /* async */);
|
||||||
|
} else {
|
||||||
|
if (type_ == BROWSER_WINDOW && owner_window()) {
|
||||||
|
owner_window()->CloseContents(nullptr);
|
||||||
|
} else {
|
||||||
|
DestroyWebContents(true /* async */);
|
||||||
|
}
|
||||||
|
// The WebContentsDestroyed will not be called automatically because we
|
||||||
|
// destroy the webContents in the next tick. So we have to manually
|
||||||
|
// call it here to make sure "destroyed" event is emitted.
|
||||||
|
WebContentsDestroyed();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebContents::DestroyWebContents() {
|
void WebContents::DestroyWebContents(bool async) {
|
||||||
// This event is only for internal use, which is emitted when WebContents is
|
// This event is only for internal use, which is emitted when WebContents is
|
||||||
// being destroyed.
|
// being destroyed.
|
||||||
Emit("will-destroy");
|
Emit("will-destroy");
|
||||||
ResetManagedWebContents();
|
ResetManagedWebContents(async);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WebContents::DidAddMessageToConsole(content::WebContents* source,
|
bool WebContents::DidAddMessageToConsole(content::WebContents* source,
|
||||||
|
@ -478,7 +491,7 @@ void WebContents::AddNewContents(content::WebContents* source,
|
||||||
if (Emit("-add-new-contents", api_web_contents, disposition, user_gesture,
|
if (Emit("-add-new-contents", api_web_contents, disposition, user_gesture,
|
||||||
initial_rect.x(), initial_rect.y(), initial_rect.width(),
|
initial_rect.x(), initial_rect.y(), initial_rect.width(),
|
||||||
initial_rect.height())) {
|
initial_rect.height())) {
|
||||||
api_web_contents->DestroyWebContents();
|
api_web_contents->DestroyWebContents(true /* async */);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -817,10 +830,8 @@ void WebContents::DidFinishNavigation(
|
||||||
|
|
||||||
void WebContents::TitleWasSet(content::NavigationEntry* entry,
|
void WebContents::TitleWasSet(content::NavigationEntry* entry,
|
||||||
bool explicit_set) {
|
bool explicit_set) {
|
||||||
if (entry)
|
auto title = entry ? entry->GetTitle() : base::string16();
|
||||||
Emit("-page-title-updated", entry->GetTitle(), explicit_set);
|
Emit("page-title-updated", title, explicit_set);
|
||||||
else
|
|
||||||
Emit("-page-title-updated", "", explicit_set);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebContents::DidUpdateFaviconURL(
|
void WebContents::DidUpdateFaviconURL(
|
||||||
|
|
|
@ -78,7 +78,7 @@ class WebContents : public mate::TrackableObject<WebContents>,
|
||||||
v8::Local<v8::FunctionTemplate> prototype);
|
v8::Local<v8::FunctionTemplate> prototype);
|
||||||
|
|
||||||
// Notifies to destroy any guest web contents before destroying self.
|
// Notifies to destroy any guest web contents before destroying self.
|
||||||
void DestroyWebContents();
|
void DestroyWebContents(bool async);
|
||||||
|
|
||||||
int64_t GetID() const;
|
int64_t GetID() const;
|
||||||
int GetProcessID() const;
|
int GetProcessID() const;
|
||||||
|
|
|
@ -173,7 +173,7 @@ void Window::WillDestroyNativeObject() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::OnWindowClosed() {
|
void Window::OnWindowClosed() {
|
||||||
api_web_contents_->DestroyWebContents();
|
api_web_contents_->DestroyWebContents(true /* async */);
|
||||||
|
|
||||||
RemoveFromWeakMap();
|
RemoveFromWeakMap();
|
||||||
window_->RemoveObserver(this);
|
window_->RemoveObserver(this);
|
||||||
|
|
|
@ -188,8 +188,13 @@ void CommonWebContentsDelegate::SetOwnerWindow(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommonWebContentsDelegate::ResetManagedWebContents() {
|
void CommonWebContentsDelegate::ResetManagedWebContents(bool async) {
|
||||||
web_contents_.reset();
|
if (async) {
|
||||||
|
base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE,
|
||||||
|
web_contents_.release());
|
||||||
|
} else {
|
||||||
|
web_contents_.reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
content::WebContents* CommonWebContentsDelegate::GetWebContents() const {
|
content::WebContents* CommonWebContentsDelegate::GetWebContents() const {
|
||||||
|
|
|
@ -112,7 +112,7 @@ class CommonWebContentsDelegate
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Destroy the managed InspectableWebContents object.
|
// Destroy the managed InspectableWebContents object.
|
||||||
void ResetManagedWebContents();
|
void ResetManagedWebContents(bool async);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Callback for when DevToolsSaveToFile has completed.
|
// Callback for when DevToolsSaveToFile has completed.
|
||||||
|
|
|
@ -17,9 +17,9 @@
|
||||||
<key>CFBundleIconFile</key>
|
<key>CFBundleIconFile</key>
|
||||||
<string>electron.icns</string>
|
<string>electron.icns</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>1.6.7</string>
|
<string>1.6.8</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>1.6.7</string>
|
<string>1.6.8</string>
|
||||||
<key>LSApplicationCategoryType</key>
|
<key>LSApplicationCategoryType</key>
|
||||||
<string>public.app-category.developer-tools</string>
|
<string>public.app-category.developer-tools</string>
|
||||||
<key>LSMinimumSystemVersion</key>
|
<key>LSMinimumSystemVersion</key>
|
||||||
|
|
|
@ -56,8 +56,8 @@ END
|
||||||
//
|
//
|
||||||
|
|
||||||
VS_VERSION_INFO VERSIONINFO
|
VS_VERSION_INFO VERSIONINFO
|
||||||
FILEVERSION 1,6,7,0
|
FILEVERSION 1,6,8,0
|
||||||
PRODUCTVERSION 1,6,7,0
|
PRODUCTVERSION 1,6,8,0
|
||||||
FILEFLAGSMASK 0x3fL
|
FILEFLAGSMASK 0x3fL
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
FILEFLAGS 0x1L
|
FILEFLAGS 0x1L
|
||||||
|
@ -74,12 +74,12 @@ BEGIN
|
||||||
BEGIN
|
BEGIN
|
||||||
VALUE "CompanyName", "GitHub, Inc."
|
VALUE "CompanyName", "GitHub, Inc."
|
||||||
VALUE "FileDescription", "Electron"
|
VALUE "FileDescription", "Electron"
|
||||||
VALUE "FileVersion", "1.6.7"
|
VALUE "FileVersion", "1.6.8"
|
||||||
VALUE "InternalName", "electron.exe"
|
VALUE "InternalName", "electron.exe"
|
||||||
VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved."
|
VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved."
|
||||||
VALUE "OriginalFilename", "electron.exe"
|
VALUE "OriginalFilename", "electron.exe"
|
||||||
VALUE "ProductName", "Electron"
|
VALUE "ProductName", "Electron"
|
||||||
VALUE "ProductVersion", "1.6.7"
|
VALUE "ProductVersion", "1.6.8"
|
||||||
VALUE "SquirrelAwareVersion", "1"
|
VALUE "SquirrelAwareVersion", "1"
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
|
|
|
@ -12,8 +12,7 @@ namespace atom {
|
||||||
|
|
||||||
ObjectLifeMonitor::ObjectLifeMonitor(v8::Isolate* isolate,
|
ObjectLifeMonitor::ObjectLifeMonitor(v8::Isolate* isolate,
|
||||||
v8::Local<v8::Object> target)
|
v8::Local<v8::Object> target)
|
||||||
: context_(isolate, isolate->GetCurrentContext()),
|
: target_(isolate, target),
|
||||||
target_(isolate, target),
|
|
||||||
weak_ptr_factory_(this) {
|
weak_ptr_factory_(this) {
|
||||||
target_.SetWeak(this, OnObjectGC, v8::WeakCallbackType::kParameter);
|
target_.SetWeak(this, OnObjectGC, v8::WeakCallbackType::kParameter);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,6 @@ class ObjectLifeMonitor {
|
||||||
static void OnObjectGC(const v8::WeakCallbackInfo<ObjectLifeMonitor>& data);
|
static void OnObjectGC(const v8::WeakCallbackInfo<ObjectLifeMonitor>& data);
|
||||||
static void Free(const v8::WeakCallbackInfo<ObjectLifeMonitor>& data);
|
static void Free(const v8::WeakCallbackInfo<ObjectLifeMonitor>& data);
|
||||||
|
|
||||||
v8::Global<v8::Context> context_;
|
|
||||||
v8::Global<v8::Object> target_;
|
v8::Global<v8::Object> target_;
|
||||||
|
|
||||||
base::WeakPtrFactory<ObjectLifeMonitor> weak_ptr_factory_;
|
base::WeakPtrFactory<ObjectLifeMonitor> weak_ptr_factory_;
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
#define ATOM_MAJOR_VERSION 1
|
#define ATOM_MAJOR_VERSION 1
|
||||||
#define ATOM_MINOR_VERSION 6
|
#define ATOM_MINOR_VERSION 6
|
||||||
#define ATOM_PATCH_VERSION 7
|
#define ATOM_PATCH_VERSION 8
|
||||||
|
|
||||||
#define ATOM_VERSION_IS_RELEASE 1
|
#define ATOM_VERSION_IS_RELEASE 1
|
||||||
|
|
||||||
|
|
|
@ -241,7 +241,7 @@ Linux. Here are some notes on making your app's menu more native-like.
|
||||||
|
|
||||||
On macOS there are many system-defined standard menus, like the `Services` and
|
On macOS there are many system-defined standard menus, like the `Services` and
|
||||||
`Windows` menus. To make your menu a standard menu, you should set your menu's
|
`Windows` menus. To make your menu a standard menu, you should set your menu's
|
||||||
`role` to one of following and Electron will recognize them and make them
|
`role` to one of the following and Electron will recognize them and make them
|
||||||
become standard menus:
|
become standard menus:
|
||||||
|
|
||||||
* `window`
|
* `window`
|
||||||
|
|
|
@ -138,3 +138,13 @@ app.once('ready', () => {
|
||||||
window.setTouchBar(touchBar)
|
window.setTouchBar(touchBar)
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Running the above example
|
||||||
|
|
||||||
|
To run the example above, you'll need to (assuming you've got a terminal open in the dirtectory you want to run the example):
|
||||||
|
|
||||||
|
1. Save the above file to your computer as `touchbar.js`
|
||||||
|
2. Install Electron via `npm install electron`
|
||||||
|
3. Run the example inside Electron: `./node_modules/.bin/electron touchbar.js`
|
||||||
|
|
||||||
|
You should then see a new Electron window and the app running in your touch bar (or touch bar emulator).
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
'product_name%': 'Electron',
|
'product_name%': 'Electron',
|
||||||
'company_name%': 'GitHub, Inc',
|
'company_name%': 'GitHub, Inc',
|
||||||
'company_abbr%': 'github',
|
'company_abbr%': 'github',
|
||||||
'version%': '1.6.7',
|
'version%': '1.6.8',
|
||||||
'js2c_input_dir': '<(SHARED_INTERMEDIATE_DIR)/js2c',
|
'js2c_input_dir': '<(SHARED_INTERMEDIATE_DIR)/js2c',
|
||||||
},
|
},
|
||||||
'includes': [
|
'includes': [
|
||||||
|
|
|
@ -76,13 +76,9 @@ BrowserWindow.prototype._init = function () {
|
||||||
|
|
||||||
// Change window title to page title.
|
// Change window title to page title.
|
||||||
this.webContents.on('page-title-updated', (event, title) => {
|
this.webContents.on('page-title-updated', (event, title) => {
|
||||||
// The page-title-updated event is not emitted immediately (see #3645), so
|
|
||||||
// when the callback is called the BrowserWindow might have been closed.
|
|
||||||
if (this.isDestroyed()) return
|
|
||||||
|
|
||||||
// Route the event to BrowserWindow.
|
// Route the event to BrowserWindow.
|
||||||
this.emit('page-title-updated', event, title)
|
this.emit('page-title-updated', event, title)
|
||||||
if (!event.defaultPrevented) this.setTitle(title)
|
if (!this.isDestroyed() && !event.defaultPrevented) this.setTitle(title)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Sometimes the webContents doesn't get focus when window is shown, so we
|
// Sometimes the webContents doesn't get focus when window is shown, so we
|
||||||
|
|
|
@ -268,13 +268,6 @@ WebContents.prototype._init = function () {
|
||||||
this.reload()
|
this.reload()
|
||||||
})
|
})
|
||||||
|
|
||||||
// Delays the page-title-updated event to next tick.
|
|
||||||
this.on('-page-title-updated', function (...args) {
|
|
||||||
setImmediate(() => {
|
|
||||||
this.emit('page-title-updated', ...args)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
app.emit('web-contents-created', {}, this)
|
app.emit('web-contents-created', {}, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -309,7 +309,7 @@ ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', function (event,
|
||||||
// The W3C does not seem to have word on how postMessage should work when the
|
// The W3C does not seem to have word on how postMessage should work when the
|
||||||
// origins do not match, so we do not do |canAccessWindow| check here since
|
// origins do not match, so we do not do |canAccessWindow| check here since
|
||||||
// postMessage across origins is useful and not harmful.
|
// postMessage across origins is useful and not harmful.
|
||||||
if (guestContents.getURL().indexOf(targetOrigin) === 0 || targetOrigin === '*') {
|
if (targetOrigin === '*' || isSameOrigin(guestContents.getURL(), targetOrigin)) {
|
||||||
const sourceId = event.sender.id
|
const sourceId = event.sender.id
|
||||||
guestContents.send('ELECTRON_GUEST_WINDOW_POSTMESSAGE', sourceId, message, sourceOrigin)
|
guestContents.send('ELECTRON_GUEST_WINDOW_POSTMESSAGE', sourceId, message, sourceOrigin)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "electron",
|
"name": "electron",
|
||||||
"version": "1.6.7",
|
"version": "1.6.8",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"asar": "^0.11.0",
|
"asar": "^0.11.0",
|
||||||
"browserify": "^13.1.0",
|
"browserify": "^13.1.0",
|
||||||
|
|
|
@ -86,6 +86,42 @@ describe('BrowserWindow module', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('BrowserWindow.close()', function () {
|
describe('BrowserWindow.close()', function () {
|
||||||
|
let server
|
||||||
|
|
||||||
|
before(function (done) {
|
||||||
|
server = http.createServer((request, response) => {
|
||||||
|
switch (request.url) {
|
||||||
|
case '/404':
|
||||||
|
response.statusCode = '404'
|
||||||
|
response.end()
|
||||||
|
break
|
||||||
|
case '/301':
|
||||||
|
response.statusCode = '301'
|
||||||
|
response.setHeader('Location', '/200')
|
||||||
|
response.end()
|
||||||
|
break
|
||||||
|
case '/200':
|
||||||
|
response.statusCode = '200'
|
||||||
|
response.end('hello')
|
||||||
|
break
|
||||||
|
case '/title':
|
||||||
|
response.statusCode = '200'
|
||||||
|
response.end('<title>Hello</title>')
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
done('unsupported endpoint')
|
||||||
|
}
|
||||||
|
}).listen(0, '127.0.0.1', () => {
|
||||||
|
server.url = 'http://127.0.0.1:' + server.address().port
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
after(function () {
|
||||||
|
server.close()
|
||||||
|
server = null
|
||||||
|
})
|
||||||
|
|
||||||
it('should emit unload handler', function (done) {
|
it('should emit unload handler', function (done) {
|
||||||
w.webContents.on('did-finish-load', function () {
|
w.webContents.on('did-finish-load', function () {
|
||||||
w.close()
|
w.close()
|
||||||
|
@ -109,6 +145,38 @@ describe('BrowserWindow module', function () {
|
||||||
})
|
})
|
||||||
w.loadURL('file://' + path.join(fixtures, 'api', 'beforeunload-false.html'))
|
w.loadURL('file://' + path.join(fixtures, 'api', 'beforeunload-false.html'))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should not crash when invoked synchronously inside navigation observer', function (done) {
|
||||||
|
const events = [
|
||||||
|
{ name: 'did-start-loading', url: `${server.url}/200` },
|
||||||
|
{ name: 'did-get-redirect-request', url: `${server.url}/301` },
|
||||||
|
{ name: 'did-get-response-details', url: `${server.url}/200` },
|
||||||
|
{ name: 'dom-ready', url: `${server.url}/200` },
|
||||||
|
{ name: 'page-title-updated', url: `${server.url}/title` },
|
||||||
|
{ name: 'did-stop-loading', url: `${server.url}/200` },
|
||||||
|
{ name: 'did-finish-load', url: `${server.url}/200` },
|
||||||
|
{ name: 'did-frame-finish-load', url: `${server.url}/200` },
|
||||||
|
{ name: 'did-fail-load', url: `${server.url}/404` }
|
||||||
|
]
|
||||||
|
const responseEvent = 'window-webContents-destroyed'
|
||||||
|
|
||||||
|
function* genNavigationEvent () {
|
||||||
|
let eventOptions = null
|
||||||
|
while ((eventOptions = events.shift()) && events.length) {
|
||||||
|
let w = new BrowserWindow({show: false})
|
||||||
|
eventOptions.id = w.id
|
||||||
|
eventOptions.responseEvent = responseEvent
|
||||||
|
ipcRenderer.send('test-webcontents-navigation-observer', eventOptions)
|
||||||
|
yield 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let gen = genNavigationEvent()
|
||||||
|
ipcRenderer.on(responseEvent, function () {
|
||||||
|
if (!gen.next().value) done()
|
||||||
|
})
|
||||||
|
gen.next()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('window.close()', function () {
|
describe('window.close()', function () {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
const assert = require('assert')
|
const assert = require('assert')
|
||||||
|
const http = require('http')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const {closeWindow} = require('./window-helpers')
|
const {closeWindow} = require('./window-helpers')
|
||||||
const BrowserWindow = require('electron').remote.BrowserWindow
|
const BrowserWindow = require('electron').remote.BrowserWindow
|
||||||
|
@ -70,6 +71,15 @@ describe('debugger module', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('debugger.sendCommand', function () {
|
describe('debugger.sendCommand', function () {
|
||||||
|
let server
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
if (server != null) {
|
||||||
|
server.close()
|
||||||
|
server = null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
it('retuns response', function (done) {
|
it('retuns response', function (done) {
|
||||||
w.webContents.loadURL('about:blank')
|
w.webContents.loadURL('about:blank')
|
||||||
try {
|
try {
|
||||||
|
@ -125,5 +135,33 @@ describe('debugger module', function () {
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('handles invalid unicode characters in message', function (done) {
|
||||||
|
try {
|
||||||
|
w.webContents.debugger.attach()
|
||||||
|
} catch (err) {
|
||||||
|
done('unexpected error : ' + err)
|
||||||
|
}
|
||||||
|
|
||||||
|
w.webContents.debugger.on('message', (event, method, params) => {
|
||||||
|
if (method === 'Network.loadingFinished') {
|
||||||
|
w.webContents.debugger.sendCommand('Network.getResponseBody', {
|
||||||
|
requestId: params.requestId
|
||||||
|
}, () => {
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
server = http.createServer((req, res) => {
|
||||||
|
res.setHeader('Content-Type', 'text/plain; charset=utf-8')
|
||||||
|
res.end('\uFFFF')
|
||||||
|
})
|
||||||
|
|
||||||
|
server.listen(0, '127.0.0.1', () => {
|
||||||
|
w.webContents.debugger.sendCommand('Network.enable')
|
||||||
|
w.loadURL(`http://127.0.0.1:${server.address().port}`)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -542,4 +542,70 @@ describe('webContents module', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('destroy()', () => {
|
||||||
|
let server
|
||||||
|
|
||||||
|
before(function (done) {
|
||||||
|
server = http.createServer((request, response) => {
|
||||||
|
switch (request.url) {
|
||||||
|
case '/404':
|
||||||
|
response.statusCode = '404'
|
||||||
|
response.end()
|
||||||
|
break
|
||||||
|
case '/301':
|
||||||
|
response.statusCode = '301'
|
||||||
|
response.setHeader('Location', '/200')
|
||||||
|
response.end()
|
||||||
|
break
|
||||||
|
case '/200':
|
||||||
|
response.statusCode = '200'
|
||||||
|
response.end('hello')
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
done('unsupported endpoint')
|
||||||
|
}
|
||||||
|
}).listen(0, '127.0.0.1', () => {
|
||||||
|
server.url = 'http://127.0.0.1:' + server.address().port
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
after(function () {
|
||||||
|
server.close()
|
||||||
|
server = null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not crash when invoked synchronously inside navigation observer', (done) => {
|
||||||
|
const events = [
|
||||||
|
{ name: 'did-start-loading', url: `${server.url}/200` },
|
||||||
|
{ name: 'did-get-redirect-request', url: `${server.url}/301` },
|
||||||
|
{ name: 'did-get-response-details', url: `${server.url}/200` },
|
||||||
|
{ name: 'dom-ready', url: `${server.url}/200` },
|
||||||
|
{ name: 'did-stop-loading', url: `${server.url}/200` },
|
||||||
|
{ name: 'did-finish-load', url: `${server.url}/200` },
|
||||||
|
// FIXME: Multiple Emit calls inside an observer assume that object
|
||||||
|
// will be alive till end of the observer. Synchronous `destroy` api
|
||||||
|
// violates this contract and crashes.
|
||||||
|
// { name: 'did-frame-finish-load', url: `${server.url}/200` },
|
||||||
|
{ name: 'did-fail-load', url: `${server.url}/404` }
|
||||||
|
]
|
||||||
|
const responseEvent = 'webcontents-destroyed'
|
||||||
|
|
||||||
|
function* genNavigationEvent () {
|
||||||
|
let eventOptions = null
|
||||||
|
while ((eventOptions = events.shift()) && events.length) {
|
||||||
|
eventOptions.responseEvent = responseEvent
|
||||||
|
ipcRenderer.send('test-webcontents-navigation-observer', eventOptions)
|
||||||
|
yield 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let gen = genNavigationEvent()
|
||||||
|
ipcRenderer.on(responseEvent, () => {
|
||||||
|
if (!gen.next().value) done()
|
||||||
|
})
|
||||||
|
gen.next()
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
const assert = require('assert')
|
const assert = require('assert')
|
||||||
|
const fs = require('fs')
|
||||||
const http = require('http')
|
const http = require('http')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const ws = require('ws')
|
const ws = require('ws')
|
||||||
|
@ -618,6 +619,39 @@ describe('chromium feature', function () {
|
||||||
})
|
})
|
||||||
document.body.appendChild(webview)
|
document.body.appendChild(webview)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('targetOrigin argument', function () {
|
||||||
|
let serverURL
|
||||||
|
let server
|
||||||
|
|
||||||
|
beforeEach(function (done) {
|
||||||
|
server = http.createServer(function (req, res) {
|
||||||
|
res.writeHead(200)
|
||||||
|
const filePath = path.join(fixtures, 'pages', 'window-opener-targetOrigin.html')
|
||||||
|
res.end(fs.readFileSync(filePath, 'utf8'))
|
||||||
|
})
|
||||||
|
server.listen(0, '127.0.0.1', function () {
|
||||||
|
serverURL = `http://127.0.0.1:${server.address().port}`
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
server.close()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('delivers messages that match the origin', function (done) {
|
||||||
|
let b
|
||||||
|
listener = function (event) {
|
||||||
|
window.removeEventListener('message', listener)
|
||||||
|
b.close()
|
||||||
|
assert.equal(event.data, 'deliver')
|
||||||
|
done()
|
||||||
|
}
|
||||||
|
window.addEventListener('message', listener)
|
||||||
|
b = window.open(serverURL, '', 'show=no')
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('creating a Uint8Array under browser side', function () {
|
describe('creating a Uint8Array under browser side', function () {
|
||||||
|
|
24
spec/fixtures/pages/window-opener-targetOrigin.html
vendored
Normal file
24
spec/fixtures/pages/window-opener-targetOrigin.html
vendored
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<script type="text/javascript" charset="utf-8">
|
||||||
|
const url = require('url')
|
||||||
|
if (url.parse(window.location.href, true).query.opened != null) {
|
||||||
|
// Ensure origins are properly checked by removing a single character from the end
|
||||||
|
window.opener.postMessage('do not deliver substring origin', window.location.origin.substring(0, window.location.origin.length - 1))
|
||||||
|
window.opener.postMessage('do not deliver file://', 'file://')
|
||||||
|
window.opener.postMessage('do not deliver http without port', 'http://127.0.0.1')
|
||||||
|
window.opener.postMessage('do not deliver atom', 'atom://')
|
||||||
|
window.opener.postMessage('do not deliver null', 'null')
|
||||||
|
window.opener.postMessage('do not deliver \\:/', '\\:/')
|
||||||
|
window.opener.postMessage('do not deliver empty', '')
|
||||||
|
window.opener.postMessage('deliver', window.location.origin)
|
||||||
|
} else {
|
||||||
|
const opened = window.open(`${window.location.href}?opened=true`, '', 'show=no')
|
||||||
|
window.addEventListener('message', function (event) {
|
||||||
|
window.opener.postMessage(event.data, '*')
|
||||||
|
opened.close()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -338,6 +338,27 @@ ipcMain.on('crash-service-pid', (event, pid) => {
|
||||||
event.returnValue = null
|
event.returnValue = null
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ipcMain.on('test-webcontents-navigation-observer', (event, options) => {
|
||||||
|
let contents = null
|
||||||
|
let destroy = () => {}
|
||||||
|
if (options.id) {
|
||||||
|
const w = BrowserWindow.fromId(options.id)
|
||||||
|
contents = w.webContents
|
||||||
|
destroy = () => w.close()
|
||||||
|
} else {
|
||||||
|
contents = webContents.create()
|
||||||
|
destroy = () => contents.destroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
contents.once(options.name, () => destroy())
|
||||||
|
|
||||||
|
contents.once('destroyed', () => {
|
||||||
|
event.sender.send(options.responseEvent)
|
||||||
|
})
|
||||||
|
|
||||||
|
contents.loadURL(options.url)
|
||||||
|
})
|
||||||
|
|
||||||
// Suspend listeners until the next event and then restore them
|
// Suspend listeners until the next event and then restore them
|
||||||
const suspendListeners = (emitter, eventName, callback) => {
|
const suspendListeners = (emitter, eventName, callback) => {
|
||||||
const listeners = emitter.listeners(eventName)
|
const listeners = emitter.listeners(eventName)
|
||||||
|
|
Loading…
Reference in a new issue