Merge branch 'master' into native-window-open
This commit is contained in:
commit
61fa8693d2
105 changed files with 2439 additions and 907 deletions
|
@ -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())
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ namespace {
|
|||
bool g_update_available = false;
|
||||
std::string update_url_ = "";
|
||||
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::string AutoUpdater::GetFeedURL() {
|
||||
return update_url_;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -64,7 +64,7 @@ void Browser::SetLoginItemSettings(LoginItemSettings settings) {
|
|||
}
|
||||
|
||||
Browser::LoginItemSettings Browser::GetLoginItemSettings(
|
||||
LoginItemSettings options) {
|
||||
const LoginItemSettings& options) {
|
||||
return LoginItemSettings();
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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_) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>>
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -145,4 +145,4 @@ OffScreenRenderWidgetHostView::GetDelegatedFrameHost() const {
|
|||
return browser_compositor_->GetDelegatedFrameHost();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace atom
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -70,7 +70,7 @@ Role kRolesMap[] = {
|
|||
// while its context menu is still open.
|
||||
[self cancel];
|
||||
|
||||
model_ = NULL;
|
||||
model_ = nullptr;
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -66,7 +66,8 @@ void WebContentsPermissionHelper::RequestPermission(
|
|||
|
||||
void WebContentsPermissionHelper::RequestFullscreenPermission(
|
||||
const base::Callback<void(bool)>& callback) {
|
||||
RequestPermission((content::PermissionType)(PermissionType::FULLSCREEN),
|
||||
RequestPermission(
|
||||
static_cast<content::PermissionType>(PermissionType::FULLSCREEN),
|
||||
callback);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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[];
|
||||
|
|
|
@ -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"
|
||||
|
@ -99,9 +101,20 @@ bool MoveItemToTrash(const base::FilePath& full_path) {
|
|||
std::string trash;
|
||||
if (getenv(ELECTRON_TRASH) != NULL) {
|
||||
trash = getenv(ELECTRON_TRASH);
|
||||
} else {
|
||||
// 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;
|
||||
|
||||
|
|
|
@ -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()";
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
190
atom/renderer/renderer_client_base.cc
Normal file
190
atom/renderer/renderer_client_base.cc
Normal 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
|
50
atom/renderer/renderer_client_base.h
Normal file
50
atom/renderer/renderer_client_base.h
Normal 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_
|
|
@ -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
|
||||
|
|
|
@ -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
|
@ -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的所有升级都是由您(开发人员)进行的手动操作。
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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`.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -46,14 +46,6 @@ 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.
|
||||
* `callback` Function (optional)
|
||||
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
165
docs/api/menu.md
165
docs/api/menu.md
|
@ -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'}
|
||||
]
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
195
docs/api/sandbox-option.md
Normal 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.
|
|
@ -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`.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)`
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
14
docs/development/chromium-development.md
Normal file
14
docs/development/chromium-development.md
Normal 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)
|
|
@ -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)
|
||||
|
|
11
docs/development/v8-development.md
Normal file
11
docs/development/v8-development.md
Normal 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)
|
|
@ -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
|
||||
|
|
10
electron.gyp
10
electron.gyp
|
@ -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',
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
1
lib/sandboxed_renderer/api/exports/child_process.js
Normal file
1
lib/sandboxed_renderer/api/exports/child_process.js
Normal file
|
@ -0,0 +1 @@
|
|||
module.exports = require('electron').remote.require('child_process')
|
|
@ -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')
|
||||
|
|
1
lib/sandboxed_renderer/api/exports/fs.js
Normal file
1
lib/sandboxed_renderer/api/exports/fs.js
Normal file
|
@ -0,0 +1 @@
|
|||
module.exports = require('electron').remote.require('fs')
|
1
lib/sandboxed_renderer/api/exports/os.js
Normal file
1
lib/sandboxed_renderer/api/exports/os.js
Normal file
|
@ -0,0 +1 @@
|
|||
module.exports = require('electron').remote.require('os')
|
|
@ -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)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "electron",
|
||||
"version": "1.6.4",
|
||||
"version": "1.6.5",
|
||||
"devDependencies": {
|
||||
"asar": "^0.11.0",
|
||||
"browserify": "^13.1.0",
|
||||
|
|
|
@ -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=[]):
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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
75
script/verify-ffmpeg.py
Executable 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())
|
|
@ -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')
|
||||
|
|
|
@ -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,15 +12,20 @@ const {remote} = require('electron')
|
|||
const {app, BrowserWindow, crashReporter} = remote.require('electron')
|
||||
|
||||
describe('crashReporter module', function () {
|
||||
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
|
||||
|
||||
beforeEach(function () {
|
||||
w = new BrowserWindow({
|
||||
w = new BrowserWindow(Object.assign({
|
||||
show: false
|
||||
})
|
||||
}, browserWindowOpts))
|
||||
tempDirectory = temp.mkdirSync('electronCrashReporterSpec-')
|
||||
originalTempDirectory = app.getPath('temp')
|
||||
app.setPath('temp', tempDirectory)
|
||||
|
@ -30,10 +36,6 @@ describe('crashReporter module', function () {
|
|||
return closeWindow(w).then(function () { w = null })
|
||||
})
|
||||
|
||||
if (process.mas) {
|
||||
return
|
||||
}
|
||||
|
||||
it('should send minidump when renderer crashes', function (done) {
|
||||
if (process.env.APPVEYOR === 'True') return done()
|
||||
if (process.env.TRAVIS === 'true') return done()
|
||||
|
@ -72,6 +74,93 @@ describe('crashReporter module', function () {
|
|||
})
|
||||
})
|
||||
|
||||
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()
|
||||
|
@ -91,6 +180,16 @@ describe('crashReporter module', function () {
|
|||
done: done
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
generateSpecs('without sandbox', {})
|
||||
generateSpecs('with sandbox ', {
|
||||
webPreferences: {
|
||||
sandbox: true,
|
||||
preload: path.join(fixtures, 'module', 'preload-sandbox.js')
|
||||
}
|
||||
})
|
||||
|
||||
describe('.start(options)', function () {
|
||||
it('requires that the companyName and submitURL options be specified', function () {
|
||||
|
@ -204,4 +303,5 @@ const startServer = ({callback, processType, done}) => {
|
|||
}
|
||||
callback(port)
|
||||
})
|
||||
return server
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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 = [{
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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'))
|
||||
|
|
12
spec/fixtures/api/crash.html
vendored
12
spec/fixtures/api/crash.html
vendored
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue