Merge branch 'master' of github.com:electron/electron into branch
This commit is contained in:
commit
4a42738db9
75 changed files with 2774 additions and 443 deletions
|
@ -9,16 +9,9 @@ jobs:
|
||||||
resource_class: xlarge
|
resource_class: xlarge
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- run:
|
|
||||||
name: Setup for headless testing
|
|
||||||
command: sh -e /etc/init.d/xvfb start
|
|
||||||
- run:
|
- run:
|
||||||
name: Check for release
|
name: Check for release
|
||||||
command: |
|
command: |
|
||||||
MESSAGE="$(git log --format=%B -n 1 HEAD)"
|
|
||||||
case ${MESSAGE} in
|
|
||||||
Bump* ) echo 'export ELECTRON_RELEASE=1' >> $BASH_ENV
|
|
||||||
esac
|
|
||||||
if [ -n "${RUN_RELEASE_BUILD}" ]; then
|
if [ -n "${RUN_RELEASE_BUILD}" ]; then
|
||||||
echo 'release build triggered from api'
|
echo 'release build triggered from api'
|
||||||
echo 'export ELECTRON_RELEASE=1 TRIGGERED_BY_API=1' >> $BASH_ENV
|
echo 'export ELECTRON_RELEASE=1 TRIGGERED_BY_API=1' >> $BASH_ENV
|
||||||
|
@ -73,16 +66,9 @@ jobs:
|
||||||
resource_class: xlarge
|
resource_class: xlarge
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- run:
|
|
||||||
name: Setup for headless testing
|
|
||||||
command: sh -e /etc/init.d/xvfb start
|
|
||||||
- run:
|
- run:
|
||||||
name: Check for release
|
name: Check for release
|
||||||
command: |
|
command: |
|
||||||
MESSAGE="$(git log --format=%B -n 1 HEAD)"
|
|
||||||
case ${MESSAGE} in
|
|
||||||
Bump* ) echo 'export ELECTRON_RELEASE=1' >> $BASH_ENV
|
|
||||||
esac
|
|
||||||
if [ -n "${RUN_RELEASE_BUILD}" ]; then
|
if [ -n "${RUN_RELEASE_BUILD}" ]; then
|
||||||
echo 'release build triggered from api'
|
echo 'release build triggered from api'
|
||||||
echo 'export ELECTRON_RELEASE=1 TRIGGERED_BY_API=1' >> $BASH_ENV
|
echo 'export ELECTRON_RELEASE=1 TRIGGERED_BY_API=1' >> $BASH_ENV
|
||||||
|
@ -137,16 +123,9 @@ jobs:
|
||||||
resource_class: xlarge
|
resource_class: xlarge
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- run:
|
|
||||||
name: Setup for headless testing
|
|
||||||
command: sh -e /etc/init.d/xvfb start
|
|
||||||
- run:
|
- run:
|
||||||
name: Check for release
|
name: Check for release
|
||||||
command: |
|
command: |
|
||||||
MESSAGE="$(git log --format=%B -n 1 HEAD)"
|
|
||||||
case ${MESSAGE} in
|
|
||||||
Bump* ) echo 'export ELECTRON_RELEASE=1' >> $BASH_ENV
|
|
||||||
esac
|
|
||||||
if [ -n "${RUN_RELEASE_BUILD}" ]; then
|
if [ -n "${RUN_RELEASE_BUILD}" ]; then
|
||||||
echo 'release build triggered from api'
|
echo 'release build triggered from api'
|
||||||
echo 'export ELECTRON_RELEASE=1 TRIGGERED_BY_API=1' >> $BASH_ENV
|
echo 'export ELECTRON_RELEASE=1 TRIGGERED_BY_API=1' >> $BASH_ENV
|
||||||
|
@ -199,6 +178,7 @@ jobs:
|
||||||
- image: electronbuilds/electron:0.0.3
|
- image: electronbuilds/electron:0.0.3
|
||||||
environment:
|
environment:
|
||||||
TARGET_ARCH: x64
|
TARGET_ARCH: x64
|
||||||
|
DISPLAY: ':99.0'
|
||||||
resource_class: xlarge
|
resource_class: xlarge
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
|
@ -208,10 +188,6 @@ jobs:
|
||||||
- run:
|
- run:
|
||||||
name: Check for release
|
name: Check for release
|
||||||
command: |
|
command: |
|
||||||
MESSAGE="$(git log --format=%B -n 1 HEAD)"
|
|
||||||
case ${MESSAGE} in
|
|
||||||
Bump* ) echo 'export ELECTRON_RELEASE=1' >> $BASH_ENV
|
|
||||||
esac
|
|
||||||
if [ -n "${RUN_RELEASE_BUILD}" ]; then
|
if [ -n "${RUN_RELEASE_BUILD}" ]; then
|
||||||
echo 'release build triggered from api'
|
echo 'release build triggered from api'
|
||||||
echo 'export ELECTRON_RELEASE=1 TRIGGERED_BY_API=1' >> $BASH_ENV
|
echo 'export ELECTRON_RELEASE=1 TRIGGERED_BY_API=1' >> $BASH_ENV
|
||||||
|
|
25
README.md
25
README.md
|
@ -1,21 +1,22 @@
|
||||||
[![Electron Logo](https://electron.atom.io/images/electron-logo.svg)](https://electron.atom.io/)
|
[![Electron Logo](https://electronjs.org/images/electron-logo.svg)](https://electronjs.org)
|
||||||
|
|
||||||
[![Travis Build Status](https://travis-ci.org/electron/electron.svg?branch=master)](https://travis-ci.org/electron/electron)
|
[![Travis Build Status](https://travis-ci.org/electron/electron.svg?branch=master)](https://travis-ci.org/electron/electron)
|
||||||
[![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/bc56v83355fi3369/branch/master?svg=true)](https://ci.appveyor.com/project/electron-bot/electron/branch/master)
|
[![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/bc56v83355fi3369/branch/master?svg=true)](https://ci.appveyor.com/project/electron-bot/electron/branch/master)
|
||||||
[![devDependency Status](https://david-dm.org/electron/electron/dev-status.svg)](https://david-dm.org/electron/electron?type=dev)
|
[![devDependency Status](https://david-dm.org/electron/electron/dev-status.svg)](https://david-dm.org/electron/electron?type=dev)
|
||||||
[![Join the Electron Community on Slack](http://atom-slack.herokuapp.com/badge.svg)](http://atom-slack.herokuapp.com/)
|
[![Join the Electron Community on Slack](http://atom-slack.herokuapp.com/badge.svg)](http://atom-slack.herokuapp.com/)
|
||||||
|
|
||||||
:memo: Available Translations: [Korean](https://github.com/electron/electron/tree/master/docs-translations/ko-KR/project/README.md) | [Simplified Chinese](https://github.com/electron/electron/tree/master/docs-translations/zh-CN/project/README.md) | [Brazilian Portuguese](https://github.com/electron/electron/tree/master/docs-translations/pt-BR/project/README.md) | [Traditional Chinese](https://github.com/electron/electron/tree/master/docs-translations/zh-TW/project/README.md) | [Spanish](https://github.com/electron/electron/tree/master/docs-translations/es/project/README.md) | [Turkish](https://github.com/electron/electron/tree/master/docs-translations/tr-TR/project/README.md) | [German](https://github.com/electron/electron/tree/master/docs-translations/de-DE/project/README.md)
|
:memo: Available Translations: 🇨🇳 🇹🇼 🇧🇷 🇪🇸 🇰🇷 🇯🇵 🇷🇺 🇫🇷 🇹🇭 🇳🇱 🇹🇷 🇮🇩 🇺🇦 🇨🇿 🇮🇹.
|
||||||
|
View these docs in other languages at [electron/electron-i18n](https://github.com/electron/electron-i18n/tree/master/content/).
|
||||||
|
|
||||||
The Electron framework lets you write cross-platform desktop applications
|
The Electron framework lets you write cross-platform desktop applications
|
||||||
using JavaScript, HTML and CSS. It is based on [Node.js](https://nodejs.org/) and
|
using JavaScript, HTML and CSS. It is based on [Node.js](https://nodejs.org/) and
|
||||||
[Chromium](http://www.chromium.org) and is used by the [Atom
|
[Chromium](http://www.chromium.org) and is used by the [Atom
|
||||||
editor](https://github.com/atom/atom) and many other [apps](https://electron.atom.io/apps).
|
editor](https://github.com/atom/atom) and many other [apps](https://electronjs.org/apps).
|
||||||
|
|
||||||
Follow [@ElectronJS](https://twitter.com/electronjs) on Twitter for important
|
Follow [@ElectronJS](https://twitter.com/electronjs) on Twitter for important
|
||||||
announcements.
|
announcements.
|
||||||
|
|
||||||
This project adheres to the Contributor Covenant
|
This project adheres to the Contributor Covenant
|
||||||
[code of conduct](https://github.com/electron/electron/tree/master/CODE_OF_CONDUCT.md).
|
[code of conduct](https://github.com/electron/electron/tree/master/CODE_OF_CONDUCT.md).
|
||||||
By participating, you are expected to uphold this code. Please report unacceptable
|
By participating, you are expected to uphold this code. Please report unacceptable
|
||||||
behavior to [electron@github.com](mailto:electron@github.com).
|
behavior to [electron@github.com](mailto:electron@github.com).
|
||||||
|
@ -32,29 +33,29 @@ npm install electron --save-dev --save-exact
|
||||||
|
|
||||||
The `--save-exact` flag is recommended as Electron does not follow semantic
|
The `--save-exact` flag is recommended as Electron does not follow semantic
|
||||||
versioning. For info on how to manage Electron versions in your apps, see
|
versioning. For info on how to manage Electron versions in your apps, see
|
||||||
[Electron versioning](https://electron.atom.io/docs/tutorial/electron-versioning/).
|
[Electron versioning](https://electronjs.org/docs/tutorial/electron-versioning).
|
||||||
|
|
||||||
For more installation options and troubleshooting tips, see
|
For more installation options and troubleshooting tips, see
|
||||||
[installation](https://electron.atom.io/docs/tutorial/installation/).
|
[installation](https://electronjs.org/docs/tutorial/installation).
|
||||||
|
|
||||||
## Quick Start
|
## Quick start
|
||||||
|
|
||||||
Clone and run the
|
Clone and run the
|
||||||
[electron/electron-quick-start](https://github.com/electron/electron-quick-start)
|
[electron/electron-quick-start](https://github.com/electron/electron-quick-start)
|
||||||
repository to see a minimal Electron app in action:
|
repository to see a minimal Electron app in action:
|
||||||
|
|
||||||
```
|
```sh
|
||||||
git clone https://github.com/electron/electron-quick-start
|
git clone https://github.com/electron/electron-quick-start
|
||||||
cd electron-quick-start
|
cd electron-quick-start
|
||||||
npm install
|
npm install
|
||||||
npm start
|
npm start
|
||||||
```
|
```
|
||||||
|
|
||||||
## Resources for Learning Electron
|
## Resources for learning Electron
|
||||||
|
|
||||||
- [electron.atom.io/docs](http://electron.atom.io/docs) - all of Electron's documentation
|
- [electronjs.org/docs](https://electronjs.org/docs) - all of Electron's documentation
|
||||||
- [electron/electron-quick-start](https://github.com/electron/electron-quick-start) - a very basic starter Electron app
|
- [electron/electron-quick-start](https://github.com/electron/electron-quick-start) - a very basic starter Electron app
|
||||||
- [electron.atom.io/community/#boilerplates](http://electron.atom.io/community/#boilerplates) - sample starter apps created by the community
|
- [electronjs.org/community#boilerplates](https://electronjs.org/community#boilerplates) - sample starter apps created by the community
|
||||||
- [electron/simple-samples](https://github.com/electron/simple-samples) - small applications with ideas for taking them further
|
- [electron/simple-samples](https://github.com/electron/simple-samples) - small applications with ideas for taking them further
|
||||||
- [electron/electron-api-demos](https://github.com/electron/electron-api-demos) - an Electron app that teaches you how to use Electron
|
- [electron/electron-api-demos](https://github.com/electron/electron-api-demos) - an Electron app that teaches you how to use Electron
|
||||||
- [hokein/electron-sample-apps](https://github.com/hokein/electron-sample-apps) - small demo apps for the various Electron APIs
|
- [hokein/electron-sample-apps](https://github.com/hokein/electron-sample-apps) - small demo apps for the various Electron APIs
|
||||||
|
|
|
@ -633,12 +633,17 @@ void App::OnLogin(LoginHandler* login_handler,
|
||||||
const base::DictionaryValue& request_details) {
|
const base::DictionaryValue& request_details) {
|
||||||
v8::Locker locker(isolate());
|
v8::Locker locker(isolate());
|
||||||
v8::HandleScope handle_scope(isolate());
|
v8::HandleScope handle_scope(isolate());
|
||||||
bool prevent_default = Emit(
|
bool prevent_default = false;
|
||||||
"login",
|
content::WebContents* web_contents = login_handler->GetWebContents();
|
||||||
WebContents::CreateFrom(isolate(), login_handler->GetWebContents()),
|
if (web_contents) {
|
||||||
request_details,
|
prevent_default =
|
||||||
login_handler->auth_info(),
|
Emit("login",
|
||||||
base::Bind(&PassLoginInformation, make_scoped_refptr(login_handler)));
|
WebContents::CreateFrom(isolate(), web_contents),
|
||||||
|
request_details,
|
||||||
|
login_handler->auth_info(),
|
||||||
|
base::Bind(&PassLoginInformation,
|
||||||
|
make_scoped_refptr(login_handler)));
|
||||||
|
}
|
||||||
|
|
||||||
// Default behavior is to always cancel the auth.
|
// Default behavior is to always cancel the auth.
|
||||||
if (!prevent_default)
|
if (!prevent_default)
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "atom/browser/net/url_request_async_asar_job.h"
|
#include "atom/browser/net/url_request_async_asar_job.h"
|
||||||
#include "atom/browser/net/url_request_buffer_job.h"
|
#include "atom/browser/net/url_request_buffer_job.h"
|
||||||
#include "atom/browser/net/url_request_fetch_job.h"
|
#include "atom/browser/net/url_request_fetch_job.h"
|
||||||
|
#include "atom/browser/net/url_request_stream_job.h"
|
||||||
#include "atom/browser/net/url_request_string_job.h"
|
#include "atom/browser/net/url_request_string_job.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"
|
||||||
|
@ -208,6 +209,8 @@ void Protocol::BuildPrototype(
|
||||||
&Protocol::RegisterProtocol<URLRequestAsyncAsarJob>)
|
&Protocol::RegisterProtocol<URLRequestAsyncAsarJob>)
|
||||||
.SetMethod("registerHttpProtocol",
|
.SetMethod("registerHttpProtocol",
|
||||||
&Protocol::RegisterProtocol<URLRequestFetchJob>)
|
&Protocol::RegisterProtocol<URLRequestFetchJob>)
|
||||||
|
.SetMethod("registerStreamProtocol",
|
||||||
|
&Protocol::RegisterProtocol<URLRequestStreamJob>)
|
||||||
.SetMethod("unregisterProtocol", &Protocol::UnregisterProtocol)
|
.SetMethod("unregisterProtocol", &Protocol::UnregisterProtocol)
|
||||||
.SetMethod("isProtocolHandled", &Protocol::IsProtocolHandled)
|
.SetMethod("isProtocolHandled", &Protocol::IsProtocolHandled)
|
||||||
.SetMethod("interceptStringProtocol",
|
.SetMethod("interceptStringProtocol",
|
||||||
|
@ -218,6 +221,8 @@ void Protocol::BuildPrototype(
|
||||||
&Protocol::InterceptProtocol<URLRequestAsyncAsarJob>)
|
&Protocol::InterceptProtocol<URLRequestAsyncAsarJob>)
|
||||||
.SetMethod("interceptHttpProtocol",
|
.SetMethod("interceptHttpProtocol",
|
||||||
&Protocol::InterceptProtocol<URLRequestFetchJob>)
|
&Protocol::InterceptProtocol<URLRequestFetchJob>)
|
||||||
|
.SetMethod("interceptStreamProtocol",
|
||||||
|
&Protocol::InterceptProtocol<URLRequestStreamJob>)
|
||||||
.SetMethod("uninterceptProtocol", &Protocol::UninterceptProtocol);
|
.SetMethod("uninterceptProtocol", &Protocol::UninterceptProtocol);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -78,6 +78,10 @@ class Protocol : public mate::TrackableObject<Protocol> {
|
||||||
net::URLRequestJob* MaybeCreateJob(
|
net::URLRequestJob* MaybeCreateJob(
|
||||||
net::URLRequest* request,
|
net::URLRequest* request,
|
||||||
net::NetworkDelegate* network_delegate) const override {
|
net::NetworkDelegate* network_delegate) const override {
|
||||||
|
if (!request->initiator().has_value()) {
|
||||||
|
// Don't intercept this request as it was created by `net.request`.
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
RequestJob* request_job = new RequestJob(request, network_delegate);
|
RequestJob* request_job = new RequestJob(request, network_delegate);
|
||||||
request_job->SetHandlerInfo(isolate_, request_context_.get(), handler_);
|
request_job->SetHandlerInfo(isolate_, request_context_.get(), handler_);
|
||||||
return request_job;
|
return request_job;
|
||||||
|
|
121
atom/browser/api/event_subscriber.cc
Normal file
121
atom/browser/api/event_subscriber.cc
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
// Copyright (c) 2017 GitHub, Inc.
|
||||||
|
// Use of this source code is governed by the MIT license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "atom/browser/api/event_subscriber.h"
|
||||||
|
#include "atom/common/native_mate_converters/callback.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// A FunctionTemplate lifetime is bound to the v8 context, so it can be safely
|
||||||
|
// stored as a global here since there's only one for the main process.
|
||||||
|
v8::Global<v8::FunctionTemplate> g_cached_template;
|
||||||
|
|
||||||
|
struct JSHandlerData {
|
||||||
|
JSHandlerData(v8::Isolate* isolate,
|
||||||
|
mate::internal::EventSubscriberBase* subscriber)
|
||||||
|
: handle_(isolate, v8::External::New(isolate, this)),
|
||||||
|
subscriber_(subscriber) {
|
||||||
|
handle_.SetWeak(this, GC, v8::WeakCallbackType::kFinalizer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GC(const v8::WeakCallbackInfo<JSHandlerData>& data) {
|
||||||
|
delete data.GetParameter();
|
||||||
|
}
|
||||||
|
|
||||||
|
v8::Global<v8::External> handle_;
|
||||||
|
mate::internal::EventSubscriberBase* subscriber_;
|
||||||
|
};
|
||||||
|
|
||||||
|
void InvokeCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
|
||||||
|
v8::Locker locker(info.GetIsolate());
|
||||||
|
v8::HandleScope handle_scope(info.GetIsolate());
|
||||||
|
v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
|
||||||
|
v8::Context::Scope context_scope(context);
|
||||||
|
mate::Arguments args(info);
|
||||||
|
v8::Local<v8::Value> handler, event;
|
||||||
|
args.GetNext(&handler);
|
||||||
|
args.GetNext(&event);
|
||||||
|
DCHECK(handler->IsExternal());
|
||||||
|
DCHECK(event->IsString());
|
||||||
|
JSHandlerData* handler_data = static_cast<JSHandlerData*>(
|
||||||
|
v8::Local<v8::External>::Cast(handler)->Value());
|
||||||
|
handler_data->subscriber_->EventEmitted(mate::V8ToString(event), &args);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace mate {
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
EventSubscriberBase::EventSubscriberBase(v8::Isolate* isolate,
|
||||||
|
v8::Local<v8::Object> emitter)
|
||||||
|
: isolate_(isolate), emitter_(isolate, emitter) {
|
||||||
|
if (g_cached_template.IsEmpty()) {
|
||||||
|
g_cached_template = v8::Global<v8::FunctionTemplate>(
|
||||||
|
isolate_, v8::FunctionTemplate::New(isolate_, InvokeCallback));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EventSubscriberBase::~EventSubscriberBase() {
|
||||||
|
if (!isolate_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
RemoveAllListeners();
|
||||||
|
emitter_.Reset();
|
||||||
|
DCHECK_EQ(js_handlers_.size(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventSubscriberBase::On(const std::string& event_name) {
|
||||||
|
DCHECK(js_handlers_.find(event_name) == js_handlers_.end());
|
||||||
|
v8::Locker locker(isolate_);
|
||||||
|
v8::Isolate::Scope isolate_scope(isolate_);
|
||||||
|
v8::HandleScope handle_scope(isolate_);
|
||||||
|
auto fn_template = g_cached_template.Get(isolate_);
|
||||||
|
auto event = mate::StringToV8(isolate_, event_name);
|
||||||
|
auto js_handler_data = new JSHandlerData(isolate_, this);
|
||||||
|
v8::Local<v8::Value> fn = internal::BindFunctionWith(
|
||||||
|
isolate_, isolate_->GetCurrentContext(), fn_template->GetFunction(),
|
||||||
|
js_handler_data->handle_.Get(isolate_), event);
|
||||||
|
js_handlers_.insert(
|
||||||
|
std::make_pair(event_name, v8::Global<v8::Value>(isolate_, fn)));
|
||||||
|
internal::ValueVector converted_args = {event, fn};
|
||||||
|
internal::CallMethodWithArgs(isolate_, emitter_.Get(isolate_), "on",
|
||||||
|
&converted_args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventSubscriberBase::Off(const std::string& event_name) {
|
||||||
|
v8::Locker locker(isolate_);
|
||||||
|
v8::Isolate::Scope isolate_scope(isolate_);
|
||||||
|
v8::HandleScope handle_scope(isolate_);
|
||||||
|
auto js_handler = js_handlers_.find(event_name);
|
||||||
|
DCHECK(js_handler != js_handlers_.end());
|
||||||
|
RemoveListener(js_handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventSubscriberBase::RemoveAllListeners() {
|
||||||
|
v8::Locker locker(isolate_);
|
||||||
|
v8::Isolate::Scope isolate_scope(isolate_);
|
||||||
|
v8::HandleScope handle_scope(isolate_);
|
||||||
|
while (!js_handlers_.empty()) {
|
||||||
|
RemoveListener(js_handlers_.begin());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<std::string, v8::Global<v8::Value>>::iterator
|
||||||
|
EventSubscriberBase::RemoveListener(
|
||||||
|
std::map<std::string, v8::Global<v8::Value>>::iterator it) {
|
||||||
|
internal::ValueVector args = {StringToV8(isolate_, it->first),
|
||||||
|
it->second.Get(isolate_)};
|
||||||
|
internal::CallMethodWithArgs(
|
||||||
|
isolate_, v8::Local<v8::Object>::Cast(emitter_.Get(isolate_)),
|
||||||
|
"removeListener", &args);
|
||||||
|
it->second.Reset();
|
||||||
|
return js_handlers_.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
} // namespace mate
|
132
atom/browser/api/event_subscriber.h
Normal file
132
atom/browser/api/event_subscriber.h
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
// Copyright (c) 2017 GitHub, Inc.
|
||||||
|
// Use of this source code is governed by the MIT license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef ATOM_BROWSER_API_EVENT_SUBSCRIBER_H_
|
||||||
|
#define ATOM_BROWSER_API_EVENT_SUBSCRIBER_H_
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "atom/common/api/event_emitter_caller.h"
|
||||||
|
#include "base/synchronization/lock.h"
|
||||||
|
#include "content/public/browser/browser_thread.h"
|
||||||
|
#include "native_mate/native_mate/arguments.h"
|
||||||
|
|
||||||
|
namespace mate {
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
class EventSubscriberBase {
|
||||||
|
public:
|
||||||
|
EventSubscriberBase(v8::Isolate* isolate, v8::Local<v8::Object> emitter);
|
||||||
|
virtual ~EventSubscriberBase();
|
||||||
|
virtual void EventEmitted(const std::string& event_name,
|
||||||
|
mate::Arguments* args) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void On(const std::string& event_name);
|
||||||
|
void Off(const std::string& event_name);
|
||||||
|
void RemoveAllListeners();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::map<std::string, v8::Global<v8::Value>>::iterator RemoveListener(
|
||||||
|
std::map<std::string, v8::Global<v8::Value>>::iterator it);
|
||||||
|
|
||||||
|
v8::Isolate* isolate_;
|
||||||
|
v8::Global<v8::Object> emitter_;
|
||||||
|
std::map<std::string, v8::Global<v8::Value>> js_handlers_;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(EventSubscriberBase);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
template <typename HandlerType>
|
||||||
|
class EventSubscriber : internal::EventSubscriberBase {
|
||||||
|
public:
|
||||||
|
using EventCallback = void (HandlerType::*)(mate::Arguments* args);
|
||||||
|
// Alias to unique_ptr with deleter.
|
||||||
|
using unique_ptr = std::unique_ptr<EventSubscriber<HandlerType>,
|
||||||
|
void (*)(EventSubscriber<HandlerType>*)>;
|
||||||
|
// EventSubscriber should only be created/deleted in the main thread since it
|
||||||
|
// communicates with the V8 engine. This smart pointer makes it simpler to
|
||||||
|
// bind the lifetime of EventSubscriber with a class whose lifetime is managed
|
||||||
|
// by a non-UI thread.
|
||||||
|
class SafePtr : public unique_ptr {
|
||||||
|
public:
|
||||||
|
SafePtr() : SafePtr(nullptr) {}
|
||||||
|
explicit SafePtr(EventSubscriber<HandlerType>* ptr)
|
||||||
|
: unique_ptr(ptr, Deleter) {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Custom deleter that schedules destructor invocation to the main thread.
|
||||||
|
static void Deleter(EventSubscriber<HandlerType>* ptr) {
|
||||||
|
DCHECK(
|
||||||
|
!::content::BrowserThread::CurrentlyOn(::content::BrowserThread::UI));
|
||||||
|
DCHECK(ptr);
|
||||||
|
// Acquire handler lock and reset handler_ to ensure that any new events
|
||||||
|
// emitted will be ignored after this function returns
|
||||||
|
base::AutoLock auto_lock(ptr->handler_lock_);
|
||||||
|
ptr->handler_ = nullptr;
|
||||||
|
content::BrowserThread::PostTask(
|
||||||
|
content::BrowserThread::UI, FROM_HERE,
|
||||||
|
base::Bind(
|
||||||
|
[](EventSubscriber<HandlerType>* subscriber) {
|
||||||
|
delete subscriber;
|
||||||
|
},
|
||||||
|
ptr));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
EventSubscriber(HandlerType* handler,
|
||||||
|
v8::Isolate* isolate,
|
||||||
|
v8::Local<v8::Object> emitter)
|
||||||
|
: EventSubscriberBase(isolate, emitter), handler_(handler) {
|
||||||
|
DCHECK_CURRENTLY_ON(::content::BrowserThread::UI);
|
||||||
|
}
|
||||||
|
|
||||||
|
void On(const std::string& event, EventCallback callback) {
|
||||||
|
DCHECK_CURRENTLY_ON(::content::BrowserThread::UI);
|
||||||
|
EventSubscriberBase::On(event);
|
||||||
|
callbacks_.insert(std::make_pair(event, callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Off(const std::string& event) {
|
||||||
|
DCHECK_CURRENTLY_ON(::content::BrowserThread::UI);
|
||||||
|
EventSubscriberBase::Off(event);
|
||||||
|
DCHECK(callbacks_.find(event) != callbacks_.end());
|
||||||
|
callbacks_.erase(callbacks_.find(event));
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoveAllListeners() {
|
||||||
|
DCHECK_CURRENTLY_ON(::content::BrowserThread::UI);
|
||||||
|
EventSubscriberBase::RemoveAllListeners();
|
||||||
|
callbacks_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void EventEmitted(const std::string& event_name,
|
||||||
|
mate::Arguments* args) override {
|
||||||
|
DCHECK_CURRENTLY_ON(::content::BrowserThread::UI);
|
||||||
|
base::AutoLock auto_lock(handler_lock_);
|
||||||
|
if (!handler_) {
|
||||||
|
// handler_ was probably destroyed by another thread and we should not
|
||||||
|
// access it.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto it = callbacks_.find(event_name);
|
||||||
|
if (it != callbacks_.end()) {
|
||||||
|
auto method = it->second;
|
||||||
|
(handler_->*method)(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HandlerType* handler_;
|
||||||
|
base::Lock handler_lock_;
|
||||||
|
std::map<std::string, EventCallback> callbacks_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace mate
|
||||||
|
|
||||||
|
#endif // ATOM_BROWSER_API_EVENT_SUBSCRIBER_H_
|
|
@ -13,7 +13,6 @@
|
||||||
#include "atom/browser/net/about_protocol_handler.h"
|
#include "atom/browser/net/about_protocol_handler.h"
|
||||||
#include "atom/browser/net/asar/asar_protocol_handler.h"
|
#include "atom/browser/net/asar/asar_protocol_handler.h"
|
||||||
#include "atom/browser/net/atom_cert_verifier.h"
|
#include "atom/browser/net/atom_cert_verifier.h"
|
||||||
#include "atom/browser/net/atom_ct_delegate.h"
|
|
||||||
#include "atom/browser/net/atom_network_delegate.h"
|
#include "atom/browser/net/atom_network_delegate.h"
|
||||||
#include "atom/browser/net/atom_url_request_job_factory.h"
|
#include "atom/browser/net/atom_url_request_job_factory.h"
|
||||||
#include "atom/browser/net/http_protocol_handler.h"
|
#include "atom/browser/net/http_protocol_handler.h"
|
||||||
|
@ -72,7 +71,6 @@ AtomBrowserContext::AtomBrowserContext(const std::string& partition,
|
||||||
bool in_memory,
|
bool in_memory,
|
||||||
const base::DictionaryValue& options)
|
const base::DictionaryValue& options)
|
||||||
: brightray::BrowserContext(partition, in_memory),
|
: brightray::BrowserContext(partition, in_memory),
|
||||||
ct_delegate_(new AtomCTDelegate),
|
|
||||||
network_delegate_(new AtomNetworkDelegate),
|
network_delegate_(new AtomNetworkDelegate),
|
||||||
cookie_delegate_(new AtomCookieDelegate) {
|
cookie_delegate_(new AtomCookieDelegate) {
|
||||||
// Construct user agent string.
|
// Construct user agent string.
|
||||||
|
@ -192,8 +190,9 @@ content::PermissionManager* AtomBrowserContext::GetPermissionManager() {
|
||||||
return permission_manager_.get();
|
return permission_manager_.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<net::CertVerifier> AtomBrowserContext::CreateCertVerifier() {
|
std::unique_ptr<net::CertVerifier> AtomBrowserContext::CreateCertVerifier(
|
||||||
return base::WrapUnique(new AtomCertVerifier(ct_delegate_.get()));
|
brightray::RequireCTDelegate* ct_delegate) {
|
||||||
|
return base::WrapUnique(new AtomCertVerifier(ct_delegate));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> AtomBrowserContext::GetCookieableSchemes() {
|
std::vector<std::string> AtomBrowserContext::GetCookieableSchemes() {
|
||||||
|
@ -204,11 +203,6 @@ std::vector<std::string> AtomBrowserContext::GetCookieableSchemes() {
|
||||||
return default_schemes;
|
return default_schemes;
|
||||||
}
|
}
|
||||||
|
|
||||||
net::TransportSecurityState::RequireCTDelegate*
|
|
||||||
AtomBrowserContext::GetRequireCTDelegate() {
|
|
||||||
return ct_delegate_.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AtomBrowserContext::RegisterPrefs(PrefRegistrySimple* pref_registry) {
|
void AtomBrowserContext::RegisterPrefs(PrefRegistrySimple* pref_registry) {
|
||||||
pref_registry->RegisterFilePathPref(prefs::kSelectFileLastDirectory,
|
pref_registry->RegisterFilePathPref(prefs::kSelectFileLastDirectory,
|
||||||
base::FilePath());
|
base::FilePath());
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
namespace atom {
|
namespace atom {
|
||||||
|
|
||||||
class AtomBlobReader;
|
class AtomBlobReader;
|
||||||
class AtomCTDelegate;
|
|
||||||
class AtomDownloadManagerDelegate;
|
class AtomDownloadManagerDelegate;
|
||||||
class AtomNetworkDelegate;
|
class AtomNetworkDelegate;
|
||||||
class AtomPermissionManager;
|
class AtomPermissionManager;
|
||||||
|
@ -40,10 +39,9 @@ class AtomBrowserContext : public brightray::BrowserContext {
|
||||||
content::ProtocolHandlerMap* protocol_handlers) override;
|
content::ProtocolHandlerMap* protocol_handlers) override;
|
||||||
net::HttpCache::BackendFactory* CreateHttpCacheBackendFactory(
|
net::HttpCache::BackendFactory* CreateHttpCacheBackendFactory(
|
||||||
const base::FilePath& base_path) override;
|
const base::FilePath& base_path) override;
|
||||||
std::unique_ptr<net::CertVerifier> CreateCertVerifier() override;
|
std::unique_ptr<net::CertVerifier> CreateCertVerifier(
|
||||||
|
brightray::RequireCTDelegate* ct_delegate) override;
|
||||||
std::vector<std::string> GetCookieableSchemes() override;
|
std::vector<std::string> GetCookieableSchemes() override;
|
||||||
net::TransportSecurityState::RequireCTDelegate* GetRequireCTDelegate()
|
|
||||||
override;
|
|
||||||
|
|
||||||
// content::BrowserContext:
|
// content::BrowserContext:
|
||||||
content::DownloadManagerDelegate* GetDownloadManagerDelegate() override;
|
content::DownloadManagerDelegate* GetDownloadManagerDelegate() override;
|
||||||
|
@ -69,7 +67,6 @@ class AtomBrowserContext : public brightray::BrowserContext {
|
||||||
std::unique_ptr<WebViewManager> guest_manager_;
|
std::unique_ptr<WebViewManager> guest_manager_;
|
||||||
std::unique_ptr<AtomPermissionManager> permission_manager_;
|
std::unique_ptr<AtomPermissionManager> permission_manager_;
|
||||||
std::unique_ptr<AtomBlobReader> blob_reader_;
|
std::unique_ptr<AtomBlobReader> blob_reader_;
|
||||||
std::unique_ptr<AtomCTDelegate> ct_delegate_;
|
|
||||||
std::string user_agent_;
|
std::string user_agent_;
|
||||||
bool use_cache_;
|
bool use_cache_;
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include "base/strings/utf_string_conversions.h"
|
#include "base/strings/utf_string_conversions.h"
|
||||||
#include "content/public/browser/browser_thread.h"
|
#include "content/public/browser/browser_thread.h"
|
||||||
#include "content/public/browser/download_manager.h"
|
#include "content/public/browser/download_manager.h"
|
||||||
|
#include "content/public/browser/render_frame_host.h"
|
||||||
#include "content/public/browser/stream_info.h"
|
#include "content/public/browser/stream_info.h"
|
||||||
#include "net/base/escape.h"
|
#include "net/base/escape.h"
|
||||||
#include "net/ssl/client_cert_store.h"
|
#include "net/ssl/client_cert_store.h"
|
||||||
|
@ -34,8 +35,7 @@ namespace atom {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
void OnOpenExternal(const GURL& escaped_url,
|
void OnOpenExternal(const GURL& escaped_url, bool allowed) {
|
||||||
bool allowed) {
|
|
||||||
if (allowed)
|
if (allowed)
|
||||||
platform_util::OpenExternal(
|
platform_util::OpenExternal(
|
||||||
#if defined(OS_WIN)
|
#if defined(OS_WIN)
|
||||||
|
@ -66,6 +66,8 @@ void HandleExternalProtocolInUI(
|
||||||
|
|
||||||
void OnPdfResourceIntercepted(
|
void OnPdfResourceIntercepted(
|
||||||
const GURL& original_url,
|
const GURL& original_url,
|
||||||
|
int render_process_host_id,
|
||||||
|
int render_frame_id,
|
||||||
const content::ResourceRequestInfo::WebContentsGetter&
|
const content::ResourceRequestInfo::WebContentsGetter&
|
||||||
web_contents_getter) {
|
web_contents_getter) {
|
||||||
content::WebContents* web_contents = web_contents_getter.Run();
|
content::WebContents* web_contents = web_contents_getter.Run();
|
||||||
|
@ -75,7 +77,7 @@ void OnPdfResourceIntercepted(
|
||||||
if (!WebContentsPreferences::IsPluginsEnabled(web_contents)) {
|
if (!WebContentsPreferences::IsPluginsEnabled(web_contents)) {
|
||||||
auto browser_context = web_contents->GetBrowserContext();
|
auto browser_context = web_contents->GetBrowserContext();
|
||||||
auto download_manager =
|
auto download_manager =
|
||||||
content::BrowserContext::GetDownloadManager(browser_context);
|
content::BrowserContext::GetDownloadManager(browser_context);
|
||||||
|
|
||||||
download_manager->DownloadUrl(
|
download_manager->DownloadUrl(
|
||||||
content::DownloadUrlParameters::CreateForWebContentsMainFrame(
|
content::DownloadUrlParameters::CreateForWebContentsMainFrame(
|
||||||
|
@ -86,26 +88,29 @@ void OnPdfResourceIntercepted(
|
||||||
// The URL passes the original pdf resource url, that will be requested
|
// The URL passes the original pdf resource url, that will be requested
|
||||||
// by the webui page.
|
// by the webui page.
|
||||||
// chrome://pdf-viewer/index.html?src=https://somepage/123.pdf
|
// chrome://pdf-viewer/index.html?src=https://somepage/123.pdf
|
||||||
content::NavigationController::LoadURLParams params(
|
content::NavigationController::LoadURLParams params(GURL(base::StringPrintf(
|
||||||
GURL(base::StringPrintf(
|
"%sindex.html?%s=%s", kPdfViewerUIOrigin, kPdfPluginSrc,
|
||||||
"%sindex.html?%s=%s",
|
net::EscapeUrlEncodedData(original_url.spec(), false).c_str())));
|
||||||
kPdfViewerUIOrigin,
|
|
||||||
kPdfPluginSrc,
|
content::RenderFrameHost* frame_host =
|
||||||
net::EscapeUrlEncodedData(original_url.spec(), false).c_str())));
|
content::RenderFrameHost::FromID(render_process_host_id, render_frame_id);
|
||||||
|
if (!frame_host) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
params.frame_tree_node_id = frame_host->GetFrameTreeNodeId();
|
||||||
web_contents->GetController().LoadURLWithParams(params);
|
web_contents->GetController().LoadURLWithParams(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
AtomResourceDispatcherHostDelegate::AtomResourceDispatcherHostDelegate() {
|
AtomResourceDispatcherHostDelegate::AtomResourceDispatcherHostDelegate() {}
|
||||||
}
|
|
||||||
|
|
||||||
bool AtomResourceDispatcherHostDelegate::HandleExternalProtocol(
|
bool AtomResourceDispatcherHostDelegate::HandleExternalProtocol(
|
||||||
const GURL& url,
|
const GURL& url,
|
||||||
content::ResourceRequestInfo* info) {
|
content::ResourceRequestInfo* info) {
|
||||||
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
|
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
|
||||||
base::Bind(&HandleExternalProtocolInUI,
|
base::Bind(&HandleExternalProtocolInUI, url,
|
||||||
url,
|
|
||||||
info->GetWebContentsGetterForRequest(),
|
info->GetWebContentsGetterForRequest(),
|
||||||
info->HasUserGesture()));
|
info->HasUserGesture()));
|
||||||
return true;
|
return true;
|
||||||
|
@ -121,16 +126,16 @@ AtomResourceDispatcherHostDelegate::CreateLoginDelegate(
|
||||||
std::unique_ptr<net::ClientCertStore>
|
std::unique_ptr<net::ClientCertStore>
|
||||||
AtomResourceDispatcherHostDelegate::CreateClientCertStore(
|
AtomResourceDispatcherHostDelegate::CreateClientCertStore(
|
||||||
content::ResourceContext* resource_context) {
|
content::ResourceContext* resource_context) {
|
||||||
#if defined(USE_NSS_CERTS)
|
#if defined(USE_NSS_CERTS)
|
||||||
return std::unique_ptr<net::ClientCertStore>(new net::ClientCertStoreNSS(
|
return std::unique_ptr<net::ClientCertStore>(new net::ClientCertStoreNSS(
|
||||||
net::ClientCertStoreNSS::PasswordDelegateFactory()));
|
net::ClientCertStoreNSS::PasswordDelegateFactory()));
|
||||||
#elif defined(OS_WIN)
|
#elif defined(OS_WIN)
|
||||||
return std::unique_ptr<net::ClientCertStore>(new net::ClientCertStoreWin());
|
return std::unique_ptr<net::ClientCertStore>(new net::ClientCertStoreWin());
|
||||||
#elif defined(OS_MACOSX)
|
#elif defined(OS_MACOSX)
|
||||||
return std::unique_ptr<net::ClientCertStore>(new net::ClientCertStoreMac());
|
return std::unique_ptr<net::ClientCertStore>(new net::ClientCertStoreMac());
|
||||||
#elif defined(USE_OPENSSL)
|
#elif defined(USE_OPENSSL)
|
||||||
return std::unique_ptr<net::ClientCertStore>();
|
return std::unique_ptr<net::ClientCertStore>();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AtomResourceDispatcherHostDelegate::ShouldInterceptResourceAsStream(
|
bool AtomResourceDispatcherHostDelegate::ShouldInterceptResourceAsStream(
|
||||||
|
@ -141,11 +146,20 @@ bool AtomResourceDispatcherHostDelegate::ShouldInterceptResourceAsStream(
|
||||||
std::string* payload) {
|
std::string* payload) {
|
||||||
const content::ResourceRequestInfo* info =
|
const content::ResourceRequestInfo* info =
|
||||||
content::ResourceRequestInfo::ForRequest(request);
|
content::ResourceRequestInfo::ForRequest(request);
|
||||||
if (mime_type == "application/pdf" && info->IsMainFrame()) {
|
|
||||||
|
int render_process_host_id;
|
||||||
|
int render_frame_id;
|
||||||
|
if (!info->GetAssociatedRenderFrame(&render_process_host_id,
|
||||||
|
&render_frame_id)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mime_type == "application/pdf") {
|
||||||
*origin = GURL(kPdfViewerUIOrigin);
|
*origin = GURL(kPdfViewerUIOrigin);
|
||||||
content::BrowserThread::PostTask(
|
content::BrowserThread::PostTask(
|
||||||
BrowserThread::UI, FROM_HERE,
|
BrowserThread::UI, FROM_HERE,
|
||||||
base::Bind(&OnPdfResourceIntercepted, request->url(),
|
base::Bind(&OnPdfResourceIntercepted, request->url(),
|
||||||
|
render_process_host_id, render_frame_id,
|
||||||
info->GetWebContentsGetterForRequest()));
|
info->GetWebContentsGetterForRequest()));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -337,7 +337,7 @@ bool ScopedDisableResize::disable_resize_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)windowWillEnterFullScreen:(NSNotification*)notification {
|
- (void)windowWillEnterFullScreen:(NSNotification*)notification {
|
||||||
// Setting resizable to true before entering fullscreen
|
// Setting resizable to true before entering fullscreen
|
||||||
is_resizable_ = shell_->IsResizable();
|
is_resizable_ = shell_->IsResizable();
|
||||||
shell_->SetResizable(true);
|
shell_->SetResizable(true);
|
||||||
// Hide the native toolbar before entering fullscreen, so there is no visual
|
// Hide the native toolbar before entering fullscreen, so there is no visual
|
||||||
|
@ -962,10 +962,16 @@ NativeWindowMac::NativeWindowMac(
|
||||||
// We will manage window's lifetime ourselves.
|
// We will manage window's lifetime ourselves.
|
||||||
[window_ setReleasedWhenClosed:NO];
|
[window_ setReleasedWhenClosed:NO];
|
||||||
|
|
||||||
|
// Hide the title bar background
|
||||||
|
if (title_bar_style_ != NORMAL) {
|
||||||
|
if (base::mac::IsAtLeastOS10_10()) {
|
||||||
|
[window_ setTitlebarAppearsTransparent:YES];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Hide the title bar.
|
// Hide the title bar.
|
||||||
if (title_bar_style_ == HIDDEN_INSET) {
|
if (title_bar_style_ == HIDDEN_INSET) {
|
||||||
if (base::mac::IsAtLeastOS10_10()) {
|
if (base::mac::IsAtLeastOS10_10()) {
|
||||||
[window_ setTitlebarAppearsTransparent:YES];
|
|
||||||
base::scoped_nsobject<NSToolbar> toolbar(
|
base::scoped_nsobject<NSToolbar> toolbar(
|
||||||
[[NSToolbar alloc] initWithIdentifier:@"titlebarStylingToolbar"]);
|
[[NSToolbar alloc] initWithIdentifier:@"titlebarStylingToolbar"]);
|
||||||
[toolbar setShowsBaselineSeparator:NO];
|
[toolbar setShowsBaselineSeparator:NO];
|
||||||
|
|
|
@ -5,11 +5,11 @@
|
||||||
#include "atom/browser/net/atom_cert_verifier.h"
|
#include "atom/browser/net/atom_cert_verifier.h"
|
||||||
|
|
||||||
#include "atom/browser/browser.h"
|
#include "atom/browser/browser.h"
|
||||||
#include "atom/browser/net/atom_ct_delegate.h"
|
|
||||||
#include "atom/common/native_mate_converters/net_converter.h"
|
#include "atom/common/native_mate_converters/net_converter.h"
|
||||||
#include "base/containers/linked_list.h"
|
#include "base/containers/linked_list.h"
|
||||||
#include "base/memory/ptr_util.h"
|
#include "base/memory/ptr_util.h"
|
||||||
#include "base/memory/weak_ptr.h"
|
#include "base/memory/weak_ptr.h"
|
||||||
|
#include "brightray/browser/net/require_ct_delegate.h"
|
||||||
#include "content/public/browser/browser_thread.h"
|
#include "content/public/browser/browser_thread.h"
|
||||||
#include "net/base/net_errors.h"
|
#include "net/base/net_errors.h"
|
||||||
#include "net/cert/cert_verify_result.h"
|
#include "net/cert/cert_verify_result.h"
|
||||||
|
@ -147,7 +147,7 @@ class CertVerifierRequest : public AtomCertVerifier::Request {
|
||||||
base::WeakPtrFactory<CertVerifierRequest> weak_ptr_factory_;
|
base::WeakPtrFactory<CertVerifierRequest> weak_ptr_factory_;
|
||||||
};
|
};
|
||||||
|
|
||||||
AtomCertVerifier::AtomCertVerifier(AtomCTDelegate* ct_delegate)
|
AtomCertVerifier::AtomCertVerifier(brightray::RequireCTDelegate* ct_delegate)
|
||||||
: default_cert_verifier_(net::CertVerifier::CreateDefault()),
|
: default_cert_verifier_(net::CertVerifier::CreateDefault()),
|
||||||
ct_delegate_(ct_delegate) {}
|
ct_delegate_(ct_delegate) {}
|
||||||
|
|
||||||
|
|
|
@ -11,9 +11,14 @@
|
||||||
|
|
||||||
#include "net/cert/cert_verifier.h"
|
#include "net/cert/cert_verifier.h"
|
||||||
|
|
||||||
|
namespace brightray {
|
||||||
|
|
||||||
|
class RequireCTDelegate;
|
||||||
|
|
||||||
|
} // namespace brightray
|
||||||
|
|
||||||
namespace atom {
|
namespace atom {
|
||||||
|
|
||||||
class AtomCTDelegate;
|
|
||||||
class CertVerifierRequest;
|
class CertVerifierRequest;
|
||||||
|
|
||||||
struct VerifyRequestParams {
|
struct VerifyRequestParams {
|
||||||
|
@ -25,7 +30,7 @@ struct VerifyRequestParams {
|
||||||
|
|
||||||
class AtomCertVerifier : public net::CertVerifier {
|
class AtomCertVerifier : public net::CertVerifier {
|
||||||
public:
|
public:
|
||||||
explicit AtomCertVerifier(AtomCTDelegate* ct_delegate);
|
explicit AtomCertVerifier(brightray::RequireCTDelegate* ct_delegate);
|
||||||
virtual ~AtomCertVerifier();
|
virtual ~AtomCertVerifier();
|
||||||
|
|
||||||
using VerifyProc = base::Callback<void(const VerifyRequestParams& request,
|
using VerifyProc = base::Callback<void(const VerifyRequestParams& request,
|
||||||
|
@ -34,7 +39,7 @@ class AtomCertVerifier : public net::CertVerifier {
|
||||||
void SetVerifyProc(const VerifyProc& proc);
|
void SetVerifyProc(const VerifyProc& proc);
|
||||||
|
|
||||||
const VerifyProc verify_proc() const { return verify_proc_; }
|
const VerifyProc verify_proc() const { return verify_proc_; }
|
||||||
AtomCTDelegate* ct_delegate() const { return ct_delegate_; }
|
brightray::RequireCTDelegate* ct_delegate() const { return ct_delegate_; }
|
||||||
net::CertVerifier* default_verifier() const {
|
net::CertVerifier* default_verifier() const {
|
||||||
return default_cert_verifier_.get();
|
return default_cert_verifier_.get();
|
||||||
}
|
}
|
||||||
|
@ -58,7 +63,7 @@ class AtomCertVerifier : public net::CertVerifier {
|
||||||
std::map<RequestParams, CertVerifierRequest*> inflight_requests_;
|
std::map<RequestParams, CertVerifierRequest*> inflight_requests_;
|
||||||
VerifyProc verify_proc_;
|
VerifyProc verify_proc_;
|
||||||
std::unique_ptr<net::CertVerifier> default_cert_verifier_;
|
std::unique_ptr<net::CertVerifier> default_cert_verifier_;
|
||||||
AtomCTDelegate* ct_delegate_;
|
brightray::RequireCTDelegate* ct_delegate_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(AtomCertVerifier);
|
DISALLOW_COPY_AND_ASSIGN(AtomCertVerifier);
|
||||||
};
|
};
|
||||||
|
|
|
@ -71,6 +71,7 @@ class JsAsker : public RequestJob {
|
||||||
void Start() override {
|
void Start() override {
|
||||||
std::unique_ptr<base::DictionaryValue> request_details(
|
std::unique_ptr<base::DictionaryValue> request_details(
|
||||||
new base::DictionaryValue);
|
new base::DictionaryValue);
|
||||||
|
request_start_time_ = base::TimeTicks::Now();
|
||||||
FillRequestDetails(request_details.get(), RequestJob::request());
|
FillRequestDetails(request_details.get(), RequestJob::request());
|
||||||
content::BrowserThread::PostTask(
|
content::BrowserThread::PostTask(
|
||||||
content::BrowserThread::UI, FROM_HERE,
|
content::BrowserThread::UI, FROM_HERE,
|
||||||
|
@ -86,6 +87,15 @@ class JsAsker : public RequestJob {
|
||||||
|
|
||||||
int GetResponseCode() const override { return net::HTTP_OK; }
|
int GetResponseCode() const override { return net::HTTP_OK; }
|
||||||
|
|
||||||
|
// NOTE: We have to implement this method or risk a crash in blink for
|
||||||
|
// redirects!
|
||||||
|
void GetLoadTimingInfo(net::LoadTimingInfo* load_timing_info) const override {
|
||||||
|
load_timing_info->send_start = request_start_time_;
|
||||||
|
load_timing_info->send_end = request_start_time_;
|
||||||
|
load_timing_info->request_start = request_start_time_;
|
||||||
|
load_timing_info->receive_headers_end = response_start_time_;
|
||||||
|
}
|
||||||
|
|
||||||
void GetResponseInfo(net::HttpResponseInfo* info) override {
|
void GetResponseInfo(net::HttpResponseInfo* info) override {
|
||||||
info->headers = new net::HttpResponseHeaders("");
|
info->headers = new net::HttpResponseHeaders("");
|
||||||
}
|
}
|
||||||
|
@ -93,6 +103,7 @@ class JsAsker : public RequestJob {
|
||||||
// Called when the JS handler has sent the response, we need to decide whether
|
// Called when the JS handler has sent the response, we need to decide whether
|
||||||
// to start, or fail the job.
|
// to start, or fail the job.
|
||||||
void OnResponse(bool success, std::unique_ptr<base::Value> value) {
|
void OnResponse(bool success, std::unique_ptr<base::Value> value) {
|
||||||
|
response_start_time_ = base::TimeTicks::Now();
|
||||||
int error = net::ERR_NOT_IMPLEMENTED;
|
int error = net::ERR_NOT_IMPLEMENTED;
|
||||||
if (success && value && !internal::IsErrorOptions(value.get(), &error)) {
|
if (success && value && !internal::IsErrorOptions(value.get(), &error)) {
|
||||||
StartAsync(std::move(value));
|
StartAsync(std::move(value));
|
||||||
|
@ -105,6 +116,8 @@ class JsAsker : public RequestJob {
|
||||||
v8::Isolate* isolate_;
|
v8::Isolate* isolate_;
|
||||||
net::URLRequestContextGetter* request_context_getter_;
|
net::URLRequestContextGetter* request_context_getter_;
|
||||||
JavaScriptHandler handler_;
|
JavaScriptHandler handler_;
|
||||||
|
base::TimeTicks request_start_time_;
|
||||||
|
base::TimeTicks response_start_time_;
|
||||||
|
|
||||||
base::WeakPtrFactory<JsAsker> weak_factory_;
|
base::WeakPtrFactory<JsAsker> weak_factory_;
|
||||||
|
|
||||||
|
|
204
atom/browser/net/url_request_stream_job.cc
Normal file
204
atom/browser/net/url_request_stream_job.cc
Normal file
|
@ -0,0 +1,204 @@
|
||||||
|
// Copyright (c) 2017 GitHub, Inc.
|
||||||
|
// Use of this source code is governed by the MIT license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <ostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "atom/browser/net/url_request_stream_job.h"
|
||||||
|
#include "atom/common/api/event_emitter_caller.h"
|
||||||
|
#include "atom/common/atom_constants.h"
|
||||||
|
#include "atom/common/native_mate_converters/net_converter.h"
|
||||||
|
#include "atom/common/node_includes.h"
|
||||||
|
#include "base/strings/string_number_conversions.h"
|
||||||
|
#include "base/strings/string_util.h"
|
||||||
|
#include "base/time/time.h"
|
||||||
|
#include "net/filter/gzip_source_stream.h"
|
||||||
|
|
||||||
|
namespace atom {
|
||||||
|
|
||||||
|
URLRequestStreamJob::URLRequestStreamJob(net::URLRequest* request,
|
||||||
|
net::NetworkDelegate* network_delegate)
|
||||||
|
: JsAsker<net::URLRequestJob>(request, network_delegate),
|
||||||
|
ended_(false),
|
||||||
|
errored_(false),
|
||||||
|
pending_io_buf_(nullptr),
|
||||||
|
pending_io_buf_size_(0),
|
||||||
|
response_headers_(nullptr),
|
||||||
|
weak_factory_(this) {}
|
||||||
|
|
||||||
|
void URLRequestStreamJob::BeforeStartInUI(v8::Isolate* isolate,
|
||||||
|
v8::Local<v8::Value> value) {
|
||||||
|
if (value->IsNull() || value->IsUndefined() || !value->IsObject()) {
|
||||||
|
// Invalid opts.
|
||||||
|
ended_ = true;
|
||||||
|
errored_ = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mate::Dictionary opts(isolate, v8::Local<v8::Object>::Cast(value));
|
||||||
|
int status_code;
|
||||||
|
if (!opts.Get("statusCode", &status_code)) {
|
||||||
|
// assume HTTP OK if statusCode is not passed.
|
||||||
|
status_code = 200;
|
||||||
|
}
|
||||||
|
std::string status("HTTP/1.1 ");
|
||||||
|
status.append(base::IntToString(status_code));
|
||||||
|
status.append(" ");
|
||||||
|
status.append(
|
||||||
|
net::GetHttpReasonPhrase(static_cast<net::HttpStatusCode>(status_code)));
|
||||||
|
status.append("\0\0", 2);
|
||||||
|
response_headers_ = new net::HttpResponseHeaders(status);
|
||||||
|
|
||||||
|
if (opts.Get("headers", &value)) {
|
||||||
|
mate::Converter<net::HttpResponseHeaders*>::FromV8(isolate, value,
|
||||||
|
response_headers_.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!opts.Get("data", &value)) {
|
||||||
|
// Assume the opts is already a stream
|
||||||
|
value = opts.GetHandle();
|
||||||
|
} else if (value->IsNullOrUndefined()) {
|
||||||
|
// "data" was explicitly passed as null or undefined, assume the user wants
|
||||||
|
// to send an empty body.
|
||||||
|
ended_ = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mate::Dictionary data(isolate, v8::Local<v8::Object>::Cast(value));
|
||||||
|
if (!data.Get("on", &value) || !value->IsFunction() ||
|
||||||
|
!data.Get("removeListener", &value) || !value->IsFunction()) {
|
||||||
|
// If data is passed but it is not a stream, signal an error.
|
||||||
|
ended_ = true;
|
||||||
|
errored_ = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
subscriber_.reset(new mate::EventSubscriber<URLRequestStreamJob>(
|
||||||
|
this, isolate, data.GetHandle()));
|
||||||
|
subscriber_->On("data", &URLRequestStreamJob::OnData);
|
||||||
|
subscriber_->On("end", &URLRequestStreamJob::OnEnd);
|
||||||
|
subscriber_->On("error", &URLRequestStreamJob::OnError);
|
||||||
|
}
|
||||||
|
|
||||||
|
void URLRequestStreamJob::StartAsync(std::unique_ptr<base::Value> options) {
|
||||||
|
NotifyHeadersComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
void URLRequestStreamJob::OnData(mate::Arguments* args) {
|
||||||
|
v8::Local<v8::Value> node_data;
|
||||||
|
args->GetNext(&node_data);
|
||||||
|
if (node_data->IsUint8Array()) {
|
||||||
|
const char* data = node::Buffer::Data(node_data);
|
||||||
|
size_t data_size = node::Buffer::Length(node_data);
|
||||||
|
std::copy(data, data + data_size, std::back_inserter(buffer_));
|
||||||
|
} else {
|
||||||
|
NOTREACHED();
|
||||||
|
}
|
||||||
|
if (pending_io_buf_) {
|
||||||
|
CopyMoreData(pending_io_buf_, pending_io_buf_size_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void URLRequestStreamJob::OnEnd(mate::Arguments* args) {
|
||||||
|
ended_ = true;
|
||||||
|
if (pending_io_buf_) {
|
||||||
|
CopyMoreData(pending_io_buf_, pending_io_buf_size_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void URLRequestStreamJob::OnError(mate::Arguments* args) {
|
||||||
|
errored_ = true;
|
||||||
|
if (pending_io_buf_) {
|
||||||
|
CopyMoreData(pending_io_buf_, pending_io_buf_size_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int URLRequestStreamJob::ReadRawData(net::IOBuffer* dest, int dest_size) {
|
||||||
|
content::BrowserThread::PostTask(
|
||||||
|
content::BrowserThread::UI, FROM_HERE,
|
||||||
|
base::Bind(&URLRequestStreamJob::CopyMoreData, weak_factory_.GetWeakPtr(),
|
||||||
|
make_scoped_refptr(dest), dest_size));
|
||||||
|
return net::ERR_IO_PENDING;
|
||||||
|
}
|
||||||
|
|
||||||
|
void URLRequestStreamJob::DoneReading() {
|
||||||
|
subscriber_.reset();
|
||||||
|
buffer_.clear();
|
||||||
|
ended_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void URLRequestStreamJob::DoneReadingRedirectResponse() {
|
||||||
|
DoneReading();
|
||||||
|
}
|
||||||
|
|
||||||
|
void URLRequestStreamJob::CopyMoreDataDone(scoped_refptr<net::IOBuffer> io_buf,
|
||||||
|
int status) {
|
||||||
|
if (status <= 0) {
|
||||||
|
subscriber_.reset();
|
||||||
|
}
|
||||||
|
ReadRawDataComplete(status);
|
||||||
|
io_buf = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void URLRequestStreamJob::CopyMoreData(scoped_refptr<net::IOBuffer> io_buf,
|
||||||
|
int io_buf_size) {
|
||||||
|
// reset any instance references to io_buf
|
||||||
|
pending_io_buf_ = nullptr;
|
||||||
|
pending_io_buf_size_ = 0;
|
||||||
|
|
||||||
|
int read_count = 0;
|
||||||
|
if (buffer_.size()) {
|
||||||
|
size_t count = std::min((size_t)io_buf_size, buffer_.size());
|
||||||
|
std::copy(buffer_.begin(), buffer_.begin() + count, io_buf->data());
|
||||||
|
buffer_.erase(buffer_.begin(), buffer_.begin() + count);
|
||||||
|
read_count = count;
|
||||||
|
} else if (!ended_ && !errored_) {
|
||||||
|
// No data available yet, save references to the IOBuffer, which will be
|
||||||
|
// passed back to this function when OnData/OnEnd/OnError are called
|
||||||
|
pending_io_buf_ = io_buf;
|
||||||
|
pending_io_buf_size_ = io_buf_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pending_io_buf_) {
|
||||||
|
// Only call CopyMoreDataDone if we have read something.
|
||||||
|
int status = (errored_ && !read_count) ? net::ERR_FAILED : read_count;
|
||||||
|
content::BrowserThread::PostTask(
|
||||||
|
content::BrowserThread::IO, FROM_HERE,
|
||||||
|
base::Bind(&URLRequestStreamJob::CopyMoreDataDone,
|
||||||
|
weak_factory_.GetWeakPtr(), io_buf, status));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<net::SourceStream> URLRequestStreamJob::SetUpSourceStream() {
|
||||||
|
std::unique_ptr<net::SourceStream> source =
|
||||||
|
net::URLRequestJob::SetUpSourceStream();
|
||||||
|
size_t i = 0;
|
||||||
|
std::string type;
|
||||||
|
while (response_headers_->EnumerateHeader(&i, "Content-Encoding", &type)) {
|
||||||
|
if (base::LowerCaseEqualsASCII(type, "gzip") ||
|
||||||
|
base::LowerCaseEqualsASCII(type, "x-gzip")) {
|
||||||
|
return net::GzipSourceStream::Create(std::move(source),
|
||||||
|
net::SourceStream::TYPE_GZIP);
|
||||||
|
} else if (base::LowerCaseEqualsASCII(type, "deflate")) {
|
||||||
|
return net::GzipSourceStream::Create(std::move(source),
|
||||||
|
net::SourceStream::TYPE_DEFLATE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool URLRequestStreamJob::GetMimeType(std::string* mime_type) const {
|
||||||
|
return response_headers_->GetMimeType(mime_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
int URLRequestStreamJob::GetResponseCode() const {
|
||||||
|
return response_headers_->response_code();
|
||||||
|
}
|
||||||
|
|
||||||
|
void URLRequestStreamJob::GetResponseInfo(net::HttpResponseInfo* info) {
|
||||||
|
info->headers = response_headers_;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace atom
|
66
atom/browser/net/url_request_stream_job.h
Normal file
66
atom/browser/net/url_request_stream_job.h
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
// Copyright (c) 2017 GitHub, Inc.
|
||||||
|
// Use of this source code is governed by the MIT license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef ATOM_BROWSER_NET_URL_REQUEST_STREAM_JOB_H_
|
||||||
|
#define ATOM_BROWSER_NET_URL_REQUEST_STREAM_JOB_H_
|
||||||
|
|
||||||
|
#include <deque>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "atom/browser/api/event_subscriber.h"
|
||||||
|
#include "atom/browser/net/js_asker.h"
|
||||||
|
#include "base/memory/ref_counted_memory.h"
|
||||||
|
#include "native_mate/persistent_dictionary.h"
|
||||||
|
#include "net/base/io_buffer.h"
|
||||||
|
#include "net/http/http_status_code.h"
|
||||||
|
#include "net/url_request/url_request_context_getter.h"
|
||||||
|
#include "v8/include/v8.h"
|
||||||
|
|
||||||
|
namespace atom {
|
||||||
|
|
||||||
|
class URLRequestStreamJob : public JsAsker<net::URLRequestJob> {
|
||||||
|
public:
|
||||||
|
URLRequestStreamJob(net::URLRequest* request,
|
||||||
|
net::NetworkDelegate* network_delegate);
|
||||||
|
|
||||||
|
void OnData(mate::Arguments* args);
|
||||||
|
void OnEnd(mate::Arguments* args);
|
||||||
|
void OnError(mate::Arguments* args);
|
||||||
|
|
||||||
|
// URLRequestJob
|
||||||
|
void GetResponseInfo(net::HttpResponseInfo* info) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// URLRequestJob
|
||||||
|
int ReadRawData(net::IOBuffer* buf, int buf_size) override;
|
||||||
|
void DoneReading() override;
|
||||||
|
void DoneReadingRedirectResponse() override;
|
||||||
|
std::unique_ptr<net::SourceStream> SetUpSourceStream() override;
|
||||||
|
bool GetMimeType(std::string* mime_type) const override;
|
||||||
|
int GetResponseCode() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// JSAsker
|
||||||
|
void BeforeStartInUI(v8::Isolate*, v8::Local<v8::Value>) override;
|
||||||
|
void StartAsync(std::unique_ptr<base::Value> options) override;
|
||||||
|
void OnResponse(bool success, std::unique_ptr<base::Value> value);
|
||||||
|
|
||||||
|
// Callback after data is asynchronously read from the file into |buf|.
|
||||||
|
void CopyMoreData(scoped_refptr<net::IOBuffer> io_buf, int io_buf_size);
|
||||||
|
void CopyMoreDataDone(scoped_refptr<net::IOBuffer> io_buf, int read_count);
|
||||||
|
|
||||||
|
std::deque<char> buffer_;
|
||||||
|
bool ended_;
|
||||||
|
bool errored_;
|
||||||
|
scoped_refptr<net::IOBuffer> pending_io_buf_;
|
||||||
|
int pending_io_buf_size_;
|
||||||
|
scoped_refptr<net::HttpResponseHeaders> response_headers_;
|
||||||
|
mate::EventSubscriber<URLRequestStreamJob>::SafePtr subscriber_;
|
||||||
|
base::WeakPtrFactory<URLRequestStreamJob> weak_factory_;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(URLRequestStreamJob);
|
||||||
|
};
|
||||||
|
} // namespace atom
|
||||||
|
|
||||||
|
#endif // ATOM_BROWSER_NET_URL_REQUEST_STREAM_JOB_H_
|
|
@ -58,7 +58,9 @@ void PopulateStreamInfo(base::DictionaryValue* stream_info,
|
||||||
PdfViewerHandler::PdfViewerHandler(const std::string& src)
|
PdfViewerHandler::PdfViewerHandler(const std::string& src)
|
||||||
: stream_(nullptr), original_url_(src) {}
|
: stream_(nullptr), original_url_(src) {}
|
||||||
|
|
||||||
PdfViewerHandler::~PdfViewerHandler() {}
|
PdfViewerHandler::~PdfViewerHandler() {
|
||||||
|
RemoveObserver();
|
||||||
|
}
|
||||||
|
|
||||||
void PdfViewerHandler::SetPdfResourceStream(content::StreamInfo* stream) {
|
void PdfViewerHandler::SetPdfResourceStream(content::StreamInfo* stream) {
|
||||||
stream_ = stream;
|
stream_ = stream;
|
||||||
|
@ -90,15 +92,11 @@ void PdfViewerHandler::RegisterMessages() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PdfViewerHandler::OnJavascriptAllowed() {
|
void PdfViewerHandler::OnJavascriptAllowed() {
|
||||||
auto zoom_controller = WebContentsZoomController::FromWebContents(
|
AddObserver();
|
||||||
web_ui()->GetWebContents());
|
|
||||||
zoom_controller->AddObserver(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PdfViewerHandler::OnJavascriptDisallowed() {
|
void PdfViewerHandler::OnJavascriptDisallowed() {
|
||||||
auto zoom_controller = WebContentsZoomController::FromWebContents(
|
RemoveObserver();
|
||||||
web_ui()->GetWebContents());
|
|
||||||
zoom_controller->RemoveObserver(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PdfViewerHandler::Initialize(const base::ListValue* args) {
|
void PdfViewerHandler::Initialize(const base::ListValue* args) {
|
||||||
|
@ -214,4 +212,16 @@ void PdfViewerHandler::OnZoomLevelChanged(content::WebContents* web_contents,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PdfViewerHandler::AddObserver() {
|
||||||
|
auto zoom_controller =
|
||||||
|
WebContentsZoomController::FromWebContents(web_ui()->GetWebContents());
|
||||||
|
zoom_controller->AddObserver(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PdfViewerHandler::RemoveObserver() {
|
||||||
|
auto zoom_controller =
|
||||||
|
WebContentsZoomController::FromWebContents(web_ui()->GetWebContents());
|
||||||
|
zoom_controller->RemoveObserver(this);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace atom
|
} // namespace atom
|
||||||
|
|
|
@ -45,7 +45,8 @@ class PdfViewerHandler : public content::WebUIMessageHandler,
|
||||||
void Reload(const base::ListValue* args);
|
void Reload(const base::ListValue* args);
|
||||||
void OnZoomLevelChanged(content::WebContents* web_contents, double level,
|
void OnZoomLevelChanged(content::WebContents* web_contents, double level,
|
||||||
bool is_temporary);
|
bool is_temporary);
|
||||||
|
void AddObserver();
|
||||||
|
void RemoveObserver();
|
||||||
std::unique_ptr<base::Value> initialize_callback_id_;
|
std::unique_ptr<base::Value> initialize_callback_id_;
|
||||||
content::StreamInfo* stream_;
|
content::StreamInfo* stream_;
|
||||||
std::string original_url_;
|
std::string original_url_;
|
||||||
|
|
|
@ -20,8 +20,18 @@ v8::Local<v8::Value> CallMethodWithArgs(v8::Isolate* isolate,
|
||||||
v8::MicrotasksScope::kRunMicrotasks);
|
v8::MicrotasksScope::kRunMicrotasks);
|
||||||
// Use node::MakeCallback to call the callback, and it will also run pending
|
// Use node::MakeCallback to call the callback, and it will also run pending
|
||||||
// tasks in Node.js.
|
// tasks in Node.js.
|
||||||
return node::MakeCallback(isolate, obj, method, args->size(), &args->front(),
|
v8::MaybeLocal<v8::Value> ret = node::MakeCallback(isolate, obj, method,
|
||||||
{0, 0}).ToLocalChecked();
|
args->size(),
|
||||||
|
&args->front(), {0, 0});
|
||||||
|
// If the JS function throws an exception (doesn't return a value) the result
|
||||||
|
// of MakeCallback will be empty and therefore ToLocal will be false, in this
|
||||||
|
// case we need to return "false" as that indicates that the event emitter did
|
||||||
|
// not handle the event
|
||||||
|
v8::Local<v8::Value> localRet;
|
||||||
|
if (ret.ToLocal(&localRet)) {
|
||||||
|
return localRet;
|
||||||
|
}
|
||||||
|
return v8::Boolean::New(isolate, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
|
@ -8,11 +8,10 @@
|
||||||
#define ATOM_MAJOR_VERSION 1
|
#define ATOM_MAJOR_VERSION 1
|
||||||
#define ATOM_MINOR_VERSION 8
|
#define ATOM_MINOR_VERSION 8
|
||||||
#define ATOM_PATCH_VERSION 2
|
#define ATOM_PATCH_VERSION 2
|
||||||
|
#define ATOM_PRE_RELEASE_VERSION -beta.2
|
||||||
|
|
||||||
#define ATOM_VERSION_IS_RELEASE 1
|
#ifndef ATOM_PRE_RELEASE_VERSION
|
||||||
|
# define ATOM_PRE_RELEASE_VERSION ""
|
||||||
#ifndef ATOM_TAG
|
|
||||||
# define ATOM_TAG ""
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef ATOM_STRINGIFY
|
#ifndef ATOM_STRINGIFY
|
||||||
|
@ -20,17 +19,10 @@
|
||||||
#define ATOM_STRINGIFY_HELPER(n) #n
|
#define ATOM_STRINGIFY_HELPER(n) #n
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if ATOM_VERSION_IS_RELEASE
|
|
||||||
# define ATOM_VERSION_STRING ATOM_STRINGIFY(ATOM_MAJOR_VERSION) "." \
|
# define ATOM_VERSION_STRING ATOM_STRINGIFY(ATOM_MAJOR_VERSION) "." \
|
||||||
ATOM_STRINGIFY(ATOM_MINOR_VERSION) "." \
|
ATOM_STRINGIFY(ATOM_MINOR_VERSION) "." \
|
||||||
ATOM_STRINGIFY(ATOM_PATCH_VERSION) \
|
ATOM_STRINGIFY(ATOM_PATCH_VERSION) \
|
||||||
ATOM_TAG
|
ATOM_STRINGIFY(ATOM_PRE_RELEASE_VERSION)
|
||||||
#else
|
|
||||||
# define ATOM_VERSION_STRING ATOM_STRINGIFY(ATOM_MAJOR_VERSION) "." \
|
|
||||||
ATOM_STRINGIFY(ATOM_MINOR_VERSION) "." \
|
|
||||||
ATOM_STRINGIFY(ATOM_PATCH_VERSION) \
|
|
||||||
ATOM_TAG "-pre"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define ATOM_VERSION "v" ATOM_VERSION_STRING
|
#define ATOM_VERSION "v" ATOM_VERSION_STRING
|
||||||
|
|
||||||
|
|
|
@ -38,22 +38,6 @@ void CallTranslater(v8::Local<v8::External> external,
|
||||||
delete holder;
|
delete holder;
|
||||||
}
|
}
|
||||||
|
|
||||||
// func.bind(func, arg1).
|
|
||||||
// NB(zcbenz): Using C++11 version crashes VS.
|
|
||||||
v8::Local<v8::Value> BindFunctionWith(v8::Isolate* isolate,
|
|
||||||
v8::Local<v8::Context> context,
|
|
||||||
v8::Local<v8::Function> func,
|
|
||||||
v8::Local<v8::Value> arg1,
|
|
||||||
v8::Local<v8::Value> arg2) {
|
|
||||||
v8::MaybeLocal<v8::Value> bind = func->Get(mate::StringToV8(isolate, "bind"));
|
|
||||||
CHECK(!bind.IsEmpty());
|
|
||||||
v8::Local<v8::Function> bind_func =
|
|
||||||
v8::Local<v8::Function>::Cast(bind.ToLocalChecked());
|
|
||||||
v8::Local<v8::Value> converted[] = { func, arg1, arg2 };
|
|
||||||
return bind_func->Call(
|
|
||||||
context, func, arraysize(converted), converted).ToLocalChecked();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
// Destroy the class on UI thread when possible.
|
// Destroy the class on UI thread when possible.
|
||||||
|
@ -130,6 +114,22 @@ v8::Local<v8::Value> CreateFunctionFromTranslater(
|
||||||
v8::Object::New(isolate));
|
v8::Object::New(isolate));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// func.bind(func, arg1).
|
||||||
|
// NB(zcbenz): Using C++11 version crashes VS.
|
||||||
|
v8::Local<v8::Value> BindFunctionWith(v8::Isolate* isolate,
|
||||||
|
v8::Local<v8::Context> context,
|
||||||
|
v8::Local<v8::Function> func,
|
||||||
|
v8::Local<v8::Value> arg1,
|
||||||
|
v8::Local<v8::Value> arg2) {
|
||||||
|
v8::MaybeLocal<v8::Value> bind = func->Get(mate::StringToV8(isolate, "bind"));
|
||||||
|
CHECK(!bind.IsEmpty());
|
||||||
|
v8::Local<v8::Function> bind_func =
|
||||||
|
v8::Local<v8::Function>::Cast(bind.ToLocalChecked());
|
||||||
|
v8::Local<v8::Value> converted[] = {func, arg1, arg2};
|
||||||
|
return bind_func->Call(context, func, arraysize(converted), converted)
|
||||||
|
.ToLocalChecked();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
} // namespace mate
|
} // namespace mate
|
||||||
|
|
|
@ -111,6 +111,11 @@ struct V8FunctionInvoker<ReturnType(ArgTypes...)> {
|
||||||
using Translater = base::Callback<void(Arguments* args)>;
|
using Translater = base::Callback<void(Arguments* args)>;
|
||||||
v8::Local<v8::Value> CreateFunctionFromTranslater(
|
v8::Local<v8::Value> CreateFunctionFromTranslater(
|
||||||
v8::Isolate* isolate, const Translater& translater);
|
v8::Isolate* isolate, const Translater& translater);
|
||||||
|
v8::Local<v8::Value> BindFunctionWith(v8::Isolate* isolate,
|
||||||
|
v8::Local<v8::Context> context,
|
||||||
|
v8::Local<v8::Function> func,
|
||||||
|
v8::Local<v8::Value> arg1,
|
||||||
|
v8::Local<v8::Value> arg2);
|
||||||
|
|
||||||
// Calls callback with Arguments.
|
// Calls callback with Arguments.
|
||||||
template <typename Sig>
|
template <typename Sig>
|
||||||
|
|
|
@ -165,6 +165,35 @@ v8::Local<v8::Value> Converter<net::HttpResponseHeaders*>::ToV8(
|
||||||
return ConvertToV8(isolate, response_headers);
|
return ConvertToV8(isolate, response_headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Converter<net::HttpResponseHeaders*>::FromV8(
|
||||||
|
v8::Isolate* isolate,
|
||||||
|
v8::Local<v8::Value> val,
|
||||||
|
net::HttpResponseHeaders* out) {
|
||||||
|
if (!val->IsObject()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto context = isolate->GetCurrentContext();
|
||||||
|
auto headers = v8::Local<v8::Object>::Cast(val);
|
||||||
|
auto keys = headers->GetOwnPropertyNames();
|
||||||
|
for (uint32_t i = 0; i < keys->Length(); i++) {
|
||||||
|
v8::Local<v8::String> key, value;
|
||||||
|
if (!keys->Get(i)->ToString(context).ToLocal(&key)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!headers->Get(key)->ToString(context).ToLocal(&value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
v8::String::Utf8Value key_utf8(key);
|
||||||
|
v8::String::Utf8Value value_utf8(value);
|
||||||
|
std::string k(*key_utf8, key_utf8.length());
|
||||||
|
std::string v(*value_utf8, value_utf8.length());
|
||||||
|
std::ostringstream tmp;
|
||||||
|
tmp << k << ": " << v;
|
||||||
|
out->AddHeader(tmp.str());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace mate
|
} // namespace mate
|
||||||
|
|
||||||
namespace atom {
|
namespace atom {
|
||||||
|
@ -180,6 +209,13 @@ void FillRequestDetails(base::DictionaryValue* details,
|
||||||
GetUploadData(list.get(), request);
|
GetUploadData(list.get(), request);
|
||||||
if (!list->empty())
|
if (!list->empty())
|
||||||
details->Set("uploadData", std::move(list));
|
details->Set("uploadData", std::move(list));
|
||||||
|
std::unique_ptr<base::DictionaryValue> headers_value(
|
||||||
|
new base::DictionaryValue);
|
||||||
|
for (net::HttpRequestHeaders::Iterator it(request->extra_request_headers());
|
||||||
|
it.GetNext();) {
|
||||||
|
headers_value->SetString(it.name(), it.value());
|
||||||
|
}
|
||||||
|
details->Set("headers", std::move(headers_value));
|
||||||
}
|
}
|
||||||
|
|
||||||
void GetUploadData(base::ListValue* upload_data_list,
|
void GetUploadData(base::ListValue* upload_data_list,
|
||||||
|
|
|
@ -49,6 +49,9 @@ template <>
|
||||||
struct Converter<net::HttpResponseHeaders*> {
|
struct Converter<net::HttpResponseHeaders*> {
|
||||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||||
net::HttpResponseHeaders* headers);
|
net::HttpResponseHeaders* headers);
|
||||||
|
static bool FromV8(v8::Isolate* isolate,
|
||||||
|
v8::Local<v8::Value> val,
|
||||||
|
net::HttpResponseHeaders* out);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace mate
|
} // namespace mate
|
||||||
|
|
|
@ -1,28 +1,28 @@
|
||||||
// Copyright (c) 2016 GitHub, Inc.
|
// Copyright (c) 2017 GitHub, Inc.
|
||||||
// Use of this source code is governed by the MIT license that can be
|
// Use of this source code is governed by the MIT license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
#include "atom/browser/net/atom_ct_delegate.h"
|
#include "brightray/browser/net/require_ct_delegate.h"
|
||||||
|
|
||||||
#include "content/public/browser/browser_thread.h"
|
#include "content/public/browser/browser_thread.h"
|
||||||
|
|
||||||
namespace atom {
|
namespace brightray {
|
||||||
|
|
||||||
AtomCTDelegate::AtomCTDelegate() {}
|
RequireCTDelegate::RequireCTDelegate() {}
|
||||||
|
|
||||||
AtomCTDelegate::~AtomCTDelegate() {}
|
RequireCTDelegate::~RequireCTDelegate() {}
|
||||||
|
|
||||||
void AtomCTDelegate::AddCTExcludedHost(const std::string& host) {
|
void RequireCTDelegate::AddCTExcludedHost(const std::string& host) {
|
||||||
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
||||||
ct_excluded_hosts_.insert(host);
|
ct_excluded_hosts_.insert(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AtomCTDelegate::ClearCTExcludedHostsList() {
|
void RequireCTDelegate::ClearCTExcludedHostsList() {
|
||||||
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
||||||
ct_excluded_hosts_.clear();
|
ct_excluded_hosts_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
AtomCTDelegate::CTRequirementLevel AtomCTDelegate::IsCTRequiredForHost(
|
RequireCTDelegate::CTRequirementLevel RequireCTDelegate::IsCTRequiredForHost(
|
||||||
const std::string& host) {
|
const std::string& host) {
|
||||||
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
||||||
if (!ct_excluded_hosts_.empty() &&
|
if (!ct_excluded_hosts_.empty() &&
|
||||||
|
@ -31,4 +31,4 @@ AtomCTDelegate::CTRequirementLevel AtomCTDelegate::IsCTRequiredForHost(
|
||||||
return CTRequirementLevel::DEFAULT;
|
return CTRequirementLevel::DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace atom
|
} // namespace brightray
|
|
@ -1,21 +1,22 @@
|
||||||
// Copyright (c) 2016 GitHub, Inc.
|
// Copyright (c) 2017 GitHub, Inc.
|
||||||
// Use of this source code is governed by the MIT license that can be
|
// Use of this source code is governed by the MIT license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
#ifndef ATOM_BROWSER_NET_ATOM_CT_DELEGATE_H_
|
#ifndef BRIGHTRAY_BROWSER_NET_REQUIRE_CT_DELEGATE_H_
|
||||||
#define ATOM_BROWSER_NET_ATOM_CT_DELEGATE_H_
|
#define BRIGHTRAY_BROWSER_NET_REQUIRE_CT_DELEGATE_H_
|
||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "net/http/transport_security_state.h"
|
#include "net/http/transport_security_state.h"
|
||||||
|
|
||||||
namespace atom {
|
namespace brightray {
|
||||||
|
|
||||||
class AtomCTDelegate : public net::TransportSecurityState::RequireCTDelegate {
|
class RequireCTDelegate
|
||||||
|
: public net::TransportSecurityState::RequireCTDelegate {
|
||||||
public:
|
public:
|
||||||
AtomCTDelegate();
|
RequireCTDelegate();
|
||||||
~AtomCTDelegate() override;
|
~RequireCTDelegate() override;
|
||||||
|
|
||||||
void AddCTExcludedHost(const std::string& host);
|
void AddCTExcludedHost(const std::string& host);
|
||||||
void ClearCTExcludedHostsList();
|
void ClearCTExcludedHostsList();
|
||||||
|
@ -25,9 +26,9 @@ class AtomCTDelegate : public net::TransportSecurityState::RequireCTDelegate {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::set<std::string> ct_excluded_hosts_;
|
std::set<std::string> ct_excluded_hosts_;
|
||||||
DISALLOW_COPY_AND_ASSIGN(AtomCTDelegate);
|
DISALLOW_COPY_AND_ASSIGN(RequireCTDelegate);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace atom
|
} // namespace brightray
|
||||||
|
|
||||||
#endif // ATOM_BROWSER_NET_ATOM_CT_DELEGATE_H_
|
#endif // BRIGHTRAY_BROWSER_NET_REQUIRE_CT_DELEGATE_H_
|
|
@ -14,6 +14,7 @@
|
||||||
#include "base/threading/worker_pool.h"
|
#include "base/threading/worker_pool.h"
|
||||||
#include "brightray/browser/net/devtools_network_controller_handle.h"
|
#include "brightray/browser/net/devtools_network_controller_handle.h"
|
||||||
#include "brightray/browser/net/devtools_network_transaction_factory.h"
|
#include "brightray/browser/net/devtools_network_transaction_factory.h"
|
||||||
|
#include "brightray/browser/net/require_ct_delegate.h"
|
||||||
#include "brightray/browser/net_log.h"
|
#include "brightray/browser/net_log.h"
|
||||||
#include "brightray/browser/network_delegate.h"
|
#include "brightray/browser/network_delegate.h"
|
||||||
#include "brightray/common/switches.h"
|
#include "brightray/common/switches.h"
|
||||||
|
@ -107,7 +108,8 @@ URLRequestContextGetter::Delegate::CreateHttpCacheBackendFactory(
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<net::CertVerifier>
|
std::unique_ptr<net::CertVerifier>
|
||||||
URLRequestContextGetter::Delegate::CreateCertVerifier() {
|
URLRequestContextGetter::Delegate::CreateCertVerifier(
|
||||||
|
RequireCTDelegate* ct_delegate) {
|
||||||
return net::CertVerifier::CreateDefault();
|
return net::CertVerifier::CreateDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,6 +172,7 @@ net::URLRequestContext* URLRequestContextGetter::GetURLRequestContext() {
|
||||||
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
||||||
|
|
||||||
if (!url_request_context_.get()) {
|
if (!url_request_context_.get()) {
|
||||||
|
ct_delegate_.reset(new RequireCTDelegate);
|
||||||
auto& command_line = *base::CommandLine::ForCurrentProcess();
|
auto& command_line = *base::CommandLine::ForCurrentProcess();
|
||||||
url_request_context_.reset(new net::URLRequestContext);
|
url_request_context_.reset(new net::URLRequestContext);
|
||||||
|
|
||||||
|
@ -280,10 +283,10 @@ net::URLRequestContext* URLRequestContextGetter::GetURLRequestContext() {
|
||||||
|
|
||||||
std::unique_ptr<net::TransportSecurityState> transport_security_state =
|
std::unique_ptr<net::TransportSecurityState> transport_security_state =
|
||||||
base::WrapUnique(new net::TransportSecurityState);
|
base::WrapUnique(new net::TransportSecurityState);
|
||||||
transport_security_state->SetRequireCTDelegate(
|
transport_security_state->SetRequireCTDelegate(ct_delegate_.get());
|
||||||
delegate_->GetRequireCTDelegate());
|
|
||||||
storage_->set_transport_security_state(std::move(transport_security_state));
|
storage_->set_transport_security_state(std::move(transport_security_state));
|
||||||
storage_->set_cert_verifier(delegate_->CreateCertVerifier());
|
storage_->set_cert_verifier(
|
||||||
|
delegate_->CreateCertVerifier(ct_delegate_.get()));
|
||||||
storage_->set_ssl_config_service(delegate_->CreateSSLConfigService());
|
storage_->set_ssl_config_service(delegate_->CreateSSLConfigService());
|
||||||
storage_->set_http_auth_handler_factory(std::move(auth_handler_factory));
|
storage_->set_http_auth_handler_factory(std::move(auth_handler_factory));
|
||||||
std::unique_ptr<net::HttpServerProperties> server_properties(
|
std::unique_ptr<net::HttpServerProperties> server_properties(
|
||||||
|
|
|
@ -33,6 +33,7 @@ class URLRequestJobFactory;
|
||||||
|
|
||||||
namespace brightray {
|
namespace brightray {
|
||||||
|
|
||||||
|
class RequireCTDelegate;
|
||||||
class DevToolsNetworkControllerHandle;
|
class DevToolsNetworkControllerHandle;
|
||||||
class MediaDeviceIDSalt;
|
class MediaDeviceIDSalt;
|
||||||
class NetLog;
|
class NetLog;
|
||||||
|
@ -53,13 +54,10 @@ class URLRequestContextGetter : public net::URLRequestContextGetter {
|
||||||
CreateURLRequestJobFactory(content::ProtocolHandlerMap* protocol_handlers);
|
CreateURLRequestJobFactory(content::ProtocolHandlerMap* protocol_handlers);
|
||||||
virtual net::HttpCache::BackendFactory* CreateHttpCacheBackendFactory(
|
virtual net::HttpCache::BackendFactory* CreateHttpCacheBackendFactory(
|
||||||
const base::FilePath& base_path);
|
const base::FilePath& base_path);
|
||||||
virtual std::unique_ptr<net::CertVerifier> CreateCertVerifier();
|
virtual std::unique_ptr<net::CertVerifier> CreateCertVerifier(
|
||||||
|
RequireCTDelegate* ct_delegate);
|
||||||
virtual net::SSLConfigService* CreateSSLConfigService();
|
virtual net::SSLConfigService* CreateSSLConfigService();
|
||||||
virtual std::vector<std::string> GetCookieableSchemes();
|
virtual std::vector<std::string> GetCookieableSchemes();
|
||||||
virtual net::TransportSecurityState::RequireCTDelegate*
|
|
||||||
GetRequireCTDelegate() {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
virtual MediaDeviceIDSalt* GetMediaDeviceIDSalt() { return nullptr; }
|
virtual MediaDeviceIDSalt* GetMediaDeviceIDSalt() { return nullptr; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -98,6 +96,7 @@ class URLRequestContextGetter : public net::URLRequestContextGetter {
|
||||||
|
|
||||||
std::string user_agent_;
|
std::string user_agent_;
|
||||||
|
|
||||||
|
std::unique_ptr<RequireCTDelegate> ct_delegate_;
|
||||||
std::unique_ptr<net::ProxyConfigService> proxy_config_service_;
|
std::unique_ptr<net::ProxyConfigService> proxy_config_service_;
|
||||||
std::unique_ptr<net::NetworkDelegate> network_delegate_;
|
std::unique_ptr<net::NetworkDelegate> network_delegate_;
|
||||||
std::unique_ptr<net::URLRequestContextStorage> storage_;
|
std::unique_ptr<net::URLRequestContextStorage> storage_;
|
||||||
|
|
|
@ -61,6 +61,8 @@
|
||||||
'browser/net/devtools_network_transaction.h',
|
'browser/net/devtools_network_transaction.h',
|
||||||
'browser/net/devtools_network_upload_data_stream.cc',
|
'browser/net/devtools_network_upload_data_stream.cc',
|
||||||
'browser/net/devtools_network_upload_data_stream.h',
|
'browser/net/devtools_network_upload_data_stream.h',
|
||||||
|
'browser/net/require_ct_delegate.cc',
|
||||||
|
'browser/net/require_ct_delegate.h',
|
||||||
'browser/net_log.cc',
|
'browser/net_log.cc',
|
||||||
'browser/net_log.h',
|
'browser/net_log.h',
|
||||||
'browser/network_delegate.cc',
|
'browser/network_delegate.cc',
|
||||||
|
|
|
@ -204,7 +204,7 @@
|
||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
<div class="linkcol">
|
<div class="linkcol">
|
||||||
<a class="hero-link" href="https://electron.atom.io/blog">
|
<a class="hero-link" href="https://electronjs.org/blog">
|
||||||
<span class="octicon hero-octicon octicon-gist" aria-hidden="true"></span>
|
<span class="octicon hero-octicon octicon-gist" aria-hidden="true"></span>
|
||||||
<h4>Blog</h4>
|
<h4>Blog</h4>
|
||||||
</a>
|
</a>
|
||||||
|
@ -216,7 +216,7 @@
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="linkcol">
|
<div class="linkcol">
|
||||||
<a class="hero-link" href="https://electron.atom.io/docs">
|
<a class="hero-link" href="https://electronjs.org/docs">
|
||||||
<span class="octicon hero-octicon octicon-gear" aria-hidden="true"></span>
|
<span class="octicon hero-octicon octicon-gear" aria-hidden="true"></span>
|
||||||
<h4>Docs</h4>
|
<h4>Docs</h4>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -138,7 +138,7 @@ app.once('ready', () => {
|
||||||
{
|
{
|
||||||
label: 'Learn More',
|
label: 'Learn More',
|
||||||
click () {
|
click () {
|
||||||
shell.openExternal('https://electron.atom.io')
|
shell.openExternal('https://electronjs.org')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -26,8 +26,8 @@ git clone https://github.com/electron/electron-i18n
|
||||||
vmd electron-i18n/content/zh-CN
|
vmd electron-i18n/content/zh-CN
|
||||||
```
|
```
|
||||||
|
|
||||||
[crowdin.com/projects/electron]: https://crowdin.com/projects/electron
|
[crowdin.com/project/electron]: https://crowdin.com/project/electron
|
||||||
[Crowdin]: https://crowdin.com/projects/electron
|
[Crowdin]: https://crowdin.com/project/electron
|
||||||
[electron/electron-i18n]: https://github.com/electron/electron-i18n#readme
|
[electron/electron-i18n]: https://github.com/electron/electron-i18n#readme
|
||||||
[electron/electron-i18n/tree/master/content]: https://github.com/electron/electron-i18n/tree/master/content
|
[electron/electron-i18n/tree/master/content]: https://github.com/electron/electron-i18n/tree/master/content
|
||||||
[vmd]: http://ghub.io/vmd
|
[vmd]: http://ghub.io/vmd
|
|
@ -30,7 +30,7 @@ let view = new BrowserView({
|
||||||
})
|
})
|
||||||
win.setBrowserView(view)
|
win.setBrowserView(view)
|
||||||
view.setBounds({ x: 0, y: 0, width: 300, height: 300 })
|
view.setBounds({ x: 0, y: 0, width: 300, height: 300 })
|
||||||
view.webContents.loadURL('https://electron.atom.io')
|
view.webContents.loadURL('https://electronjs.org')
|
||||||
```
|
```
|
||||||
|
|
||||||
### `new BrowserView([options])` _Experimental_
|
### `new BrowserView([options])` _Experimental_
|
||||||
|
|
|
@ -103,7 +103,7 @@ clipboard.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
clipboard.write({
|
clipboard.write({
|
||||||
text: 'https://electron.atom.io',
|
text: 'https://electronjs.org',
|
||||||
bookmark: 'Electron Homepage'
|
bookmark: 'Electron Homepage'
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
## Class: Menu
|
## Class: Menu
|
||||||
|
|
||||||
|
|
||||||
> Create native application menus and context menus.
|
> Create native application menus and context menus.
|
||||||
|
|
||||||
Process: [Main](../glossary.md#main-process)
|
Process: [Main](../glossary.md#main-process)
|
||||||
|
@ -15,7 +14,7 @@ The `menu` class has the following static methods:
|
||||||
|
|
||||||
#### `Menu.setApplicationMenu(menu)`
|
#### `Menu.setApplicationMenu(menu)`
|
||||||
|
|
||||||
* `menu` Menu
|
* `menu` Menu | null
|
||||||
|
|
||||||
Sets `menu` as the application menu on macOS. On Windows and Linux, the
|
Sets `menu` as the application menu on macOS. On Windows and Linux, the
|
||||||
`menu` will be set as each window's top menu.
|
`menu` will be set as each window's top menu.
|
||||||
|
@ -27,7 +26,7 @@ effect on macOS.
|
||||||
|
|
||||||
#### `Menu.getApplicationMenu()`
|
#### `Menu.getApplicationMenu()`
|
||||||
|
|
||||||
Returns `Menu` - The application menu, if set, or `null`, if not set.
|
Returns `Menu | null` - The application menu, if set, or `null`, if not set.
|
||||||
|
|
||||||
**Note:** The returned `Menu` instance doesn't support dynamic addition or
|
**Note:** The returned `Menu` instance doesn't support dynamic addition or
|
||||||
removal of menu items. [Instance properties](#instance-properties) can still
|
removal of menu items. [Instance properties](#instance-properties) can still
|
||||||
|
@ -167,7 +166,7 @@ const template = [
|
||||||
submenu: [
|
submenu: [
|
||||||
{
|
{
|
||||||
label: 'Learn More',
|
label: 'Learn More',
|
||||||
click () { require('electron').shell.openExternal('https://electron.atom.io') }
|
click () { require('electron').shell.openExternal('https://electronjs.org') }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -194,6 +194,67 @@ request to have a different session you should set `session` to `null`.
|
||||||
|
|
||||||
For POST requests the `uploadData` object must be provided.
|
For POST requests the `uploadData` object must be provided.
|
||||||
|
|
||||||
|
### `protocol.registerStreamProtocol(scheme, handler[, completion])`
|
||||||
|
|
||||||
|
* `scheme` String
|
||||||
|
* `handler` Function
|
||||||
|
* `request` Object
|
||||||
|
* `url` String
|
||||||
|
* `headers` Object
|
||||||
|
* `referrer` String
|
||||||
|
* `method` String
|
||||||
|
* `uploadData` [UploadData[]](structures/upload-data.md)
|
||||||
|
* `callback` Function
|
||||||
|
* `stream` (ReadableStream | [StreamProtocolResponse](structures/stream-protocol-response.md)) (optional)
|
||||||
|
* `completion` Function (optional)
|
||||||
|
* `error` Error
|
||||||
|
|
||||||
|
Registers a protocol of `scheme` that will send a `Readable` as a response.
|
||||||
|
|
||||||
|
The usage is similar to the other `register{Any}Protocol`, except that the
|
||||||
|
`callback` should be called with either a `Readable` object or an object that
|
||||||
|
has the `data`, `statusCode`, and `headers` properties.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const {protocol} = require('electron')
|
||||||
|
const {PassThrough} = require('stream')
|
||||||
|
|
||||||
|
function createStream (text) {
|
||||||
|
const rv = new PassThrough() // PassThrough is also a Readable stream
|
||||||
|
rv.push(text)
|
||||||
|
rv.push(null)
|
||||||
|
return rv
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol.registerStreamProtocol('atom', (request, callback) => {
|
||||||
|
callback({
|
||||||
|
statusCode: 200,
|
||||||
|
headers: {
|
||||||
|
'content-type': 'text/html'
|
||||||
|
},
|
||||||
|
data: createStream('<h5>Response</h5>')
|
||||||
|
})
|
||||||
|
}, (error) => {
|
||||||
|
if (error) console.error('Failed to register protocol')
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
It is possible to pass any object that implements the readable stream API (emits
|
||||||
|
`data`/`end`/`error` events). For example, here's how a file could be returned:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const {protocol} = require('electron')
|
||||||
|
const fs = require('fs')
|
||||||
|
|
||||||
|
protocol.registerStreamProtocol('atom', (request, callback) => {
|
||||||
|
callback(fs.createReadStream('index.html'))
|
||||||
|
}, (error) => {
|
||||||
|
if (error) console.error('Failed to register protocol')
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
### `protocol.unregisterProtocol(scheme[, completion])`
|
### `protocol.unregisterProtocol(scheme[, completion])`
|
||||||
|
|
||||||
* `scheme` String
|
* `scheme` String
|
||||||
|
@ -285,6 +346,24 @@ which sends a `Buffer` as a response.
|
||||||
Intercepts `scheme` protocol and uses `handler` as the protocol's new handler
|
Intercepts `scheme` protocol and uses `handler` as the protocol's new handler
|
||||||
which sends a new HTTP request as a response.
|
which sends a new HTTP request as a response.
|
||||||
|
|
||||||
|
### `protocol.interceptStreamProtocol(scheme, handler[, completion])`
|
||||||
|
|
||||||
|
* `scheme` String
|
||||||
|
* `handler` Function
|
||||||
|
* `request` Object
|
||||||
|
* `url` String
|
||||||
|
* `headers` Object
|
||||||
|
* `referrer` String
|
||||||
|
* `method` String
|
||||||
|
* `uploadData` [UploadData[]](structures/upload-data.md)
|
||||||
|
* `callback` Function
|
||||||
|
* `stream` (ReadableStream | [StreamProtocolResponse](structures/stream-protocol-response.md)) (optional)
|
||||||
|
* `completion` Function (optional)
|
||||||
|
* `error` Error
|
||||||
|
|
||||||
|
Same as `protocol.registerStreamProtocol`, except that it replaces an existing
|
||||||
|
protocol handler.
|
||||||
|
|
||||||
### `protocol.uninterceptProtocol(scheme[, completion])`
|
### `protocol.uninterceptProtocol(scheme[, completion])`
|
||||||
|
|
||||||
* `scheme` String
|
* `scheme` String
|
||||||
|
|
|
@ -259,7 +259,7 @@ the original network configuration.
|
||||||
* `verificationResult` Integer - Value can be one of certificate error codes
|
* `verificationResult` Integer - Value can be one of certificate error codes
|
||||||
from [here](https://code.google.com/p/chromium/codesearch#chromium/src/net/base/net_error_list.h).
|
from [here](https://code.google.com/p/chromium/codesearch#chromium/src/net/base/net_error_list.h).
|
||||||
Apart from the certificate error codes, the following special codes can be used.
|
Apart from the certificate error codes, the following special codes can be used.
|
||||||
* `0` - Indicates success and disables Certificate Transperancy verification.
|
* `0` - Indicates success and disables Certificate Transparency verification.
|
||||||
* `-2` - Indicates failure.
|
* `-2` - Indicates failure.
|
||||||
* `-3` - Uses the verification result from chromium.
|
* `-3` - Uses the verification result from chromium.
|
||||||
|
|
||||||
|
@ -287,7 +287,7 @@ win.webContents.session.setCertificateVerifyProc((request, callback) => {
|
||||||
|
|
||||||
#### `ses.setPermissionRequestHandler(handler)`
|
#### `ses.setPermissionRequestHandler(handler)`
|
||||||
|
|
||||||
* `handler` Function
|
* `handler` Function | null
|
||||||
* `webContents` [WebContents](web-contents.md) - WebContents requesting the permission.
|
* `webContents` [WebContents](web-contents.md) - WebContents requesting the permission.
|
||||||
* `permission` String - Enum of 'media', 'geolocation', 'notifications', 'midiSysex',
|
* `permission` String - Enum of 'media', 'geolocation', 'notifications', 'midiSysex',
|
||||||
'pointerLock', 'fullscreen', 'openExternal'.
|
'pointerLock', 'fullscreen', 'openExternal'.
|
||||||
|
@ -296,6 +296,7 @@ win.webContents.session.setCertificateVerifyProc((request, callback) => {
|
||||||
|
|
||||||
Sets the handler which can be used to respond to permission requests for the `session`.
|
Sets the handler which can be used to respond to permission requests for the `session`.
|
||||||
Calling `callback(true)` will allow the permission and `callback(false)` will reject it.
|
Calling `callback(true)` will allow the permission and `callback(false)` will reject it.
|
||||||
|
To clear the handler, call `setPermissionRequestHandler(null)`.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const {session} = require('electron')
|
const {session} = require('electron')
|
||||||
|
|
5
docs/api/structures/stream-protocol-response.md
Normal file
5
docs/api/structures/stream-protocol-response.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# StreamProtocolResponse Object
|
||||||
|
|
||||||
|
* `statusCode` Number - The HTTP response code
|
||||||
|
* `headers` Object - An object containing the response headers
|
||||||
|
* `data` ReadableStream - A Node.js readable stream representing the response body
|
|
@ -177,7 +177,7 @@ Web security is enabled by default.
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<webview src="https://github.com" partition="persist:github"></webview>
|
<webview src="https://github.com" partition="persist:github"></webview>
|
||||||
<webview src="https://electron.atom.io" partition="electron"></webview>
|
<webview src="https://electronjs.org" partition="electron"></webview>
|
||||||
```
|
```
|
||||||
|
|
||||||
Sets the session used by the page. If `partition` starts with `persist:`, the
|
Sets the session used by the page. If `partition` starts with `persist:`, the
|
||||||
|
|
|
@ -149,18 +149,19 @@ information may help you.
|
||||||
### Building `libchromiumcontent` locally
|
### Building `libchromiumcontent` locally
|
||||||
|
|
||||||
To avoid using the prebuilt binaries of `libchromiumcontent`, you can build `libchromiumcontent` locally. To do so, follow these steps:
|
To avoid using the prebuilt binaries of `libchromiumcontent`, you can build `libchromiumcontent` locally. To do so, follow these steps:
|
||||||
1. Install [depot_tools](https://chromium.googlesource.com/chromium/src/+/master/docs/linux_build_instructions.md#Install)
|
|
||||||
2. Install [additional build dependencies](https://chromium.googlesource.com/chromium/src/+/master/docs/linux_build_instructions.md#Install-additional-build-dependencies)
|
|
||||||
3. Fetch the git submodules:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ git submodule update --init --recursive
|
|
||||||
```
|
|
||||||
4. Pass the `--build_release_libcc` switch to `bootstrap.py` script:
|
|
||||||
|
|
||||||
```bash
|
1. Install [depot_tools](https://chromium.googlesource.com/chromium/src/+/master/docs/linux_build_instructions.md#Install)
|
||||||
$ ./script/bootstrap.py -v --build_release_libcc
|
2. Install [additional build dependencies](https://chromium.googlesource.com/chromium/src/+/master/docs/linux_build_instructions.md#Install-additional-build-dependencies)
|
||||||
```
|
3. Fetch the git submodules:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ git submodule update --init --recursive
|
||||||
|
```
|
||||||
|
4. Pass the `--build_release_libcc` switch to `bootstrap.py` script:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ ./script/bootstrap.py -v --build_release_libcc
|
||||||
|
```
|
||||||
|
|
||||||
Note that by default the `shared_library` configuration is not built, so you can
|
Note that by default the `shared_library` configuration is not built, so you can
|
||||||
only build `Release` version of Electron if you use this mode:
|
only build `Release` version of Electron if you use this mode:
|
||||||
|
@ -218,4 +219,4 @@ custom the building configurations:
|
||||||
* `LDFLAGS`
|
* `LDFLAGS`
|
||||||
|
|
||||||
The environment variables have to be set when executing the `bootstrap.py`
|
The environment variables have to be set when executing the `bootstrap.py`
|
||||||
script, it won't work in the `build.py` script.
|
script, it won't work in the `build.py` script.
|
|
@ -64,7 +64,7 @@ The following rules only apply to the documentation of APIs.
|
||||||
Each page must use the actual object name returned by `require('electron')`
|
Each page must use the actual object name returned by `require('electron')`
|
||||||
as the title, such as `BrowserWindow`, `autoUpdater`, and `session`.
|
as the title, such as `BrowserWindow`, `autoUpdater`, and `session`.
|
||||||
|
|
||||||
Under the page tile must be a one-line description starting with `>`.
|
Under the page title must be a one-line description starting with `>`.
|
||||||
|
|
||||||
Using `session` as example:
|
Using `session` as example:
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
# About Electron
|
# About Electron
|
||||||
|
|
||||||
[Electron](https://electron.atom.io) is an open source library developed by GitHub for building cross-platform desktop applications with HTML, CSS, and JavaScript. Electron accomplishes this by combining [Chromium](https://www.chromium.org/Home) and [Node.js](https://nodejs.org) into a single runtime and apps can be packaged for Mac, Windows, and Linux.
|
[Electron](https://electronjs.org) is an open source library developed by GitHub for building cross-platform desktop applications with HTML, CSS, and JavaScript. Electron accomplishes this by combining [Chromium](https://www.chromium.org/Home) and [Node.js](https://nodejs.org) into a single runtime and apps can be packaged for Mac, Windows, and Linux.
|
||||||
|
|
||||||
Electron began in 2013 as the framework on which [Atom](https://atom.io), GitHub's hackable text editor, would be built. The two were open sourced in the Spring of 2014.
|
Electron began in 2013 as the framework on which [Atom](https://atom.io), GitHub's hackable text editor, would be built. The two were open sourced in the Spring of 2014.
|
||||||
|
|
||||||
It has since become a popular tool used by open source developers, startups, and established companies. [See who is building on Electron](https://electron.atom.io/apps/).
|
It has since become a popular tool used by open source developers, startups, and established companies. [See who is building on Electron](https://electronjs.org/apps).
|
||||||
|
|
||||||
Read on to learn more about the contributors and releases of Electron or get started building with Electron in the [Quick Start Guide](quick-start.md).
|
Read on to learn more about the contributors and releases of Electron or get started building with Electron in the [Quick Start Guide](quick-start.md).
|
||||||
|
|
||||||
|
@ -27,13 +27,13 @@ In Electron, Node.js and Chromium share a single V8 instance—usually the versi
|
||||||
|
|
||||||
### Versioning
|
### Versioning
|
||||||
|
|
||||||
Due to the hard dependency on Node.js and Chromium, Electron is in a tricky versioning position and [does not follow `semver`](http://semver.org). You should therefore always reference a specific version of Electron. [Read more about Electron's versioning](https://electron.atom.io/docs/tutorial/electron-versioning/) or see the [versions currently in use](https://electron.atom.io/#electron-versions).
|
Due to the hard dependency on Node.js and Chromium, Electron is in a tricky versioning position and [does not follow `semver`](http://semver.org). You should therefore always reference a specific version of Electron. [Read more about Electron's versioning](https://electronjs.org/docs/tutorial/electron-versioning) or see the [versions currently in use](https://electronjs.org/#electron-versions).
|
||||||
|
|
||||||
### LTS
|
### LTS
|
||||||
|
|
||||||
Long term support of older versions of Electron does not currently exist. If your current version of Electron works for you, you can stay on it for as long as you'd like. If you want to make use of new features as they come in you should upgrade to a newer version.
|
Long term support of older versions of Electron does not currently exist. If your current version of Electron works for you, you can stay on it for as long as you'd like. If you want to make use of new features as they come in you should upgrade to a newer version.
|
||||||
|
|
||||||
A major update came with version `v1.0.0`. If you're not yet using this version, you should [read more about the `v1.0.0` changes](https://electron.atom.io/blog/2016/05/11/electron-1-0).
|
A major update came with version `v1.0.0`. If you're not yet using this version, you should [read more about the `v1.0.0` changes](https://electronjs.org/blog/electron-1-0).
|
||||||
|
|
||||||
## Core Philosophy
|
## Core Philosophy
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ In order to keep Electron small (file size) and sustainable (the spread of depen
|
||||||
|
|
||||||
For instance, Electron uses just the rendering library from Chromium rather than all of Chromium. This makes it easier to upgrade Chromium but also means some browser features found in Google Chrome do not exist in Electron.
|
For instance, Electron uses just the rendering library from Chromium rather than all of Chromium. This makes it easier to upgrade Chromium but also means some browser features found in Google Chrome do not exist in Electron.
|
||||||
|
|
||||||
New features added to Electron should primarily be native APIs. If a feature can be its own Node.js module, it probably should be. See the [Electron tools built by the community](https://electron.atom.io/community).
|
New features added to Electron should primarily be native APIs. If a feature can be its own Node.js module, it probably should be. See the [Electron tools built by the community](https://electronjs.org/community).
|
||||||
|
|
||||||
## History
|
## History
|
||||||
|
|
||||||
|
@ -52,6 +52,6 @@ Below are milestones in Electron's history.
|
||||||
| **April 2013**| [Atom Shell is started](https://github.com/electron/electron/commit/6ef8875b1e93787fa9759f602e7880f28e8e6b45).|
|
| **April 2013**| [Atom Shell is started](https://github.com/electron/electron/commit/6ef8875b1e93787fa9759f602e7880f28e8e6b45).|
|
||||||
| **May 2014** | [Atom Shell is open sourced](http://blog.atom.io/2014/05/06/atom-is-now-open-source.html). |
|
| **May 2014** | [Atom Shell is open sourced](http://blog.atom.io/2014/05/06/atom-is-now-open-source.html). |
|
||||||
| **April 2015** | [Atom Shell is re-named Electron](https://github.com/electron/electron/pull/1389). |
|
| **April 2015** | [Atom Shell is re-named Electron](https://github.com/electron/electron/pull/1389). |
|
||||||
| **May 2016** | [Electron releases `v1.0.0`](https://electron.atom.io/blog/2016/05/11/electron-1-0).|
|
| **May 2016** | [Electron releases `v1.0.0`](https://electronjs.org/blog/electron-1-0).|
|
||||||
| **May 2016** | [Electron apps compatible with Mac App Store](https://electron.atom.io/docs/tutorial/mac-app-store-submission-guide).|
|
| **May 2016** | [Electron apps compatible with Mac App Store](https://electronjs.org/docs/tutorial/mac-app-store-submission-guide).|
|
||||||
| **August 2016** | [Windows Store support for Electron apps](https://electron.atom.io/docs/tutorial/windows-store-guide).|
|
| **August 2016** | [Windows Store support for Electron apps](https://electronjs.org/docs/tutorial/windows-store-guide).|
|
||||||
|
|
|
@ -6,7 +6,7 @@ Making accessible applications is important and we're happy to introduce new fun
|
||||||
|
|
||||||
Accessibility concerns in Electron applications are similar to those of websites because they're both ultimately HTML. With Electron apps, however, you can't use the online resources for accessibility audits because your app doesn't have a URL to point the auditor to.
|
Accessibility concerns in Electron applications are similar to those of websites because they're both ultimately HTML. With Electron apps, however, you can't use the online resources for accessibility audits because your app doesn't have a URL to point the auditor to.
|
||||||
|
|
||||||
These new features bring those auditing tools to your Electron app. You can choose to add audits to your tests with Spectron or use them within DevTools with Devtron. Read on for a summary of the tools or checkout our [accessibility documentation](https://electron.atom.io/docs/tutorial/accessibility) for more information.
|
These new features bring those auditing tools to your Electron app. You can choose to add audits to your tests with Spectron or use them within DevTools with Devtron. Read on for a summary of the tools or checkout our [accessibility documentation](https://electronjs.org/docs/tutorial/accessibility) for more information.
|
||||||
|
|
||||||
## Spectron
|
## Spectron
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ In Devtron, there is a new accessibility tab which will allow you to audit a pag
|
||||||
|
|
||||||
Both of these tools are using the [Accessibility Developer Tools](https://github.com/GoogleChrome/accessibility-developer-tools) library built by Google for Chrome. You can learn more about the accessibility audit rules this library uses on that [repository's wiki](https://github.com/GoogleChrome/accessibility-developer-tools/wiki/Audit-Rules).
|
Both of these tools are using the [Accessibility Developer Tools](https://github.com/GoogleChrome/accessibility-developer-tools) library built by Google for Chrome. You can learn more about the accessibility audit rules this library uses on that [repository's wiki](https://github.com/GoogleChrome/accessibility-developer-tools/wiki/Audit-Rules).
|
||||||
|
|
||||||
If you know of other great accessibility tools for Electron, add them to the [accessibility documentation](https://electron.atom.io/docs/tutorial/accessibility) with a pull request.
|
If you know of other great accessibility tools for Electron, add them to the [accessibility documentation](https://electronjs.org/docs/tutorial/accessibility) with a pull request.
|
||||||
|
|
||||||
## Enabling Accessibility
|
## Enabling Accessibility
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ npm install electron --save-dev
|
||||||
```
|
```
|
||||||
|
|
||||||
See the
|
See the
|
||||||
[Electron versioning doc](https://electron.atom.io/docs/tutorial/electron-versioning/)
|
[Electron versioning doc](https://electronjs.org/docs/tutorial/electron-versioning)
|
||||||
for info on how to manage Electron versions in your apps.
|
for info on how to manage Electron versions in your apps.
|
||||||
|
|
||||||
## Global Installation
|
## Global Installation
|
||||||
|
|
|
@ -244,7 +244,7 @@ $ npm start
|
||||||
```
|
```
|
||||||
|
|
||||||
For more example apps, see the
|
For more example apps, see the
|
||||||
[list of boilerplates](https://electron.atom.io/community/#boilerplates)
|
[list of boilerplates](https://electronjs.org/community#boilerplates)
|
||||||
created by the awesome electron community.
|
created by the awesome electron community.
|
||||||
|
|
||||||
[share-data]: ../faq.md#how-to-share-data-between-web-pages
|
[share-data]: ../faq.md#how-to-share-data-between-web-pages
|
||||||
|
|
|
@ -21,6 +21,7 @@ and can be deployed for free on [Now](https://zeit.co/now).
|
||||||
but caches app updates on disk and supports private repositories.
|
but caches app updates on disk and supports private repositories.
|
||||||
- [electron-release-server](https://github.com/ArekSredzki/electron-release-server) –
|
- [electron-release-server](https://github.com/ArekSredzki/electron-release-server) –
|
||||||
Provides a dashboard for handling releases
|
Provides a dashboard for handling releases
|
||||||
|
- [Nucleus](https://github.com/atlassian/nucleus) - A complete update server for Electron apps maintained by Atlassian. Supports multiple applications and channels; uses a static file store to minify server cost.
|
||||||
|
|
||||||
If your app is packaged with [electron-builder][electron-builder-lib] you can use the
|
If your app is packaged with [electron-builder][electron-builder-lib] you can use the
|
||||||
[electron-updater] module, which does not require a server and allows for updates
|
[electron-updater] module, which does not require a server and allows for updates
|
||||||
|
|
|
@ -163,6 +163,8 @@
|
||||||
'atom/browser/api/event.h',
|
'atom/browser/api/event.h',
|
||||||
'atom/browser/api/event_emitter.cc',
|
'atom/browser/api/event_emitter.cc',
|
||||||
'atom/browser/api/event_emitter.h',
|
'atom/browser/api/event_emitter.h',
|
||||||
|
'atom/browser/api/event_subscriber.cc',
|
||||||
|
'atom/browser/api/event_subscriber.h',
|
||||||
'atom/browser/api/trackable_object.cc',
|
'atom/browser/api/trackable_object.cc',
|
||||||
'atom/browser/api/trackable_object.h',
|
'atom/browser/api/trackable_object.h',
|
||||||
'atom/browser/api/frame_subscriber.cc',
|
'atom/browser/api/frame_subscriber.cc',
|
||||||
|
@ -248,8 +250,6 @@
|
||||||
'atom/browser/net/asar/url_request_asar_job.h',
|
'atom/browser/net/asar/url_request_asar_job.h',
|
||||||
'atom/browser/net/atom_cert_verifier.cc',
|
'atom/browser/net/atom_cert_verifier.cc',
|
||||||
'atom/browser/net/atom_cert_verifier.h',
|
'atom/browser/net/atom_cert_verifier.h',
|
||||||
'atom/browser/net/atom_ct_delegate.cc',
|
|
||||||
'atom/browser/net/atom_ct_delegate.h',
|
|
||||||
'atom/browser/net/atom_cookie_delegate.cc',
|
'atom/browser/net/atom_cookie_delegate.cc',
|
||||||
'atom/browser/net/atom_cookie_delegate.h',
|
'atom/browser/net/atom_cookie_delegate.h',
|
||||||
'atom/browser/net/atom_network_delegate.cc',
|
'atom/browser/net/atom_network_delegate.cc',
|
||||||
|
@ -272,6 +272,8 @@
|
||||||
'atom/browser/net/url_request_buffer_job.h',
|
'atom/browser/net/url_request_buffer_job.h',
|
||||||
'atom/browser/net/url_request_fetch_job.cc',
|
'atom/browser/net/url_request_fetch_job.cc',
|
||||||
'atom/browser/net/url_request_fetch_job.h',
|
'atom/browser/net/url_request_fetch_job.h',
|
||||||
|
'atom/browser/net/url_request_stream_job.cc',
|
||||||
|
'atom/browser/net/url_request_stream_job.h',
|
||||||
'atom/browser/node_debugger.cc',
|
'atom/browser/node_debugger.cc',
|
||||||
'atom/browser/node_debugger.h',
|
'atom/browser/node_debugger.h',
|
||||||
'atom/browser/relauncher_linux.cc',
|
'atom/browser/relauncher_linux.cc',
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
const url = require('url')
|
const url = require('url')
|
||||||
const {EventEmitter} = require('events')
|
const {EventEmitter} = require('events')
|
||||||
const util = require('util')
|
|
||||||
const {Readable} = require('stream')
|
const {Readable} = require('stream')
|
||||||
const {app} = require('electron')
|
const {app} = require('electron')
|
||||||
const {Session} = process.atomBinding('session')
|
const {Session} = process.atomBinding('session')
|
||||||
|
@ -115,7 +114,7 @@ class ClientRequest extends EventEmitter {
|
||||||
if (typeof options === 'string') {
|
if (typeof options === 'string') {
|
||||||
options = url.parse(options)
|
options = url.parse(options)
|
||||||
} else {
|
} else {
|
||||||
options = util._extend({}, options)
|
options = Object.assign({}, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
const method = (options.method || 'GET').toUpperCase()
|
const method = (options.method || 'GET').toUpperCase()
|
||||||
|
|
|
@ -112,6 +112,16 @@ const webFrameMethods = [
|
||||||
]
|
]
|
||||||
const webFrameMethodsWithResult = []
|
const webFrameMethodsWithResult = []
|
||||||
|
|
||||||
|
const errorConstructors = {
|
||||||
|
Error,
|
||||||
|
EvalError,
|
||||||
|
RangeError,
|
||||||
|
ReferenceError,
|
||||||
|
SyntaxError,
|
||||||
|
TypeError,
|
||||||
|
URIError
|
||||||
|
}
|
||||||
|
|
||||||
const asyncWebFrameMethods = function (requestId, method, callback, ...args) {
|
const asyncWebFrameMethods = function (requestId, method, callback, ...args) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.send('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_METHOD', requestId, method, args)
|
this.send('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_METHOD', requestId, method, args)
|
||||||
|
@ -120,7 +130,14 @@ const asyncWebFrameMethods = function (requestId, method, callback, ...args) {
|
||||||
if (typeof callback === 'function') callback(result)
|
if (typeof callback === 'function') callback(result)
|
||||||
resolve(result)
|
resolve(result)
|
||||||
} else {
|
} else {
|
||||||
reject(error)
|
if (error.__ELECTRON_SERIALIZED_ERROR__ && errorConstructors[error.name]) {
|
||||||
|
const rehydratedError = new errorConstructors[error.name](error.message)
|
||||||
|
rehydratedError.stack = error.stack
|
||||||
|
|
||||||
|
reject(rehydratedError)
|
||||||
|
} else {
|
||||||
|
reject(error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -10,26 +10,25 @@ class CallbacksRegistry {
|
||||||
|
|
||||||
add (callback) {
|
add (callback) {
|
||||||
// The callback is already added.
|
// The callback is already added.
|
||||||
var filenameAndLine, id, location, match, ref, regexp, stackString
|
let id = v8Util.getHiddenValue(callback, 'callbackId')
|
||||||
id = v8Util.getHiddenValue(callback, 'callbackId')
|
if (id != null) return id
|
||||||
if (id != null) {
|
|
||||||
return id
|
id = this.nextId += 1
|
||||||
}
|
|
||||||
id = ++this.nextId
|
|
||||||
|
|
||||||
// Capture the location of the function and put it in the ID string,
|
// Capture the location of the function and put it in the ID string,
|
||||||
// so that release errors can be tracked down easily.
|
// so that release errors can be tracked down easily.
|
||||||
regexp = /at (.*)/gi
|
const regexp = /at (.*)/gi
|
||||||
stackString = (new Error()).stack
|
const stackString = (new Error()).stack
|
||||||
|
|
||||||
|
let filenameAndLine
|
||||||
|
let match
|
||||||
|
|
||||||
while ((match = regexp.exec(stackString)) !== null) {
|
while ((match = regexp.exec(stackString)) !== null) {
|
||||||
location = match[1]
|
const location = match[1]
|
||||||
if (location.indexOf('(native)') !== -1) {
|
if (location.includes('native')) continue
|
||||||
continue
|
if (location.includes('electron.asar')) continue
|
||||||
}
|
|
||||||
if (location.indexOf('electron.asar') !== -1) {
|
const ref = /([^/^)]*)\)?$/gi.exec(location)
|
||||||
continue
|
|
||||||
}
|
|
||||||
ref = /([^/^)]*)\)?$/gi.exec(location)
|
|
||||||
filenameAndLine = ref[1]
|
filenameAndLine = ref[1]
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -40,8 +39,7 @@ class CallbacksRegistry {
|
||||||
}
|
}
|
||||||
|
|
||||||
get (id) {
|
get (id) {
|
||||||
var ref
|
return this.callbacks[id] || function () {}
|
||||||
return (ref = this.callbacks[id]) != null ? ref : function () {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
apply (id, ...args) {
|
apply (id, ...args) {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
// Deprecate a method.
|
// Deprecate a method.
|
||||||
const deprecate = function (oldName, newName, fn) {
|
const deprecate = function (oldName, newName, fn) {
|
||||||
var warned
|
let warned = false
|
||||||
warned = false
|
|
||||||
return function () {
|
return function () {
|
||||||
if (!(warned || process.noDeprecation)) {
|
if (!(warned || process.noDeprecation)) {
|
||||||
warned = true
|
warned = true
|
||||||
|
@ -11,80 +10,31 @@ const deprecate = function (oldName, newName, fn) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The method is renamed.
|
// The method is aliases and the old method is retained for backwards compat
|
||||||
deprecate.rename = function (object, oldName, newName) {
|
deprecate.alias = function (object, deprecatedName, existingName) {
|
||||||
var newMethod, warned
|
let warned = false
|
||||||
warned = false
|
const newMethod = function () {
|
||||||
newMethod = function () {
|
|
||||||
if (!(warned || process.noDeprecation)) {
|
if (!(warned || process.noDeprecation)) {
|
||||||
warned = true
|
warned = true
|
||||||
deprecate.warn(oldName, newName)
|
deprecate.warn(deprecatedName, existingName)
|
||||||
}
|
}
|
||||||
return this[newName].apply(this, arguments)
|
return this[existingName].apply(this, arguments)
|
||||||
}
|
}
|
||||||
if (typeof object === 'function') {
|
if (typeof object === 'function') {
|
||||||
object.prototype[oldName] = newMethod
|
object.prototype[deprecatedName] = newMethod
|
||||||
} else {
|
} else {
|
||||||
object[oldName] = newMethod
|
object[deprecatedName] = newMethod
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Forward the method to member.
|
deprecate.warn = (oldName, newName) => {
|
||||||
deprecate.member = function (object, method, member) {
|
return deprecate.log(`'${oldName}' is deprecated. Use '${newName}' instead.`)
|
||||||
var warned
|
|
||||||
warned = false
|
|
||||||
object.prototype[method] = function () {
|
|
||||||
if (!(warned || process.noDeprecation)) {
|
|
||||||
warned = true
|
|
||||||
deprecate.warn(method, member + '.' + method)
|
|
||||||
}
|
|
||||||
return this[member][method].apply(this[member], arguments)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecate a property.
|
let deprecationHandler = null
|
||||||
deprecate.property = function (object, property, method) {
|
|
||||||
return Object.defineProperty(object, property, {
|
|
||||||
get: function () {
|
|
||||||
var warned
|
|
||||||
warned = false
|
|
||||||
if (!(warned || process.noDeprecation)) {
|
|
||||||
warned = true
|
|
||||||
deprecate.warn(property + ' property', method + ' method')
|
|
||||||
}
|
|
||||||
return this[method]()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecate an event.
|
|
||||||
deprecate.event = function (emitter, oldName, newName, fn) {
|
|
||||||
var warned = false
|
|
||||||
return emitter.on(newName, function (...args) {
|
|
||||||
// there is listeners for old API.
|
|
||||||
if (this.listenerCount(oldName) > 0) {
|
|
||||||
if (!(warned || process.noDeprecation)) {
|
|
||||||
warned = true
|
|
||||||
deprecate.warn("'" + oldName + "' event", "'" + newName + "' event")
|
|
||||||
}
|
|
||||||
if (fn != null) {
|
|
||||||
fn.apply(this, arguments)
|
|
||||||
} else {
|
|
||||||
this.emit.apply(this, [oldName].concat(args))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print deprecation warning.
|
|
||||||
deprecate.warn = function (oldName, newName) {
|
|
||||||
return deprecate.log(oldName + ' is deprecated. Use ' + newName + ' instead.')
|
|
||||||
}
|
|
||||||
|
|
||||||
var deprecationHandler = null
|
|
||||||
|
|
||||||
// Print deprecation message.
|
// Print deprecation message.
|
||||||
deprecate.log = function (message) {
|
deprecate.log = (message) => {
|
||||||
if (typeof deprecationHandler === 'function') {
|
if (typeof deprecationHandler === 'function') {
|
||||||
deprecationHandler(message)
|
deprecationHandler(message)
|
||||||
} else if (process.throwDeprecation) {
|
} else if (process.throwDeprecation) {
|
||||||
|
@ -92,16 +42,61 @@ deprecate.log = function (message) {
|
||||||
} else if (process.traceDeprecation) {
|
} else if (process.traceDeprecation) {
|
||||||
return console.trace(message)
|
return console.trace(message)
|
||||||
} else {
|
} else {
|
||||||
return console.warn('(electron) ' + message)
|
return console.warn(`(electron) ${message}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
deprecate.setHandler = function (handler) {
|
deprecate.setHandler = (handler) => {
|
||||||
deprecationHandler = handler
|
deprecationHandler = handler
|
||||||
}
|
}
|
||||||
|
|
||||||
deprecate.getHandler = function () {
|
deprecate.getHandler = () => deprecationHandler
|
||||||
return deprecationHandler
|
|
||||||
}
|
// None of the below methods are used, and so will be commented
|
||||||
|
// out until such time that they are needed to be used and tested.
|
||||||
|
|
||||||
|
// // Forward the method to member.
|
||||||
|
// deprecate.member = (object, method, member) => {
|
||||||
|
// let warned = false
|
||||||
|
// object.prototype[method] = function () {
|
||||||
|
// if (!(warned || process.noDeprecation)) {
|
||||||
|
// warned = true
|
||||||
|
// deprecate.warn(method, `${member}.${method}`)
|
||||||
|
// }
|
||||||
|
// return this[member][method].apply(this[member], arguments)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Deprecate a property.
|
||||||
|
// deprecate.property = (object, property, method) => {
|
||||||
|
// return Object.defineProperty(object, property, {
|
||||||
|
// get: function () {
|
||||||
|
// let warned = false
|
||||||
|
// if (!(warned || process.noDeprecation)) {
|
||||||
|
// warned = true
|
||||||
|
// deprecate.warn(`${property} property`, `${method} method`)
|
||||||
|
// }
|
||||||
|
// return this[method]()
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Deprecate an event.
|
||||||
|
// deprecate.event = (emitter, oldName, newName, fn) => {
|
||||||
|
// let warned = false
|
||||||
|
// return emitter.on(newName, function (...args) {
|
||||||
|
// if (this.listenerCount(oldName) > 0) {
|
||||||
|
// if (!(warned || process.noDeprecation)) {
|
||||||
|
// warned = true
|
||||||
|
// deprecate.warn(`'${oldName}' event`, `'${newName}' event`)
|
||||||
|
// }
|
||||||
|
// if (fn != null) {
|
||||||
|
// fn.apply(this, arguments)
|
||||||
|
// } else {
|
||||||
|
// this.emit.apply(this, [oldName].concat(args))
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
module.exports = deprecate
|
module.exports = deprecate
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
const {Buffer} = require('buffer')
|
const {Buffer} = require('buffer')
|
||||||
const childProcess = require('child_process')
|
const childProcess = require('child_process')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const util = require('util')
|
|
||||||
|
|
||||||
const hasProp = {}.hasOwnProperty
|
const hasProp = {}.hasOwnProperty
|
||||||
|
|
||||||
|
@ -460,11 +459,15 @@
|
||||||
options = {
|
options = {
|
||||||
encoding: null
|
encoding: null
|
||||||
}
|
}
|
||||||
} else if (util.isString(options)) {
|
} else if (typeof options === 'string') {
|
||||||
options = {
|
options = {
|
||||||
encoding: options
|
encoding: options
|
||||||
}
|
}
|
||||||
} else if (!util.isObject(options)) {
|
} else if (options === null || options === undefined) {
|
||||||
|
options = {
|
||||||
|
encoding: null
|
||||||
|
}
|
||||||
|
} else if (typeof options !== 'object') {
|
||||||
throw new TypeError('Bad arguments')
|
throw new TypeError('Bad arguments')
|
||||||
}
|
}
|
||||||
const {encoding} = options
|
const {encoding} = options
|
||||||
|
@ -527,11 +530,11 @@
|
||||||
options = {
|
options = {
|
||||||
encoding: null
|
encoding: null
|
||||||
}
|
}
|
||||||
} else if (util.isString(options)) {
|
} else if (typeof options === 'string') {
|
||||||
options = {
|
options = {
|
||||||
encoding: options
|
encoding: options
|
||||||
}
|
}
|
||||||
} else if (!util.isObject(options)) {
|
} else if (typeof options !== 'object') {
|
||||||
throw new TypeError('Bad arguments')
|
throw new TypeError('Bad arguments')
|
||||||
}
|
}
|
||||||
const {encoding} = options
|
const {encoding} = options
|
||||||
|
|
|
@ -45,6 +45,17 @@ electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_METHOD', (ev
|
||||||
event.sender.send(`ELECTRON_INTERNAL_BROWSER_ASYNC_WEB_FRAME_RESPONSE_${requestId}`, null, resolvedResult)
|
event.sender.send(`ELECTRON_INTERNAL_BROWSER_ASYNC_WEB_FRAME_RESPONSE_${requestId}`, null, resolvedResult)
|
||||||
})
|
})
|
||||||
.catch((resolvedError) => {
|
.catch((resolvedError) => {
|
||||||
|
if (resolvedError instanceof Error) {
|
||||||
|
// Errors get lost, because: JSON.stringify(new Error('Message')) === {}
|
||||||
|
// Take the serializable properties and construct a generic object
|
||||||
|
resolvedError = {
|
||||||
|
message: resolvedError.message,
|
||||||
|
stack: resolvedError.stack,
|
||||||
|
name: resolvedError.name,
|
||||||
|
__ELECTRON_SERIALIZED_ERROR__: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
event.sender.send(`ELECTRON_INTERNAL_BROWSER_ASYNC_WEB_FRAME_RESPONSE_${requestId}`, resolvedError)
|
event.sender.send(`ELECTRON_INTERNAL_BROWSER_ASYNC_WEB_FRAME_RESPONSE_${requestId}`, resolvedError)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -135,7 +146,7 @@ if (nodeIntegration === 'true') {
|
||||||
let warning = 'This renderer process has Node.js integration enabled '
|
let warning = 'This renderer process has Node.js integration enabled '
|
||||||
warning += 'and attempted to load remote content. This exposes users of this app to severe '
|
warning += 'and attempted to load remote content. This exposes users of this app to severe '
|
||||||
warning += 'security risks.\n'
|
warning += 'security risks.\n'
|
||||||
warning += 'For more information and help, consult https://electron.atom.io/docs/tutorial/security/'
|
warning += 'For more information and help, consult https://electronjs.org/docs/tutorial/security'
|
||||||
|
|
||||||
console.warn('%cElectron Security Warning', 'font-weight: bold;', warning)
|
console.warn('%cElectron Security Warning', 'font-weight: bold;', warning)
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,5 +32,15 @@ Object.defineProperties(exports, {
|
||||||
get: function () {
|
get: function () {
|
||||||
return require('../../../common/api/is-promise')
|
return require('../../../common/api/is-promise')
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
desktopCapturer: {
|
||||||
|
get: function () {
|
||||||
|
return require('../../../renderer/api/desktop-capturer')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
nativeImage: {
|
||||||
|
get: function () {
|
||||||
|
return require('../../../common/api/native-image')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -11,8 +11,8 @@
|
||||||
"dotenv-safe": "^4.0.4",
|
"dotenv-safe": "^4.0.4",
|
||||||
"dugite": "^1.45.0",
|
"dugite": "^1.45.0",
|
||||||
"electabul": "~0.0.4",
|
"electabul": "~0.0.4",
|
||||||
"electron-docs-linter": "^2.3.3",
|
"electron-docs-linter": "^2.3.4",
|
||||||
"electron-typescript-definitions": "^1.2.10",
|
"electron-typescript-definitions": "^1.2.11",
|
||||||
"github": "^9.2.0",
|
"github": "^9.2.0",
|
||||||
"husky": "^0.14.3",
|
"husky": "^0.14.3",
|
||||||
"minimist": "^1.2.0",
|
"minimist": "^1.2.0",
|
||||||
|
|
|
@ -85,7 +85,7 @@ def main():
|
||||||
with scoped_cwd(SOURCE_ROOT):
|
with scoped_cwd(SOURCE_ROOT):
|
||||||
update_electron_gyp(version, suffix)
|
update_electron_gyp(version, suffix)
|
||||||
update_win_rc(version, versions)
|
update_win_rc(version, versions)
|
||||||
update_version_h(versions)
|
update_version_h(versions, suffix)
|
||||||
update_info_plist(version)
|
update_info_plist(version)
|
||||||
update_package_json(version, suffix)
|
update_package_json(version, suffix)
|
||||||
tag_version(version, suffix)
|
tag_version(version, suffix)
|
||||||
|
@ -138,7 +138,7 @@ def update_win_rc(version, versions):
|
||||||
f.write(''.join(lines))
|
f.write(''.join(lines))
|
||||||
|
|
||||||
|
|
||||||
def update_version_h(versions):
|
def update_version_h(versions, suffix):
|
||||||
version_h = os.path.join('atom', 'common', 'atom_version.h')
|
version_h = os.path.join('atom', 'common', 'atom_version.h')
|
||||||
with open(version_h, 'r') as f:
|
with open(version_h, 'r') as f:
|
||||||
lines = f.readlines()
|
lines = f.readlines()
|
||||||
|
@ -150,6 +150,11 @@ def update_version_h(versions):
|
||||||
lines[i + 1] = '#define ATOM_MINOR_VERSION {0}\n'.format(versions[1])
|
lines[i + 1] = '#define ATOM_MINOR_VERSION {0}\n'.format(versions[1])
|
||||||
lines[i + 2] = '#define ATOM_PATCH_VERSION {0}\n'.format(versions[2])
|
lines[i + 2] = '#define ATOM_PATCH_VERSION {0}\n'.format(versions[2])
|
||||||
|
|
||||||
|
if (suffix):
|
||||||
|
lines[i + 3] = '#define ATOM_PRE_RELEASE_VERSION {0}\n'.format(suffix)
|
||||||
|
else:
|
||||||
|
lines[i + 3] = '// #define ATOM_PRE_RELEASE_VERSION\n'
|
||||||
|
|
||||||
with open(version_h, 'w') as f:
|
with open(version_h, 'w') as f:
|
||||||
f.write(''.join(lines))
|
f.write(''.join(lines))
|
||||||
return
|
return
|
||||||
|
|
|
@ -1,56 +1,210 @@
|
||||||
const args = require('minimist')(process.argv.slice(2))
|
|
||||||
const assert = require('assert')
|
const assert = require('assert')
|
||||||
const request = require('request')
|
const request = require('request')
|
||||||
|
const buildAppVeyorURL = 'https://windows-ci.electronjs.org/api/builds'
|
||||||
|
const jenkinsServer = 'https://mac-ci.electronjs.org'
|
||||||
|
|
||||||
const ciJobs = [
|
const circleCIJobs = [
|
||||||
'electron-linux-arm64',
|
'electron-linux-arm64',
|
||||||
'electron-linux-ia32',
|
'electron-linux-ia32',
|
||||||
'electron-linux-x64',
|
'electron-linux-x64',
|
||||||
'electron-linux-arm'
|
'electron-linux-arm'
|
||||||
]
|
]
|
||||||
|
|
||||||
const CIcall = (buildUrl, targetBranch, job) => {
|
const jenkinsJobs = [
|
||||||
console.log(`Triggering CircleCI to run build job: ${job} on branch: ${targetBranch} with release flag.`)
|
'electron-mas-x64-release',
|
||||||
|
'electron-osx-x64-release'
|
||||||
|
]
|
||||||
|
|
||||||
request({
|
async function makeRequest (requestOptions, parseResponse) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
request(requestOptions, (err, res, body) => {
|
||||||
|
if (!err && res.statusCode >= 200 && res.statusCode < 300) {
|
||||||
|
if (parseResponse) {
|
||||||
|
const build = JSON.parse(body)
|
||||||
|
resolve(build)
|
||||||
|
} else {
|
||||||
|
resolve(body)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (parseResponse) {
|
||||||
|
console.log('Error: ', `(status ${res.statusCode})`, err || JSON.parse(res.body), requestOptions)
|
||||||
|
} else {
|
||||||
|
console.log('Error: ', `(status ${res.statusCode})`, err || res.body, requestOptions)
|
||||||
|
}
|
||||||
|
reject()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function circleCIcall (buildUrl, targetBranch, job, ghRelease) {
|
||||||
|
assert(process.env.CIRCLE_TOKEN, 'CIRCLE_TOKEN not found in environment')
|
||||||
|
console.log(`Triggering CircleCI to run build job: ${job} on branch: ${targetBranch} with release flag.`)
|
||||||
|
let buildRequest = {
|
||||||
|
'build_parameters': {
|
||||||
|
'CIRCLE_JOB': job
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ghRelease) {
|
||||||
|
buildRequest.build_parameters.ELECTRON_RELEASE = 1
|
||||||
|
} else {
|
||||||
|
buildRequest.build_parameters.RUN_RELEASE_BUILD = 'true'
|
||||||
|
}
|
||||||
|
|
||||||
|
let circleResponse = await makeRequest({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: buildUrl,
|
url: buildUrl,
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Accept': 'application/json'
|
'Accept': 'application/json'
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify(buildRequest)
|
||||||
'build_parameters': {
|
}, true).catch(err => {
|
||||||
'RUN_RELEASE_BUILD': 'true',
|
console.log('Error calling CircleCI:', err)
|
||||||
'CIRCLE_JOB': job
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}, (err, res, body) => {
|
|
||||||
if (!err && res.statusCode >= 200 && res.statusCode < 300) {
|
|
||||||
const build = JSON.parse(body)
|
|
||||||
console.log(`Check ${build.build_url} for status. (${job})`)
|
|
||||||
} else {
|
|
||||||
console.log('Error: ', `(status ${res.statusCode})`, err || JSON.parse(res.body), job)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
console.log(`Check ${circleResponse.build_url} for status. (${job})`)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args._.length < 1) {
|
async function buildAppVeyor (targetBranch, ghRelease) {
|
||||||
console.log(`Trigger Circle CI to build release builds of electron.
|
console.log(`Triggering AppVeyor to run build on branch: ${targetBranch} with release flag.`)
|
||||||
Usage: ci-release-build.js [--job=CI_JOB_NAME] TARGET_BRANCH
|
assert(process.env.APPVEYOR_TOKEN, 'APPVEYOR_TOKEN not found in environment')
|
||||||
`)
|
let environmentVariables = {}
|
||||||
process.exit(0)
|
|
||||||
|
if (ghRelease) {
|
||||||
|
environmentVariables.ELECTRON_RELEASE = 1
|
||||||
|
} else {
|
||||||
|
environmentVariables.RUN_RELEASE_BUILD = 'true'
|
||||||
|
}
|
||||||
|
|
||||||
|
const requestOpts = {
|
||||||
|
url: buildAppVeyorURL,
|
||||||
|
auth: {
|
||||||
|
bearer: process.env.APPVEYOR_TOKEN
|
||||||
|
},
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
accountName: 'AppVeyor',
|
||||||
|
projectSlug: 'electron',
|
||||||
|
branch: targetBranch,
|
||||||
|
environmentVariables
|
||||||
|
}),
|
||||||
|
method: 'POST'
|
||||||
|
}
|
||||||
|
let appVeyorResponse = await makeRequest(requestOpts, true).catch(err => {
|
||||||
|
console.log('Error calling AppVeyor:', err)
|
||||||
|
})
|
||||||
|
const buildUrl = `https://windows-ci.electronjs.org/project/AppVeyor/electron/build/${appVeyorResponse.version}`
|
||||||
|
console.log(`AppVeyor release build request successful. Check build status at ${buildUrl}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(process.env.CIRCLE_TOKEN, 'CIRCLE_TOKEN not found in environment')
|
function buildCircleCI (targetBranch, ghRelease, job) {
|
||||||
|
const circleBuildUrl = `https://circleci.com/api/v1.1/project/github/electron/electron/tree/${targetBranch}?circle-token=${process.env.CIRCLE_TOKEN}`
|
||||||
const targetBranch = args._[0]
|
if (job) {
|
||||||
const job = args['job']
|
assert(circleCIJobs.includes(job), `Unknown CI job name: ${job}.`)
|
||||||
const circleBuildUrl = `https://circleci.com/api/v1.1/project/github/electron/electron/tree/${targetBranch}?circle-token=${process.env.CIRCLE_TOKEN}`
|
circleCIcall(circleBuildUrl, targetBranch, job, ghRelease)
|
||||||
|
} else {
|
||||||
if (job) {
|
circleCIJobs.forEach((job) => circleCIcall(circleBuildUrl, targetBranch, job, ghRelease))
|
||||||
assert(ciJobs.includes(job), `Unknown CI job name: ${job}.`)
|
}
|
||||||
CIcall(circleBuildUrl, targetBranch, job)
|
}
|
||||||
} else {
|
|
||||||
ciJobs.forEach((job) => CIcall(circleBuildUrl, targetBranch, job))
|
async function buildJenkins (targetBranch, ghRelease, job) {
|
||||||
|
assert(process.env.JENKINS_AUTH_TOKEN, 'JENKINS_AUTH_TOKEN not found in environment')
|
||||||
|
assert(process.env.JENKINS_BUILD_TOKEN, 'JENKINS_BUILD_TOKEN not found in environment')
|
||||||
|
let jenkinsCrumb = await getJenkinsCrumb()
|
||||||
|
|
||||||
|
if (job) {
|
||||||
|
assert(jenkinsJobs.includes(job), `Unknown CI job name: ${job}.`)
|
||||||
|
callJenkinsBuild(job, jenkinsCrumb, targetBranch, ghRelease)
|
||||||
|
} else {
|
||||||
|
jenkinsJobs.forEach((job) => {
|
||||||
|
callJenkinsBuild(job, jenkinsCrumb, targetBranch, ghRelease)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function callJenkins (path, requestParameters, requestHeaders) {
|
||||||
|
let requestOptions = {
|
||||||
|
url: `${jenkinsServer}/${path}`,
|
||||||
|
auth: {
|
||||||
|
user: 'build',
|
||||||
|
pass: process.env.JENKINS_AUTH_TOKEN
|
||||||
|
},
|
||||||
|
qs: requestParameters
|
||||||
|
}
|
||||||
|
if (requestHeaders) {
|
||||||
|
requestOptions.headers = requestHeaders
|
||||||
|
}
|
||||||
|
let jenkinsResponse = await makeRequest(requestOptions).catch(err => {
|
||||||
|
console.log(`Error calling Jenkins:`, err)
|
||||||
|
})
|
||||||
|
return jenkinsResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
async function callJenkinsBuild (job, jenkinsCrumb, targetBranch, ghRelease) {
|
||||||
|
console.log(`Triggering Jenkins to run build job: ${job} on branch: ${targetBranch} with release flag.`)
|
||||||
|
let jenkinsParams = {
|
||||||
|
token: process.env.JENKINS_BUILD_TOKEN,
|
||||||
|
BRANCH: targetBranch
|
||||||
|
}
|
||||||
|
if (!ghRelease) {
|
||||||
|
jenkinsParams.RUN_RELEASE_BUILD = 1
|
||||||
|
}
|
||||||
|
await callJenkins(`job/${job}/buildWithParameters`, jenkinsParams, jenkinsCrumb)
|
||||||
|
.catch(err => {
|
||||||
|
console.log(`Error calling Jenkins build`, err)
|
||||||
|
})
|
||||||
|
let buildUrl = `${jenkinsServer}/job/${job}/lastBuild/`
|
||||||
|
console.log(`Jenkins build request successful. Check build status at ${buildUrl}.`)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getJenkinsCrumb () {
|
||||||
|
let crumbResponse = await callJenkins('crumbIssuer/api/xml', {
|
||||||
|
xpath: 'concat(//crumbRequestField,":",//crumb)'
|
||||||
|
}).catch(err => {
|
||||||
|
console.log(`Error getting jenkins crumb:`, err)
|
||||||
|
})
|
||||||
|
let crumbDetails = crumbResponse.split(':')
|
||||||
|
let crumbHeader = {}
|
||||||
|
crumbHeader[crumbDetails[0]] = crumbDetails[1]
|
||||||
|
return crumbHeader
|
||||||
|
}
|
||||||
|
|
||||||
|
function runRelease (targetBranch, options) {
|
||||||
|
if (options.ci) {
|
||||||
|
switch (options.ci) {
|
||||||
|
case 'CircleCI': {
|
||||||
|
buildCircleCI(targetBranch, options.ghRelease, options.job)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'AppVeyor': {
|
||||||
|
buildAppVeyor(targetBranch, options.ghRelease)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'Jenkins': {
|
||||||
|
buildJenkins(targetBranch, options.ghRelease, options.job)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
buildCircleCI(targetBranch, options.ghRelease, options.job)
|
||||||
|
buildAppVeyor(targetBranch, options.ghRelease)
|
||||||
|
buildJenkins(targetBranch, options.ghRelease, options.job)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = runRelease
|
||||||
|
|
||||||
|
if (require.main === module) {
|
||||||
|
const args = require('minimist')(process.argv.slice(2))
|
||||||
|
const targetBranch = args._[0]
|
||||||
|
if (args._.length < 1) {
|
||||||
|
console.log(`Trigger CI to build release builds of electron.
|
||||||
|
Usage: ci-release-build.js [--job=CI_JOB_NAME] [--ci=CircleCI|AppVeyor|Jenkins] [--ghRelease] TARGET_BRANCH
|
||||||
|
`)
|
||||||
|
process.exit(0)
|
||||||
|
}
|
||||||
|
runRelease(targetBranch, args)
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,8 @@ import stat
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
import _winreg
|
import _winreg
|
||||||
|
|
||||||
from lib.config import BASE_URL, PLATFORM, get_target_arch, get_zip_name
|
from lib.config import BASE_URL, PLATFORM, enable_verbose_mode, \
|
||||||
|
get_target_arch, get_zip_name
|
||||||
from lib.util import scoped_cwd, rm_rf, get_electron_version, make_zip, \
|
from lib.util import scoped_cwd, rm_rf, get_electron_version, make_zip, \
|
||||||
execute, electron_gyp
|
execute, electron_gyp
|
||||||
|
|
||||||
|
@ -79,6 +80,11 @@ TARGET_DIRECTORIES = {
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
args = parse_args()
|
||||||
|
|
||||||
|
if args.verbose:
|
||||||
|
enable_verbose_mode()
|
||||||
|
|
||||||
rm_rf(DIST_DIR)
|
rm_rf(DIST_DIR)
|
||||||
os.makedirs(DIST_DIR)
|
os.makedirs(DIST_DIR)
|
||||||
|
|
||||||
|
@ -92,8 +98,6 @@ def main():
|
||||||
copy_vcruntime_binaries()
|
copy_vcruntime_binaries()
|
||||||
copy_ucrt_binaries()
|
copy_ucrt_binaries()
|
||||||
|
|
||||||
args = parse_args()
|
|
||||||
|
|
||||||
if PLATFORM != 'win32' and not args.no_api_docs:
|
if PLATFORM != 'win32' and not args.no_api_docs:
|
||||||
create_api_json_schema()
|
create_api_json_schema()
|
||||||
create_typescript_definitions()
|
create_typescript_definitions()
|
||||||
|
@ -307,6 +311,9 @@ def parse_args():
|
||||||
parser.add_argument('--no_api_docs',
|
parser.add_argument('--no_api_docs',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
help='Skip generating the Electron API Documentation!')
|
help='Skip generating the Electron API Documentation!')
|
||||||
|
parser.add_argument('-v', '--verbose',
|
||||||
|
action='store_true',
|
||||||
|
help='Prints the output of the subprocesses')
|
||||||
return parser.parse_args()
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
require('colors')
|
require('colors')
|
||||||
const args = require('minimist')(process.argv.slice(2))
|
const args = require('minimist')(process.argv.slice(2))
|
||||||
const assert = require('assert')
|
const assert = require('assert')
|
||||||
|
const ciReleaseBuild = require('./ci-release-build')
|
||||||
const { execSync } = require('child_process')
|
const { execSync } = require('child_process')
|
||||||
const fail = '\u2717'.red
|
const fail = '\u2717'.red
|
||||||
const { GitProcess, GitError } = require('dugite')
|
const { GitProcess, GitError } = require('dugite')
|
||||||
|
@ -158,6 +159,12 @@ async function pushRelease () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function runReleaseBuilds () {
|
||||||
|
await ciReleaseBuild('release', {
|
||||||
|
ghRelease: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
async function prepareRelease (isBeta, notesOnly) {
|
async function prepareRelease (isBeta, notesOnly) {
|
||||||
let currentBranch = await getCurrentBranch(gitDir)
|
let currentBranch = await getCurrentBranch(gitDir)
|
||||||
if (notesOnly) {
|
if (notesOnly) {
|
||||||
|
@ -167,6 +174,7 @@ async function prepareRelease (isBeta, notesOnly) {
|
||||||
await createReleaseBranch()
|
await createReleaseBranch()
|
||||||
await createRelease(currentBranch, isBeta)
|
await createRelease(currentBranch, isBeta)
|
||||||
await pushRelease()
|
await pushRelease()
|
||||||
|
await runReleaseBuilds()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2514,6 +2514,15 @@ describe('BrowserWindow module', () => {
|
||||||
const code = `(() => "${expected}")()`
|
const code = `(() => "${expected}")()`
|
||||||
const asyncCode = `(() => new Promise(r => setTimeout(() => r("${expected}"), 500)))()`
|
const asyncCode = `(() => new Promise(r => setTimeout(() => r("${expected}"), 500)))()`
|
||||||
const badAsyncCode = `(() => new Promise((r, e) => setTimeout(() => e("${expectedErrorMsg}"), 500)))()`
|
const badAsyncCode = `(() => new Promise((r, e) => setTimeout(() => e("${expectedErrorMsg}"), 500)))()`
|
||||||
|
const errorTypes = new Set([
|
||||||
|
Error,
|
||||||
|
ReferenceError,
|
||||||
|
EvalError,
|
||||||
|
RangeError,
|
||||||
|
SyntaxError,
|
||||||
|
TypeError,
|
||||||
|
URIError
|
||||||
|
])
|
||||||
|
|
||||||
it('doesnt throw when no calback is provided', () => {
|
it('doesnt throw when no calback is provided', () => {
|
||||||
const result = ipcRenderer.sendSync('executeJavaScript', code, false)
|
const result = ipcRenderer.sendSync('executeJavaScript', code, false)
|
||||||
|
@ -2561,6 +2570,17 @@ describe('BrowserWindow module', () => {
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
it('rejects the returned promise with an error if an Error.prototype is thrown', async () => {
|
||||||
|
for (const error in errorTypes) {
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
ipcRenderer.send('executeJavaScript', `Promise.reject(new ${error.name}("Wamp-wamp")`, true)
|
||||||
|
ipcRenderer.once('executeJavaScript-promise-error-name', (event, name) => {
|
||||||
|
assert.equal(name, error.name)
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
it('works after page load and during subframe load', (done) => {
|
it('works after page load and during subframe load', (done) => {
|
||||||
w.webContents.once('did-finish-load', () => {
|
w.webContents.once('did-finish-load', () => {
|
||||||
// initiate a sub-frame load, then try and execute script during it
|
// initiate a sub-frame load, then try and execute script during it
|
||||||
|
|
48
spec/api-callbacks-registry-spec.js
Normal file
48
spec/api-callbacks-registry-spec.js
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
const {assert} = require('chai')
|
||||||
|
const {CallbacksRegistry} = require('electron')
|
||||||
|
|
||||||
|
describe('CallbacksRegistry module', () => {
|
||||||
|
let registry = null
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
registry = new CallbacksRegistry()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('adds a callback to the registry', () => {
|
||||||
|
const cb = () => [1, 2, 3, 4, 5]
|
||||||
|
const key = registry.add(cb)
|
||||||
|
|
||||||
|
assert.exists(key)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns a specified callback if it is in the registry', () => {
|
||||||
|
const cb = () => [1, 2, 3, 4, 5]
|
||||||
|
const key = registry.add(cb)
|
||||||
|
const callback = registry.get(key)
|
||||||
|
|
||||||
|
assert.equal(callback.toString(), cb.toString())
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns an empty function if the cb doesnt exist', () => {
|
||||||
|
const callback = registry.get(1)
|
||||||
|
|
||||||
|
assert.isFunction(callback)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('removes a callback to the registry', () => {
|
||||||
|
const cb = () => [1, 2, 3, 4, 5]
|
||||||
|
const key = registry.add(cb)
|
||||||
|
|
||||||
|
assert.exists(key)
|
||||||
|
|
||||||
|
const beforeCB = registry.get(key)
|
||||||
|
|
||||||
|
assert.equal(beforeCB.toString(), cb.toString())
|
||||||
|
|
||||||
|
registry.remove(key)
|
||||||
|
const afterCB = registry.get(key)
|
||||||
|
|
||||||
|
assert.isFunction(afterCB)
|
||||||
|
assert.notEqual(afterCB.toString(), cb.toString())
|
||||||
|
})
|
||||||
|
})
|
|
@ -45,10 +45,10 @@ describe('clipboard module', () => {
|
||||||
it('returns title and url', () => {
|
it('returns title and url', () => {
|
||||||
if (process.platform === 'linux') return
|
if (process.platform === 'linux') return
|
||||||
|
|
||||||
clipboard.writeBookmark('a title', 'https://electron.atom.io')
|
clipboard.writeBookmark('a title', 'https://electronjs.org')
|
||||||
assert.deepEqual(clipboard.readBookmark(), {
|
assert.deepEqual(clipboard.readBookmark(), {
|
||||||
title: 'a title',
|
title: 'a title',
|
||||||
url: 'https://electron.atom.io'
|
url: 'https://electronjs.org'
|
||||||
})
|
})
|
||||||
|
|
||||||
clipboard.writeText('no bookmark')
|
clipboard.writeText('no bookmark')
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
const assert = require('assert')
|
const assert = require('assert')
|
||||||
const {deprecations, deprecate} = require('electron')
|
const {deprecations, deprecate, nativeImage} = require('electron')
|
||||||
|
|
||||||
describe('deprecations', () => {
|
describe('deprecations', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
@ -18,6 +18,37 @@ describe('deprecations', () => {
|
||||||
assert.deepEqual(messages, ['this is deprecated'])
|
assert.deepEqual(messages, ['this is deprecated'])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('returns a deprecation handler after one is set', () => {
|
||||||
|
const messages = []
|
||||||
|
|
||||||
|
deprecations.setHandler((message) => {
|
||||||
|
messages.push(message)
|
||||||
|
})
|
||||||
|
|
||||||
|
deprecate.log('this is deprecated')
|
||||||
|
assert(typeof deprecations.getHandler() === 'function')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns a deprecation warning', () => {
|
||||||
|
const messages = []
|
||||||
|
|
||||||
|
deprecations.setHandler((message) => {
|
||||||
|
messages.push(message)
|
||||||
|
})
|
||||||
|
|
||||||
|
deprecate.warn('old', 'new')
|
||||||
|
assert.deepEqual(messages, [`'old' is deprecated. Use 'new' instead.`])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renames a method', () => {
|
||||||
|
assert.equal(typeof nativeImage.createFromDataUrl, 'undefined')
|
||||||
|
assert.equal(typeof nativeImage.createFromDataURL, 'function')
|
||||||
|
|
||||||
|
deprecate.alias(nativeImage, 'createFromDataUrl', 'createFromDataURL')
|
||||||
|
|
||||||
|
assert.equal(typeof nativeImage.createFromDataUrl, 'function')
|
||||||
|
})
|
||||||
|
|
||||||
it('throws an exception if no deprecation handler is specified', () => {
|
it('throws an exception if no deprecation handler is specified', () => {
|
||||||
assert.throws(() => {
|
assert.throws(() => {
|
||||||
deprecate.log('this is deprecated')
|
deprecate.log('this is deprecated')
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const assert = require('assert')
|
const {expect} = require('chai')
|
||||||
const {nativeImage} = require('electron')
|
const {nativeImage} = require('electron')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
|
|
||||||
|
@ -104,71 +104,71 @@ describe('nativeImage module', () => {
|
||||||
describe('createEmpty()', () => {
|
describe('createEmpty()', () => {
|
||||||
it('returns an empty image', () => {
|
it('returns an empty image', () => {
|
||||||
const empty = nativeImage.createEmpty()
|
const empty = nativeImage.createEmpty()
|
||||||
assert.equal(empty.isEmpty(), true)
|
expect(empty.isEmpty())
|
||||||
assert.equal(empty.getAspectRatio(), 1)
|
expect(empty.getAspectRatio()).to.equal(1)
|
||||||
assert.equal(empty.toDataURL(), 'data:image/png;base64,')
|
expect(empty.toDataURL()).to.equal('data:image/png;base64,')
|
||||||
assert.equal(empty.toDataURL({scaleFactor: 2.0}), 'data:image/png;base64,')
|
expect(empty.toDataURL({scaleFactor: 2.0})).to.equal('data:image/png;base64,')
|
||||||
assert.deepEqual(empty.getSize(), {width: 0, height: 0})
|
expect(empty.getSize()).to.deep.equal({width: 0, height: 0})
|
||||||
assert.deepEqual(empty.getBitmap(), [])
|
expect(empty.getBitmap()).to.be.empty
|
||||||
assert.deepEqual(empty.getBitmap({scaleFactor: 2.0}), [])
|
expect(empty.getBitmap({scaleFactor: 2.0})).to.be.empty
|
||||||
assert.deepEqual(empty.toBitmap(), [])
|
expect(empty.toBitmap()).to.be.empty
|
||||||
assert.deepEqual(empty.toBitmap({scaleFactor: 2.0}), [])
|
expect(empty.toBitmap({scaleFactor: 2.0})).to.be.empty
|
||||||
assert.deepEqual(empty.toJPEG(100), [])
|
expect(empty.toJPEG(100)).to.be.empty
|
||||||
assert.deepEqual(empty.toPNG(), [])
|
expect(empty.toPNG()).to.be.empty
|
||||||
assert.deepEqual(empty.toPNG({scaleFactor: 2.0}), [])
|
expect(empty.toPNG({scaleFactor: 2.0})).to.be.empty
|
||||||
|
|
||||||
if (process.platform === 'darwin') {
|
if (process.platform === 'darwin') {
|
||||||
assert.deepEqual(empty.getNativeHandle(), [])
|
expect(empty.getNativeHandle()).to.be.empty
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('createFromBuffer(buffer, scaleFactor)', () => {
|
describe('createFromBuffer(buffer, scaleFactor)', () => {
|
||||||
it('returns an empty image when the buffer is empty', () => {
|
it('returns an empty image when the buffer is empty', () => {
|
||||||
assert(nativeImage.createFromBuffer(Buffer.from([])).isEmpty())
|
expect(nativeImage.createFromBuffer(Buffer.from([])).isEmpty())
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns an image created from the given buffer', () => {
|
it('returns an image created from the given buffer', () => {
|
||||||
const imageA = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png'))
|
const imageA = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png'))
|
||||||
|
|
||||||
const imageB = nativeImage.createFromBuffer(imageA.toPNG())
|
const imageB = nativeImage.createFromBuffer(imageA.toPNG())
|
||||||
assert.deepEqual(imageB.getSize(), {width: 538, height: 190})
|
expect(imageB.getSize()).to.deep.equal({width: 538, height: 190})
|
||||||
assert(imageA.toBitmap().equals(imageB.toBitmap()))
|
expect(imageA.toBitmap().equals(imageB.toBitmap())).to.be.true
|
||||||
|
|
||||||
const imageC = nativeImage.createFromBuffer(imageA.toJPEG(100))
|
const imageC = nativeImage.createFromBuffer(imageA.toJPEG(100))
|
||||||
assert.deepEqual(imageC.getSize(), {width: 538, height: 190})
|
expect(imageC.getSize()).to.deep.equal({width: 538, height: 190})
|
||||||
|
|
||||||
const imageD = nativeImage.createFromBuffer(imageA.toBitmap(),
|
const imageD = nativeImage.createFromBuffer(imageA.toBitmap(),
|
||||||
{width: 538, height: 190})
|
{width: 538, height: 190})
|
||||||
assert.deepEqual(imageD.getSize(), {width: 538, height: 190})
|
expect(imageD.getSize()).to.deep.equal({width: 538, height: 190})
|
||||||
|
|
||||||
const imageE = nativeImage.createFromBuffer(imageA.toBitmap(),
|
const imageE = nativeImage.createFromBuffer(imageA.toBitmap(),
|
||||||
{width: 100, height: 200})
|
{width: 100, height: 200})
|
||||||
assert.deepEqual(imageE.getSize(), {width: 100, height: 200})
|
expect(imageE.getSize()).to.deep.equal({width: 100, height: 200})
|
||||||
|
|
||||||
const imageF = nativeImage.createFromBuffer(imageA.toBitmap())
|
const imageF = nativeImage.createFromBuffer(imageA.toBitmap())
|
||||||
assert(imageF.isEmpty())
|
expect(imageF.isEmpty())
|
||||||
|
|
||||||
const imageG = nativeImage.createFromBuffer(imageA.toPNG(),
|
const imageG = nativeImage.createFromBuffer(imageA.toPNG(),
|
||||||
{width: 100, height: 200})
|
{width: 100, height: 200})
|
||||||
assert.deepEqual(imageG.getSize(), {width: 538, height: 190})
|
expect(imageG.getSize()).to.deep.equal({width: 538, height: 190})
|
||||||
|
|
||||||
const imageH = nativeImage.createFromBuffer(imageA.toJPEG(100),
|
const imageH = nativeImage.createFromBuffer(imageA.toJPEG(100),
|
||||||
{width: 100, height: 200})
|
{width: 100, height: 200})
|
||||||
assert.deepEqual(imageH.getSize(), {width: 538, height: 190})
|
expect(imageH.getSize()).to.deep.equal({width: 538, height: 190})
|
||||||
|
|
||||||
const imageI = nativeImage.createFromBuffer(imageA.toBitmap(),
|
const imageI = nativeImage.createFromBuffer(imageA.toBitmap(),
|
||||||
{width: 538, height: 190, scaleFactor: 2.0})
|
{width: 538, height: 190, scaleFactor: 2.0})
|
||||||
assert.deepEqual(imageI.getSize(), {width: 269, height: 95})
|
expect(imageI.getSize()).to.deep.equal({width: 269, height: 95})
|
||||||
|
|
||||||
const imageJ = nativeImage.createFromBuffer(imageA.toPNG(), 2.0)
|
const imageJ = nativeImage.createFromBuffer(imageA.toPNG(), 2.0)
|
||||||
assert.deepEqual(imageJ.getSize(), {width: 269, height: 95})
|
expect(imageJ.getSize()).to.deep.equal({width: 269, height: 95})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('createFromDataURL(dataURL)', () => {
|
describe('createFromDataURL(dataURL)', () => {
|
||||||
it('returns an empty image from the empty string', () => {
|
it('returns an empty image from the empty string', () => {
|
||||||
assert(nativeImage.createFromDataURL('').isEmpty())
|
expect(nativeImage.createFromDataURL('').isEmpty())
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns an image created from the given string', () => {
|
it('returns an image created from the given string', () => {
|
||||||
|
@ -177,9 +177,10 @@ describe('nativeImage module', () => {
|
||||||
const imageFromPath = nativeImage.createFromPath(imageData.path)
|
const imageFromPath = nativeImage.createFromPath(imageData.path)
|
||||||
const imageFromDataUrl = nativeImage.createFromDataURL(imageData.dataUrl)
|
const imageFromDataUrl = nativeImage.createFromDataURL(imageData.dataUrl)
|
||||||
|
|
||||||
assert(!imageFromDataUrl.isEmpty())
|
expect(imageFromDataUrl.isEmpty())
|
||||||
assert.deepEqual(imageFromDataUrl.getSize(), imageFromPath.getSize())
|
expect(imageFromDataUrl.getSize()).to.deep.equal(imageFromPath.getSize())
|
||||||
assert(imageFromPath.toBitmap().equals(imageFromDataUrl.toBitmap()))
|
expect(imageFromDataUrl.toBitmap()).to.satisfy(
|
||||||
|
bitmap => imageFromPath.toBitmap().equals(bitmap))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -190,8 +191,8 @@ describe('nativeImage module', () => {
|
||||||
for (const imageData of imagesData) {
|
for (const imageData of imagesData) {
|
||||||
const imageFromPath = nativeImage.createFromPath(imageData.path)
|
const imageFromPath = nativeImage.createFromPath(imageData.path)
|
||||||
|
|
||||||
assert.equal(imageFromPath.toDataURL(), imageData.dataUrl)
|
expect(imageFromPath.toDataURL()).to.equal(imageData.dataUrl)
|
||||||
assert.equal(imageFromPath.toDataURL({scaleFactor: 2.0}), imageData.dataUrl)
|
expect(imageFromPath.toDataURL({scaleFactor: 2.0})).to.equal(imageData.dataUrl)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -204,14 +205,14 @@ describe('nativeImage module', () => {
|
||||||
height: image.getSize().height,
|
height: image.getSize().height,
|
||||||
scaleFactor: 2.0
|
scaleFactor: 2.0
|
||||||
})
|
})
|
||||||
assert.deepEqual(imageOne.getSize(),
|
expect(imageOne.getSize()).to.deep.equal(
|
||||||
{width: imageData.width / 2, height: imageData.height / 2})
|
{width: imageData.width / 2, height: imageData.height / 2})
|
||||||
|
|
||||||
const imageTwo = nativeImage.createFromDataURL(imageOne.toDataURL())
|
const imageTwo = nativeImage.createFromDataURL(imageOne.toDataURL())
|
||||||
assert.deepEqual(imageTwo.getSize(),
|
expect(imageTwo.getSize()).to.deep.equal(
|
||||||
{width: imageData.width, height: imageData.height})
|
{width: imageData.width, height: imageData.height})
|
||||||
|
|
||||||
assert(imageOne.toBitmap().equals(imageTwo.toBitmap()))
|
expect(imageOne.toBitmap().equals(imageTwo.toBitmap())).to.be.true
|
||||||
})
|
})
|
||||||
|
|
||||||
it('supports a scale factor', () => {
|
it('supports a scale factor', () => {
|
||||||
|
@ -221,11 +222,11 @@ describe('nativeImage module', () => {
|
||||||
|
|
||||||
const imageFromDataUrlOne = nativeImage.createFromDataURL(
|
const imageFromDataUrlOne = nativeImage.createFromDataURL(
|
||||||
image.toDataURL({scaleFactor: 1.0}))
|
image.toDataURL({scaleFactor: 1.0}))
|
||||||
assert.deepEqual(imageFromDataUrlOne.getSize(), expectedSize)
|
expect(imageFromDataUrlOne.getSize()).to.deep.equal(expectedSize)
|
||||||
|
|
||||||
const imageFromDataUrlTwo = nativeImage.createFromDataURL(
|
const imageFromDataUrlTwo = nativeImage.createFromDataURL(
|
||||||
image.toDataURL({scaleFactor: 2.0}))
|
image.toDataURL({scaleFactor: 2.0}))
|
||||||
assert.deepEqual(imageFromDataUrlTwo.getSize(), expectedSize)
|
expect(imageFromDataUrlTwo.getSize()).to.deep.equal(expectedSize)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -239,14 +240,14 @@ describe('nativeImage module', () => {
|
||||||
height: imageA.getSize().height,
|
height: imageA.getSize().height,
|
||||||
scaleFactor: 2.0
|
scaleFactor: 2.0
|
||||||
})
|
})
|
||||||
assert.deepEqual(imageB.getSize(),
|
expect(imageB.getSize()).to.deep.equal(
|
||||||
{width: imageData.width / 2, height: imageData.height / 2})
|
{width: imageData.width / 2, height: imageData.height / 2})
|
||||||
|
|
||||||
const imageC = nativeImage.createFromBuffer(imageB.toPNG())
|
const imageC = nativeImage.createFromBuffer(imageB.toPNG())
|
||||||
assert.deepEqual(imageC.getSize(),
|
expect(imageC.getSize()).to.deep.equal(
|
||||||
{width: imageData.width, height: imageData.height})
|
{width: imageData.width, height: imageData.height})
|
||||||
|
|
||||||
assert(imageB.toBitmap().equals(imageC.toBitmap()))
|
expect(imageB.toBitmap().equals(imageC.toBitmap())).to.be.true
|
||||||
})
|
})
|
||||||
|
|
||||||
it('supports a scale factor', () => {
|
it('supports a scale factor', () => {
|
||||||
|
@ -255,46 +256,44 @@ describe('nativeImage module', () => {
|
||||||
|
|
||||||
const imageFromBufferOne = nativeImage.createFromBuffer(
|
const imageFromBufferOne = nativeImage.createFromBuffer(
|
||||||
image.toPNG({scaleFactor: 1.0}))
|
image.toPNG({scaleFactor: 1.0}))
|
||||||
assert.deepEqual(
|
expect(imageFromBufferOne.getSize()).to.deep.equal(
|
||||||
imageFromBufferOne.getSize(),
|
|
||||||
{width: imageData.width, height: imageData.height})
|
{width: imageData.width, height: imageData.height})
|
||||||
|
|
||||||
const imageFromBufferTwo = nativeImage.createFromBuffer(
|
const imageFromBufferTwo = nativeImage.createFromBuffer(
|
||||||
image.toPNG({scaleFactor: 2.0}), {scaleFactor: 2.0})
|
image.toPNG({scaleFactor: 2.0}), {scaleFactor: 2.0})
|
||||||
assert.deepEqual(
|
expect(imageFromBufferTwo.getSize()).to.deep.equal(
|
||||||
imageFromBufferTwo.getSize(),
|
|
||||||
{width: imageData.width / 2, height: imageData.height / 2})
|
{width: imageData.width / 2, height: imageData.height / 2})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('createFromPath(path)', () => {
|
describe('createFromPath(path)', () => {
|
||||||
it('returns an empty image for invalid paths', () => {
|
it('returns an empty image for invalid paths', () => {
|
||||||
assert(nativeImage.createFromPath('').isEmpty())
|
expect(nativeImage.createFromPath('').isEmpty())
|
||||||
assert(nativeImage.createFromPath('does-not-exist.png').isEmpty())
|
expect(nativeImage.createFromPath('does-not-exist.png').isEmpty())
|
||||||
assert(nativeImage.createFromPath('does-not-exist.ico').isEmpty())
|
expect(nativeImage.createFromPath('does-not-exist.ico').isEmpty())
|
||||||
assert(nativeImage.createFromPath(__dirname).isEmpty())
|
expect(nativeImage.createFromPath(__dirname).isEmpty())
|
||||||
assert(nativeImage.createFromPath(__filename).isEmpty())
|
expect(nativeImage.createFromPath(__filename).isEmpty())
|
||||||
})
|
})
|
||||||
|
|
||||||
it('loads images from paths relative to the current working directory', () => {
|
it('loads images from paths relative to the current working directory', () => {
|
||||||
const imagePath = `.${path.sep}${path.join('spec', 'fixtures', 'assets', 'logo.png')}`
|
const imagePath = `.${path.sep}${path.join('spec', 'fixtures', 'assets', 'logo.png')}`
|
||||||
const image = nativeImage.createFromPath(imagePath)
|
const image = nativeImage.createFromPath(imagePath)
|
||||||
assert(!image.isEmpty())
|
expect(image.isEmpty()).to.be.false
|
||||||
assert.deepEqual(image.getSize(), {width: 538, height: 190})
|
expect(image.getSize()).to.deep.equal({width: 538, height: 190})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('loads images from paths with `.` segments', () => {
|
it('loads images from paths with `.` segments', () => {
|
||||||
const imagePath = `${path.join(__dirname, 'fixtures')}${path.sep}.${path.sep}${path.join('assets', 'logo.png')}`
|
const imagePath = `${path.join(__dirname, 'fixtures')}${path.sep}.${path.sep}${path.join('assets', 'logo.png')}`
|
||||||
const image = nativeImage.createFromPath(imagePath)
|
const image = nativeImage.createFromPath(imagePath)
|
||||||
assert(!image.isEmpty())
|
expect(image.isEmpty()).to.be.false
|
||||||
assert.deepEqual(image.getSize(), {width: 538, height: 190})
|
expect(image.getSize()).to.deep.equal({width: 538, height: 190})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('loads images from paths with `..` segments', () => {
|
it('loads images from paths with `..` segments', () => {
|
||||||
const imagePath = `${path.join(__dirname, 'fixtures', 'api')}${path.sep}..${path.sep}${path.join('assets', 'logo.png')}`
|
const imagePath = `${path.join(__dirname, 'fixtures', 'api')}${path.sep}..${path.sep}${path.join('assets', 'logo.png')}`
|
||||||
const image = nativeImage.createFromPath(imagePath)
|
const image = nativeImage.createFromPath(imagePath)
|
||||||
assert(!image.isEmpty())
|
expect(image.isEmpty()).to.be.false
|
||||||
assert.deepEqual(image.getSize(), {width: 538, height: 190})
|
expect(image.getSize()).to.deep.equal({width: 538, height: 190})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Gets an NSImage pointer on macOS', () => {
|
it('Gets an NSImage pointer on macOS', () => {
|
||||||
|
@ -304,10 +303,11 @@ describe('nativeImage module', () => {
|
||||||
const image = nativeImage.createFromPath(imagePath)
|
const image = nativeImage.createFromPath(imagePath)
|
||||||
const nsimage = image.getNativeHandle()
|
const nsimage = image.getNativeHandle()
|
||||||
|
|
||||||
assert.equal(nsimage.length, 8)
|
expect(nsimage).to.have.lengthOf(8)
|
||||||
|
|
||||||
// If all bytes are null, that's Bad
|
// If all bytes are null, that's Bad
|
||||||
assert.equal(nsimage.reduce((acc, x) => acc || (x !== 0), false), true)
|
const allBytesAreNotNull = nsimage.reduce((acc, x) => acc || (x !== 0), false)
|
||||||
|
expect(allBytesAreNotNull)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('loads images from .ico files on Windows', () => {
|
it('loads images from .ico files on Windows', () => {
|
||||||
|
@ -315,56 +315,61 @@ describe('nativeImage module', () => {
|
||||||
|
|
||||||
const imagePath = path.join(__dirname, 'fixtures', 'assets', 'icon.ico')
|
const imagePath = path.join(__dirname, 'fixtures', 'assets', 'icon.ico')
|
||||||
const image = nativeImage.createFromPath(imagePath)
|
const image = nativeImage.createFromPath(imagePath)
|
||||||
assert(!image.isEmpty())
|
expect(image.isEmpty()).to.be.false
|
||||||
assert.deepEqual(image.getSize(), {width: 256, height: 256})
|
expect(image.getSize()).to.deep.equal({width: 256, height: 256})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('createFromNamedImage(name)', () => {
|
describe('createFromNamedImage(name)', () => {
|
||||||
it('returns empty for invalid options', () => {
|
it('returns empty for invalid options', () => {
|
||||||
const image = nativeImage.createFromNamedImage('totally_not_real')
|
const image = nativeImage.createFromNamedImage('totally_not_real')
|
||||||
assert(image.isEmpty())
|
expect(image.isEmpty())
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns empty on non-darwin platforms', () => {
|
it('returns empty on non-darwin platforms', () => {
|
||||||
if (process.platform === 'darwin') return
|
if (process.platform === 'darwin') return
|
||||||
|
|
||||||
const image = nativeImage.createFromNamedImage('NSActionTemplate')
|
const image = nativeImage.createFromNamedImage('NSActionTemplate')
|
||||||
assert(image.isEmpty())
|
expect(image.isEmpty())
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns a valid image on darwin', () => {
|
it('returns a valid image on darwin', () => {
|
||||||
if (process.platform !== 'darwin') return
|
if (process.platform !== 'darwin') return
|
||||||
|
|
||||||
const image = nativeImage.createFromNamedImage('NSActionTemplate')
|
const image = nativeImage.createFromNamedImage('NSActionTemplate')
|
||||||
assert(!image.isEmpty())
|
expect(image.isEmpty()).to.be.false
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns allows an HSL shift for a valid image on darwin', () => {
|
it('returns allows an HSL shift for a valid image on darwin', () => {
|
||||||
if (process.platform !== 'darwin') return
|
if (process.platform !== 'darwin') return
|
||||||
|
|
||||||
const image = nativeImage.createFromNamedImage('NSActionTemplate', [0.5, 0.2, 0.8])
|
const image = nativeImage.createFromNamedImage('NSActionTemplate', [0.5, 0.2, 0.8])
|
||||||
assert(!image.isEmpty())
|
expect(image.isEmpty()).to.be.false
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('resize(options)', () => {
|
describe('resize(options)', () => {
|
||||||
it('returns a resized image', () => {
|
it('returns a resized image', () => {
|
||||||
const image = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png'))
|
const image = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png'))
|
||||||
assert.deepEqual(image.resize({}).getSize(), {width: 538, height: 190})
|
for (const [resizeTo, expectedSize] of new Map([
|
||||||
assert.deepEqual(image.resize({width: 269}).getSize(), {width: 269, height: 95})
|
[{}, {width: 538, height: 190}],
|
||||||
assert.deepEqual(image.resize({width: 600}).getSize(), {width: 600, height: 212})
|
[{width: 269}, {width: 269, height: 95}],
|
||||||
assert.deepEqual(image.resize({height: 95}).getSize(), {width: 269, height: 95})
|
[{width: 600}, {width: 600, height: 212}],
|
||||||
assert.deepEqual(image.resize({height: 200}).getSize(), {width: 566, height: 200})
|
[{height: 95}, {width: 269, height: 95}],
|
||||||
assert.deepEqual(image.resize({width: 80, height: 65}).getSize(), {width: 80, height: 65})
|
[{height: 200}, {width: 566, height: 200}],
|
||||||
assert.deepEqual(image.resize({width: 600, height: 200}).getSize(), {width: 600, height: 200})
|
[{width: 80, height: 65}, {width: 80, height: 65}],
|
||||||
assert.deepEqual(image.resize({width: 0, height: 0}).getSize(), {width: 0, height: 0})
|
[{width: 600, height: 200}, {width: 600, height: 200}],
|
||||||
assert.deepEqual(image.resize({width: -1, height: -1}).getSize(), {width: 0, height: 0})
|
[{width: 0, height: 0}, {width: 0, height: 0}],
|
||||||
|
[{width: -1, height: -1}, {width: 0, height: 0}]
|
||||||
|
])) {
|
||||||
|
const actualSize = image.resize(resizeTo).getSize()
|
||||||
|
expect(actualSize).to.deep.equal(expectedSize)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns an empty image when called on an empty image', () => {
|
it('returns an empty image when called on an empty image', () => {
|
||||||
assert(nativeImage.createEmpty().resize({width: 1, height: 1}).isEmpty())
|
expect(nativeImage.createEmpty().resize({width: 1, height: 1}).isEmpty())
|
||||||
assert(nativeImage.createEmpty().resize({width: 0, height: 0}).isEmpty())
|
expect(nativeImage.createEmpty().resize({width: 0, height: 0}).isEmpty())
|
||||||
})
|
})
|
||||||
|
|
||||||
it('supports a quality option', () => {
|
it('supports a quality option', () => {
|
||||||
|
@ -372,38 +377,39 @@ describe('nativeImage module', () => {
|
||||||
const good = image.resize({width: 100, height: 100, quality: 'good'})
|
const good = image.resize({width: 100, height: 100, quality: 'good'})
|
||||||
const better = image.resize({width: 100, height: 100, quality: 'better'})
|
const better = image.resize({width: 100, height: 100, quality: 'better'})
|
||||||
const best = image.resize({width: 100, height: 100, quality: 'best'})
|
const best = image.resize({width: 100, height: 100, quality: 'best'})
|
||||||
assert(good.toPNG().length <= better.toPNG().length)
|
|
||||||
assert(better.toPNG().length < best.toPNG().length)
|
expect(good.toPNG()).to.have.lengthOf.at.most(better.toPNG().length)
|
||||||
|
expect(better.toPNG()).to.have.lengthOf.below(best.toPNG().length)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('crop(bounds)', () => {
|
describe('crop(bounds)', () => {
|
||||||
it('returns an empty image when called on an empty image', () => {
|
it('returns an empty image when called on an empty image', () => {
|
||||||
assert(nativeImage.createEmpty().crop({width: 1, height: 2, x: 0, y: 0}).isEmpty())
|
expect(nativeImage.createEmpty().crop({width: 1, height: 2, x: 0, y: 0}).isEmpty())
|
||||||
assert(nativeImage.createEmpty().crop({width: 0, height: 0, x: 0, y: 0}).isEmpty())
|
expect(nativeImage.createEmpty().crop({width: 0, height: 0, x: 0, y: 0}).isEmpty())
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns an empty image when the bounds are invalid', () => {
|
it('returns an empty image when the bounds are invalid', () => {
|
||||||
const image = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png'))
|
const image = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png'))
|
||||||
assert(image.crop({width: 0, height: 0, x: 0, y: 0}).isEmpty())
|
expect(image.crop({width: 0, height: 0, x: 0, y: 0}).isEmpty())
|
||||||
assert(image.crop({width: -1, height: 10, x: 0, y: 0}).isEmpty())
|
expect(image.crop({width: -1, height: 10, x: 0, y: 0}).isEmpty())
|
||||||
assert(image.crop({width: 10, height: -35, x: 0, y: 0}).isEmpty())
|
expect(image.crop({width: 10, height: -35, x: 0, y: 0}).isEmpty())
|
||||||
assert(image.crop({width: 100, height: 100, x: 1000, y: 1000}).isEmpty())
|
expect(image.crop({width: 100, height: 100, x: 1000, y: 1000}).isEmpty())
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns a cropped image', () => {
|
it('returns a cropped image', () => {
|
||||||
const image = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png'))
|
const image = nativeImage.createFromPath(path.join(__dirname, 'fixtures', 'assets', 'logo.png'))
|
||||||
const cropA = image.crop({width: 25, height: 64, x: 0, y: 0})
|
const cropA = image.crop({width: 25, height: 64, x: 0, y: 0})
|
||||||
const cropB = image.crop({width: 25, height: 64, x: 30, y: 40})
|
const cropB = image.crop({width: 25, height: 64, x: 30, y: 40})
|
||||||
assert.deepEqual(cropA.getSize(), {width: 25, height: 64})
|
expect(cropA.getSize()).to.deep.equal({width: 25, height: 64})
|
||||||
assert.deepEqual(cropB.getSize(), {width: 25, height: 64})
|
expect(cropB.getSize()).to.deep.equal({width: 25, height: 64})
|
||||||
assert(!cropA.toPNG().equals(cropB.toPNG()))
|
expect(cropA.toPNG().equals(cropB.toPNG())).to.be.false
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('getAspectRatio()', () => {
|
describe('getAspectRatio()', () => {
|
||||||
it('returns an aspect ratio of an empty image', () => {
|
it('returns an aspect ratio of an empty image', () => {
|
||||||
assert.equal(nativeImage.createEmpty().getAspectRatio(), 1.0)
|
expect(nativeImage.createEmpty().getAspectRatio()).to.equal(1.0)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns an aspect ratio of an image', () => {
|
it('returns an aspect ratio of an image', () => {
|
||||||
|
@ -412,7 +418,7 @@ describe('nativeImage module', () => {
|
||||||
const expectedAspectRatio = 2.8315789699554443
|
const expectedAspectRatio = 2.8315789699554443
|
||||||
|
|
||||||
const image = nativeImage.createFromPath(imageData.path)
|
const image = nativeImage.createFromPath(imageData.path)
|
||||||
assert.equal(image.getAspectRatio(), expectedAspectRatio)
|
expect(image.getAspectRatio()).to.equal(expectedAspectRatio)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -443,13 +449,13 @@ describe('nativeImage module', () => {
|
||||||
buffer: 'invalid'
|
buffer: 'invalid'
|
||||||
})
|
})
|
||||||
|
|
||||||
assert.equal(image.isEmpty(), false)
|
expect(image.isEmpty()).to.be.false
|
||||||
assert.deepEqual(image.getSize(), {width: 1, height: 1})
|
expect(image.getSize()).to.deep.equal({width: 1, height: 1})
|
||||||
|
|
||||||
assert.equal(image.toDataURL({scaleFactor: 1.0}), imageDataOne.dataUrl)
|
expect(image.toDataURL({scaleFactor: 1.0})).to.equal(imageDataOne.dataUrl)
|
||||||
assert.equal(image.toDataURL({scaleFactor: 2.0}), imageDataTwo.dataUrl)
|
expect(image.toDataURL({scaleFactor: 2.0})).to.equal(imageDataTwo.dataUrl)
|
||||||
assert.equal(image.toDataURL({scaleFactor: 3.0}), imageDataThree.dataUrl)
|
expect(image.toDataURL({scaleFactor: 3.0})).to.equal(imageDataThree.dataUrl)
|
||||||
assert.equal(image.toDataURL({scaleFactor: 4.0}), imageDataThree.dataUrl)
|
expect(image.toDataURL({scaleFactor: 4.0})).to.equal(imageDataThree.dataUrl)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('supports adding a data URL representation for a scale factor', () => {
|
it('supports adding a data URL representation for a scale factor', () => {
|
||||||
|
@ -478,13 +484,13 @@ describe('nativeImage module', () => {
|
||||||
dataURL: 'invalid'
|
dataURL: 'invalid'
|
||||||
})
|
})
|
||||||
|
|
||||||
assert.equal(image.isEmpty(), false)
|
expect(image.isEmpty()).to.be.false
|
||||||
assert.deepEqual(image.getSize(), {width: 1, height: 1})
|
expect(image.getSize()).to.deep.equal({width: 1, height: 1})
|
||||||
|
|
||||||
assert.equal(image.toDataURL({scaleFactor: 1.0}), imageDataOne.dataUrl)
|
expect(image.toDataURL({scaleFactor: 1.0})).to.equal(imageDataOne.dataUrl)
|
||||||
assert.equal(image.toDataURL({scaleFactor: 2.0}), imageDataTwo.dataUrl)
|
expect(image.toDataURL({scaleFactor: 2.0})).to.equal(imageDataTwo.dataUrl)
|
||||||
assert.equal(image.toDataURL({scaleFactor: 3.0}), imageDataThree.dataUrl)
|
expect(image.toDataURL({scaleFactor: 3.0})).to.equal(imageDataThree.dataUrl)
|
||||||
assert.equal(image.toDataURL({scaleFactor: 4.0}), imageDataThree.dataUrl)
|
expect(image.toDataURL({scaleFactor: 4.0})).to.equal(imageDataThree.dataUrl)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('supports adding a representation to an existing image', () => {
|
it('supports adding a representation to an existing image', () => {
|
||||||
|
@ -503,8 +509,8 @@ describe('nativeImage module', () => {
|
||||||
dataURL: imageDataThree.dataUrl
|
dataURL: imageDataThree.dataUrl
|
||||||
})
|
})
|
||||||
|
|
||||||
assert.equal(image.toDataURL({scaleFactor: 1.0}), imageDataOne.dataUrl)
|
expect(image.toDataURL({scaleFactor: 1.0})).to.equal(imageDataOne.dataUrl)
|
||||||
assert.equal(image.toDataURL({scaleFactor: 2.0}), imageDataTwo.dataUrl)
|
expect(image.toDataURL({scaleFactor: 2.0})).to.equal(imageDataTwo.dataUrl)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -5,6 +5,10 @@ const qs = require('querystring')
|
||||||
const {closeWindow} = require('./window-helpers')
|
const {closeWindow} = require('./window-helpers')
|
||||||
const {remote} = require('electron')
|
const {remote} = require('electron')
|
||||||
const {BrowserWindow, ipcMain, protocol, session, webContents} = remote
|
const {BrowserWindow, ipcMain, protocol, session, webContents} = remote
|
||||||
|
// The RPC API doesn't seem to support calling methods on remote objects very
|
||||||
|
// well. In order to test stream protocol, we must work around this limitation
|
||||||
|
// and use Stream instances created in the browser process.
|
||||||
|
const stream = remote.require('stream')
|
||||||
|
|
||||||
describe('protocol module', () => {
|
describe('protocol module', () => {
|
||||||
const protocolName = 'sp'
|
const protocolName = 'sp'
|
||||||
|
@ -14,6 +18,33 @@ describe('protocol module', () => {
|
||||||
type: 'string'
|
type: 'string'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function delay (ms) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
setTimeout(resolve, ms)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStream (chunkSize = text.length, data = text) {
|
||||||
|
const body = stream.PassThrough()
|
||||||
|
|
||||||
|
async function sendChunks () {
|
||||||
|
let buf = new Buffer(data)
|
||||||
|
for (;;) {
|
||||||
|
body.push(buf.slice(0, chunkSize))
|
||||||
|
buf = buf.slice(chunkSize)
|
||||||
|
if (!buf.length) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// emulate network delay
|
||||||
|
await delay(50)
|
||||||
|
}
|
||||||
|
body.push(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
sendChunks()
|
||||||
|
return body
|
||||||
|
}
|
||||||
|
|
||||||
afterEach((done) => {
|
afterEach((done) => {
|
||||||
protocol.unregisterProtocol(protocolName, () => {
|
protocol.unregisterProtocol(protocolName, () => {
|
||||||
protocol.uninterceptProtocol('http', () => done())
|
protocol.uninterceptProtocol('http', () => done())
|
||||||
|
@ -443,6 +474,120 @@ describe('protocol module', () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('protocol.registerStreamProtocol', () => {
|
||||||
|
it('sends Stream as response', (done) => {
|
||||||
|
const handler = (request, callback) => callback(getStream())
|
||||||
|
protocol.registerStreamProtocol(protocolName, handler, (error) => {
|
||||||
|
if (error) return done(error)
|
||||||
|
$.ajax({
|
||||||
|
url: protocolName + '://fake-host',
|
||||||
|
cache: false,
|
||||||
|
success: (data) => {
|
||||||
|
assert.equal(data, text)
|
||||||
|
done()
|
||||||
|
},
|
||||||
|
error: (xhr, errorType, error) => {
|
||||||
|
done(error || new Error(`Request failed: ${xhr.status}`))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('sends object as response', (done) => {
|
||||||
|
const handler = (request, callback) => callback({data: getStream()})
|
||||||
|
protocol.registerStreamProtocol(protocolName, handler, (error) => {
|
||||||
|
if (error) return done(error)
|
||||||
|
$.ajax({
|
||||||
|
url: protocolName + '://fake-host',
|
||||||
|
cache: false,
|
||||||
|
success: (data, _, request) => {
|
||||||
|
assert.equal(request.status, 200)
|
||||||
|
assert.equal(data, text)
|
||||||
|
done()
|
||||||
|
},
|
||||||
|
error: (xhr, errorType, error) => {
|
||||||
|
done(error || new Error(`Request failed: ${xhr.status}`))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('sends custom response headers', (done) => {
|
||||||
|
const handler = (request, callback) => callback({
|
||||||
|
data: getStream(3),
|
||||||
|
headers: {
|
||||||
|
'x-electron': ['a', 'b']
|
||||||
|
}
|
||||||
|
})
|
||||||
|
protocol.registerStreamProtocol(protocolName, handler, (error) => {
|
||||||
|
if (error) return done(error)
|
||||||
|
$.ajax({
|
||||||
|
url: protocolName + '://fake-host',
|
||||||
|
cache: false,
|
||||||
|
success: (data, _, request) => {
|
||||||
|
assert.equal(request.status, 200)
|
||||||
|
assert.equal(request.getResponseHeader('x-electron'), 'a,b')
|
||||||
|
assert.equal(data, text)
|
||||||
|
done()
|
||||||
|
},
|
||||||
|
error: (xhr, errorType, error) => {
|
||||||
|
done(error || new Error(`Request failed: ${xhr.status}`))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('sends custom status code', (done) => {
|
||||||
|
const handler = (request, callback) => callback({
|
||||||
|
statusCode: 204,
|
||||||
|
data: null
|
||||||
|
})
|
||||||
|
protocol.registerStreamProtocol(protocolName, handler, (error) => {
|
||||||
|
if (error) return done(error)
|
||||||
|
$.ajax({
|
||||||
|
url: protocolName + '://fake-host',
|
||||||
|
cache: false,
|
||||||
|
success: (data, _, request) => {
|
||||||
|
assert.equal(request.status, 204)
|
||||||
|
assert.equal(data, undefined)
|
||||||
|
done()
|
||||||
|
},
|
||||||
|
error: (xhr, errorType, error) => {
|
||||||
|
done(error || new Error(`Request failed: ${xhr.status}`))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('receives request headers', (done) => {
|
||||||
|
const handler = (request, callback) => {
|
||||||
|
callback({
|
||||||
|
headers: {
|
||||||
|
'content-type': 'application/json'
|
||||||
|
},
|
||||||
|
data: getStream(5, JSON.stringify(Object.assign({}, request.headers)))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
protocol.registerStreamProtocol(protocolName, handler, (error) => {
|
||||||
|
if (error) return done(error)
|
||||||
|
$.ajax({
|
||||||
|
url: protocolName + '://fake-host',
|
||||||
|
headers: {
|
||||||
|
'x-return-headers': 'yes'
|
||||||
|
},
|
||||||
|
cache: false,
|
||||||
|
success: (data) => {
|
||||||
|
assert.equal(data['x-return-headers'], 'yes')
|
||||||
|
done()
|
||||||
|
},
|
||||||
|
error: (xhr, errorType, error) => {
|
||||||
|
done(error || new Error(`Request failed: ${xhr.status}`))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('protocol.isProtocolHandled', () => {
|
describe('protocol.isProtocolHandled', () => {
|
||||||
it('returns true for about:', (done) => {
|
it('returns true for about:', (done) => {
|
||||||
protocol.isProtocolHandled('about', (result) => {
|
protocol.isProtocolHandled('about', (result) => {
|
||||||
|
@ -722,6 +867,81 @@ describe('protocol module', () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('protocol.interceptStreamProtocol', () => {
|
||||||
|
it('can intercept http protocol', (done) => {
|
||||||
|
const handler = (request, callback) => callback(getStream())
|
||||||
|
protocol.interceptStreamProtocol('http', handler, (error) => {
|
||||||
|
if (error) return done(error)
|
||||||
|
$.ajax({
|
||||||
|
url: 'http://fake-host',
|
||||||
|
cache: false,
|
||||||
|
success: (data) => {
|
||||||
|
assert.equal(data, text)
|
||||||
|
done()
|
||||||
|
},
|
||||||
|
error: (xhr, errorType, error) => {
|
||||||
|
done(error || new Error(`Request failed: ${xhr.status}`))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('can receive post data', (done) => {
|
||||||
|
const handler = (request, callback) => {
|
||||||
|
callback(getStream(3, request.uploadData[0].bytes.toString()))
|
||||||
|
}
|
||||||
|
protocol.interceptStreamProtocol('http', handler, (error) => {
|
||||||
|
if (error) return done(error)
|
||||||
|
$.ajax({
|
||||||
|
url: 'http://fake-host',
|
||||||
|
cache: false,
|
||||||
|
type: 'POST',
|
||||||
|
data: postData,
|
||||||
|
success: (data) => {
|
||||||
|
assert.deepEqual(qs.parse(data), postData)
|
||||||
|
done()
|
||||||
|
},
|
||||||
|
error: (xhr, errorType, error) => {
|
||||||
|
done(error || new Error(`Request failed: ${xhr.status}`))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('can execute redirects', (done) => {
|
||||||
|
const handler = (request, callback) => {
|
||||||
|
if (request.url.indexOf('http://fake-host') === 0) {
|
||||||
|
setTimeout(() => {
|
||||||
|
callback({
|
||||||
|
data: null,
|
||||||
|
statusCode: 302,
|
||||||
|
headers: {
|
||||||
|
Location: 'http://fake-redirect'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, 300)
|
||||||
|
} else {
|
||||||
|
assert.equal(request.url.indexOf('http://fake-redirect'), 0)
|
||||||
|
callback(getStream(1, 'redirect'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
protocol.interceptStreamProtocol('http', handler, (error) => {
|
||||||
|
if (error) return done(error)
|
||||||
|
$.ajax({
|
||||||
|
url: 'http://fake-host',
|
||||||
|
cache: false,
|
||||||
|
success: (data) => {
|
||||||
|
assert.equal(data, 'redirect')
|
||||||
|
done()
|
||||||
|
},
|
||||||
|
error: (xhr, errorType, error) => {
|
||||||
|
done(error || new Error(`Request failed: ${xhr.status}`))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('protocol.uninterceptProtocol', () => {
|
describe('protocol.uninterceptProtocol', () => {
|
||||||
it('returns error when scheme does not exist', (done) => {
|
it('returns error when scheme does not exist', (done) => {
|
||||||
protocol.uninterceptProtocol('not-exist', (error) => {
|
protocol.uninterceptProtocol('not-exist', (error) => {
|
||||||
|
|
|
@ -955,18 +955,40 @@ describe('chromium feature', () => {
|
||||||
slashes: true
|
slashes: true
|
||||||
})
|
})
|
||||||
|
|
||||||
function createBrowserWindow ({plugins}) {
|
function createBrowserWindow ({plugins, preload}) {
|
||||||
w = new BrowserWindow({
|
w = new BrowserWindow({
|
||||||
show: false,
|
show: false,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
preload: path.join(fixtures, 'module', 'preload-pdf-loaded.js'),
|
preload: path.join(fixtures, 'module', preload),
|
||||||
plugins: plugins
|
plugins: plugins
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function testPDFIsLoadedInSubFrame (page, preloadFile, done) {
|
||||||
|
const pagePath = url.format({
|
||||||
|
pathname: path.join(fixtures, 'pages', page).replace(/\\/g, '/'),
|
||||||
|
protocol: 'file',
|
||||||
|
slashes: true
|
||||||
|
})
|
||||||
|
|
||||||
|
createBrowserWindow({plugins: true, preload: preloadFile})
|
||||||
|
ipcMain.once('pdf-loaded', (event, state) => {
|
||||||
|
assert.equal(state, 'success')
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
w.webContents.on('page-title-updated', () => {
|
||||||
|
const parsedURL = url.parse(w.webContents.getURL(), true)
|
||||||
|
assert.equal(parsedURL.protocol, 'chrome:')
|
||||||
|
assert.equal(parsedURL.hostname, 'pdf-viewer')
|
||||||
|
assert.equal(parsedURL.query.src, pagePath)
|
||||||
|
assert.equal(w.webContents.getTitle(), 'cat.pdf')
|
||||||
|
})
|
||||||
|
w.webContents.loadURL(pagePath)
|
||||||
|
}
|
||||||
|
|
||||||
it('opens when loading a pdf resource as top level navigation', (done) => {
|
it('opens when loading a pdf resource as top level navigation', (done) => {
|
||||||
createBrowserWindow({plugins: true})
|
createBrowserWindow({plugins: true, preload: 'preload-pdf-loaded.js'})
|
||||||
ipcMain.once('pdf-loaded', (event, state) => {
|
ipcMain.once('pdf-loaded', (event, state) => {
|
||||||
assert.equal(state, 'success')
|
assert.equal(state, 'success')
|
||||||
done()
|
done()
|
||||||
|
@ -982,7 +1004,7 @@ describe('chromium feature', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('opens a pdf link given params, the query string should be escaped', (done) => {
|
it('opens a pdf link given params, the query string should be escaped', (done) => {
|
||||||
createBrowserWindow({plugins: true})
|
createBrowserWindow({plugins: true, preload: 'preload-pdf-loaded.js'})
|
||||||
ipcMain.once('pdf-loaded', (event, state) => {
|
ipcMain.once('pdf-loaded', (event, state) => {
|
||||||
assert.equal(state, 'success')
|
assert.equal(state, 'success')
|
||||||
done()
|
done()
|
||||||
|
@ -1000,7 +1022,7 @@ describe('chromium feature', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should download a pdf when plugins are disabled', (done) => {
|
it('should download a pdf when plugins are disabled', (done) => {
|
||||||
createBrowserWindow({plugins: false})
|
createBrowserWindow({plugins: false, preload: 'preload-pdf-loaded.js'})
|
||||||
ipcRenderer.sendSync('set-download-option', false, false)
|
ipcRenderer.sendSync('set-download-option', false, false)
|
||||||
ipcRenderer.once('download-done', (event, state, url, mimeType, receivedBytes, totalBytes, disposition, filename) => {
|
ipcRenderer.once('download-done', (event, state, url, mimeType, receivedBytes, totalBytes, disposition, filename) => {
|
||||||
assert.equal(state, 'completed')
|
assert.equal(state, 'completed')
|
||||||
|
@ -1013,7 +1035,7 @@ describe('chromium feature', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should not open when pdf is requested as sub resource', (done) => {
|
it('should not open when pdf is requested as sub resource', (done) => {
|
||||||
createBrowserWindow({plugins: true})
|
createBrowserWindow({plugins: true, preload: 'preload-pdf-loaded.js'})
|
||||||
webFrame.registerURLSchemeAsPrivileged('file', {
|
webFrame.registerURLSchemeAsPrivileged('file', {
|
||||||
secure: false,
|
secure: false,
|
||||||
bypassCSP: false,
|
bypassCSP: false,
|
||||||
|
@ -1026,6 +1048,14 @@ describe('chromium feature', () => {
|
||||||
done()
|
done()
|
||||||
}).catch((e) => done(e))
|
}).catch((e) => done(e))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('opens when loading a pdf resource in a iframe', (done) => {
|
||||||
|
testPDFIsLoadedInSubFrame('pdf-in-iframe.html', 'preload-pdf-loaded-in-subframe.js', done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('opens when loading a pdf resource in a nested iframe', (done) => {
|
||||||
|
testPDFIsLoadedInSubFrame('pdf-in-nested-iframe.html', 'preload-pdf-loaded-in-nested-subframe.js', done)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('window.alert(message, title)', () => {
|
describe('window.alert(message, title)', () => {
|
||||||
|
|
15
spec/fixtures/module/preload-pdf-loaded-in-nested-subframe.js
vendored
Normal file
15
spec/fixtures/module/preload-pdf-loaded-in-nested-subframe.js
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
const {ipcRenderer} = require('electron')
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', (event) => {
|
||||||
|
var outerFrame = document.getElementById('outer-frame')
|
||||||
|
if (outerFrame) {
|
||||||
|
outerFrame.onload = function () {
|
||||||
|
var pdframe = outerFrame.contentWindow.document.getElementById('pdf-frame')
|
||||||
|
if (pdframe) {
|
||||||
|
pdframe.contentWindow.addEventListener('pdf-loaded', (event) => {
|
||||||
|
ipcRenderer.send('pdf-loaded', event.detail)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
10
spec/fixtures/module/preload-pdf-loaded-in-subframe.js
vendored
Normal file
10
spec/fixtures/module/preload-pdf-loaded-in-subframe.js
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
const {ipcRenderer} = require('electron')
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', (event) => {
|
||||||
|
var subframe = document.getElementById('pdf-frame')
|
||||||
|
if (subframe) {
|
||||||
|
subframe.contentWindow.addEventListener('pdf-loaded', (event) => {
|
||||||
|
ipcRenderer.send('pdf-loaded', event.detail)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
6
spec/fixtures/pages/pdf-in-iframe.html
vendored
Normal file
6
spec/fixtures/pages/pdf-in-iframe.html
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<iframe id="pdf-frame" src="../assets/cat.pdf"/>
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
5
spec/fixtures/pages/pdf-in-nested-iframe.html
vendored
Normal file
5
spec/fixtures/pages/pdf-in-nested-iframe.html
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<iframe id="outer-frame" src="./pdf-in-iframe.html"/>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -336,7 +336,7 @@ describe('node feature', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('includes the electron version in process.versions', () => {
|
it('includes the electron version in process.versions', () => {
|
||||||
assert(/^\d+\.\d+\.\d+$/.test(process.versions.electron))
|
assert(/^\d+\.\d+\.\d+(\S*)?$/.test(process.versions.electron))
|
||||||
})
|
})
|
||||||
|
|
||||||
it('includes the chrome version in process.versions', () => {
|
it('includes the chrome version in process.versions', () => {
|
||||||
|
|
1035
spec/package-lock.json
generated
Normal file
1035
spec/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
|
@ -5,6 +5,7 @@
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"basic-auth": "^1.0.4",
|
"basic-auth": "^1.0.4",
|
||||||
|
"chai": "^4.1.2",
|
||||||
"coffee-script": "^1.12.3",
|
"coffee-script": "^1.12.3",
|
||||||
"graceful-fs": "^4.1.9",
|
"graceful-fs": "^4.1.9",
|
||||||
"mkdirp": "^0.5.1",
|
"mkdirp": "^0.5.1",
|
||||||
|
|
|
@ -217,6 +217,10 @@ app.on('ready', function () {
|
||||||
window.webContents.send('executeJavaScript-promise-response', result)
|
window.webContents.send('executeJavaScript-promise-response', result)
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
window.webContents.send('executeJavaScript-promise-error', error)
|
window.webContents.send('executeJavaScript-promise-error', error)
|
||||||
|
|
||||||
|
if (error && error.name) {
|
||||||
|
window.webContents.send('executeJavaScript-promise-error-name', error.name)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!hasCallback) {
|
if (!hasCallback) {
|
||||||
|
|
2
vendor/libchromiumcontent
vendored
2
vendor/libchromiumcontent
vendored
|
@ -1 +1 @@
|
||||||
Subproject commit 0784acb6519ea45c0cef26d8599fa8c7f6b9c7ca
|
Subproject commit b0c0a9e10bfac39d6da64a9e66e3509731d6fa69
|
Loading…
Reference in a new issue