Merge branch 'master' into native-window-open

This commit is contained in:
Ryohei Ikegami 2017-04-04 20:54:37 +09:00
commit 61fa8693d2
105 changed files with 2439 additions and 907 deletions

View file

@ -44,7 +44,7 @@ content::PepperPluginInfo CreatePepperFlashInfo(const base::FilePath& path,
std::vector<std::string> flash_version_numbers = base::SplitString(
version, ".", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
if (flash_version_numbers.size() < 1)
if (flash_version_numbers.empty())
flash_version_numbers.push_back("11");
// |SplitString()| puts in an empty string given an empty string. :(
else if (flash_version_numbers[0].empty())

View file

@ -427,7 +427,7 @@ void OnClientCertificateSelected(
auto certs = net::X509Certificate::CreateCertificateListFromBytes(
data.c_str(), data.length(), net::X509Certificate::FORMAT_AUTO);
if (certs.size() > 0)
if (!certs.empty())
delegate->ContinueWithCertificate(certs[0].get());
}
@ -520,7 +520,7 @@ void App::OnQuit() {
int exitCode = AtomBrowserMainParts::Get()->GetExitCode();
Emit("quit", exitCode);
if (process_singleton_.get()) {
if (process_singleton_) {
process_singleton_->Cleanup();
process_singleton_.reset();
}
@ -695,7 +695,7 @@ std::string App::GetLocale() {
bool App::MakeSingleInstance(
const ProcessSingleton::NotificationCallback& callback) {
if (process_singleton_.get())
if (process_singleton_)
return false;
base::FilePath user_dir;
@ -716,7 +716,7 @@ bool App::MakeSingleInstance(
}
void App::ReleaseSingleInstance() {
if (process_singleton_.get()) {
if (process_singleton_) {
process_singleton_->Cleanup();
process_singleton_.reset();
}

View file

@ -88,7 +88,7 @@ void AutoUpdater::SetFeedURL(const std::string& url, mate::Arguments* args) {
void AutoUpdater::QuitAndInstall() {
// If we don't have any window then quitAndInstall immediately.
WindowList* window_list = WindowList::GetInstance();
if (window_list->size() == 0) {
if (window_list->empty()) {
auto_updater::AutoUpdater::QuitAndInstall();
return;
}

View file

@ -78,13 +78,14 @@ void ShowMessageBox(int type,
if (mate::Converter<atom::MessageBoxCallback>::FromV8(args->isolate(),
peek,
&callback)) {
atom::ShowMessageBox(window, (atom::MessageBoxType)type, buttons,
default_id, cancel_id, options, title, message, detail,
checkbox_label, checkbox_checked, icon, callback);
atom::ShowMessageBox(window, static_cast<atom::MessageBoxType>(type),
buttons, default_id, cancel_id, options, title,
message, detail, checkbox_label, checkbox_checked,
icon, callback);
} else {
int chosen = atom::ShowMessageBox(window, (atom::MessageBoxType)type,
buttons, default_id, cancel_id,
options, title, message, detail, icon);
int chosen = atom::ShowMessageBox(
window, static_cast<atom::MessageBoxType>(type), buttons, default_id,
cancel_id, options, title, message, detail, icon);
args->Return(chosen);
}
}

View file

@ -233,7 +233,7 @@ class ResolveProxyHelper {
public:
ResolveProxyHelper(AtomBrowserContext* browser_context,
const GURL& url,
Session::ResolveProxyCallback callback)
const Session::ResolveProxyCallback& callback)
: callback_(callback),
original_thread_(base::ThreadTaskRunnerHandle::Get()) {
scoped_refptr<net::URLRequestContextGetter> context_getter =

View file

@ -8,6 +8,7 @@
#include "atom/browser/net/atom_url_request.h"
#include "atom/common/api/event_emitter_caller.h"
#include "atom/common/native_mate_converters/callback.h"
#include "atom/common/native_mate_converters/gurl_converter.h"
#include "atom/common/native_mate_converters/net_converter.h"
#include "atom/common/native_mate_converters/string16_converter.h"
#include "atom/common/node_includes.h"
@ -145,6 +146,8 @@ mate::WrappableBase* URLRequest::New(mate::Arguments* args) {
dict.Get("method", &method);
std::string url;
dict.Get("url", &url);
std::string redirect_policy;
dict.Get("redirect", &redirect_policy);
std::string partition;
mate::Handle<api::Session> session;
if (dict.Get("session", &session)) {
@ -156,8 +159,8 @@ mate::WrappableBase* URLRequest::New(mate::Arguments* args) {
}
auto browser_context = session->browser_context();
auto api_url_request = new URLRequest(args->isolate(), args->GetThis());
auto atom_url_request =
AtomURLRequest::Create(browser_context, method, url, api_url_request);
auto atom_url_request = AtomURLRequest::Create(
browser_context, method, url, redirect_policy, api_url_request);
api_url_request->atom_request_ = atom_url_request;
@ -176,6 +179,7 @@ void URLRequest::BuildPrototype(v8::Isolate* isolate,
.SetMethod("setExtraHeader", &URLRequest::SetExtraHeader)
.SetMethod("removeExtraHeader", &URLRequest::RemoveExtraHeader)
.SetMethod("setChunkedUpload", &URLRequest::SetChunkedUpload)
.SetMethod("followRedirect", &URLRequest::FollowRedirect)
.SetMethod("_setLoadFlags", &URLRequest::SetLoadFlags)
.SetProperty("notStarted", &URLRequest::NotStarted)
.SetProperty("finished", &URLRequest::Finished)
@ -246,6 +250,17 @@ void URLRequest::Cancel() {
Close();
}
void URLRequest::FollowRedirect() {
if (request_state_.Canceled() || request_state_.Closed()) {
return;
}
DCHECK(atom_request_);
if (atom_request_) {
atom_request_->FollowRedirect();
}
}
bool URLRequest::SetExtraHeader(const std::string& name,
const std::string& value) {
// Request state must be in the initial non started state.
@ -305,6 +320,24 @@ void URLRequest::SetLoadFlags(int flags) {
}
}
void URLRequest::OnReceivedRedirect(
int status_code,
const std::string& method,
const GURL& url,
scoped_refptr<net::HttpResponseHeaders> response_headers) {
if (request_state_.Canceled() || request_state_.Closed()) {
return;
}
DCHECK(atom_request_);
if (!atom_request_) {
return;
}
EmitRequestEvent(false, "redirect", status_code, method, url,
response_headers.get());
}
void URLRequest::OnAuthenticationRequired(
scoped_refptr<const net::AuthChallengeInfo> auth_info) {
if (request_state_.Canceled() || request_state_.Closed()) {

View file

@ -99,6 +99,11 @@ class URLRequest : public mate::EventEmitter<URLRequest> {
v8::Local<v8::FunctionTemplate> prototype);
// Methods for reporting events into JavaScript.
void OnReceivedRedirect(
int status_code,
const std::string& method,
const GURL& url,
scoped_refptr<net::HttpResponseHeaders> response_headers);
void OnAuthenticationRequired(
scoped_refptr<const net::AuthChallengeInfo> auth_info);
void OnResponseStarted(
@ -170,6 +175,7 @@ class URLRequest : public mate::EventEmitter<URLRequest> {
bool Failed() const;
bool Write(scoped_refptr<const net::IOBufferWithSize> buffer, bool is_last);
void Cancel();
void FollowRedirect();
bool SetExtraHeader(const std::string& name, const std::string& value);
void RemoveExtraHeader(const std::string& name);
void SetChunkedUpload(bool is_chunked_upload);

View file

@ -240,7 +240,7 @@ content::ServiceWorkerContext* GetServiceWorkerContext(
}
// Called when CapturePage is done.
void OnCapturePageDone(base::Callback<void(const gfx::Image&)> callback,
void OnCapturePageDone(const base::Callback<void(const gfx::Image&)>& callback,
const SkBitmap& bitmap,
content::ReadbackResponse response) {
callback.Run(gfx::Image::CreateFrom1xBitmap(bitmap));
@ -411,14 +411,18 @@ WebContents::~WebContents() {
if (type_ == WEB_VIEW)
guest_delegate_->Destroy();
// The WebContentsDestroyed will not be called automatically because we
// unsubscribe from webContents before destroying it. So we have to manually
// call it here to make sure "destroyed" event is emitted.
RenderViewDeleted(web_contents()->GetRenderViewHost());
WebContentsDestroyed();
DestroyWebContents();
}
}
void WebContents::DestroyWebContents() {
// This event is only for internal use, which is emitted when WebContents is
// being destroyed.
Emit("will-destroy");
ResetManagedWebContents();
}
bool WebContents::DidAddMessageToConsole(content::WebContents* source,
int32_t level,
const base::string16& message,
@ -919,10 +923,6 @@ bool WebContents::OnMessageReceived(const IPC::Message& message) {
// be destroyed on close, and WebContentsDestroyed would be called for it, so
// we need to make sure the api::WebContents is also deleted.
void WebContents::WebContentsDestroyed() {
// This event is only for internal use, which is emitted when WebContents is
// being destroyed.
Emit("will-destroy");
// Cleanup relationships with other parts.
RemoveFromWeakMap();

View file

@ -76,6 +76,9 @@ class WebContents : public mate::TrackableObject<WebContents>,
static void BuildPrototype(v8::Isolate* isolate,
v8::Local<v8::FunctionTemplate> prototype);
// Notifies to destroy any guest web contents before destroying self.
void DestroyWebContents();
int64_t GetID() const;
int GetProcessID() const;
Type GetType() const;

View file

@ -7,11 +7,13 @@
#include <string>
#include <utility>
#include "atom/browser/atom_browser_context.h"
#include "atom/common/google_api_key.h"
#include "base/environment.h"
#include "content/public/browser/browser_thread.h"
#include "device/geolocation/geolocation_provider.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_builder.h"
#include "net/url_request/url_request_context_getter.h"
using content::BrowserThread;
@ -19,51 +21,40 @@ namespace atom {
namespace internal {
// Loads access tokens and other necessary data on the UI thread, and
// calls back to the originator on the originating thread.
class TokenLoadingJob : public base::RefCountedThreadSafe<TokenLoadingJob> {
class GeoURLRequestContextGetter : public net::URLRequestContextGetter {
public:
explicit TokenLoadingJob(
const device::AccessTokenStore::LoadAccessTokensCallback& callback)
: callback_(callback), request_context_getter_(nullptr) {}
net::URLRequestContext* GetURLRequestContext() override {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!url_request_context_.get()) {
net::URLRequestContextBuilder builder;
builder.set_proxy_config_service(
net::ProxyService::CreateSystemProxyConfigService(
BrowserThread::GetTaskRunnerForThread(BrowserThread::IO),
BrowserThread::GetTaskRunnerForThread(BrowserThread::FILE)));
url_request_context_ = builder.Build();
}
return url_request_context_.get();
}
void Run(AtomBrowserContext* browser_context) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
request_context_getter_ = browser_context->GetRequestContext();
std::unique_ptr<base::Environment> env(base::Environment::Create());
if (!env->GetVar("GOOGLE_API_KEY", &api_key_))
api_key_ = GOOGLEAPIS_API_KEY;
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&TokenLoadingJob::RespondOnIOThread, this));
scoped_refptr<base::SingleThreadTaskRunner> GetNetworkTaskRunner()
const override {
return BrowserThread::GetTaskRunnerForThread(BrowserThread::IO);
}
private:
friend class base::RefCountedThreadSafe<TokenLoadingJob>;
friend class atom::AtomAccessTokenStore;
~TokenLoadingJob() {}
GeoURLRequestContextGetter() {}
~GeoURLRequestContextGetter() override {}
void RespondOnIOThread() {
// Equivalent to access_token_map[kGeolocationProviderURL].
// Somehow base::string16 is causing compilation errors when used in a pair
// of std::map on Linux, this can work around it.
device::AccessTokenStore::AccessTokenMap access_token_map;
std::pair<GURL, base::string16> token_pair;
token_pair.first = GURL(GOOGLEAPIS_ENDPOINT + api_key_);
access_token_map.insert(token_pair);
callback_.Run(access_token_map, request_context_getter_);
}
device::AccessTokenStore::LoadAccessTokensCallback callback_;
net::URLRequestContextGetter* request_context_getter_;
std::string api_key_;
std::unique_ptr<net::URLRequestContext> url_request_context_;
DISALLOW_COPY_AND_ASSIGN(GeoURLRequestContextGetter);
};
} // namespace internal
AtomAccessTokenStore::AtomAccessTokenStore() {
browser_context_ = AtomBrowserContext::From("", false);
AtomAccessTokenStore::AtomAccessTokenStore()
: request_context_getter_(new internal::GeoURLRequestContextGetter) {
device::GeolocationProvider::GetInstance()->UserDidOptIntoLocationServices();
}
@ -72,16 +63,19 @@ AtomAccessTokenStore::~AtomAccessTokenStore() {
void AtomAccessTokenStore::LoadAccessTokens(
const LoadAccessTokensCallback& callback) {
scoped_refptr<internal::TokenLoadingJob> job(
new internal::TokenLoadingJob(callback));
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::Bind(&AtomAccessTokenStore::RunTokenLoadingJob,
this, base::RetainedRef(job)));
}
std::unique_ptr<base::Environment> env(base::Environment::Create());
std::string api_key;
if (!env->GetVar("GOOGLE_API_KEY", &api_key))
api_key = GOOGLEAPIS_API_KEY;
// Equivalent to access_token_map[kGeolocationProviderURL].
// Somehow base::string16 is causing compilation errors when used in a pair
// of std::map on Linux, this can work around it.
device::AccessTokenStore::AccessTokenMap access_token_map;
std::pair<GURL, base::string16> token_pair;
token_pair.first = GURL(GOOGLEAPIS_ENDPOINT + api_key);
access_token_map.insert(token_pair);
void AtomAccessTokenStore::RunTokenLoadingJob(
scoped_refptr<internal::TokenLoadingJob> job) {
job->Run(browser_context_.get());
callback.Run(access_token_map, request_context_getter_.get());
}
void AtomAccessTokenStore::SaveAccessToken(const GURL& server_url,

View file

@ -9,10 +9,8 @@
namespace atom {
class AtomBrowserContext;
namespace internal {
class TokenLoadingJob;
class GeoURLRequestContextGetter;
}
class AtomAccessTokenStore : public device::AccessTokenStore {
@ -27,9 +25,7 @@ class AtomAccessTokenStore : public device::AccessTokenStore {
const base::string16& access_token) override;
private:
void RunTokenLoadingJob(scoped_refptr<internal::TokenLoadingJob> job);
scoped_refptr<AtomBrowserContext> browser_context_;
scoped_refptr<internal::GeoURLRequestContextGetter> request_context_getter_;
DISALLOW_COPY_AND_ASSIGN(AtomAccessTokenStore);
};

View file

@ -27,7 +27,7 @@ namespace {
bool g_update_available = false;
std::string update_url_ = "";
}
} // namespace
std::string AutoUpdater::GetFeedURL() {
return update_url_;

View file

@ -44,7 +44,7 @@ void Browser::Quit() {
return;
atom::WindowList* window_list = atom::WindowList::GetInstance();
if (window_list->size() == 0)
if (window_list->empty())
NotifyAndShutdown();
window_list->CloseAllWindows();
@ -66,7 +66,7 @@ void Browser::Exit(mate::Arguments* args) {
// Must destroy windows before quitting, otherwise bad things can happen.
atom::WindowList* window_list = atom::WindowList::GetInstance();
if (window_list->size() == 0) {
if (window_list->empty()) {
Shutdown();
} else {
// Unlike Quit(), we do not ask to close window, but destroy the window

View file

@ -102,7 +102,7 @@ class Browser : public WindowListObserver {
std::vector<base::string16> args;
};
void SetLoginItemSettings(LoginItemSettings settings);
LoginItemSettings GetLoginItemSettings(LoginItemSettings options);
LoginItemSettings GetLoginItemSettings(const LoginItemSettings& options);
#if defined(OS_MACOSX)
// Hide the application.

View file

@ -64,7 +64,7 @@ void Browser::SetLoginItemSettings(LoginItemSettings settings) {
}
Browser::LoginItemSettings Browser::GetLoginItemSettings(
LoginItemSettings options) {
const LoginItemSettings& options) {
return LoginItemSettings();
}

View file

@ -64,8 +64,9 @@ bool Browser::RemoveAsDefaultProtocolClient(const std::string& protocol,
// On macOS, we can't query the default, but the handlers list seems to put
// Apple's defaults first, so we'll use the first option that isn't our bundle
CFStringRef other = nil;
for (CFIndex i = 0; i < CFArrayGetCount(bundleList); i++) {
other = (CFStringRef)CFArrayGetValueAtIndex(bundleList, i);
for (CFIndex i = 0; i < CFArrayGetCount(bundleList); ++i) {
other = base::mac::CFCast<CFStringRef>(CFArrayGetValueAtIndex(bundleList,
i));
if (![identifier isEqualToString: (__bridge NSString *)other]) {
break;
}
@ -152,7 +153,7 @@ bool Browser::ContinueUserActivity(const std::string& type,
}
Browser::LoginItemSettings Browser::GetLoginItemSettings(
LoginItemSettings options) {
const LoginItemSettings& options) {
LoginItemSettings settings;
settings.open_at_login = base::mac::CheckLoginItemStatus(
&settings.open_as_hidden);
@ -179,7 +180,7 @@ std::string Browser::GetExecutableFileProductName() const {
int Browser::DockBounce(BounceType type) {
return [[AtomApplication sharedApplication]
requestUserAttention:(NSRequestUserAttentionType)type];
requestUserAttention:static_cast<NSRequestUserAttentionType>(type)];
}
void Browser::DockCancelBounce(int request_id) {

View file

@ -287,7 +287,7 @@ void Browser::SetLoginItemSettings(LoginItemSettings settings) {
}
Browser::LoginItemSettings Browser::GetLoginItemSettings(
LoginItemSettings options) {
const LoginItemSettings& options) {
LoginItemSettings settings;
base::string16 keyPath = L"Software\\Microsoft\\Windows\\CurrentVersion\\Run";
base::win::RegKey key(HKEY_CURRENT_USER, keyPath.c_str(), KEY_ALL_ACCESS);

View file

@ -183,7 +183,7 @@ void CommonWebContentsDelegate::SetOwnerWindow(
web_contents->SetUserData(relay->key, relay);
}
void CommonWebContentsDelegate::DestroyWebContents() {
void CommonWebContentsDelegate::ResetManagedWebContents() {
web_contents_.reset();
}
@ -338,7 +338,7 @@ void CommonWebContentsDelegate::DevToolsRequestFileSystems() {
}
std::vector<FileSystem> file_systems;
for (auto file_system_path : file_system_paths) {
for (const auto& file_system_path : file_system_paths) {
base::FilePath path = base::FilePath::FromUTF8Unsafe(file_system_path);
std::string file_system_id = RegisterFileSystem(GetDevToolsWebContents(),
path);

View file

@ -42,9 +42,6 @@ class CommonWebContentsDelegate
void SetOwnerWindow(content::WebContents* web_contents,
NativeWindow* owner_window);
// Destroy the managed InspectableWebContents object.
void DestroyWebContents();
// Returns the WebContents managed by this delegate.
content::WebContents* GetWebContents() const;
@ -114,6 +111,9 @@ class CommonWebContentsDelegate
std::string* name, std::string* class_name) override;
#endif
// Destroy the managed InspectableWebContents object.
void ResetManagedWebContents();
private:
// Callback for when DevToolsSaveToFile has completed.
void OnDevToolsSaveToFile(const std::string& url);

View file

@ -10,10 +10,6 @@
#include "base/strings/sys_string_conversions.h"
#include "base/values.h"
@interface NSWindow (SierraSDK)
@property(class) BOOL allowsAutomaticWindowTabbing;
@end
@implementation AtomApplicationDelegate
- (void)setApplicationDockMenu:(atom::AtomMenuModel*)model {
@ -25,10 +21,6 @@
// Don't add the "Enter Full Screen" menu item automatically.
[[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"NSFullScreenMenuItemEverywhere"];
// Don't add the "Show Tab Bar" menu item.
if ([NSWindow respondsToSelector:@selector(allowsAutomaticWindowTabbing)])
NSWindow.allowsAutomaticWindowTabbing = NO;
atom::Browser::Get()->WillFinishLaunching();
}

View file

@ -336,6 +336,19 @@ bool ScopedDisableResize::disable_resize_ = false;
@end
#if !defined(MAC_OS_X_VERSION_10_12)
enum {
NSWindowTabbingModeDisallowed = 2
};
@interface NSWindow (SierraSDK)
- (void)setTabbingMode:(NSInteger)mode;
- (void)setTabbingIdentifier:(NSString*)identifier;
@end
#endif // MAC_OS_X_VERSION_10_12
@interface AtomNSWindow : EventDispatchingWindow<QLPreviewPanelDataSource, QLPreviewPanelDelegate, NSTouchBarDelegate> {
@private
atom::NativeWindowMac* shell_;
@ -682,6 +695,9 @@ NativeWindowMac::NativeWindowMac(
options.Get(options::kTitleBarStyle, &title_bar_style_);
std::string tabbingIdentifier;
options.Get(options::kTabbingIdentifier, &tabbingIdentifier);
std::string windowType;
options.Get(options::kType, &windowType);
@ -754,6 +770,18 @@ NativeWindowMac::NativeWindowMac(
[window_ setOpaque:NO];
}
// Create a tab only if tabbing identifier is specified and window has
// a native title bar.
if (tabbingIdentifier.empty() || transparent() || !has_frame()) {
if ([window_ respondsToSelector:@selector(tabbingMode)]) {
[window_ setTabbingMode:NSWindowTabbingModeDisallowed];
}
} else {
if ([window_ respondsToSelector:@selector(tabbingIdentifier)]) {
[window_ setTabbingIdentifier:base::SysUTF8ToNSString(tabbingIdentifier)];
}
}
// We will manage window's lifetime ourselves.
[window_ setReleasedWhenClosed:NO];
@ -1262,7 +1290,7 @@ void NativeWindowMac::SetProgressBar(double progress, const NativeWindow::Progre
NSDockTile* dock_tile = [NSApp dockTile];
// For the first time API invoked, we need to create a ContentView in DockTile.
if (dock_tile.contentView == NULL) {
if (dock_tile.contentView == nullptr) {
NSImageView* image_view = [[NSImageView alloc] init];
[image_view setImage:[NSApp applicationIconImage]];
[dock_tile setContentView:image_view];
@ -1358,22 +1386,22 @@ void NativeWindowMac::SetVibrancy(const std::string& type) {
// they are available in the minimum SDK version
if (type == "selection") {
// NSVisualEffectMaterialSelection
vibrancyType = (NSVisualEffectMaterial) 4;
vibrancyType = static_cast<NSVisualEffectMaterial>(4);
} else if (type == "menu") {
// NSVisualEffectMaterialMenu
vibrancyType = (NSVisualEffectMaterial) 5;
vibrancyType = static_cast<NSVisualEffectMaterial>(5);
} else if (type == "popover") {
// NSVisualEffectMaterialPopover
vibrancyType = (NSVisualEffectMaterial) 6;
vibrancyType = static_cast<NSVisualEffectMaterial>(6);
} else if (type == "sidebar") {
// NSVisualEffectMaterialSidebar
vibrancyType = (NSVisualEffectMaterial) 7;
vibrancyType = static_cast<NSVisualEffectMaterial>(7);
} else if (type == "medium-light") {
// NSVisualEffectMaterialMediumLight
vibrancyType = (NSVisualEffectMaterial) 8;
vibrancyType = static_cast<NSVisualEffectMaterial>(8);
} else if (type == "ultra-dark") {
// NSVisualEffectMaterialUltraDark
vibrancyType = (NSVisualEffectMaterial) 9;
vibrancyType = static_cast<NSVisualEffectMaterial>(9);
}
}

View file

@ -50,7 +50,7 @@ class CertVerifierRequest : public AtomCertVerifier::Request {
first_response_(true),
weak_ptr_factory_(this) {}
~CertVerifierRequest() {
~CertVerifierRequest() override {
cert_verifier_->RemoveRequest(params_);
default_verifier_request_.reset();
while (!response_list_.empty() && !first_response_) {

View file

@ -402,7 +402,7 @@ void AtomNetworkDelegate::OnListenerResultInIO(
if (!base::ContainsKey(callbacks_, id))
return;
ReadFromResponseObject(*response.get(), out);
ReadFromResponseObject(*response, out);
bool cancel = false;
response->GetBoolean("cancel", &cancel);

View file

@ -13,6 +13,7 @@
#include "net/base/io_buffer.h"
#include "net/base/load_flags.h"
#include "net/base/upload_bytes_element_reader.h"
#include "net/url_request/redirect_info.h"
namespace {
const int kBufferSize = 4096;
@ -58,6 +59,7 @@ scoped_refptr<AtomURLRequest> AtomURLRequest::Create(
AtomBrowserContext* browser_context,
const std::string& method,
const std::string& url,
const std::string& redirect_policy,
api::URLRequest* delegate) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
@ -76,7 +78,7 @@ scoped_refptr<AtomURLRequest> AtomURLRequest::Create(
if (content::BrowserThread::PostTask(
content::BrowserThread::IO, FROM_HERE,
base::Bind(&AtomURLRequest::DoInitialize, atom_url_request,
request_context_getter, method, url))) {
request_context_getter, method, url, redirect_policy))) {
return atom_url_request;
}
return nullptr;
@ -93,10 +95,12 @@ void AtomURLRequest::Terminate() {
void AtomURLRequest::DoInitialize(
scoped_refptr<net::URLRequestContextGetter> request_context_getter,
const std::string& method,
const std::string& url) {
const std::string& url,
const std::string& redirect_policy) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
DCHECK(request_context_getter);
redirect_policy_ = redirect_policy;
request_context_getter_ = request_context_getter;
request_context_getter_->AddObserver(this);
auto context = request_context_getter_->GetURLRequestContext();
@ -150,6 +154,13 @@ void AtomURLRequest::Cancel() {
base::Bind(&AtomURLRequest::DoCancel, this));
}
void AtomURLRequest::FollowRedirect() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
content::BrowserThread::PostTask(
content::BrowserThread::IO, FROM_HERE,
base::Bind(&AtomURLRequest::DoFollowRedirect, this));
}
void AtomURLRequest::SetExtraHeader(const std::string& name,
const std::string& value) const {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
@ -246,6 +257,13 @@ void AtomURLRequest::DoCancel() {
DoTerminate();
}
void AtomURLRequest::DoFollowRedirect() {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
if (request_ && request_->is_redirecting() && redirect_policy_ == "manual") {
request_->FollowDeferredRedirect();
}
}
void AtomURLRequest::DoSetExtraHeader(const std::string& name,
const std::string& value) const {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
@ -297,6 +315,29 @@ void AtomURLRequest::DoSetLoadFlags(int flags) const {
request_->SetLoadFlags(request_->load_flags() | flags);
}
void AtomURLRequest::OnReceivedRedirect(net::URLRequest* request,
const net::RedirectInfo& info,
bool* defer_redirect) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
if (!request_ || redirect_policy_ == "follow")
return;
if (redirect_policy_ == "error") {
request->Cancel();
DoCancelWithError(
"Request cannot follow redirect with the current redirect mode", true);
} else if (redirect_policy_ == "manual") {
*defer_redirect = true;
scoped_refptr<net::HttpResponseHeaders> response_headers =
request->response_headers();
content::BrowserThread::PostTask(
content::BrowserThread::UI, FROM_HERE,
base::Bind(&AtomURLRequest::InformDelegateReceivedRedirect, this,
info.status_code, info.new_method, info.new_url,
response_headers));
}
}
void AtomURLRequest::OnAuthRequired(net::URLRequest* request,
net::AuthChallengeInfo* auth_info) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
@ -348,6 +389,14 @@ void AtomURLRequest::OnReadCompleted(net::URLRequest* request, int bytes_read) {
DCHECK_EQ(request, request_.get());
const auto status = request_->status();
if (status.error() == bytes_read &&
bytes_read == net::ERR_CONTENT_DECODING_INIT_FAILED) {
// When the request job is unable to create a source stream for the
// content encoding, we fail the request.
DoCancelWithError(net::ErrorToString(net::ERR_CONTENT_DECODING_INIT_FAILED),
true);
return;
}
bool response_error = false;
bool data_ended = false;
@ -399,6 +448,16 @@ bool AtomURLRequest::CopyAndPostBuffer(int bytes_read) {
buffer_copy));
}
void AtomURLRequest::InformDelegateReceivedRedirect(
int status_code,
const std::string& method,
const GURL& url,
scoped_refptr<net::HttpResponseHeaders> response_headers) const {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (delegate_)
delegate_->OnReceivedRedirect(status_code, method, url, response_headers);
}
void AtomURLRequest::InformDelegateAuthenticationRequired(
scoped_refptr<net::AuthChallengeInfo> auth_info) const {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);

View file

@ -30,12 +30,14 @@ class AtomURLRequest : public base::RefCountedThreadSafe<AtomURLRequest>,
AtomBrowserContext* browser_context,
const std::string& method,
const std::string& url,
const std::string& redirect_policy,
api::URLRequest* delegate);
void Terminate();
bool Write(scoped_refptr<const net::IOBufferWithSize> buffer, bool is_last);
void SetChunkedUpload(bool is_chunked_upload);
void Cancel();
void FollowRedirect();
void SetExtraHeader(const std::string& name, const std::string& value) const;
void RemoveExtraHeader(const std::string& name) const;
void PassLoginInformation(const base::string16& username,
@ -44,6 +46,9 @@ class AtomURLRequest : public base::RefCountedThreadSafe<AtomURLRequest>,
protected:
// Overrides of net::URLRequest::Delegate
void OnReceivedRedirect(net::URLRequest* request,
const net::RedirectInfo& info,
bool* defer_redirect) override;
void OnAuthRequired(net::URLRequest* request,
net::AuthChallengeInfo* auth_info) override;
void OnResponseStarted(net::URLRequest* request) override;
@ -60,11 +65,13 @@ class AtomURLRequest : public base::RefCountedThreadSafe<AtomURLRequest>,
void DoInitialize(scoped_refptr<net::URLRequestContextGetter>,
const std::string& method,
const std::string& url);
const std::string& url,
const std::string& redirect_policy);
void DoTerminate();
void DoWriteBuffer(scoped_refptr<const net::IOBufferWithSize> buffer,
bool is_last);
void DoCancel();
void DoFollowRedirect();
void DoSetExtraHeader(const std::string& name,
const std::string& value) const;
void DoRemoveExtraHeader(const std::string& name) const;
@ -77,6 +84,11 @@ class AtomURLRequest : public base::RefCountedThreadSafe<AtomURLRequest>,
void ReadResponse();
bool CopyAndPostBuffer(int bytes_read);
void InformDelegateReceivedRedirect(
int status_code,
const std::string& method,
const GURL& url,
scoped_refptr<net::HttpResponseHeaders> response_headers) const;
void InformDelegateAuthenticationRequired(
scoped_refptr<net::AuthChallengeInfo> auth_info) const;
void InformDelegateResponseStarted(
@ -92,6 +104,7 @@ class AtomURLRequest : public base::RefCountedThreadSafe<AtomURLRequest>,
scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
bool is_chunked_upload_;
std::string redirect_policy_;
std::unique_ptr<net::ChunkedUploadDataStream> chunked_stream_;
std::unique_ptr<net::ChunkedUploadDataStream::Writer> chunked_stream_writer_;
std::vector<std::unique_ptr<net::UploadElementReader>>

View file

@ -164,7 +164,7 @@ void NodeDebugger::DidRead(net::test_server::StreamListenSocket* socket,
buffer_.append(data, len);
do {
if (buffer_.size() == 0)
if (buffer_.empty())
return;
// Read the "Content-Length" header.

View file

@ -851,12 +851,12 @@ void OffScreenRenderWidgetHostView::SetupFrameRate(bool force) {
GetCompositor()->vsync_manager()->SetAuthoritativeVSyncInterval(
base::TimeDelta::FromMilliseconds(frame_rate_threshold_ms_));
if (copy_frame_generator_.get()) {
if (copy_frame_generator_) {
copy_frame_generator_->set_frame_rate_threshold_ms(
frame_rate_threshold_ms_);
}
if (begin_frame_timer_.get()) {
if (begin_frame_timer_) {
begin_frame_timer_->SetFrameRateThresholdMs(frame_rate_threshold_ms_);
} else {
begin_frame_timer_.reset(new AtomBeginFrameTimer(
@ -871,7 +871,7 @@ void OffScreenRenderWidgetHostView::Invalidate() {
if (software_output_device_) {
software_output_device_->OnPaint(bounds_in_pixels);
} else if (copy_frame_generator_.get()) {
} else if (copy_frame_generator_) {
copy_frame_generator_->GenerateCopyFrame(true, bounds_in_pixels);
}
}

View file

@ -145,4 +145,4 @@ OffScreenRenderWidgetHostView::GetDelegatedFrameHost() const {
return browser_compositor_->GetDelegatedFrameHost();
}
} // namespace
} // namespace atom

View file

@ -17,9 +17,9 @@
<key>CFBundleIconFile</key>
<string>electron.icns</string>
<key>CFBundleVersion</key>
<string>1.6.4</string>
<string>1.6.5</string>
<key>CFBundleShortVersionString</key>
<string>1.6.4</string>
<string>1.6.5</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.developer-tools</string>
<key>LSMinimumSystemVersion</key>

View file

@ -56,8 +56,8 @@ END
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,6,4,0
PRODUCTVERSION 1,6,4,0
FILEVERSION 1,6,5,0
PRODUCTVERSION 1,6,5,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@ -74,12 +74,12 @@ BEGIN
BEGIN
VALUE "CompanyName", "GitHub, Inc."
VALUE "FileDescription", "Electron"
VALUE "FileVersion", "1.6.4"
VALUE "FileVersion", "1.6.5"
VALUE "InternalName", "electron.exe"
VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved."
VALUE "OriginalFilename", "electron.exe"
VALUE "ProductName", "Electron"
VALUE "ProductVersion", "1.6.4"
VALUE "ProductVersion", "1.6.5"
VALUE "SquirrelAwareVersion", "1"
END
END

View file

@ -70,7 +70,7 @@ Role kRolesMap[] = {
// while its context menu is still open.
[self cancel];
model_ = NULL;
model_ = nullptr;
[super dealloc];
}

View file

@ -319,7 +319,7 @@ static NSString* const ImageScrubberItemIdentifier = @"scrubber.image.item";
- (void)updateColorPicker:(NSColorPickerTouchBarItem*)item
withSettings:(const mate::PersistentDictionary&)settings {
std::vector<std::string> colors;
if (settings.Get("availableColors", &colors) && colors.size() > 0) {
if (settings.Get("availableColors", &colors) && !colors.empty()) {
NSColorList* color_list = [[[NSColorList alloc] initWithName:@""] autorelease];
for (size_t i = 0; i < colors.size(); ++i) {
[color_list insertColor:[self colorFromHexColorString:colors[i]]
@ -414,7 +414,7 @@ static NSString* const ImageScrubberItemIdentifier = @"scrubber.image.item";
NSMutableArray* generatedItems = [NSMutableArray array];
NSMutableArray* identifiers = [self identifiersFromSettings:items];
for (NSUInteger i = 0; i < [identifiers count]; i++) {
for (NSUInteger i = 0; i < [identifiers count]; ++i) {
if ([identifiers objectAtIndex:i] != NSTouchBarItemIdentifierOtherItemsProxy) {
NSTouchBarItem* generatedItem = [self makeItemForIdentifier:[identifiers objectAtIndex:i]];
if (generatedItem) {
@ -474,7 +474,7 @@ static NSString* const ImageScrubberItemIdentifier = @"scrubber.image.item";
settings.Get("segments", &segments);
control.segmentCount = segments.size();
for (int i = 0; i < (int)segments.size(); i++) {
for (size_t i = 0; i < segments.size(); ++i) {
std::string label;
gfx::Image image;
bool enabled = true;
@ -581,7 +581,7 @@ static NSString* const ImageScrubberItemIdentifier = @"scrubber.image.item";
std::vector<mate::PersistentDictionary> items;
if (!settings.Get("items", &items)) return nil;
if (index >= (long)items.size()) return nil;
if (index >= static_cast<NSInteger>(items.size())) return nil;
mate::PersistentDictionary item = items[index];

View file

@ -154,7 +154,7 @@ void ShowOpenDialog(const DialogSettings& settings,
NSWindow* window = settings.parent_window ?
settings.parent_window->GetNativeWindow() :
NULL;
nullptr;
[dialog beginSheetModalForWindow:window
completionHandler:^(NSInteger chosen) {
if (chosen == NSFileHandlingPanelCancelButton) {
@ -193,7 +193,7 @@ void ShowSaveDialog(const DialogSettings& settings,
NSWindow* window = settings.parent_window ?
settings.parent_window->GetNativeWindow() :
NULL;
nullptr;
[dialog beginSheetModalForWindow:window
completionHandler:^(NSInteger chosen) {
if (chosen == NSFileHandlingPanelCancelButton) {

View file

@ -16,21 +16,10 @@
namespace atom {
namespace {
// Filter out the "&" in menu label.
base::string16 FilterAccelerator(const base::string16& label) {
base::string16 out;
base::RemoveChars(label, base::ASCIIToUTF16("&").c_str(), &out);
return out;
}
} // namespace
SubmenuButton::SubmenuButton(const base::string16& title,
views::MenuButtonListener* menu_button_listener,
const SkColor& background_color)
: views::MenuButton(FilterAccelerator(title),
: views::MenuButton(gfx::RemoveAcceleratorChar(title, '&', NULL, NULL),
menu_button_listener, false),
accelerator_(0),
show_underline_(false),

View file

@ -66,8 +66,9 @@ void WebContentsPermissionHelper::RequestPermission(
void WebContentsPermissionHelper::RequestFullscreenPermission(
const base::Callback<void(bool)>& callback) {
RequestPermission((content::PermissionType)(PermissionType::FULLSCREEN),
callback);
RequestPermission(
static_cast<content::PermissionType>(PermissionType::FULLSCREEN),
callback);
}
void WebContentsPermissionHelper::RequestMediaAccessPermission(
@ -86,17 +87,17 @@ void WebContentsPermissionHelper::RequestWebNotificationPermission(
void WebContentsPermissionHelper::RequestPointerLockPermission(
bool user_gesture) {
RequestPermission((content::PermissionType)(PermissionType::POINTER_LOCK),
base::Bind(&OnPointerLockResponse, web_contents_),
user_gesture);
RequestPermission(
static_cast<content::PermissionType>(PermissionType::POINTER_LOCK),
base::Bind(&OnPointerLockResponse, web_contents_), user_gesture);
}
void WebContentsPermissionHelper::RequestOpenExternalPermission(
const base::Callback<void(bool)>& callback,
bool user_gesture) {
RequestPermission((content::PermissionType)(PermissionType::OPEN_EXTERNAL),
callback,
user_gesture);
RequestPermission(
static_cast<content::PermissionType>(PermissionType::OPEN_EXTERNAL),
callback, user_gesture);
}
} // namespace atom

View file

@ -51,7 +51,7 @@ class FileSelectHelper : public base::RefCounted<FileSelectHelper>,
private:
friend class base::RefCounted<FileSelectHelper>;
~FileSelectHelper() {}
~FileSelectHelper() override {}
void OnOpenDialogDone(bool result, const std::vector<base::FilePath>& paths) {
std::vector<content::FileChooserFileInfo> file_info;

View file

@ -46,7 +46,7 @@ void WindowList::RemoveWindow(NativeWindow* window) {
for (WindowListObserver& observer : observers_.Get())
observer.OnWindowRemoved(window);
if (windows.size() == 0) {
if (windows.empty()) {
for (WindowListObserver& observer : observers_.Get())
observer.OnWindowAllClosed();
}

View file

@ -7,7 +7,7 @@
#define ATOM_MAJOR_VERSION 1
#define ATOM_MINOR_VERSION 6
#define ATOM_PATCH_VERSION 4
#define ATOM_PATCH_VERSION 5
#define ATOM_VERSION_IS_RELEASE 1

View file

@ -179,7 +179,7 @@ void CrashReporterWin::InitBreakpad(const std::string& product_name,
google_breakpad::ExceptionHandler::HANDLER_ALL,
kSmallDumpType,
pipe_name.c_str(),
GetCustomInfo(product_name, version, company_name)));
GetCustomInfo(product_name, version, company_name, upload_to_server)));
if (!breakpad_->IsOutOfProcess())
LOG(ERROR) << "Cannot initialize out-of-process crash handler";
@ -238,14 +238,19 @@ bool CrashReporterWin::MinidumpCallback(const wchar_t* dump_path,
google_breakpad::CustomClientInfo* CrashReporterWin::GetCustomInfo(
const std::string& product_name,
const std::string& version,
const std::string& company_name) {
const std::string& company_name,
bool upload_to_server) {
custom_info_entries_.clear();
custom_info_entries_.reserve(2 + upload_parameters_.size());
custom_info_entries_.reserve(3 + upload_parameters_.size());
custom_info_entries_.push_back(google_breakpad::CustomInfoEntry(
L"prod", L"Electron"));
custom_info_entries_.push_back(google_breakpad::CustomInfoEntry(
L"ver", base::UTF8ToWide(version).c_str()));
if (!upload_to_server) {
custom_info_entries_.push_back(google_breakpad::CustomInfoEntry(
L"skip_upload", L"1"));
}
for (StringMap::const_iterator iter = upload_parameters_.begin();
iter != upload_parameters_.end(); ++iter) {

View file

@ -56,7 +56,8 @@ class CrashReporterWin : public CrashReporter {
google_breakpad::CustomClientInfo* GetCustomInfo(
const std::string& product_name,
const std::string& version,
const std::string& company_name);
const std::string& company_name,
bool upload_to_server);
// Custom information to be passed to crash handler.
std::vector<google_breakpad::CustomInfoEntry> custom_info_entries_;

View file

@ -391,7 +391,7 @@ void CrashService::OnClientDumpRequest(void* context,
LOG(ERROR) << "could not write custom info file";
}
if (!self->sender_)
if (!self->sender_ || map.find(L"skip_upload") != map.end())
return;
// Send the crash dump using a worker thread. This operation has retry

View file

@ -168,11 +168,13 @@ v8::Local<v8::Value> Converter<content::PermissionType>::ToV8(
break;
}
if (val == (content::PermissionType)(PermissionType::POINTER_LOCK))
if (val == static_cast<content::PermissionType>(PermissionType::POINTER_LOCK))
return StringToV8(isolate, "pointerLock");
else if (val == (content::PermissionType)(PermissionType::FULLSCREEN))
else if (val ==
static_cast<content::PermissionType>(PermissionType::FULLSCREEN))
return StringToV8(isolate, "fullscreen");
else if (val == (content::PermissionType)(PermissionType::OPEN_EXTERNAL))
else if (val ==
static_cast<content::PermissionType>(PermissionType::OPEN_EXTERNAL))
return StringToV8(isolate, "openExternal");
return StringToV8(isolate, "unknown");

View file

@ -232,6 +232,12 @@ void NodeBindings::RunMessageLoop() {
void NodeBindings::UvRunOnce() {
node::Environment* env = uv_env();
// When doing navigation without restarting renderer process, it may happen
// that the node environment is destroyed but the message loop is still there.
// In this case we should not run uv loop.
if (!env)
return;
// Use Locker in browser process.
mate::Locker locker(env->isolate());
v8::HandleScope handle_scope(env->isolate());

View file

@ -51,6 +51,9 @@ const char kZoomToPageWidth[] = "zoomToPageWidth";
// The requested title bar style for the window
const char kTitleBarStyle[] = "titleBarStyle";
// Tabbing identifier for the window if native tabs are enabled on macOS.
const char kTabbingIdentifier[] = "tabbingIdentifier";
// The menu bar is hidden unless "Alt" is pressed.
const char kAutoHideMenuBar[] = "autoHideMenuBar";

View file

@ -36,6 +36,7 @@ extern const char kAcceptFirstMouse[];
extern const char kUseContentSize[];
extern const char kZoomToPageWidth[];
extern const char kTitleBarStyle[];
extern const char kTabbingIdentifier[];
extern const char kAutoHideMenuBar[];
extern const char kEnableLargerThanScreen[];
extern const char kDarkTheme[];

View file

@ -7,7 +7,9 @@
#include <stdio.h>
#include "base/cancelable_callback.h"
#include "base/environment.h"
#include "base/files/file_util.h"
#include "base/nix/xdg_util.h"
#include "base/process/kill.h"
#include "base/process/launch.h"
#include "url/gurl.h"
@ -100,7 +102,18 @@ bool MoveItemToTrash(const base::FilePath& full_path) {
if (getenv(ELECTRON_TRASH) != NULL) {
trash = getenv(ELECTRON_TRASH);
} else {
trash = ELECTRON_DEFAULT_TRASH;
// Determine desktop environment and set accordingly.
std::unique_ptr<base::Environment> env(base::Environment::Create());
base::nix::DesktopEnvironment desktop_env(
base::nix::GetDesktopEnvironment(env.get()));
if (desktop_env == base::nix::DESKTOP_ENVIRONMENT_KDE4 ||
desktop_env == base::nix::DESKTOP_ENVIRONMENT_KDE5) {
trash = "kioclient5";
} else if (desktop_env == base::nix::DESKTOP_ENVIRONMENT_KDE3) {
trash = "kioclient";
} else {
trash = ELECTRON_DEFAULT_TRASH;
}
}
std::vector<std::string> argv;

View file

@ -11,6 +11,7 @@
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/mac/foundation_util.h"
#include "base/mac/mac_logging.h"
#include "base/mac/scoped_aedesc.h"
#include "base/strings/stringprintf.h"
@ -71,10 +72,10 @@ std::string MessageForOSStatus(OSStatus status, const char* default_message) {
// thread safe, including LSGetApplicationForURL (> 10.2) and
// NSWorkspace#openURLs.
std::string OpenURL(NSURL* ns_url, bool activate) {
CFURLRef openingApp = NULL;
OSStatus status = LSGetApplicationForURL((CFURLRef)ns_url,
CFURLRef openingApp = nullptr;
OSStatus status = LSGetApplicationForURL(base::mac::NSToCFCast(ns_url),
kLSRolesAll,
NULL,
nullptr,
&openingApp);
if (status != noErr)
return MessageForOSStatus(status, "Failed to open");
@ -156,7 +157,7 @@ bool OpenItem(const base::FilePath& full_path) {
// Create the list of files (only ever one) to open.
base::mac::ScopedAEDesc<AEDescList> fileList;
status = AECreateList(NULL, // factoringPtr
status = AECreateList(nullptr, // factoringPtr
0, // factoredSize
false, // isRecord
fileList.OutPointer()); // resultList
@ -167,7 +168,8 @@ bool OpenItem(const base::FilePath& full_path) {
// Add the single path to the file list. C-style cast to avoid both a
// static_cast and a const_cast to get across the toll-free bridge.
CFURLRef pathURLRef = (CFURLRef)[NSURL fileURLWithPath:path_string];
CFURLRef pathURLRef = base::mac::NSToCFCast(
[NSURL fileURLWithPath:path_string]);
FSRef pathRef;
if (CFURLGetFSRef(pathURLRef, &pathRef)) {
status = AEPutPtr(fileList.OutPointer(), // theAEDescList
@ -202,8 +204,8 @@ bool OpenItem(const base::FilePath& full_path) {
kAENoReply + kAEAlwaysInteract, // sendMode
kAENormalPriority, // sendPriority
kAEDefaultTimeout, // timeOutInTicks
NULL, // idleProc
NULL); // filterProc
nullptr, // idleProc
nullptr); // filterProc
if (status != noErr) {
OSSTATUS_LOG(WARNING, status)
<< "Could not send AE to Finder in OpenItem()";

View file

@ -9,53 +9,23 @@
#include "atom_natives.h" // NOLINT: This file is generated with js2c
#include "atom/common/api/api_messages.h"
#include "atom/common/api/atom_bindings.h"
#include "atom/common/api/event_emitter_caller.h"
#include "atom/common/asar/asar_util.h"
#include "atom/common/atom_constants.h"
#include "atom/common/color_util.h"
#include "atom/common/native_mate_converters/value_converter.h"
#include "atom/common/node_bindings.h"
#include "atom/common/options_switches.h"
#include "atom/renderer/api/atom_api_renderer_ipc.h"
#include "atom/renderer/atom_render_view_observer.h"
#include "atom/renderer/content_settings_observer.h"
#include "atom/renderer/guest_view_container.h"
#include "atom/renderer/node_array_buffer_bridge.h"
#include "atom/renderer/preferences_manager.h"
#include "atom/renderer/web_worker_observer.h"
#include "base/command_line.h"
#include "chrome/renderer/media/chrome_key_systems.h"
#include "chrome/renderer/pepper/pepper_helper.h"
#include "chrome/renderer/printing/print_web_view_helper.h"
#include "chrome/renderer/tts_dispatcher.h"
#include "content/public/common/content_constants.h"
#include "content/public/renderer/render_frame.h"
#include "content/public/renderer/render_frame_observer.h"
#include "content/public/renderer/render_thread.h"
#include "content/public/renderer/render_view.h"
#include "ipc/ipc_message_macros.h"
#include "native_mate/dictionary.h"
#include "third_party/WebKit/public/web/WebCustomElement.h"
#include "third_party/WebKit/public/web/WebDocument.h"
#include "third_party/WebKit/public/web/WebFrameWidget.h"
#include "third_party/WebKit/public/web/WebKit.h"
#include "third_party/WebKit/public/web/WebLocalFrame.h"
#include "third_party/WebKit/public/web/WebPluginParams.h"
#include "third_party/WebKit/public/web/WebRuntimeFeatures.h"
#include "third_party/WebKit/public/web/WebScriptSource.h"
#include "third_party/WebKit/public/web/WebSecurityPolicy.h"
#include "third_party/WebKit/public/web/WebView.h"
#if defined(OS_MACOSX)
#include "base/mac/mac_util.h"
#include "base/strings/sys_string_conversions.h"
#endif
#if defined(OS_WIN)
#include <shlobj.h>
#endif
#include "atom/common/node_includes.h"
@ -115,7 +85,7 @@ class AtomRenderFrameObserver : public content::RenderFrameObserver {
// an argument.
std::string bundle(node::isolated_bundle_data,
node::isolated_bundle_data + sizeof(node::isolated_bundle_data));
std::string wrapper = "(function (binding) {\n" + bundle + "\n})";
std::string wrapper = "(function (binding, require) {\n" + bundle + "\n})";
auto script = v8::Script::Compile(
mate::ConvertToV8(isolate, wrapper)->ToString());
auto func = v8::Handle<v8::Function>::Cast(
@ -184,35 +154,11 @@ class AtomRenderFrameObserver : public content::RenderFrameObserver {
DISALLOW_COPY_AND_ASSIGN(AtomRenderFrameObserver);
};
v8::Local<v8::Value> GetRenderProcessPreferences(
const PreferencesManager* preferences_manager, v8::Isolate* isolate) {
if (preferences_manager->preferences())
return mate::ConvertToV8(isolate, *preferences_manager->preferences());
else
return v8::Null(isolate);
}
void AddRenderBindings(v8::Isolate* isolate,
v8::Local<v8::Object> process,
const PreferencesManager* preferences_manager) {
mate::Dictionary dict(isolate, process);
dict.SetMethod(
"getRenderProcessPreferences",
base::Bind(GetRenderProcessPreferences, preferences_manager));
}
bool IsDevToolsExtension(content::RenderFrame* render_frame) {
return static_cast<GURL>(render_frame->GetWebFrame()->document().url())
.SchemeIs("chrome-extension");
}
std::vector<std::string> ParseSchemesCLISwitch(const char* switch_name) {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
std::string custom_schemes = command_line->GetSwitchValueASCII(switch_name);
return base::SplitString(
custom_schemes, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
}
} // namespace
AtomRendererClient::AtomRendererClient()
@ -221,11 +167,6 @@ AtomRendererClient::AtomRendererClient()
atom_bindings_(new AtomBindings(uv_default_loop())) {
isolated_world_ = base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kContextIsolation);
// Parse --standard-schemes=scheme1,scheme2
std::vector<std::string> standard_schemes_list =
ParseSchemesCLISwitch(switches::kStandardSchemes);
for (const std::string& scheme : standard_schemes_list)
url::AddStandardScheme(scheme.c_str(), url::SCHEME_WITHOUT_PORT);
}
AtomRendererClient::~AtomRendererClient() {
@ -233,80 +174,19 @@ AtomRendererClient::~AtomRendererClient() {
}
void AtomRendererClient::RenderThreadStarted() {
blink::WebCustomElement::addEmbedderCustomElementName("webview");
blink::WebCustomElement::addEmbedderCustomElementName("browserplugin");
OverrideNodeArrayBuffer();
preferences_manager_.reset(new PreferencesManager);
#if defined(OS_WIN)
// Set ApplicationUserModelID in renderer process.
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
base::string16 app_id =
command_line->GetSwitchValueNative(switches::kAppUserModelId);
if (!app_id.empty()) {
SetCurrentProcessExplicitAppUserModelID(app_id.c_str());
}
#endif
#if defined(OS_MACOSX)
// Disable rubber banding by default.
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (!command_line->HasSwitch(switches::kScrollBounce)) {
base::ScopedCFTypeRef<CFStringRef> key(
base::SysUTF8ToCFStringRef("NSScrollViewRubberbanding"));
base::ScopedCFTypeRef<CFStringRef> value(
base::SysUTF8ToCFStringRef("false"));
CFPreferencesSetAppValue(key, value, kCFPreferencesCurrentApplication);
CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication);
}
#endif
RendererClientBase::RenderThreadStarted();
}
void AtomRendererClient::RenderFrameCreated(
content::RenderFrame* render_frame) {
new PepperHelper(render_frame);
new AtomRenderFrameObserver(render_frame, this);
new ContentSettingsObserver(render_frame);
new printing::PrintWebViewHelper(render_frame);
// Allow file scheme to handle service worker by default.
// FIXME(zcbenz): Can this be moved elsewhere?
blink::WebSecurityPolicy::registerURLSchemeAsAllowingServiceWorkers("file");
// This is required for widevine plugin detection provided during runtime.
blink::resetPluginCache();
// Allow access to file scheme from pdf viewer.
blink::WebSecurityPolicy::addOriginAccessWhitelistEntry(
GURL(kPdfViewerUIOrigin), "file", "", true);
// Parse --secure-schemes=scheme1,scheme2
std::vector<std::string> secure_schemes_list =
ParseSchemesCLISwitch(switches::kSecureSchemes);
for (const std::string& secure_scheme : secure_schemes_list)
blink::WebSecurityPolicy::registerURLSchemeAsSecure(
blink::WebString::fromUTF8(secure_scheme));
RendererClientBase::RenderFrameCreated(render_frame);
}
void AtomRendererClient::RenderViewCreated(content::RenderView* render_view) {
new AtomRenderViewObserver(render_view, this);
blink::WebFrameWidget* web_frame_widget = render_view->GetWebFrameWidget();
if (!web_frame_widget)
return;
base::CommandLine* cmd = base::CommandLine::ForCurrentProcess();
if (cmd->HasSwitch(switches::kGuestInstanceID)) { // webview.
web_frame_widget->setBaseBackgroundColor(SK_ColorTRANSPARENT);
} else { // normal window.
// If backgroundColor is specified then use it.
std::string name = cmd->GetSwitchValueASCII(switches::kBackgroundColor);
// Otherwise use white background.
SkColor color = name.empty() ? SK_ColorWHITE : ParseHexColor(name);
web_frame_widget->setBaseBackgroundColor(color);
}
RendererClientBase::RenderViewCreated(render_view);
}
void AtomRendererClient::DidClearWindowObject(
@ -335,26 +215,6 @@ void AtomRendererClient::RunScriptsAtDocumentEnd(
}
}
blink::WebSpeechSynthesizer* AtomRendererClient::OverrideSpeechSynthesizer(
blink::WebSpeechSynthesizerClient* client) {
return new TtsDispatcher(client);
}
bool AtomRendererClient::OverrideCreatePlugin(
content::RenderFrame* render_frame,
blink::WebLocalFrame* frame,
const blink::WebPluginParams& params,
blink::WebPlugin** plugin) {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (params.mimeType.utf8() == content::kBrowserPluginMimeType ||
params.mimeType.utf8() == kPdfPluginMimeType ||
command_line->HasSwitch(switches::kEnablePlugins))
return false;
*plugin = nullptr;
return true;
}
void AtomRendererClient::DidCreateScriptContext(
v8::Handle<v8::Context> context, content::RenderFrame* render_frame) {
// Only allow node integration for the main frame, unless it is a devtools
@ -374,8 +234,7 @@ void AtomRendererClient::DidCreateScriptContext(
// Add Electron extended APIs.
atom_bindings_->BindTo(env->isolate(), env->process_object());
AddRenderBindings(env->isolate(), env->process_object(),
preferences_manager_.get());
AddRenderBindings(env->isolate(), env->process_object());
// Load everything.
node_bindings_->LoadEnvironment(env);
@ -423,22 +282,6 @@ bool AtomRendererClient::ShouldFork(blink::WebLocalFrame* frame,
return http_method == "GET";
}
content::BrowserPluginDelegate* AtomRendererClient::CreateBrowserPluginDelegate(
content::RenderFrame* render_frame,
const std::string& mime_type,
const GURL& original_url) {
if (mime_type == content::kBrowserPluginMimeType) {
return new GuestViewContainer(render_frame);
} else {
return nullptr;
}
}
void AtomRendererClient::AddSupportedKeySystems(
std::vector<std::unique_ptr<::media::KeySystemProperties>>* key_systems) {
AddChromeKeySystems(key_systems);
}
void AtomRendererClient::DidInitializeWorkerContextOnWorkerThread(
v8::Local<v8::Context> context) {
if (base::CommandLine::ForCurrentProcess()->HasSwitch(

View file

@ -8,15 +8,14 @@
#include <string>
#include <vector>
#include "content/public/renderer/content_renderer_client.h"
#include "atom/renderer/renderer_client_base.h"
namespace atom {
class AtomBindings;
class PreferencesManager;
class NodeBindings;
class AtomRendererClient : public content::ContentRendererClient {
class AtomRendererClient : public RendererClientBase {
public:
AtomRendererClient();
virtual ~AtomRendererClient();
@ -46,25 +45,12 @@ class AtomRendererClient : public content::ContentRendererClient {
void RenderViewCreated(content::RenderView*) override;
void RunScriptsAtDocumentStart(content::RenderFrame* render_frame) override;
void RunScriptsAtDocumentEnd(content::RenderFrame* render_frame) override;
blink::WebSpeechSynthesizer* OverrideSpeechSynthesizer(
blink::WebSpeechSynthesizerClient* client) override;
bool OverrideCreatePlugin(content::RenderFrame* render_frame,
blink::WebLocalFrame* frame,
const blink::WebPluginParams& params,
blink::WebPlugin** plugin) override;
bool ShouldFork(blink::WebLocalFrame* frame,
const GURL& url,
const std::string& http_method,
bool is_initial_navigation,
bool is_server_redirect,
bool* send_referrer) override;
content::BrowserPluginDelegate* CreateBrowserPluginDelegate(
content::RenderFrame* render_frame,
const std::string& mime_type,
const GURL& original_url) override;
void AddSupportedKeySystems(
std::vector<std::unique_ptr<::media::KeySystemProperties>>* key_systems)
override;
void DidInitializeWorkerContextOnWorkerThread(
v8::Local<v8::Context> context) override;
void WillDestroyWorkerContextOnWorkerThread(
@ -75,7 +61,6 @@ class AtomRendererClient : public content::ContentRendererClient {
std::unique_ptr<NodeBindings> node_bindings_;
std::unique_ptr<AtomBindings> atom_bindings_;
std::unique_ptr<PreferencesManager> preferences_manager_;
bool isolated_world_;
DISALLOW_COPY_AND_ASSIGN(AtomRendererClient);

View file

@ -9,6 +9,7 @@
#include "atom_natives.h" // NOLINT: This file is generated with js2c
#include "atom/common/api/api_messages.h"
#include "atom/common/api/atom_bindings.h"
#include "atom/common/native_mate_converters/string16_converter.h"
#include "atom/common/native_mate_converters/v8_value_converter.h"
#include "atom/common/native_mate_converters/value_converter.h"
@ -85,6 +86,7 @@ void InitializeBindings(v8::Local<v8::Object> binding,
auto isolate = context->GetIsolate();
mate::Dictionary b(isolate, binding);
b.SetMethod("get", GetBinding);
b.SetMethod("crash", AtomBindings::Crash);
}
class AtomSandboxedRenderFrameObserver : public content::RenderFrameObserver {
@ -180,12 +182,13 @@ AtomSandboxedRendererClient::~AtomSandboxedRendererClient() {
void AtomSandboxedRendererClient::RenderFrameCreated(
content::RenderFrame* render_frame) {
new AtomSandboxedRenderFrameObserver(render_frame, this);
new printing::PrintWebViewHelper(render_frame);
RendererClientBase::RenderFrameCreated(render_frame);
}
void AtomSandboxedRendererClient::RenderViewCreated(
content::RenderView* render_view) {
new AtomSandboxedRenderViewObserver(render_view, this);
RendererClientBase::RenderViewCreated(render_view);
}
void AtomSandboxedRendererClient::DidCreateScriptContext(
@ -204,7 +207,7 @@ void AtomSandboxedRendererClient::DidCreateScriptContext(
std::string preload_bundle_native(node::preload_bundle_data,
node::preload_bundle_data + sizeof(node::preload_bundle_data));
std::stringstream ss;
ss << "(function(binding, preloadPath) {\n";
ss << "(function(binding, preloadPath, require) {\n";
ss << preload_bundle_native << "\n";
ss << "})";
std::string preload_wrapper = ss.str();
@ -216,6 +219,7 @@ void AtomSandboxedRendererClient::DidCreateScriptContext(
// Create and initialize the binding object
auto binding = v8::Object::New(isolate);
InitializeBindings(binding, context);
AddRenderBindings(isolate, binding);
v8::Local<v8::Value> args[] = {
binding,
mate::ConvertToV8(isolate, preload_script)
@ -234,7 +238,7 @@ void AtomSandboxedRendererClient::WillReleaseScriptContext(
void AtomSandboxedRendererClient::InvokeIpcCallback(
v8::Handle<v8::Context> context,
std::string callback_name,
const std::string& callback_name,
std::vector<v8::Handle<v8::Value>> args) {
auto isolate = context->GetIsolate();
auto binding_key = mate::ConvertToV8(isolate, kIpcKey)->ToString();

View file

@ -7,12 +7,11 @@
#include <string>
#include <vector>
#include "content/public/renderer/content_renderer_client.h"
#include "content/public/renderer/render_frame.h"
#include "atom/renderer/renderer_client_base.h"
namespace atom {
class AtomSandboxedRendererClient : public content::ContentRendererClient {
class AtomSandboxedRendererClient : public RendererClientBase {
public:
AtomSandboxedRendererClient();
virtual ~AtomSandboxedRendererClient();
@ -22,7 +21,7 @@ class AtomSandboxedRendererClient : public content::ContentRendererClient {
void WillReleaseScriptContext(
v8::Handle<v8::Context> context, content::RenderFrame* render_frame);
void InvokeIpcCallback(v8::Handle<v8::Context> context,
std::string callback_name,
const std::string& callback_name,
std::vector<v8::Handle<v8::Value>> args);
// content::ContentRendererClient:
void RenderFrameCreated(content::RenderFrame*) override;

View file

@ -0,0 +1,190 @@
// 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 "atom/renderer/renderer_client_base.h"
#include <string>
#include <vector>
#include "atom/common/atom_constants.h"
#include "atom/common/color_util.h"
#include "atom/common/native_mate_converters/value_converter.h"
#include "atom/common/options_switches.h"
#include "atom/renderer/content_settings_observer.h"
#include "atom/renderer/guest_view_container.h"
#include "atom/renderer/preferences_manager.h"
#include "base/command_line.h"
#include "base/strings/string_split.h"
#include "chrome/renderer/media/chrome_key_systems.h"
#include "chrome/renderer/pepper/pepper_helper.h"
#include "chrome/renderer/printing/print_web_view_helper.h"
#include "chrome/renderer/tts_dispatcher.h"
#include "content/public/common/content_constants.h"
#include "content/public/renderer/render_view.h"
#include "native_mate/dictionary.h"
#include "third_party/WebKit/public/web/WebCustomElement.h"
#include "third_party/WebKit/public/web/WebFrameWidget.h"
#include "third_party/WebKit/public/web/WebKit.h"
#include "third_party/WebKit/public/web/WebPluginParams.h"
#include "third_party/WebKit/public/web/WebSecurityPolicy.h"
#if defined(OS_MACOSX)
#include "base/mac/mac_util.h"
#include "base/strings/sys_string_conversions.h"
#endif
#if defined(OS_WIN)
#include <shlobj.h>
#endif
namespace atom {
namespace {
v8::Local<v8::Value> GetRenderProcessPreferences(
const PreferencesManager* preferences_manager, v8::Isolate* isolate) {
if (preferences_manager->preferences())
return mate::ConvertToV8(isolate, *preferences_manager->preferences());
else
return v8::Null(isolate);
}
std::vector<std::string> ParseSchemesCLISwitch(const char* switch_name) {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
std::string custom_schemes = command_line->GetSwitchValueASCII(switch_name);
return base::SplitString(
custom_schemes, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
}
} // namespace
RendererClientBase::RendererClientBase() {
// Parse --standard-schemes=scheme1,scheme2
std::vector<std::string> standard_schemes_list =
ParseSchemesCLISwitch(switches::kStandardSchemes);
for (const std::string& scheme : standard_schemes_list)
url::AddStandardScheme(scheme.c_str(), url::SCHEME_WITHOUT_PORT);
}
RendererClientBase::~RendererClientBase() {
}
void RendererClientBase::AddRenderBindings(
v8::Isolate* isolate,
v8::Local<v8::Object> binding_object) {
mate::Dictionary dict(isolate, binding_object);
dict.SetMethod(
"getRenderProcessPreferences",
base::Bind(GetRenderProcessPreferences, preferences_manager_.get()));
}
void RendererClientBase::RenderThreadStarted() {
blink::WebCustomElement::addEmbedderCustomElementName("webview");
blink::WebCustomElement::addEmbedderCustomElementName("browserplugin");
preferences_manager_.reset(new PreferencesManager);
#if defined(OS_WIN)
// Set ApplicationUserModelID in renderer process.
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
base::string16 app_id =
command_line->GetSwitchValueNative(switches::kAppUserModelId);
if (!app_id.empty()) {
SetCurrentProcessExplicitAppUserModelID(app_id.c_str());
}
#endif
#if defined(OS_MACOSX)
// Disable rubber banding by default.
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (!command_line->HasSwitch(switches::kScrollBounce)) {
base::ScopedCFTypeRef<CFStringRef> key(
base::SysUTF8ToCFStringRef("NSScrollViewRubberbanding"));
base::ScopedCFTypeRef<CFStringRef> value(
base::SysUTF8ToCFStringRef("false"));
CFPreferencesSetAppValue(key, value, kCFPreferencesCurrentApplication);
CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication);
}
#endif
}
void RendererClientBase::RenderFrameCreated(
content::RenderFrame* render_frame) {
new PepperHelper(render_frame);
new ContentSettingsObserver(render_frame);
new printing::PrintWebViewHelper(render_frame);
// Allow file scheme to handle service worker by default.
// FIXME(zcbenz): Can this be moved elsewhere?
blink::WebSecurityPolicy::registerURLSchemeAsAllowingServiceWorkers("file");
// This is required for widevine plugin detection provided during runtime.
blink::resetPluginCache();
// Allow access to file scheme from pdf viewer.
blink::WebSecurityPolicy::addOriginAccessWhitelistEntry(
GURL(kPdfViewerUIOrigin), "file", "", true);
// Parse --secure-schemes=scheme1,scheme2
std::vector<std::string> secure_schemes_list =
ParseSchemesCLISwitch(switches::kSecureSchemes);
for (const std::string& secure_scheme : secure_schemes_list)
blink::WebSecurityPolicy::registerURLSchemeAsSecure(
blink::WebString::fromUTF8(secure_scheme));
}
void RendererClientBase::RenderViewCreated(content::RenderView* render_view) {
blink::WebFrameWidget* web_frame_widget = render_view->GetWebFrameWidget();
if (!web_frame_widget)
return;
base::CommandLine* cmd = base::CommandLine::ForCurrentProcess();
if (cmd->HasSwitch(switches::kGuestInstanceID)) { // webview.
web_frame_widget->setBaseBackgroundColor(SK_ColorTRANSPARENT);
} else { // normal window.
// If backgroundColor is specified then use it.
std::string name = cmd->GetSwitchValueASCII(switches::kBackgroundColor);
// Otherwise use white background.
SkColor color = name.empty() ? SK_ColorWHITE : ParseHexColor(name);
web_frame_widget->setBaseBackgroundColor(color);
}
}
blink::WebSpeechSynthesizer* RendererClientBase::OverrideSpeechSynthesizer(
blink::WebSpeechSynthesizerClient* client) {
return new TtsDispatcher(client);
}
bool RendererClientBase::OverrideCreatePlugin(
content::RenderFrame* render_frame,
blink::WebLocalFrame* frame,
const blink::WebPluginParams& params,
blink::WebPlugin** plugin) {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (params.mimeType.utf8() == content::kBrowserPluginMimeType ||
params.mimeType.utf8() == kPdfPluginMimeType ||
command_line->HasSwitch(switches::kEnablePlugins))
return false;
*plugin = nullptr;
return true;
}
content::BrowserPluginDelegate* RendererClientBase::CreateBrowserPluginDelegate(
content::RenderFrame* render_frame,
const std::string& mime_type,
const GURL& original_url) {
if (mime_type == content::kBrowserPluginMimeType) {
return new GuestViewContainer(render_frame);
} else {
return nullptr;
}
}
void RendererClientBase::AddSupportedKeySystems(
std::vector<std::unique_ptr<::media::KeySystemProperties>>* key_systems) {
AddChromeKeySystems(key_systems);
}
} // namespace atom

View file

@ -0,0 +1,50 @@
// 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_RENDERER_RENDERER_CLIENT_BASE_H_
#define ATOM_RENDERER_RENDERER_CLIENT_BASE_H_
#include <string>
#include <vector>
#include "content/public/renderer/content_renderer_client.h"
namespace atom {
class PreferencesManager;
class RendererClientBase : public content::ContentRendererClient {
public:
RendererClientBase();
virtual ~RendererClientBase();
protected:
void AddRenderBindings(v8::Isolate* isolate,
v8::Local<v8::Object> binding_object);
// content::ContentRendererClient:
void RenderThreadStarted() override;
void RenderFrameCreated(content::RenderFrame*) override;
void RenderViewCreated(content::RenderView*) override;
blink::WebSpeechSynthesizer* OverrideSpeechSynthesizer(
blink::WebSpeechSynthesizerClient* client) override;
bool OverrideCreatePlugin(content::RenderFrame* render_frame,
blink::WebLocalFrame* frame,
const blink::WebPluginParams& params,
blink::WebPlugin** plugin) override;
content::BrowserPluginDelegate* CreateBrowserPluginDelegate(
content::RenderFrame* render_frame,
const std::string& mime_type,
const GURL& original_url) override;
void AddSupportedKeySystems(
std::vector<std::unique_ptr<::media::KeySystemProperties>>* key_systems)
override;
private:
std::unique_ptr<PreferencesManager> preferences_manager_;
};
} // namespace atom
#endif // ATOM_RENDERER_RENDERER_CLIENT_BASE_H_

View file

@ -356,6 +356,27 @@ Windows, 使应用的第一个窗口获取焦点.
* `pictures` 用户图片目录的路径.
* `videos` 用户视频目录的路径.
### `app.getFileIcon(path[, options], callback)`
* `path` String
* `options` Object(可选)
* `size` String
* `small` - 16x16
* `normal` - 32x32
* `large` - Linux 为 48x48, Windows 为 32x32, Mac 系统不支持
* `callback` Function
* `error` Error
* `icon` [NativeImage](native-image.md)
获取文件关联的图标.
在 Windows 系统中, 有2种图标类型:
- 图标与某些文件扩展名关联, 比如 `.mp3`, `.png`, 等等.
- 图标在文件内部, 比如 `.exe`, `.dll`, `.ico`.
在 Linux 和 Mac 系统中, 图标取决于应用程序相关文件的 mime 类型
### `app.setPath(name, path)`
* `name` String

View file

@ -107,20 +107,23 @@ console.log(dialog)
* `options` Object
* `type` String - 可以是 `"none"`, `"info"`, `"error"`, `"question"`
`"warning"`. 在 Windows, "question" 与 "info" 展示图标相同, 除非你使用 "icon" 参数.
* `buttons` Array - buttons 内容,数组.
* `defaultId` Integer - 在message box 对话框打开的时候设置默认button选中值为在 buttons 数组中的button索引.
* `title` String - message box 的标题,一些平台不显示.
* `message` String - message box 内容.
* `detail` String - 额外信息.
* `icon` [NativeImage](native-image.md)
* `cancelId` Integer - 当用户关闭对话框的时候不是通过点击对话框的button就返回值.默认值为对应 "cancel" 或 "no" 标签button 的索引值, 或者如果没有这种button就返回0. 在 macOS 和 Windows 上, "Cancel" button 的索引值将一直是 `cancelId`, 不管之前是不是特别指出的.
* `noLink` Boolean - 在 Windows Electron 将尝试识别哪个button 是普通 button (如 "Cancel" 或 "Yes"), 然后在对话框中以链接命令(command links)方式展现其它的 button . 这能让对话框展示得很炫酷.如果你不喜欢这种效果,你可以设置 `noLink``true`.
* `callback` Function (可选)
* `response` Number - The index of the button that was clicked
* `buttons` String[]- (可选) - 按钮上文字的数组,在 Windows 系统中,空数组在按钮上会显示 “OK”.
* `defaultId` Integer (可选) - 在 message box 对话框打开的时候,设置默认选中的按钮,值为在 buttons 数组中的索引.
* `title` String (可选) - message box 的标题,一些平台不显示.
* `message` String (可选) - message box 的内容.
* `detail` String (可选)- 额外信息.
* `checkboxLabel` String (可选) - 如果有该参数message box 中会显示一个 checkbox 复选框,它的勾选状态可以在 `callback` 回调方法中获取。
* `checkboxChecked` Boolean (可选) - checkbox 的初始值,默认为`false`.
* `icon` [NativeImage](native-image.md)(可选)
* `cancelId` Integer - 当用户不是通过按钮而是使用其他方式关闭对话框时,比如按`Esc`键,就返回该值.默认值为对应 "cancel" 或 "no" 标签 button 的索引值, 如果没有这种 button就返回0. 该选项在 Windows 上无效.
* `noLink` Boolean(可选) - 在 Windows 系统中Electron 将尝试识别哪个button 是普通 button (如 "Cancel" 或 "Yes"), 然后在对话框中以链接命令(command links)方式展现其它的 button . 这能让对话框展示得很炫酷.如果你不喜欢这种效果,你可以设置 `noLink``true`.
* `callback` Function (可选)
* `response` Number - 被点击按钮的索引值。
* `checkboxChecked` Boolean - 如果设置了 `checkboxLabel` ,会显示 checkbox 的选中状态,否则显示 `false`
返回 `Integer`,如果提供了回调,它会返回点击的按钮的索引或者 undefined 。
展示 message box, 它会阻塞进程,直到 message box 关闭为止.返回点击按钮的索引值。
显示 message box 时, 它会阻塞进程,直到 message box 关闭为止.返回点击按钮的索引值。
`browserWindow` 参数允许对话框将自身附加到父窗口,使其成为模态。

File diff suppressed because it is too large Load diff

View file

@ -5,7 +5,7 @@
版本号使用参照以下规则:
* 主要版本: 适用于 Electron API 的突破性变更 - 如果您从 `0.37.0` 升级到 `1.0.0`, 您将需要升级您的应用程序。
* 次要版本: 适用于 Chrome 主要版本 和 Node 次要版本升级; 或重大的 Electron 变动 - 如果您从 `0.37.0` 升级到 `1.0.0`, 您的应用程序仍然可以正常运行, 但你可能需要解决一些小幅的变动。
* 补丁版本: 适用于新功能的添加和 bug 修复 - 如果您从 `0.37.0` 升级到 `1.0.0`, 你的应用程序仍然像之前一样正常运行。
* 次要版本: 适用于 Chrome 主要版本 和 Node 次要版本升级; 或重大的 Electron 变动 - 如果您从 `1.0.0` 升级到 `1.1.0`, 您的应用程序仍然可以正常运行, 但你可能需要解决一些小幅的变动。
* 补丁版本: 适用于新功能的添加和 bug 修复 - 如果您从 `1.0.0` 升级到 `1.0.1`, 你的应用程序仍然像之前一样正常运行。
如果你使用 `electron``electron-prebuilt`,我们建议您设置固定的版本号(如 1.1.0 而不是 ^1.1.0以确保Electron的所有升级都是由您开发人员进行的手动操作。

View file

@ -103,3 +103,6 @@ an issue:
* [Debug Instructions (Windows)](development/debug-instructions-windows.md)
* [Setting Up Symbol Server in debugger](development/setting-up-symbol-server.md)
* [Documentation Styleguide](styleguide.md)
* [Updating Chrome](development/updating-chrome.md)
* [Chromium Development](development/chromium-development.md)
* [V8 Development](development/v8-development.md)

View file

@ -211,6 +211,9 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
width of the web page when zoomed, `false` will cause it to zoom to the
width of the screen. This will also affect the behavior when calling
`maximize()` directly. Default is `false`.
* `tabbingIdentifier` String (optional) - Tab group name, allows opening the
window as a native tab on macOS 10.12+. Windows with the same tabbing
identifier will be grouped together.
* `webPreferences` Object (optional) - Settings of web page's features.
* `devTools` Boolean (optional) - Whether to enable DevTools. If it is set to `false`, can not use `BrowserWindow.webContents.openDevTools()` to open DevTools. Default is `true`.
* `nodeIntegration` Boolean (optional) - Whether node integration is enabled. Default
@ -225,6 +228,13 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
When node integration is turned off, the preload script can reintroduce
Node global symbols back to the global scope. See example
[here](process.md#event-loaded).
* `sandbox` Boolean (optional) - If set, this will sandbox the renderer
associated with the window, making it compatible with the Chromium
OS-level sandbox and disabling the Node.js engine. This is not the same as
the `nodeIntegration` option and the APIs available to the preload script
are more limited. Read more about the option [here](sandbox-option.md).
**Note:** This option is currently experimental and may change or be
removed in future Electron releases.
* `session` [Session](session.md#class-session) (optional) - Sets the session used by the
page. Instead of passing the Session object directly, you can also choose to
use the `partition` option instead, which accepts a partition string. When
@ -282,7 +292,6 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
window. Defaults to `false`. See the
[offscreen rendering tutorial](../tutorial/offscreen-rendering.md) for
more details.
* `sandbox` Boolean (optional) - Whether to enable Chromium OS-level sandbox.
* `contextIsolation` Boolean (optional) - Whether to run Electron APIs and
the specified `preload` script in a separate JavaScript context. Defaults
to `false`. The context that the `preload` script runs in will still

View file

@ -29,6 +29,11 @@ the hostname and the port number 'hostname:port'
* `hostname` String (optional) - The server host name.
* `port` Integer (optional) - The server's listening port number.
* `path` String (optional) - The path part of the request URL.
* `redirect` String (optional) - The redirect mode for this request. Should be
one of `follow`, `error` or `manual`. Defaults to `follow`. When mode is `error`,
any redirection will be aborted. When mode is `manual` the redirection will be
deferred until [`request.followRedirect`](#requestfollowRedirect) is invoked. Listen for the [`redirect`](#event-redirect) event in
this mode to get more details about the redirect request.
`options` properties such as `protocol`, `host`, `hostname`, `port` and `path`
strictly follow the Node.js model as described in the
@ -65,6 +70,8 @@ Returns:
* `port` Integer
* `realm` String
* `callback` Function
* `username` String
* `password` String
Emitted when an authenticating proxy is asking for user credentials.
@ -119,6 +126,19 @@ Emitted as the last event in the HTTP request-response transaction. The `close`
event indicates that no more events will be emitted on either the `request` or
`response` objects.
#### Event: 'redirect'
Returns:
* `statusCode` Integer
* `method` String
* `redirectUrl` String
* `responseHeaders` Object
Emitted when there is redirection and the mode is `manual`. Calling
[`request.followRedirect`](#requestfollowRedirect) will continue with the redirection.
### Instance Properties
#### `request.chunkedEncoding`
@ -138,17 +158,18 @@ internally buffered inside Electron process memory.
#### `request.setHeader(name, value)`
* `name` String - An extra HTTP header name.
* `value` String - An extra HTTP header value.
* `value` Object - An extra HTTP header value.
Adds an extra HTTP header. The header name will issued as it is without
lowercasing. It can be called only before first write. Calling this method after
the first write will throw an error.
the first write will throw an error. If the passed value is not a `String`, its
`toString()` method will be called to obtain the final value.
#### `request.getHeader(name)`
* `name` String - Specify an extra header name.
Returns String - The value of a previously set extra header name.
Returns Object - The value of a previously set extra header name.
#### `request.removeHeader(name)`
@ -190,3 +211,7 @@ Cancels an ongoing HTTP transaction. If the request has already emitted the
`close` event, the abort operation will have no effect. Otherwise an ongoing
event will emit `abort` and `close` events. Additionally, if there is an ongoing
response object,it will emit the `aborted` event.
#### `request.followRedirect()`
Continues any deferred redirection request when the redirection mode is `manual`.

View file

@ -40,7 +40,7 @@ The `crashReporter` module has the following methods:
* `companyName` String (optional)
* `submitURL` String - URL that crash reports will be sent to as POST.
* `productName` String (optional) - Defaults to `app.getName()`.
* `uploadToServer` Boolean (optional) _Linux_ _macOS_ - Whether crash reports should be sent to the server
* `uploadToServer` Boolean (optional) - Whether crash reports should be sent to the server
Default is `true`.
* `ignoreSystemCrashHandler` Boolean (optional) - Default is `false`.
* `extra` Object (optional) - An object you can define that will be sent along with the

View file

@ -60,8 +60,9 @@ The `desktopCapturer` module has the following methods:
* `options` Object
* `types` String[] - An array of Strings that lists the types of desktop sources
to be captured, available types are `screen` and `window`.
* `thumbnailSize` Object (optional) - The suggested size that the media source
thumbnail should be scaled to, defaults to `{width: 150, height: 150}`.
* `thumbnailSize` Object (optional) - The size that the media source thumbnail should be scaled to.
* `width` Integer - Default is `150`
* `height` Integer - Default is `150`
* `callback` Function
* `error` Error
* `sources` [DesktopCapturerSource[]](structures/desktop-capturer-source.md)

View file

@ -46,16 +46,8 @@ The `dialog` module has the following methods:
* `noResolveAliases` - Disable the automatic alias (symlink) path
resolution. Selected aliases will now return the alias path instead of
their target path. _macOS_
* `normalizeAccessKeys` Boolean (optional) - Normalize the keyboard access keys
across platforms. Default is `false`. Enabling this assumes `&` is used in
the button labels for the placement of the keyboard shortcut access key
and labels will be converted so they work correctly on each platform, `&`
characters are removed on macOS, converted to `_` on Linux, and left
untouched on Windows. For example, a button label of `Vie&w` will be
converted to `Vie_w` on Linux and `View` on macOS and can be selected
via `Alt-W` on Windows and Linux.
* `message` String (optional) _macOS_ - Message to display above input
boxes.
* `message` String (optional) _macOS_ - Message to display above input
boxes.
* `callback` Function (optional)
* `filePaths` String[] - An array of file paths chosen by the user
@ -147,6 +139,14 @@ will be passed via `callback(filename)`
others as command links in the dialog. This can make the dialog appear in
the style of modern Windows apps. If you don't like this behavior, you can
set `noLink` to `true`.
* `normalizeAccessKeys` Boolean (optional) - Normalize the keyboard access keys
across platforms. Default is `false`. Enabling this assumes `&` is used in
the button labels for the placement of the keyboard shortcut access key
and labels will be converted so they work correctly on each platform, `&`
characters are removed on macOS, converted to `_` on Linux, and left
untouched on Windows. For example, a button label of `Vie&w` will be
converted to `Vie_w` on Linux and `View` on macOS and can be selected
via `Alt-W` on Windows and Linux.
* `callback` Function (optional)
* `response` Number - The index of the button that was clicked
* `checkboxChecked` Boolean - The checked state of the checkbox if

View file

@ -15,7 +15,7 @@ See [`Menu`](menu.md) for examples.
* `browserWindow` BrowserWindow
* `event` Event
* `role` String (optional) - Define the action of the menu item, when specified the
`click` property will be ignored.
`click` property will be ignored. See [roles](#roles).
* `type` String (optional) - Can be `normal`, `separator`, `submenu`, `checkbox` or
`radio`.
* `label` String - (optional)
@ -36,12 +36,16 @@ See [`Menu`](menu.md) for examples.
* `position` String (optional) - This field allows fine-grained definition of the
specific location within a given menu.
### Roles
Roles allow menu items to have predefined behaviors.
It is best to specify `role` for any menu item that matches a standard role,
rather than trying to manually implement the behavior in a `click` function.
The built-in `role` behavior will give the best native experience.
The `label` and `accelerator` are optional when using a `role` and will default
to appropriate values for each platform.
The `label` and `accelerator` values are optional when using a `role` and will
default to appropriate values for each platform.
The `role` property can have following values:
@ -63,8 +67,10 @@ The `role` property can have following values:
* `resetzoom` - Reset the focused page's zoom level to the original size
* `zoomin` - Zoom in the focused page by 10%
* `zoomout` - Zoom out the focused page by 10%
* `editMenu` - Whole default "Edit" menu (Undo, Copy, etc.)
* `windowMenu` - Whole default "Window" menu (Minimize, Close, etc.)
On macOS `role` can also have following additional values:
The following additional roles are avaiable on macOS:
* `about` - Map to the `orderFrontStandardAboutPanel` action
* `hide` - Map to the `hide` action
@ -78,8 +84,8 @@ On macOS `role` can also have following additional values:
* `help` - The submenu is a "Help" menu
* `services` - The submenu is a "Services" menu
When specifying `role` on macOS, `label` and `accelerator` are the only options
that will affect the MenuItem. All other options will be ignored.
When specifying a `role` on macOS, `label` and `accelerator` are the only
options that will affect the menu item. All other options will be ignored.
### Instance Properties

View file

@ -30,8 +30,8 @@ Returns `Menu` - The application menu, if set, or `null`, if not set.
* `action` String
Sends the `action` to the first responder of application. This is used for
emulating default Cocoa menu behaviors, usually you would just use the
`role` property of `MenuItem`.
emulating default macOS menu behaviors. Usually you would just use the
[`role`](menu-item.md#roles) property of a [`MenuItem`](menu-item.md).
See the [macOS Cocoa Event Handling Guide](https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/EventOverview/EventArchitecture/EventArchitecture.html#//apple_ref/doc/uid/10000060i-CH3-SW7)
for more information on macOS' native actions.
@ -115,76 +115,36 @@ const template = [
{
label: 'Edit',
submenu: [
{
role: 'undo'
},
{
role: 'redo'
},
{
type: 'separator'
},
{
role: 'cut'
},
{
role: 'copy'
},
{
role: 'paste'
},
{
role: 'pasteandmatchstyle'
},
{
role: 'delete'
},
{
role: 'selectall'
}
{role: 'undo'},
{role: 'redo'},
{type: 'separator'},
{role: 'cut'},
{role: 'copy'},
{role: 'paste'},
{role: 'pasteandmatchstyle'},
{role: 'delete'},
{role: 'selectall'}
]
},
{
label: 'View',
submenu: [
{
role: 'reload'
},
{
role: 'forcereload'
},
{
role: 'toggledevtools'
},
{
type: 'separator'
},
{
role: 'resetzoom'
},
{
role: 'zoomin'
},
{
role: 'zoomout'
},
{
type: 'separator'
},
{
role: 'togglefullscreen'
}
{role: 'reload'},
{role: 'forcereload'},
{role: 'toggledevtools'},
{type: 'separator'},
{role: 'resetzoom'},
{role: 'zoomin'},
{role: 'zoomout'},
{type: 'separator'},
{role: 'togglefullscreen'}
]
},
{
role: 'window',
submenu: [
{
role: 'minimize'
},
{
role: 'close'
}
{role: 'minimize'},
{role: 'close'}
]
},
{
@ -202,76 +162,37 @@ if (process.platform === 'darwin') {
template.unshift({
label: app.getName(),
submenu: [
{
role: 'about'
},
{
type: 'separator'
},
{
role: 'services',
submenu: []
},
{
type: 'separator'
},
{
role: 'hide'
},
{
role: 'hideothers'
},
{
role: 'unhide'
},
{
type: 'separator'
},
{
role: 'quit'
}
{role: 'about'},
{type: 'separator'},
{role: 'services', submenu: []},
{type: 'separator'},
{role: 'hide'},
{role: 'hideothers'},
{role: 'unhide'},
{type: 'separator'},
{role: 'quit'}
]
})
// Edit menu.
// Edit menu
template[1].submenu.push(
{
type: 'separator'
},
{type: 'separator'},
{
label: 'Speech',
submenu: [
{
role: 'startspeaking'
},
{
role: 'stopspeaking'
}
{role: 'startspeaking'},
{role: 'stopspeaking'}
]
}
)
// Window menu.
// Window menu
template[3].submenu = [
{
label: 'Close',
accelerator: 'CmdOrCtrl+W',
role: 'close'
},
{
label: 'Minimize',
accelerator: 'CmdOrCtrl+M',
role: 'minimize'
},
{
label: 'Zoom',
role: 'zoom'
},
{
type: 'separator'
},
{
label: 'Bring All to Front',
role: 'front'
}
{role: 'close'},
{role: 'minimize'},
{role: 'zoom'},
{type: 'separator'},
{role: 'front'}
]
}

View file

@ -32,38 +32,38 @@ process.once('loaded', () => {
### `process.noAsar`
Setting this to `true` can disable the support for `asar` archives in Node's
built-in modules.
A `Boolean` that controls ASAR support inside your application. Setting this to `true`
will disable the support for `asar` archives in Node's built-in modules.
### `process.type`
Current process's type, can be `"browser"` (i.e. main process) or `"renderer"`.
A `String` representing the current process's type, can be `"browser"` (i.e. main process) or `"renderer"`.
### `process.versions.electron`
Electron's version string.
A `String` representing Electron's version string.
### `process.versions.chrome`
Chrome's version string.
A `String` representing Chrome's version string.
### `process.resourcesPath`
Path to the resources directory.
A `String` representing the path to the resources directory.
### `process.mas`
For Mac App Store build, this property is `true`, for other builds it is
A `Boolean`. For Mac App Store build, this property is `true`, for other builds it is
`undefined`.
### `process.windowsStore`
If the app is running as a Windows Store app (appx), this property is `true`,
A `Boolean`. If the app is running as a Windows Store app (appx), this property is `true`,
for otherwise it is `undefined`.
### `process.defaultApp`
When app is started by being passed as parameter to the default app, this
A `Boolean`. When app is started by being passed as parameter to the default app, this
property is `true` in the main process, otherwise it is `undefined`.
## Methods

View file

@ -141,6 +141,36 @@ The `remote` module has the following methods:
* `module` String
Returns `any` - The object returned by `require(module)` in the main process.
Modules specified by their relative path will resolve relative to the entrypoint
of the main process.
e.g.
```
project/
├── main
│   ├── foo.js
│   └── index.js
├── package.json
└── renderer
└── index.js
```
```js
// main process: main/index.js
const {app} = require('electron')
app.on('ready', () => { /* ... */ })
```
```js
// some relative module: main/foo.js
module.exports = 'bar'
```
```js
// renderer process: renderer/index.js
const foo = require('electron').remote.require('./foo') // bar
```
### `remote.getCurrentWindow()`

195
docs/api/sandbox-option.md Normal file
View file

@ -0,0 +1,195 @@
# `sandbox` Option
> Create a browser window with a renderer that can run inside Chromium OS sandbox. With this
option enabled, the renderer must communicate via IPC to the main process in order to access node APIs.
However, in order to enable the Chromium OS sandbox, electron must be run with the `--enable-sandbox`
command line argument.
One of the key security features of Chromium is that all blink rendering/JavaScript
code is executed within a sandbox. This sandbox uses OS-specific features to ensure
that exploits in the renderer process cannot harm the system.
In other words, when the sandbox is enabled, the renderers can only make changes
to the system by delegating tasks to the main process via IPC.
[Here's](https://www.chromium.org/developers/design-documents/sandbox) more
information about the sandbox.
Since a major feature in electron is the ability to run node.js in the
renderer process (making it easier to develop desktop applications using web
technologies), the sandbox is disabled by electron. This is because
most node.js APIs require system access. `require()` for example, is not
possible without file system permissions, which are not available in a sandboxed
environment.
Usually this is not a problem for desktop applications since the code is always
trusted, but it makes electron less secure than chromium for displaying
untrusted web content. For applications that require more security, the
`sandbox` flag will force electron to spawn a classic chromium renderer that is
compatible with the sandbox.
A sandboxed renderer doesn't have a node.js environment running and doesn't
expose node.js JavaScript APIs to client code. The only exception is the preload script,
which has access to a subset of the electron renderer API.
Another difference is that sandboxed renderers don't modify any of the default
JavaScript APIs. Consequently, some APIs such as `window.open` will work as they
do in chromium (i.e. they do not return a `BrowserWindowProxy`).
## Example
To create a sandboxed window, simply pass `sandbox: true` to `webPreferences`:
```js
let win
app.on('ready', () => {
win = new BrowserWindow({
webPreferences: {
sandbox: true
}
})
w.loadURL('http://google.com')
})
```
In the above code the `BrowserWindow` that was created has node.js disabled and can communicate
only via IPC. The use of this option stops electron from creating a node.js runtime in the renderer. Also,
within this new window `window.open` follows the native behaviour (by default electron creates a `BrowserWindow`
and returns a proxy to this via `window.open`).
It is important to note that this option alone won't enable the OS-enforced sandbox. To enable this feature, the
`--enable-sandbox` command-line argument must be passed to electron, which will
force `sandbox: true` for all `BrowserWindow` instances.
```js
let win
app.on('ready', () => {
// no need to pass `sandbox: true` since `--enable-sandbox` was enabled.
win = new BrowserWindow()
w.loadURL('http://google.com')
})
```
Note that it is not enough to call
`app.commandLine.appendSwitch('--enable-sandbox')`, as electron/node startup
code runs after it is possible to make changes to chromium sandbox settings. The
switch must be passed to electron on the command-line:
```
electron --enable-sandbox app.js
```
It is not possible to have the OS sandbox active only for some renderers, if
`--enable-sandbox` is enabled, normal electron windows cannot be created.
If you need to mix sandboxed and non-sandboxed renderers in one application,
simply omit the `--enable-sandbox` argument. Without this argument, windows
created with `sandbox: true` will still have node.js disabled and communicate
only via IPC, which by itself is already a gain from security POV.
## Preload
An app can make customizations to sandboxed renderers using a preload script.
Here's an example:
```js
let win
app.on('ready', () => {
win = new BrowserWindow({
webPreferences: {
sandbox: true,
preload: 'preload.js'
}
})
w.loadURL('http://google.com')
})
```
and preload.js:
```js
// This file is loaded whenever a javascript context is created. It runs in a
// private scope that can access a subset of electron renderer APIs. We must be
// careful to not leak any objects into the global scope!
const fs = require('fs')
const {ipcRenderer} = require('electron')
// read a configuration file using the `fs` module
const buf = fs.readFileSync('allowed-popup-urls.json')
const allowedUrls = JSON.parse(buf.toString('utf8'))
const defaultWindowOpen = window.open
function customWindowOpen (url, ...args) {
if (allowedUrls.indexOf(url) === -1) {
ipcRenderer.sendSync('blocked-popup-notification', location.origin, url)
return null
}
return defaultWindowOpen(url, ...args)
}
window.open = customWindowOpen
```
Important things to notice in the preload script:
- Even though the sandboxed renderer doesn't have node.js running, it still has
access to a limited node-like environment: `Buffer`, `process`, `setImmediate`
and `require` are available.
- The preload script can indirectly access all APIs from the main process through the
`remote` and `ipcRenderer` modules. This is how `fs` (used above) and other
modules are implemented: They are proxies to remote counterparts in the main
process.
- The preload script must be contained in a single script, but it is possible to have
complex preload code composed with multiple modules by using a tool like
browserify, as explained below. In fact, browserify is already used by
electron to provide a node-like environment to the preload script.
To create a browserify bundle and use it as a preload script, something like
the following should be used:
browserify preload/index.js \
-x electron \
-x fs \
--insert-global-vars=__filename,__dirname -o preload.js
The `-x` flag should be used with any required module that is already exposed in
the preload scope, and tells browserify to use the enclosing `require` function
for it. `--insert-global-vars` will ensure that `process`, `Buffer` and
`setImmediate` are also taken from the enclosing scope(normally browserify
injects code for those).
Currently the `require` function provided in the preload scope exposes the
following modules:
- `child_process`
- `electron` (crashReporter, remote and ipcRenderer)
- `fs`
- `os`
- `timers`
- `url`
More may be added as needed to expose more electron APIs in the sandbox, but any
module in the main process can already be used through
`electron.remote.require`.
## Status
Please use the `sandbox` option with care, as it is still an experimental
feature. We are still not aware of the security implications of exposing some
electron renderer APIs to the preload script, but here are some things to
consider before rendering untrusted content:
- A preload script can accidentaly leak privileged APIs to untrusted code.
- Some bug in V8 engine may allow malicious code to access the renderer preload
APIs, effectively granting full access to the system through the `remote`
module.
Since rendering untrusted content in electron is still uncharted territory,
the APIs exposed to the sandbox preload script should be considered more
unstable than the rest of electron APIs, and may have breaking changes to fix
security issues.
One planned enhancement that should greatly increase security is to block IPC
messages from sandboxed renderers by default, allowing the main process to
explicitly define a set of messages the renderer is allowed to send.

View file

@ -9,9 +9,9 @@ Process: [Main](../tutorial/quick-start.md#main-process)
* `options` Object
* `items` [ScrubberItem[]](structures/scrubber-item.md) - An array of items to place in this scrubber
* `select` Function - Called when the user taps an item that was not the last tapped item
* `selectedIndex` - The index of the item the user selected
* `selectedIndex` Integer - The index of the item the user selected
* `highlight` Function - Called when the user taps any item
* `highlightedIndex` - The index of the item the user touched
* `highlightedIndex` Integer - The index of the item the user touched
* `selectedStyle` String - Selected item style. Defaults to `null`.
* `overlayStyle` String - Selected overlay item style. Defaults to `null`.
* `showArrowButtons` Boolean - Defaults to `false`.

View file

@ -8,18 +8,23 @@ Process: [Main](../tutorial/quick-start.md#main-process)
* `options` Object
* `segmentStyle` String - (Optional) Style of the segments:
* `automatic` - Default
* `rounded`
* `textured-rounded`
* `round-rect`
* `textured-square`
* `capsule`
* `small-square`
* `separated`
* `automatic` - Default. The appearance of the segmented control is
automatically determined based on the type of window in which the control
is displayed and the position within the window.
* `rounded` - The control is displayed using the rounded style.
* `textured-rounded` - The control is displayed using the textured rounded
style.
* `round-rect` - The control is displayed using the round rect style.
* `textured-square` - The control is displayed using the textured square
style.
* `capsule` - The control is displayed using the capsule style
* `small-square` - The control is displayed using the small square style.
* `separated` - The segments in the control are displayed very close to each
other but not touching.
* `segments` [SegmentedControlSegment[]](structures/segmented-control-segment.md) - An array of segments to place in this control
* `selectedIndex` Integer (Optional) - The index of the currently selected segment, will update automatically with user interaction
* `change` Function - Called when the user selects a new segment
* `selectedIndex` - The index of the segment the user selected
* `selectedIndex` Integer - The index of the segment the user selected
### Instance Properties

View file

@ -336,6 +336,7 @@ Returns:
* `activeMatchOrdinal` Integer - Position of the active match.
* `matches` Integer - Number of Matches.
* `selectionArea` Object - Coordinates of first match region.
* `finalUpdate` Boolean
Emitted when a result is available for
[`webContents.findInPage`] request.
@ -1247,7 +1248,7 @@ one through the `'paint'` event.
#### `contents.getWebRTCIPHandlingPolicy()`
* Returns `String` - Returns the WebRTC IP Handling Policy.
Returns `String` - Returns the WebRTC IP Handling Policy.
#### `contents.setWebRTCIPHandlingPolicy(policy)`

View file

@ -136,6 +136,9 @@ Inserts `text` to the focused element.
* `callback` Function (optional) - Called after script has been executed.
* `result` Any
Returns `Promise` - A promise that resolves with the result of the executed code
or is rejected if the result of the code is a rejected promise.
Evaluates `code` in page.
In the browser window some HTML APIs like `requestFullScreen` can only be

View file

@ -749,6 +749,7 @@ Returns:
* `activeMatchOrdinal` Integer - Position of the active match.
* `matches` Integer - Number of Matches.
* `selectionArea` Object - Coordinates of first match region.
* `finalUpdate` Boolean
Fired when a result is available for
[`webview.findInPage`](webview-tag.md#webviewtagfindinpage) request.

View file

@ -0,0 +1,14 @@
# Chromium Development
> A collection of resources for learning about Chromium and tracking its development
- [chromiumdev](https://chromiumdev-slack.herokuapp.com) on Slack
- [@ChromiumDev](https://twitter.com/ChromiumDev) on Twitter
- [@googlechrome](https://twitter.com/googlechrome) on Twitter
- [Blog](https://blog.chromium.org)
- [Code Search](https://cs.chromium.org/)
- [Source Code](https://cs.chromium.org/chromium/src/)
- [Development Calendar and Release Info](https://www.chromium.org/developers/calendar)
- [Discussion Groups](http://www.chromium.org/developers/discussion-groups)
See also [V8 Development](v8-development.md)

View file

@ -45,6 +45,46 @@ Chrome/Node API changes.
- 64-bit Linux
- ARM Linux
## Verify ffmpeg Support
Electron ships with a version of `ffmpeg` that includes proprietary codecs by
default. A version without these codecs is built and distributed with each
release as well. Each Chrome upgrade should verify that switching this version is
still supported.
You can verify Electron's support for multiple `ffmpeg` builds by loading the
following page. It should work with the default `ffmpeg` library distributed
with Electron and not work with the `ffmpeg` library built without proprietary
codecs.
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Proprietary Codec Check</title>
</head>
<body>
<p>Checking if Electron is using proprietary codecs by loading video from http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4</p>
<p id="outcome"></p>
<video style="display:none" src="http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4" autoplay></video>
<script>
const video = document.querySelector('video')
video.addEventListener('error', ({target}) => {
if (target.error.code === target.error.MEDIA_ERR_SRC_NOT_SUPPORTED) {
document.querySelector('#outcome').textContent = 'Not using proprietary codecs, video emitted source not supported error event.'
} else {
document.querySelector('#outcome').textContent = `Unexpected error: ${target.error.code}`
}
})
video.addEventListener('playing', () => {
document.querySelector('#outcome').textContent = 'Using proprietary codecs, video started playing.'
})
</script>
</body>
</html>
```
## Links
- [Chrome Release Schedule](https://www.chromium.org/developers/calendar)

View file

@ -0,0 +1,11 @@
# V8 Development
> A collection of resources for learning and using V8
* [V8 Tracing](https://github.com/v8/v8/wiki/Tracing-V8)
* [V8 Profiler](https://github.com/v8/v8/wiki/V8-Profiler) - Profiler combinations which are useful for profiling: `--prof`, `--trace-ic`, `--trace-opt`, `--trace-deopt`, `--print-bytecode`, `--print-opt-code`
* [V8 Interpreter Design](https://docs.google.com/document/d/11T2CRex9hXxoJwbYqVQ32yIPMh0uouUZLdyrtmMoL44/edit?ts=56f27d9d#heading=h.6jz9dj3bnr8t)
* [Optimizing compiler](https://github.com/v8/v8/wiki/TurboFan)
* [V8 GDB Debugging](https://github.com/v8/v8/wiki/GDB-JIT-Interface)
See also [Chromium Development](chromium-development.md)

View file

@ -1,21 +1,55 @@
# Electron Versioning
If you are a seasoned Node developer, you are surely aware of `semver` - and
might be used to giving your dependency management systems only rough guidelines
rather than fixed version numbers. Due to the hard dependency on Node and
Chromium, Electron is in a slightly more difficult position and does not follow
semver. You should therefore always reference a specific version of Electron.
If you've been using Node and npm for a while, you are probably aware of [Semantic Versioning], or SemVer for short. It's a convention for specifying version numbers for software that helps communicate intentions to the users of your software.
Version numbers are bumped using the following rules:
## Overview of Semantic Versioning
* Major: For breaking changes in Electron's API - if you upgrade from `0.37.0`
to `1.0.0`, you will have to update your app.
* Minor: For major Chrome and minor Node upgrades; or significant Electron
changes - if you upgrade from `1.0.0` to `1.1.0`, your app is supposed to
Semantic versions are always made up of three numbers:
```
major.minor.patch
```
Semantic version numbers are bumped (incremented) using the following rules:
* **Major** is for changes that break backwards compatibility.
* **Minor** is for new features that don't break backwards compatibility.
* **Patch** is for bug fixes and other minor changes.
A simple mnemonic for remembering this scheme is as follows:
```
breaking.feature.fix
```
## Electron Versioning
Due to its dependency on Node and Chromium, it is not possible for the Electron
project to adhere to a SemVer policy. **You should therefore always
reference a specific version of Electron.**
Electron version numbers are bumped using the following rules:
* **Major** is for breaking changes in Electron's API. If you upgrade from `0.37.0`
to `1.0.0`, you will have to make changes to your app.
* **Minor** is for major Chrome and minor Node upgrades, or significant Electron
changes. If you upgrade from `1.5.0` to `1.6.0`, your app is supposed to
still work, but you might have to work around small changes.
* Patch: For new features and bug fixes - if you upgrade from `1.0.0` to
`1.0.1`, your app will continue to work as-is.
* **Patch** is for new features and bug fixes. If you upgrade from `1.6.2` to
`1.6.3`, your app will continue to work as-is.
If you are using `electron` or `electron-prebuilt`, we recommend that you set a fixed version
number (`1.1.0` instead of `^1.1.0`) to ensure that all upgrades of Electron are
a manual operation made by you, the developer.
We recommend that you set a fixed version when installing Electron from npm:
```sh
npm install electron --save-exact --save-dev
```
The `--save-exact` flag will add `electron` to your `package.json` file without
using a `^` or `~`, e.g. `1.6.2` instead of `^1.6.2`. This practice ensures that
all upgrades of Electron are a manual operation made by you, the developer.
Alternatively, you can use the `~` prefix in your SemVer range, like `~1.6.2`.
This will lock your major and minor version, but allow new patch versions to
be installed.
[Semantic Versioning]: http://semver.org

View file

@ -4,7 +4,7 @@
'product_name%': 'Electron',
'company_name%': 'GitHub, Inc',
'company_abbr%': 'github',
'version%': '1.6.4',
'version%': '1.6.5',
'js2c_input_dir': '<(SHARED_INTERMEDIATE_DIR)/js2c',
},
'includes': [
@ -441,7 +441,13 @@
'sandbox_args': [
'./lib/sandboxed_renderer/init.js',
'-r',
'./lib/sandboxed_renderer/api/exports/electron.js:electron'
'./lib/sandboxed_renderer/api/exports/electron.js:electron',
'-r',
'./lib/sandboxed_renderer/api/exports/fs.js:fs',
'-r',
'./lib/sandboxed_renderer/api/exports/os.js:os',
'-r',
'./lib/sandboxed_renderer/api/exports/child_process.js:child_process'
],
'isolated_args': [
'lib/isolated_renderer/init.js',

View file

@ -480,6 +480,8 @@
'atom/renderer/node_array_buffer_bridge.h',
'atom/renderer/preferences_manager.cc',
'atom/renderer/preferences_manager.h',
'atom/renderer/renderer_client_base.cc',
'atom/renderer/renderer_client_base.h',
'atom/renderer/web_worker_observer.cc',
'atom/renderer/web_worker_observer.h',
'atom/utility/atom_content_utility_client.cc',

View file

@ -154,6 +154,68 @@ const roles = {
webContents.setZoomLevel(zoomLevel - 0.5)
})
}
},
// Edit submenu (should fit both Mac & Windows)
editMenu: {
label: 'Edit',
submenu: [
{
role: 'undo'
},
{
role: 'redo'
},
{
type: 'separator'
},
{
role: 'cut'
},
{
role: 'copy'
},
{
role: 'paste'
},
process.platform === 'darwin' ? {
role: 'pasteandmatchstyle'
} : null,
{
role: 'delete'
},
process.platform === 'win32' ? {
type: 'separator'
} : null,
{
role: 'selectall'
}
]
},
// Window submenu should be used for Mac only
windowMenu: {
label: 'Window',
submenu: [
{
role: 'minimize'
},
{
role: 'close'
},
process.platform === 'darwin' ? {
type: 'separator'
} : null,
process.platform === 'darwin' ? {
role: 'front'
} : null
]
}
}
@ -176,6 +238,19 @@ exports.getDefaultAccelerator = (role) => {
if (roles.hasOwnProperty(role)) return roles[role].accelerator
}
exports.getDefaultSubmenu = (role) => {
if (!roles.hasOwnProperty(role)) return
let {submenu} = roles[role]
// remove null items from within the submenu
if (Array.isArray(submenu)) {
submenu = submenu.filter((item) => item != null)
}
return submenu
}
exports.execute = (role, focusedWindow, focusedWebContents) => {
if (!canExecuteRole(role)) return false

View file

@ -11,7 +11,7 @@ const MenuItem = function (options) {
for (let key in options) {
if (!(key in this)) this[key] = options[key]
}
this.submenu = this.submenu || roles.getDefaultSubmenu(this.role)
if (this.submenu != null && this.submenu.constructor !== Menu) {
this.submenu = Menu.buildFromTemplate(this.submenu)
}

View file

@ -156,9 +156,15 @@ class ClientRequest extends EventEmitter {
urlStr = url.format(urlObj)
}
const redirectPolicy = options.redirect || 'follow'
if (!['follow', 'error', 'manual'].includes(redirectPolicy)) {
throw new Error('redirect mode should be one of follow, error or manual')
}
let urlRequestOptions = {
method: method,
url: urlStr
url: urlStr,
redirect: redirectPolicy
}
if (options.session) {
if (options.session instanceof Session) {
@ -240,7 +246,7 @@ class ClientRequest extends EventEmitter {
if (typeof name !== 'string') {
throw new TypeError('`name` should be a string in setHeader(name, value).')
}
if (value === undefined) {
if (value == null) {
throw new Error('`value` required in setHeader("' + name + '", value).')
}
if (!this.urlRequest.notStarted) {
@ -249,11 +255,11 @@ class ClientRequest extends EventEmitter {
const key = name.toLowerCase()
this.extraHeaders[key] = value
this.urlRequest.setExtraHeader(name, value)
this.urlRequest.setExtraHeader(name, value.toString())
}
getHeader (name) {
if (arguments.length < 1) {
if (name == null) {
throw new Error('`name` is required for getHeader(name).')
}
@ -266,7 +272,7 @@ class ClientRequest extends EventEmitter {
}
removeHeader (name) {
if (arguments.length < 1) {
if (name == null) {
throw new Error('`name` is required for removeHeader(name).')
}
@ -339,6 +345,10 @@ class ClientRequest extends EventEmitter {
return this._write(data, encoding, callback, true)
}
followRedirect () {
this.urlRequest.followRedirect()
}
abort () {
this.urlRequest.cancel()
}

View file

@ -139,7 +139,12 @@ const setObjectMembers = function (ref, object, metaId, members) {
// Only set setter when it is writable.
if (member.writable) {
descriptor.set = function (value) {
ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_SET', metaId, member.name, value)
const meta = ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_SET', metaId, member.name, value)
// Meta will be non-null when a setter error occurred so parse it
// to a value so it gets re-thrown.
if (meta != null) {
metaToValue(meta)
}
return value
}
}

View file

@ -0,0 +1 @@
module.exports = require('electron').remote.require('child_process')

View file

@ -11,6 +11,12 @@ Object.defineProperties(exports, {
return require('../../../renderer/api/remote')
}
},
crashReporter: {
enumerable: true,
get: function () {
return require('../../../common/api/crash-reporter')
}
},
CallbacksRegistry: {
get: function () {
return require('../../../common/api/callbacks-registry')

View file

@ -0,0 +1 @@
module.exports = require('electron').remote.require('fs')

View file

@ -0,0 +1 @@
module.exports = require('electron').remote.require('os')

View file

@ -21,23 +21,23 @@ for (let prop of Object.keys(events.EventEmitter.prototype)) {
Object.setPrototypeOf(process, events.EventEmitter.prototype)
const electron = require('electron')
const fs = require('fs')
const preloadModules = new Map([
['electron', electron]
['child_process', require('child_process')],
['electron', electron],
['fs', fs],
['os', require('os')],
['url', require('url')],
['timers', require('timers')]
])
const extraModules = [
'fs'
]
for (let extraModule of extraModules) {
preloadModules.set(extraModule, electron.remote.require(extraModule))
}
// Fetch the preload script using the "fs" module proxy.
let preloadSrc = preloadModules.get('fs').readFileSync(preloadPath).toString()
const preloadSrc = fs.readFileSync(preloadPath).toString()
// Pass different process object to the preload script(which should not have
// access to things like `process.atomBinding`).
const preloadProcess = new events.EventEmitter()
preloadProcess.platform = electron.remote.process.platform
preloadProcess.crash = () => binding.crash()
process.on('exit', () => preloadProcess.emit('exit'))
// This is the `require` function that will be visible to the preload script
@ -67,12 +67,13 @@ function preloadRequire (module) {
// and any `require('electron')` calls in `preload.js` will work as expected
// since browserify won't try to include `electron` in the bundle, falling back
// to the `preloadRequire` function above.
let preloadWrapperSrc = `(function(require, process, Buffer, global) {
const preloadWrapperSrc = `(function(require, process, Buffer, global, setImmediate) {
${preloadSrc}
})`
// eval in window scope:
// http://www.ecma-international.org/ecma-262/5.1/#sec-10.4.2
const geval = eval
let preloadFn = geval(preloadWrapperSrc)
preloadFn(preloadRequire, preloadProcess, Buffer, global)
const preloadFn = geval(preloadWrapperSrc)
const {setImmediate} = require('timers')
preloadFn(preloadRequire, preloadProcess, Buffer, global, setImmediate)

View file

@ -1,6 +1,6 @@
{
"name": "electron",
"version": "1.6.4",
"version": "1.6.5",
"devDependencies": {
"asar": "^0.11.0",
"browserify": "^13.1.0",

View file

@ -91,6 +91,7 @@ def main():
run_script('build.py', ['-c', 'D'])
if PLATFORM == 'win32' or target_arch == 'x64':
run_script('test.py', ['--ci'])
run_script('verify-ffmpeg.py')
def run_script(script, args=[]):

View file

@ -1,5 +1,6 @@
#!/usr/bin/env python
import argparse
import glob
import os
import re
@ -87,7 +88,9 @@ def main():
copy_chrome_binary('mksnapshot')
copy_license()
if PLATFORM != 'win32':
args = parse_args()
if PLATFORM != 'win32' and not args.no_api_docs:
create_api_json_schema()
if PLATFORM == 'linux':
@ -242,5 +245,13 @@ def create_symbols_zip():
make_zip(os.path.join(DIST_DIR, pdb_name), pdbs + licenses, [])
def parse_args():
parser = argparse.ArgumentParser(description='Create Electron Distribution')
parser.add_argument('--no_api_docs',
action='store_true',
help='Skip generating the Electron API Documentation!')
return parser.parse_args()
if __name__ == '__main__':
sys.exit(main())

View file

@ -9,7 +9,7 @@ import sys
BASE_URL = os.getenv('LIBCHROMIUMCONTENT_MIRROR') or \
'https://s3.amazonaws.com/github-janky-artifacts/libchromiumcontent'
LIBCHROMIUMCONTENT_COMMIT = os.getenv('LIBCHROMIUMCONTENT_COMMIT') or \
'44448acf6a21024b9adb7140ffef6402a509f8bf'
'8d551064d2b3d11f89ce8d5c4610f34e0408bad8'
PLATFORM = {
'cygwin': 'win32',

75
script/verify-ffmpeg.py Executable file
View file

@ -0,0 +1,75 @@
#!/usr/bin/env python
import os
import shutil
import subprocess
import sys
from lib.util import electron_gyp, rm_rf
SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
FFMPEG_LIBCC_PATH = os.path.join(SOURCE_ROOT, 'vendor', 'brightray', 'vendor',
'download', 'libchromiumcontent', 'ffmpeg')
PROJECT_NAME = electron_gyp()['project_name%']
PRODUCT_NAME = electron_gyp()['product_name%']
def main():
os.chdir(SOURCE_ROOT)
if len(sys.argv) == 2 and sys.argv[1] == '-R':
config = 'R'
else:
config = 'D'
app_path = create_app_copy(config)
if sys.platform == 'darwin':
electron = os.path.join(app_path, 'Contents', 'MacOS', PRODUCT_NAME)
ffmpeg_name = 'libffmpeg.dylib'
ffmpeg_app_path = os.path.join(app_path, 'Contents', 'Frameworks',
'{0} Framework.framework'.format(PROJECT_NAME),
'Libraries')
elif sys.platform == 'win32':
electron = os.path.join(app_path, '{0}.exe'.format(PROJECT_NAME))
ffmpeg_app_path = app_path
ffmpeg_name = 'ffmpeg.dll'
else:
electron = os.path.join(app_path, PROJECT_NAME)
ffmpeg_app_path = app_path
ffmpeg_name = 'libffmpeg.so'
# Copy ffmpeg without proprietary codecs into app
shutil.copy(os.path.join(FFMPEG_LIBCC_PATH, ffmpeg_name), ffmpeg_app_path)
returncode = 0
try:
test_path = os.path.join('spec', 'fixtures', 'no-proprietary-codecs.js')
subprocess.check_call([electron, test_path] + sys.argv[1:])
except subprocess.CalledProcessError as e:
returncode = e.returncode
except KeyboardInterrupt:
returncode = 0
return returncode
# Create copy of app to install ffmpeg library without proprietary codecs into
def create_app_copy(config):
initial_app_path = os.path.join(SOURCE_ROOT, 'out', config)
app_path = os.path.join(SOURCE_ROOT, 'out', config + '-no-proprietary-codecs')
if sys.platform == 'darwin':
app_name = '{0}.app'.format(PRODUCT_NAME)
initial_app_path = os.path.join(initial_app_path, app_name)
app_path = os.path.join(app_path, app_name)
rm_rf(app_path)
shutil.copytree(initial_app_path, app_path, symlinks=True)
return app_path
if __name__ == '__main__':
sys.exit(main())

View file

@ -692,7 +692,7 @@ describe('BrowserWindow module', function () {
})
})
describe('"title-bar-style" option', function () {
describe('"titleBarStyle" option', function () {
if (process.platform !== 'darwin') {
return
}
@ -772,6 +772,20 @@ describe('BrowserWindow module', function () {
})
})
describe('"tabbingIdentifier" option', function () {
it('can be set on a window', function () {
w.destroy()
w = new BrowserWindow({
tabbingIdentifier: 'group1'
})
w.destroy()
w = new BrowserWindow({
tabbingIdentifier: 'group2',
frame: false
})
})
})
describe('"web-preferences" option', function () {
afterEach(function () {
ipcMain.removeAllListeners('answer')

View file

@ -1,5 +1,6 @@
const assert = require('assert')
const childProcess = require('child_process')
const fs = require('fs')
const http = require('http')
const multiparty = require('multiparty')
const path = require('path')
@ -11,85 +12,183 @@ const {remote} = require('electron')
const {app, BrowserWindow, crashReporter} = remote.require('electron')
describe('crashReporter module', function () {
var fixtures = path.resolve(__dirname, 'fixtures')
var w = null
var originalTempDirectory = null
var tempDirectory = null
beforeEach(function () {
w = new BrowserWindow({
show: false
})
tempDirectory = temp.mkdirSync('electronCrashReporterSpec-')
originalTempDirectory = app.getPath('temp')
app.setPath('temp', tempDirectory)
})
afterEach(function () {
app.setPath('temp', originalTempDirectory)
return closeWindow(w).then(function () { w = null })
})
if (process.mas) {
return
}
var fixtures = path.resolve(__dirname, 'fixtures')
const generateSpecs = (description, browserWindowOpts) => {
describe(description, function () {
var w = null
var originalTempDirectory = null
var tempDirectory = null
it('should send minidump when renderer crashes', function (done) {
if (process.env.APPVEYOR === 'True') return done()
if (process.env.TRAVIS === 'true') return done()
beforeEach(function () {
w = new BrowserWindow(Object.assign({
show: false
}, browserWindowOpts))
tempDirectory = temp.mkdirSync('electronCrashReporterSpec-')
originalTempDirectory = app.getPath('temp')
app.setPath('temp', tempDirectory)
})
this.timeout(120000)
afterEach(function () {
app.setPath('temp', originalTempDirectory)
return closeWindow(w).then(function () { w = null })
})
startServer({
callback (port) {
const crashUrl = url.format({
protocol: 'file',
pathname: path.join(fixtures, 'api', 'crash.html'),
search: '?port=' + port
it('should send minidump when renderer crashes', function (done) {
if (process.env.APPVEYOR === 'True') return done()
if (process.env.TRAVIS === 'true') return done()
this.timeout(120000)
startServer({
callback (port) {
const crashUrl = url.format({
protocol: 'file',
pathname: path.join(fixtures, 'api', 'crash.html'),
search: '?port=' + port
})
w.loadURL(crashUrl)
},
processType: 'renderer',
done: done
})
w.loadURL(crashUrl)
},
processType: 'renderer',
done: done
})
})
})
it('should send minidump when node processes crash', function (done) {
if (process.env.APPVEYOR === 'True') return done()
if (process.env.TRAVIS === 'true') return done()
it('should send minidump when node processes crash', function (done) {
if (process.env.APPVEYOR === 'True') return done()
if (process.env.TRAVIS === 'true') return done()
this.timeout(120000)
this.timeout(120000)
startServer({
callback (port) {
const crashesDir = path.join(app.getPath('temp'), `${app.getName()} Crashes`)
const version = app.getVersion()
const crashPath = path.join(fixtures, 'module', 'crash.js')
childProcess.fork(crashPath, [port, version, crashesDir], {silent: true})
},
processType: 'browser',
done: done
})
})
it('should send minidump with updated extra parameters', function (done) {
if (process.env.APPVEYOR === 'True') return done()
if (process.env.TRAVIS === 'true') return done()
this.timeout(10000)
startServer({
callback (port) {
const crashUrl = url.format({
protocol: 'file',
pathname: path.join(fixtures, 'api', 'crash-restart.html'),
search: '?port=' + port
startServer({
callback (port) {
const crashesDir = path.join(app.getPath('temp'), `${app.getName()} Crashes`)
const version = app.getVersion()
const crashPath = path.join(fixtures, 'module', 'crash.js')
childProcess.fork(crashPath, [port, version, crashesDir], {silent: true})
},
processType: 'browser',
done: done
})
w.loadURL(crashUrl)
},
processType: 'renderer',
done: done
})
it('should not send minidump if uploadToServer is false', function (done) {
this.timeout(120000)
if (process.platform === 'darwin') {
crashReporter.setUploadToServer(false)
}
let server
let dumpFile
let crashesDir
const testDone = (uploaded) => {
if (uploaded) {
return done(new Error('fail'))
}
server.close()
if (process.platform === 'darwin') {
crashReporter.setUploadToServer(true)
}
assert(fs.existsSync(dumpFile))
fs.unlinkSync(dumpFile)
done()
}
let pollInterval
const pollDumpFile = () => {
fs.readdir(crashesDir, (err, files) => {
if (err) {
return
}
const dumps = files.filter((file) => /\.dmp$/.test(file))
if (!dumps.length) {
return
}
assert.equal(1, dumps.length)
dumpFile = path.join(crashesDir, dumps[0])
clearInterval(pollInterval)
// dump file should not be deleted when not uploading, so we wait
// 500 ms and assert it still exists in `testDone`
setTimeout(testDone, 500)
})
}
remote.ipcMain.once('set-crash-directory', (event, dir) => {
if (process.platform === 'linux') {
crashesDir = dir
} else {
crashesDir = crashReporter.getCrashesDirectory()
if (process.platform === 'darwin') {
// crashpad uses an extra subdirectory
crashesDir = path.join(crashesDir, 'completed')
}
}
// Before starting, remove all dump files in the crash directory.
// This is required because:
// - mac crashpad not seem to allow changing the crash directory after
// the first "start" call.
// - Other tests in this suite may leave dumps there.
// - We want to verify in `testDone` that a dump file is created and
// not deleted.
fs.readdir(crashesDir, (err, files) => {
if (!err) {
for (const file of files) {
if (/\.dmp$/.test(file)) {
fs.unlinkSync(path.join(crashesDir, file))
}
}
}
event.returnValue = null // allow the renderer to crash
pollInterval = setInterval(pollDumpFile, 100)
})
})
server = startServer({
callback (port) {
const crashUrl = url.format({
protocol: 'file',
pathname: path.join(fixtures, 'api', 'crash.html'),
search: `?port=${port}&skipUpload=1`
})
w.loadURL(crashUrl)
},
processType: 'renderer',
done: testDone.bind(null, true)
})
})
it('should send minidump with updated extra parameters', function (done) {
if (process.env.APPVEYOR === 'True') return done()
if (process.env.TRAVIS === 'true') return done()
this.timeout(10000)
startServer({
callback (port) {
const crashUrl = url.format({
protocol: 'file',
pathname: path.join(fixtures, 'api', 'crash-restart.html'),
search: '?port=' + port
})
w.loadURL(crashUrl)
},
processType: 'renderer',
done: done
})
})
})
}
generateSpecs('without sandbox', {})
generateSpecs('with sandbox ', {
webPreferences: {
sandbox: true,
preload: path.join(fixtures, 'module', 'preload-sandbox.js')
}
})
describe('.start(options)', function () {
@ -204,4 +303,5 @@ const startServer = ({callback, processType, done}) => {
}
callback(port)
})
return server
}

View file

@ -187,6 +187,18 @@ describe('ipc module', function () {
property.property = 1127
})
it('rethrows errors getting/setting properties', function () {
const foo = remote.require(path.join(fixtures, 'module', 'error-properties.js'))
assert.throws(function () {
foo.bar
}, /getting error/)
assert.throws(function () {
foo.bar = 'test'
}, /setting error/)
})
it('can construct an object from its member', function () {
var call = remote.require(path.join(fixtures, 'module', 'call.js'))
var obj = new call.constructor()

View file

@ -455,6 +455,57 @@ describe('menu module', function () {
})
})
describe('MenuItem editMenu', function () {
it('includes a default submenu layout when submenu is empty', function () {
var item = new MenuItem({role: 'editMenu'})
assert.equal(item.label, 'Edit')
assert.equal(item.submenu.items[0].role, 'undo')
assert.equal(item.submenu.items[1].role, 'redo')
assert.equal(item.submenu.items[2].type, 'separator')
assert.equal(item.submenu.items[3].role, 'cut')
assert.equal(item.submenu.items[4].role, 'copy')
assert.equal(item.submenu.items[5].role, 'paste')
if (process.platform === 'darwin') {
assert.equal(item.submenu.items[6].role, 'pasteandmatchstyle')
assert.equal(item.submenu.items[7].role, 'delete')
assert.equal(item.submenu.items[8].role, 'selectall')
}
if (process.platform === 'win32') {
assert.equal(item.submenu.items[6].role, 'delete')
assert.equal(item.submenu.items[7].type, 'separator')
assert.equal(item.submenu.items[8].role, 'selectall')
}
})
it('overrides default layout when submenu is specified', function () {
var item = new MenuItem({role: 'editMenu', submenu: [{role: 'close'}]})
assert.equal(item.label, 'Edit')
assert.equal(item.submenu.items[0].role, 'close')
})
})
describe('MenuItem windowMenu', function () {
it('includes a default submenu layout when submenu is empty', function () {
var item = new MenuItem({role: 'windowMenu'})
assert.equal(item.label, 'Window')
assert.equal(item.submenu.items[0].role, 'minimize')
assert.equal(item.submenu.items[1].role, 'close')
if (process.platform === 'darwin') {
assert.equal(item.submenu.items[2].type, 'separator')
assert.equal(item.submenu.items[3].role, 'front')
}
})
it('overrides default layout when submenu is specified', function () {
var item = new MenuItem({role: 'windowMenu', submenu: [{role: 'copy'}]})
assert.equal(item.label, 'Window')
assert.equal(item.submenu.items[0].role, 'copy')
})
})
describe('MenuItem with custom properties in constructor', function () {
it('preserves the custom properties', function () {
var template = [{

View file

@ -364,6 +364,49 @@ describe('net module', function () {
urlRequest.end()
})
it('should be able to set a non-string object as a header value', function (done) {
const requestUrl = '/requestUrl'
const customHeaderName = 'Some-Integer-Value'
const customHeaderValue = 900
server.on('request', function (request, response) {
switch (request.url) {
case requestUrl:
assert.equal(request.headers[customHeaderName.toLowerCase()],
customHeaderValue.toString())
response.statusCode = 200
response.statusMessage = 'OK'
response.end()
break
default:
assert.equal(request.url, requestUrl)
}
})
const urlRequest = net.request({
method: 'GET',
url: `${server.url}${requestUrl}`
})
urlRequest.on('response', function (response) {
const statusCode = response.statusCode
assert.equal(statusCode, 200)
response.pause()
response.on('end', function () {
done()
})
response.resume()
})
urlRequest.setHeader(customHeaderName, customHeaderValue)
assert.equal(urlRequest.getHeader(customHeaderName),
customHeaderValue)
assert.equal(urlRequest.getHeader(customHeaderName.toLowerCase()),
customHeaderValue)
urlRequest.write('')
assert.equal(urlRequest.getHeader(customHeaderName),
customHeaderValue)
assert.equal(urlRequest.getHeader(customHeaderName.toLowerCase()),
customHeaderValue)
urlRequest.end()
})
it('should not be able to set a custom HTTP request header after first write', function (done) {
const requestUrl = '/requestUrl'
const customHeaderName = 'Some-Custom-Header-Name'
@ -906,6 +949,217 @@ describe('net module', function () {
urlRequest.end()
})
it('should throw if given an invalid redirect mode', function () {
const requestUrl = '/requestUrl'
const options = {
url: `${server.url}${requestUrl}`,
redirect: 'custom'
}
assert.throws(function () {
net.request(options)
}, 'redirect mode should be one of follow, error or manual')
})
it('should throw when calling getHeader without a name', function () {
assert.throws(function () {
net.request({url: `${server.url}/requestUrl`}).getHeader()
}, /`name` is required for getHeader\(name\)\./)
assert.throws(function () {
net.request({url: `${server.url}/requestUrl`}).getHeader(null)
}, /`name` is required for getHeader\(name\)\./)
})
it('should throw when calling removeHeader without a name', function () {
assert.throws(function () {
net.request({url: `${server.url}/requestUrl`}).removeHeader()
}, /`name` is required for removeHeader\(name\)\./)
assert.throws(function () {
net.request({url: `${server.url}/requestUrl`}).removeHeader(null)
}, /`name` is required for removeHeader\(name\)\./)
})
it('should follow redirect when no redirect mode is provided', function (done) {
const requestUrl = '/301'
server.on('request', function (request, response) {
switch (request.url) {
case '/301':
response.statusCode = '301'
response.setHeader('Location', '/200')
response.end()
break
case '/200':
response.statusCode = '200'
response.end()
break
default:
assert(false)
}
})
const urlRequest = net.request({
url: `${server.url}${requestUrl}`
})
urlRequest.on('response', function (response) {
assert.equal(response.statusCode, 200)
done()
})
urlRequest.end()
})
it('should follow redirect chain when no redirect mode is provided', function (done) {
const requestUrl = '/redirectChain'
server.on('request', function (request, response) {
switch (request.url) {
case '/redirectChain':
response.statusCode = '301'
response.setHeader('Location', '/301')
response.end()
break
case '/301':
response.statusCode = '301'
response.setHeader('Location', '/200')
response.end()
break
case '/200':
response.statusCode = '200'
response.end()
break
default:
assert(false)
}
})
const urlRequest = net.request({
url: `${server.url}${requestUrl}`
})
urlRequest.on('response', function (response) {
assert.equal(response.statusCode, 200)
done()
})
urlRequest.end()
})
it('should not follow redirect when mode is error', function (done) {
const requestUrl = '/301'
server.on('request', function (request, response) {
switch (request.url) {
case '/301':
response.statusCode = '301'
response.setHeader('Location', '/200')
response.end()
break
case '/200':
response.statusCode = '200'
response.end()
break
default:
assert(false)
}
})
const urlRequest = net.request({
url: `${server.url}${requestUrl}`,
redirect: 'error'
})
urlRequest.on('error', function (error) {
assert.equal(error.message, 'Request cannot follow redirect with the current redirect mode')
})
urlRequest.on('close', function () {
done()
})
urlRequest.end()
})
it('should allow follow redirect when mode is manual', function (done) {
const requestUrl = '/redirectChain'
let redirectCount = 0
server.on('request', function (request, response) {
switch (request.url) {
case '/redirectChain':
response.statusCode = '301'
response.setHeader('Location', '/301')
response.end()
break
case '/301':
response.statusCode = '301'
response.setHeader('Location', '/200')
response.end()
break
case '/200':
response.statusCode = '200'
response.end()
break
default:
assert(false)
}
})
const urlRequest = net.request({
url: `${server.url}${requestUrl}`,
redirect: 'manual'
})
urlRequest.on('response', function (response) {
assert.equal(response.statusCode, 200)
assert.equal(redirectCount, 2)
done()
})
urlRequest.on('redirect', function (status, method, url) {
if (url === `${server.url}/301` || url === `${server.url}/200`) {
redirectCount += 1
urlRequest.followRedirect()
}
})
urlRequest.end()
})
it('should allow cancelling redirect when mode is manual', function (done) {
const requestUrl = '/redirectChain'
let redirectCount = 0
server.on('request', function (request, response) {
switch (request.url) {
case '/redirectChain':
response.statusCode = '301'
response.setHeader('Location', '/redirect/1')
response.end()
break
case '/redirect/1':
response.statusCode = '200'
response.setHeader('Location', '/redirect/2')
response.end()
break
case '/redirect/2':
response.statusCode = '200'
response.end()
break
default:
assert(false)
}
})
const urlRequest = net.request({
url: `${server.url}${requestUrl}`,
redirect: 'manual'
})
urlRequest.on('response', function (response) {
assert.equal(response.statusCode, 200)
response.pause()
response.on('data', function (chunk) {
})
response.on('end', function () {
urlRequest.abort()
})
response.resume()
})
urlRequest.on('close', function () {
assert.equal(redirectCount, 1)
done()
})
urlRequest.on('redirect', function (status, method, url) {
if (url === `${server.url}/redirect/1`) {
redirectCount += 1
urlRequest.followRedirect()
}
})
urlRequest.end()
})
it('should throw if given an invalid session option', function (done) {
const requestUrl = '/requestUrl'
try {

View file

@ -226,7 +226,7 @@ describe('session module', function () {
it('clears localstorage data', function (done) {
ipcMain.on('count', function (event, count) {
ipcMain.removeAllListeners('count')
assert(!count)
assert.equal(count, 0)
done()
})
w.loadURL('file://' + path.join(fixtures, 'api', 'localstorage.html'))

View file

@ -1,20 +1,24 @@
<html>
<body>
<script type="text/javascript" charset="utf-8">
var port = require('url').parse(window.location.href, true).query.port;
var crashReporter = require('electron').crashReporter;
var url = require('url').parse(window.location.href, true);
var uploadToServer = !url.query.skipUpload;
var port = url.query.port;
var {crashReporter, ipcRenderer} = require('electron');
crashReporter.start({
productName: 'Zombies',
companyName: 'Umbrella Corporation',
submitURL: 'http://127.0.0.1:' + port,
uploadToServer: true,
uploadToServer: uploadToServer,
ignoreSystemCrashHandler: true,
extra: {
'extra1': 'extra1',
'extra2': 'extra2',
}
});
if (!uploadToServer) {
ipcRenderer.sendSync('set-crash-directory', crashReporter.getCrashesDirectory())
}
setImmediate(function() { process.crash(); });
</script>
</body>

Some files were not shown because too many files have changed in this diff Show more