Merge remote-tracking branch 'refs/remotes/atom/master'
This commit is contained in:
commit
72e8660245
83 changed files with 3193 additions and 481 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,4 +1,5 @@
|
|||
.DS_Store
|
||||
/.idea/
|
||||
/build/
|
||||
/dist/
|
||||
/external_binaries/
|
||||
|
|
2
atom.gyp
2
atom.gyp
|
@ -4,7 +4,7 @@
|
|||
'product_name%': 'Electron',
|
||||
'company_name%': 'GitHub, Inc',
|
||||
'company_abbr%': 'github',
|
||||
'version%': '0.34.0',
|
||||
'version%': '0.34.1',
|
||||
},
|
||||
'includes': [
|
||||
'filenames.gypi',
|
||||
|
|
|
@ -13,11 +13,13 @@
|
|||
|
||||
#include "atom/browser/api/atom_api_menu.h"
|
||||
#include "atom/browser/api/atom_api_session.h"
|
||||
#include "atom/browser/api/atom_api_web_contents.h"
|
||||
#include "atom/browser/atom_browser_context.h"
|
||||
#include "atom/browser/atom_browser_main_parts.h"
|
||||
#include "atom/browser/browser.h"
|
||||
#include "atom/browser/api/atom_api_web_contents.h"
|
||||
#include "atom/browser/login_handler.h"
|
||||
#include "atom/common/native_mate_converters/callback.h"
|
||||
#include "atom/common/native_mate_converters/content_converter.h"
|
||||
#include "atom/common/native_mate_converters/file_path_converter.h"
|
||||
#include "atom/common/node_includes.h"
|
||||
#include "atom/common/options_switches.h"
|
||||
|
@ -111,12 +113,27 @@ int GetPathConstant(const std::string& name) {
|
|||
return -1;
|
||||
}
|
||||
|
||||
bool NotificationCallbackWrapper(
|
||||
const ProcessSingleton::NotificationCallback& callback,
|
||||
const base::CommandLine::StringVector& cmd,
|
||||
const base::FilePath& cwd) {
|
||||
// Make sure the callback is called after app gets ready.
|
||||
if (Browser::Get()->is_ready()) {
|
||||
callback.Run(cmd, cwd);
|
||||
} else {
|
||||
scoped_refptr<base::SingleThreadTaskRunner> task_runner(
|
||||
base::ThreadTaskRunnerHandle::Get());
|
||||
task_runner->PostTask(
|
||||
FROM_HERE, base::Bind(base::IgnoreResult(callback), cmd, cwd));
|
||||
}
|
||||
// ProcessSingleton needs to know whether current process is quiting.
|
||||
return !Browser::Get()->is_shutting_down();
|
||||
}
|
||||
|
||||
void OnClientCertificateSelected(
|
||||
v8::Isolate* isolate,
|
||||
std::shared_ptr<content::ClientCertificateDelegate> delegate,
|
||||
mate::Arguments* args) {
|
||||
v8::Locker locker(isolate);
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
mate::Dictionary cert_data;
|
||||
if (!(args->Length() == 1 && args->GetNext(&cert_data))) {
|
||||
args->ThrowError();
|
||||
|
@ -130,10 +147,18 @@ void OnClientCertificateSelected(
|
|||
net::X509Certificate::CreateCertificateListFromBytes(
|
||||
encoded_data.data(), encoded_data.size(),
|
||||
net::X509Certificate::FORMAT_AUTO);
|
||||
|
||||
delegate->ContinueWithCertificate(certs[0].get());
|
||||
}
|
||||
|
||||
void PassLoginInformation(scoped_refptr<LoginHandler> login_handler,
|
||||
mate::Arguments* args) {
|
||||
base::string16 username, password;
|
||||
if (args->GetNext(&username) && args->GetNext(&password))
|
||||
login_handler->Login(username, password);
|
||||
else
|
||||
login_handler->CancelAuth();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
App::App() {
|
||||
|
@ -160,6 +185,11 @@ void App::OnWindowAllClosed() {
|
|||
|
||||
void App::OnQuit() {
|
||||
Emit("quit");
|
||||
|
||||
if (process_singleton_.get()) {
|
||||
process_singleton_->Cleanup();
|
||||
process_singleton_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void App::OnOpenFile(bool* prevent_default, const std::string& file_path) {
|
||||
|
@ -211,6 +241,31 @@ void App::OnSelectCertificate(
|
|||
cert_request_info->client_certs[0].get());
|
||||
}
|
||||
|
||||
void App::OnLogin(LoginHandler* login_handler) {
|
||||
// Convert the args explicitly since they will be passed for twice.
|
||||
v8::Locker locker(isolate());
|
||||
v8::HandleScope handle_scope(isolate());
|
||||
auto web_contents =
|
||||
WebContents::CreateFrom(isolate(), login_handler->GetWebContents());
|
||||
auto request = mate::ConvertToV8(isolate(), login_handler->request());
|
||||
auto auth_info = mate::ConvertToV8(isolate(), login_handler->auth_info());
|
||||
auto callback = mate::ConvertToV8(
|
||||
isolate(),
|
||||
base::Bind(&PassLoginInformation, make_scoped_refptr(login_handler)));
|
||||
|
||||
bool prevent_default =
|
||||
Emit("login", web_contents, request, auth_info, callback);
|
||||
|
||||
// Also pass it to WebContents.
|
||||
if (!prevent_default)
|
||||
prevent_default =
|
||||
web_contents->Emit("login", request, auth_info, callback);
|
||||
|
||||
// Default behavior is to always cancel the auth.
|
||||
if (!prevent_default)
|
||||
login_handler->CancelAuth();
|
||||
}
|
||||
|
||||
void App::OnGpuProcessCrashed(base::TerminationStatus exit_code) {
|
||||
Emit("gpu-process-crashed");
|
||||
}
|
||||
|
@ -268,6 +323,28 @@ v8::Local<v8::Value> App::DefaultSession(v8::Isolate* isolate) {
|
|||
return v8::Local<v8::Value>::New(isolate, default_session_);
|
||||
}
|
||||
|
||||
bool App::MakeSingleInstance(
|
||||
const ProcessSingleton::NotificationCallback& callback) {
|
||||
if (process_singleton_.get())
|
||||
return false;
|
||||
|
||||
base::FilePath user_dir;
|
||||
PathService::Get(brightray::DIR_USER_DATA, &user_dir);
|
||||
process_singleton_.reset(new ProcessSingleton(
|
||||
user_dir, base::Bind(NotificationCallbackWrapper, callback)));
|
||||
|
||||
switch (process_singleton_->NotifyOtherProcessOrCreate()) {
|
||||
case ProcessSingleton::NotifyResult::LOCK_ERROR:
|
||||
case ProcessSingleton::NotifyResult::PROFILE_IN_USE:
|
||||
case ProcessSingleton::NotifyResult::PROCESS_NOTIFIED:
|
||||
process_singleton_.reset();
|
||||
return true;
|
||||
case ProcessSingleton::NotifyResult::PROCESS_NONE:
|
||||
default: // Shouldn't be needed, but VS warns if it is not there.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
mate::ObjectTemplateBuilder App::GetObjectTemplateBuilder(
|
||||
v8::Isolate* isolate) {
|
||||
auto browser = base::Unretained(Browser::Get());
|
||||
|
@ -294,6 +371,7 @@ mate::ObjectTemplateBuilder App::GetObjectTemplateBuilder(
|
|||
.SetMethod("allowNTLMCredentialsForAllDomains",
|
||||
&App::AllowNTLMCredentialsForAllDomains)
|
||||
.SetMethod("getLocale", &App::GetLocale)
|
||||
.SetMethod("makeSingleInstance", &App::MakeSingleInstance)
|
||||
.SetProperty("defaultSession", &App::DefaultSession);
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
#include "atom/browser/api/event_emitter.h"
|
||||
#include "atom/browser/browser_observer.h"
|
||||
#include "atom/common/native_mate_converters/callback.h"
|
||||
#include "chrome/browser/process_singleton.h"
|
||||
#include "content/public/browser/gpu_data_manager_observer.h"
|
||||
#include "native_mate/handle.h"
|
||||
|
||||
|
@ -48,6 +50,7 @@ class App : public mate::EventEmitter,
|
|||
content::WebContents* web_contents,
|
||||
net::SSLCertRequestInfo* cert_request_info,
|
||||
scoped_ptr<content::ClientCertificateDelegate> delegate) override;
|
||||
void OnLogin(LoginHandler* login_handler) override;
|
||||
|
||||
// content::GpuDataManagerObserver:
|
||||
void OnGpuProcessCrashed(base::TerminationStatus exit_code) override;
|
||||
|
@ -66,12 +69,15 @@ class App : public mate::EventEmitter,
|
|||
void SetDesktopName(const std::string& desktop_name);
|
||||
void SetAppUserModelId(const std::string& app_id);
|
||||
void AllowNTLMCredentialsForAllDomains(bool should_allow);
|
||||
|
||||
bool MakeSingleInstance(
|
||||
const ProcessSingleton::NotificationCallback& callback);
|
||||
std::string GetLocale();
|
||||
v8::Local<v8::Value> DefaultSession(v8::Isolate* isolate);
|
||||
|
||||
v8::Global<v8::Value> default_session_;
|
||||
|
||||
scoped_ptr<ProcessSingleton> process_singleton_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(App);
|
||||
};
|
||||
|
||||
|
|
|
@ -5,12 +5,31 @@
|
|||
#include "atom/browser/api/atom_api_auto_updater.h"
|
||||
|
||||
#include "base/time/time.h"
|
||||
#include "atom/browser/auto_updater.h"
|
||||
#include "atom/browser/browser.h"
|
||||
#include "atom/browser/native_window.h"
|
||||
#include "atom/browser/window_list.h"
|
||||
#include "atom/common/native_mate_converters/callback.h"
|
||||
#include "atom/common/node_includes.h"
|
||||
#include "native_mate/dictionary.h"
|
||||
#include "native_mate/object_template_builder.h"
|
||||
|
||||
namespace mate {
|
||||
|
||||
template<>
|
||||
struct Converter<base::Time> {
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
const base::Time& val) {
|
||||
v8::MaybeLocal<v8::Value> date = v8::Date::New(
|
||||
isolate->GetCurrentContext(), val.ToJsTime());
|
||||
if (date.IsEmpty())
|
||||
return v8::Null(isolate);
|
||||
else
|
||||
return date.ToLocalChecked();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mate
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace api {
|
||||
|
@ -20,11 +39,18 @@ AutoUpdater::AutoUpdater() {
|
|||
}
|
||||
|
||||
AutoUpdater::~AutoUpdater() {
|
||||
auto_updater::AutoUpdater::SetDelegate(NULL);
|
||||
auto_updater::AutoUpdater::SetDelegate(nullptr);
|
||||
}
|
||||
|
||||
void AutoUpdater::OnError(const std::string& error) {
|
||||
Emit("error", error);
|
||||
void AutoUpdater::OnError(const std::string& message) {
|
||||
v8::Locker locker(isolate());
|
||||
v8::HandleScope handle_scope(isolate());
|
||||
auto error = v8::Exception::Error(mate::StringToV8(isolate(), message));
|
||||
EmitCustomEvent(
|
||||
"error",
|
||||
error->ToObject(isolate()->GetCurrentContext()).ToLocalChecked(),
|
||||
// Message is also emitted to keep compatibility with old code.
|
||||
message);
|
||||
}
|
||||
|
||||
void AutoUpdater::OnCheckingForUpdate() {
|
||||
|
@ -42,11 +68,14 @@ void AutoUpdater::OnUpdateNotAvailable() {
|
|||
void AutoUpdater::OnUpdateDownloaded(const std::string& release_notes,
|
||||
const std::string& release_name,
|
||||
const base::Time& release_date,
|
||||
const std::string& update_url,
|
||||
const base::Closure& quit_and_install) {
|
||||
quit_and_install_ = quit_and_install;
|
||||
Emit("update-downloaded-raw", release_notes, release_name,
|
||||
release_date.ToJsTime(), update_url);
|
||||
const std::string& url) {
|
||||
Emit("update-downloaded", release_notes, release_name, release_date, url,
|
||||
// Keep compatibility with old APIs.
|
||||
base::Bind(&AutoUpdater::QuitAndInstall, base::Unretained(this)));
|
||||
}
|
||||
|
||||
void AutoUpdater::OnWindowAllClosed() {
|
||||
QuitAndInstall();
|
||||
}
|
||||
|
||||
mate::ObjectTemplateBuilder AutoUpdater::GetObjectTemplateBuilder(
|
||||
|
@ -54,14 +83,21 @@ mate::ObjectTemplateBuilder AutoUpdater::GetObjectTemplateBuilder(
|
|||
return mate::ObjectTemplateBuilder(isolate)
|
||||
.SetMethod("setFeedUrl", &auto_updater::AutoUpdater::SetFeedURL)
|
||||
.SetMethod("checkForUpdates", &auto_updater::AutoUpdater::CheckForUpdates)
|
||||
.SetMethod("_quitAndInstall", &AutoUpdater::QuitAndInstall);
|
||||
.SetMethod("quitAndInstall", &AutoUpdater::QuitAndInstall);
|
||||
}
|
||||
|
||||
void AutoUpdater::QuitAndInstall() {
|
||||
if (quit_and_install_.is_null())
|
||||
Browser::Get()->Shutdown();
|
||||
else
|
||||
quit_and_install_.Run();
|
||||
// If we don't have any window then quitAndInstall immediately.
|
||||
WindowList* window_list = WindowList::GetInstance();
|
||||
if (window_list->size() == 0) {
|
||||
auto_updater::AutoUpdater::QuitAndInstall();
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise do the restart after all windows have been closed.
|
||||
window_list->AddObserver(this);
|
||||
for (NativeWindow* window : *window_list)
|
||||
window->Close();
|
||||
}
|
||||
|
||||
// static
|
||||
|
|
|
@ -7,9 +7,9 @@
|
|||
|
||||
#include <string>
|
||||
|
||||
#include "base/callback.h"
|
||||
#include "atom/browser/api/event_emitter.h"
|
||||
#include "atom/browser/auto_updater_delegate.h"
|
||||
#include "atom/browser/auto_updater.h"
|
||||
#include "atom/browser/window_list_observer.h"
|
||||
#include "native_mate/handle.h"
|
||||
|
||||
namespace atom {
|
||||
|
@ -17,7 +17,8 @@ namespace atom {
|
|||
namespace api {
|
||||
|
||||
class AutoUpdater : public mate::EventEmitter,
|
||||
public auto_updater::AutoUpdaterDelegate {
|
||||
public auto_updater::Delegate,
|
||||
public WindowListObserver {
|
||||
public:
|
||||
static mate::Handle<AutoUpdater> Create(v8::Isolate* isolate);
|
||||
|
||||
|
@ -25,17 +26,18 @@ class AutoUpdater : public mate::EventEmitter,
|
|||
AutoUpdater();
|
||||
virtual ~AutoUpdater();
|
||||
|
||||
// AutoUpdaterDelegate implementations.
|
||||
// Delegate implementations.
|
||||
void OnError(const std::string& error) override;
|
||||
void OnCheckingForUpdate() override;
|
||||
void OnUpdateAvailable() override;
|
||||
void OnUpdateNotAvailable() override;
|
||||
void OnUpdateDownloaded(
|
||||
const std::string& release_notes,
|
||||
const std::string& release_name,
|
||||
const base::Time& release_date,
|
||||
const std::string& update_url,
|
||||
const base::Closure& quit_and_install) override;
|
||||
void OnUpdateDownloaded(const std::string& release_notes,
|
||||
const std::string& release_name,
|
||||
const base::Time& release_date,
|
||||
const std::string& update_url) override;
|
||||
|
||||
// WindowListObserver:
|
||||
void OnWindowAllClosed() override;
|
||||
|
||||
// mate::Wrappable implementations:
|
||||
mate::ObjectTemplateBuilder GetObjectTemplateBuilder(
|
||||
|
@ -44,8 +46,6 @@ class AutoUpdater : public mate::EventEmitter,
|
|||
private:
|
||||
void QuitAndInstall();
|
||||
|
||||
base::Closure quit_and_install_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(AutoUpdater);
|
||||
};
|
||||
|
||||
|
|
|
@ -12,27 +12,12 @@
|
|||
#include "atom/browser/net/url_request_fetch_job.h"
|
||||
#include "atom/browser/net/url_request_string_job.h"
|
||||
#include "atom/common/native_mate_converters/callback.h"
|
||||
#include "atom/common/native_mate_converters/content_converter.h"
|
||||
#include "atom/common/node_includes.h"
|
||||
#include "native_mate/dictionary.h"
|
||||
|
||||
using content::BrowserThread;
|
||||
|
||||
namespace mate {
|
||||
|
||||
template<>
|
||||
struct Converter<const net::URLRequest*> {
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
const net::URLRequest* val) {
|
||||
return mate::ObjectTemplateBuilder(isolate)
|
||||
.SetValue("method", val->method())
|
||||
.SetValue("url", val->url().spec())
|
||||
.SetValue("referrer", val->referrer())
|
||||
.Build()->NewInstance();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mate
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace api {
|
||||
|
|
|
@ -105,6 +105,24 @@ struct Converter<ClearStorageDataOptions> {
|
|||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Converter<net::ProxyConfig> {
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
net::ProxyConfig* out) {
|
||||
std::string proxy;
|
||||
if (!ConvertFromV8(isolate, val, &proxy))
|
||||
return false;
|
||||
auto pac_url = GURL(proxy);
|
||||
if (pac_url.is_valid()) {
|
||||
out->set_pac_url(pac_url);
|
||||
} else {
|
||||
out->proxy_rules().ParseFromString(proxy);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mate
|
||||
|
||||
namespace atom {
|
||||
|
@ -209,12 +227,12 @@ void ClearHttpCacheInIO(
|
|||
}
|
||||
|
||||
void SetProxyInIO(net::URLRequestContextGetter* getter,
|
||||
const std::string& proxy,
|
||||
const net::ProxyConfig& config,
|
||||
const base::Closure& callback) {
|
||||
net::ProxyConfig config;
|
||||
config.proxy_rules().ParseFromString(proxy);
|
||||
auto proxy_service = getter->GetURLRequestContext()->proxy_service();
|
||||
proxy_service->ResetConfigService(new net::ProxyConfigServiceFixed(config));
|
||||
// Refetches and applies the new pac script if provided.
|
||||
proxy_service->ForceReloadProxyConfig();
|
||||
RunCallbackInUI(callback);
|
||||
}
|
||||
|
||||
|
@ -287,11 +305,11 @@ void Session::ClearStorageData(mate::Arguments* args) {
|
|||
base::Time(), base::Time::Max(), callback);
|
||||
}
|
||||
|
||||
void Session::SetProxy(const std::string& proxy,
|
||||
void Session::SetProxy(const net::ProxyConfig& config,
|
||||
const base::Closure& callback) {
|
||||
auto getter = browser_context_->GetRequestContext();
|
||||
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
|
||||
base::Bind(&SetProxyInIO, base::Unretained(getter), proxy, callback));
|
||||
base::Bind(&SetProxyInIO, base::Unretained(getter), config, callback));
|
||||
}
|
||||
|
||||
void Session::SetDownloadPath(const base::FilePath& path) {
|
||||
|
|
|
@ -23,6 +23,10 @@ class Arguments;
|
|||
class Dictionary;
|
||||
}
|
||||
|
||||
namespace net {
|
||||
class ProxyConfig;
|
||||
}
|
||||
|
||||
namespace atom {
|
||||
|
||||
class AtomBrowserContext;
|
||||
|
@ -64,7 +68,7 @@ class Session: public mate::TrackableObject<Session>,
|
|||
void ResolveProxy(const GURL& url, ResolveProxyCallback callback);
|
||||
void ClearCache(const net::CompletionCallback& callback);
|
||||
void ClearStorageData(mate::Arguments* args);
|
||||
void SetProxy(const std::string& proxy, const base::Closure& callback);
|
||||
void SetProxy(const net::ProxyConfig& config, const base::Closure& callback);
|
||||
void SetDownloadPath(const base::FilePath& path);
|
||||
void EnableNetworkEmulation(const mate::Dictionary& options);
|
||||
void DisableNetworkEmulation();
|
||||
|
|
|
@ -618,6 +618,10 @@ void WebContents::LoadURL(const GURL& url, const mate::Dictionary& options) {
|
|||
if (options.Get("userAgent", &user_agent))
|
||||
SetUserAgent(user_agent);
|
||||
|
||||
std::string extra_headers;
|
||||
if (options.Get("extraHeaders", &extra_headers))
|
||||
params.extra_headers = extra_headers;
|
||||
|
||||
params.transition_type = ui::PAGE_TRANSITION_TYPED;
|
||||
params.should_clear_history_list = true;
|
||||
params.override_user_agent = content::NavigationController::UA_OVERRIDE_TRUE;
|
||||
|
|
|
@ -60,6 +60,16 @@ void OnCapturePageDone(
|
|||
callback.Run(gfx::Image::CreateFrom1xBitmap(bitmap));
|
||||
}
|
||||
|
||||
#if defined(OS_WIN)
|
||||
v8::Local<v8::Value> ToBuffer(v8::Isolate* isolate, void* val, int size) {
|
||||
auto buffer = node::Buffer::New(isolate, static_cast<char*>(val), size);
|
||||
if (buffer.IsEmpty())
|
||||
return v8::Null(isolate);
|
||||
else
|
||||
return buffer.ToLocalChecked();
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
|
@ -189,6 +199,16 @@ void Window::OnExecuteWindowsCommand(const std::string& command_name) {
|
|||
Emit("app-command", command_name);
|
||||
}
|
||||
|
||||
#if defined(OS_WIN)
|
||||
void Window::OnWindowMessage(UINT message, WPARAM w_param, LPARAM l_param) {
|
||||
if (IsWindowMessageHooked(message)) {
|
||||
messages_callback_map_[message].Run(
|
||||
ToBuffer(isolate(), static_cast<void*>(&w_param), sizeof(WPARAM)),
|
||||
ToBuffer(isolate(), static_cast<void*>(&l_param), sizeof(LPARAM)));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// static
|
||||
mate::Wrappable* Window::New(v8::Isolate* isolate,
|
||||
const mate::Dictionary& options) {
|
||||
|
@ -385,6 +405,10 @@ bool Window::IsKiosk() {
|
|||
return window_->IsKiosk();
|
||||
}
|
||||
|
||||
void Window::SetBackgroundColor(const std::string& color_name) {
|
||||
window_->SetBackgroundColor(color_name);
|
||||
}
|
||||
|
||||
void Window::FocusOnWebView() {
|
||||
window_->FocusOnWebView();
|
||||
}
|
||||
|
@ -488,6 +512,29 @@ bool Window::IsMenuBarVisible() {
|
|||
return window_->IsMenuBarVisible();
|
||||
}
|
||||
|
||||
#if defined(OS_WIN)
|
||||
bool Window::HookWindowMessage(UINT message,
|
||||
const MessageCallback& callback) {
|
||||
messages_callback_map_[message] = callback;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Window::UnhookWindowMessage(UINT message) {
|
||||
if (!ContainsKey(messages_callback_map_, message))
|
||||
return;
|
||||
|
||||
messages_callback_map_.erase(message);
|
||||
}
|
||||
|
||||
bool Window::IsWindowMessageHooked(UINT message) {
|
||||
return ContainsKey(messages_callback_map_, message);
|
||||
}
|
||||
|
||||
void Window::UnhookAllWindowMessages() {
|
||||
messages_callback_map_.clear();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
void Window::ShowDefinitionForSelection() {
|
||||
window_->ShowDefinitionForSelection();
|
||||
|
@ -564,6 +611,7 @@ void Window::BuildPrototype(v8::Isolate* isolate,
|
|||
.SetMethod("setSkipTaskbar", &Window::SetSkipTaskbar)
|
||||
.SetMethod("setKiosk", &Window::SetKiosk)
|
||||
.SetMethod("isKiosk", &Window::IsKiosk)
|
||||
.SetMethod("setBackgroundColor", &Window::SetBackgroundColor)
|
||||
.SetMethod("setRepresentedFilename", &Window::SetRepresentedFilename)
|
||||
.SetMethod("getRepresentedFilename", &Window::GetRepresentedFilename)
|
||||
.SetMethod("setDocumentEdited", &Window::SetDocumentEdited)
|
||||
|
@ -585,6 +633,12 @@ void Window::BuildPrototype(v8::Isolate* isolate,
|
|||
&Window::SetVisibleOnAllWorkspaces)
|
||||
.SetMethod("isVisibleOnAllWorkspaces",
|
||||
&Window::IsVisibleOnAllWorkspaces)
|
||||
#if defined(OS_WIN)
|
||||
.SetMethod("hookWindowMessage", &Window::HookWindowMessage)
|
||||
.SetMethod("isWindowMessageHooked", &Window::IsWindowMessageHooked)
|
||||
.SetMethod("unhookWindowMessage", &Window::UnhookWindowMessage)
|
||||
.SetMethod("unhookAllWindowMessages", &Window::UnhookAllWindowMessages)
|
||||
#endif
|
||||
#if defined(OS_MACOSX)
|
||||
.SetMethod("showDefinitionForSelection",
|
||||
&Window::ShowDefinitionForSelection)
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#ifndef ATOM_BROWSER_API_ATOM_API_WINDOW_H_
|
||||
#define ATOM_BROWSER_API_ATOM_API_WINDOW_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
@ -75,6 +76,10 @@ class Window : public mate::TrackableObject<Window>,
|
|||
void OnRendererResponsive() override;
|
||||
void OnExecuteWindowsCommand(const std::string& command_name) override;
|
||||
|
||||
#if defined(OS_WIN)
|
||||
void OnWindowMessage(UINT message, WPARAM w_param, LPARAM l_param) override;
|
||||
#endif
|
||||
|
||||
// mate::Wrappable:
|
||||
bool IsDestroyed() const override;
|
||||
|
||||
|
@ -122,6 +127,7 @@ class Window : public mate::TrackableObject<Window>,
|
|||
void SetSkipTaskbar(bool skip);
|
||||
void SetKiosk(bool kiosk);
|
||||
bool IsKiosk();
|
||||
void SetBackgroundColor(const std::string& color_name);
|
||||
void FocusOnWebView();
|
||||
void BlurWebView();
|
||||
bool IsWebViewFocused();
|
||||
|
@ -142,6 +148,16 @@ class Window : public mate::TrackableObject<Window>,
|
|||
bool IsMenuBarVisible();
|
||||
void SetAspectRatio(double aspect_ratio, mate::Arguments* args);
|
||||
|
||||
#if defined(OS_WIN)
|
||||
typedef base::Callback<void(v8::Local<v8::Value>,
|
||||
v8::Local<v8::Value>)> MessageCallback;
|
||||
|
||||
bool HookWindowMessage(UINT message, const MessageCallback& callback);
|
||||
bool IsWindowMessageHooked(UINT message);
|
||||
void UnhookWindowMessage(UINT message);
|
||||
void UnhookAllWindowMessages();
|
||||
#endif
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
void ShowDefinitionForSelection();
|
||||
#endif
|
||||
|
@ -152,6 +168,11 @@ class Window : public mate::TrackableObject<Window>,
|
|||
int32_t ID() const;
|
||||
v8::Local<v8::Value> WebContents(v8::Isolate* isolate);
|
||||
|
||||
#if defined(OS_WIN)
|
||||
typedef std::map<UINT, MessageCallback> MessageCallbackMap;
|
||||
MessageCallbackMap messages_callback_map_;
|
||||
#endif
|
||||
|
||||
v8::Global<v8::Value> web_contents_;
|
||||
v8::Global<v8::Value> menu_;
|
||||
|
||||
|
|
|
@ -1,24 +1,7 @@
|
|||
autoUpdater = process.atomBinding('auto_updater').autoUpdater
|
||||
EventEmitter = require('events').EventEmitter
|
||||
|
||||
autoUpdater.__proto__ = EventEmitter.prototype
|
||||
|
||||
autoUpdater.on 'update-downloaded-raw', (args...) ->
|
||||
args[3] = new Date(args[3]) # releaseDate
|
||||
@emit 'update-downloaded', args..., => @quitAndInstall()
|
||||
|
||||
autoUpdater.quitAndInstall = ->
|
||||
# If we don't have any window then quitAndInstall immediately.
|
||||
BrowserWindow = require 'browser-window'
|
||||
windows = BrowserWindow.getAllWindows()
|
||||
if windows.length is 0
|
||||
@_quitAndInstall()
|
||||
return
|
||||
|
||||
# Do the restart after all windows have been closed.
|
||||
app = require 'app'
|
||||
app.removeAllListeners 'window-all-closed'
|
||||
app.once 'window-all-closed', @_quitAndInstall.bind(this)
|
||||
win.close() for win in windows
|
||||
|
||||
module.exports = autoUpdater
|
||||
switch process.platform
|
||||
when 'win32'
|
||||
module.exports = require './auto-updater/auto-updater-win'
|
||||
when 'darwin'
|
||||
module.exports = require './auto-updater/auto-updater-mac'
|
||||
else
|
||||
throw new Error('auto-updater is not implemented on this platform')
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
{EventEmitter} = require 'events'
|
||||
{autoUpdater} = process.atomBinding 'auto_updater'
|
||||
|
||||
autoUpdater.__proto__ = EventEmitter.prototype
|
||||
|
||||
module.exports = autoUpdater
|
42
atom/browser/api/lib/auto-updater/auto-updater-win.coffee
Normal file
42
atom/browser/api/lib/auto-updater/auto-updater-win.coffee
Normal file
|
@ -0,0 +1,42 @@
|
|||
app = require 'app'
|
||||
url = require 'url'
|
||||
{EventEmitter} = require 'events'
|
||||
|
||||
squirrelUpdate = require './squirrel-update-win'
|
||||
|
||||
class AutoUpdater extends EventEmitter
|
||||
quitAndInstall: ->
|
||||
squirrelUpdate.processStart()
|
||||
app.quit()
|
||||
|
||||
setFeedUrl: (updateUrl) ->
|
||||
@updateUrl = updateUrl
|
||||
|
||||
checkForUpdates: ->
|
||||
return @emitError 'Update URL is not set' unless @updateUrl
|
||||
return @emitError 'Can not find Squirrel' unless squirrelUpdate.supported()
|
||||
|
||||
@emit 'checking-for-update'
|
||||
|
||||
squirrelUpdate.download @updateUrl, (error, update) =>
|
||||
return @emitError error if error?
|
||||
return @emit 'update-not-available' unless update?
|
||||
|
||||
@emit 'update-available'
|
||||
|
||||
squirrelUpdate.update @updateUrl, (error) =>
|
||||
return @emitError error if error?
|
||||
|
||||
{releaseNotes, version} = update
|
||||
# Following information is not available on Windows, so fake them.
|
||||
date = new Date
|
||||
url = @updateUrl
|
||||
|
||||
@emit 'update-downloaded', {}, releaseNotes, version, date, url, => @quitAndInstall()
|
||||
|
||||
# Private: Emit both error object and message, this is to keep compatibility
|
||||
# with Old APIs.
|
||||
emitError: (message) ->
|
||||
@emit 'error', new Error(message), message
|
||||
|
||||
module.exports = new AutoUpdater
|
67
atom/browser/api/lib/auto-updater/squirrel-update-win.coffee
Normal file
67
atom/browser/api/lib/auto-updater/squirrel-update-win.coffee
Normal file
|
@ -0,0 +1,67 @@
|
|||
fs = require 'fs'
|
||||
path = require 'path'
|
||||
{spawn} = require 'child_process'
|
||||
|
||||
appFolder = path.dirname process.execPath # i.e. my-app/app-0.1.13/
|
||||
updateExe = path.resolve appFolder, '..', 'Update.exe' # i.e. my-app/Update.exe
|
||||
exeName = path.basename process.execPath
|
||||
|
||||
# Spawn a command and invoke the callback when it completes with an error
|
||||
# and the output from standard out.
|
||||
spawnUpdate = (args, detached, callback) ->
|
||||
try
|
||||
spawnedProcess = spawn updateExe, args, {detached}
|
||||
catch error
|
||||
# Shouldn't happen, but still guard it.
|
||||
process.nextTick -> callback error
|
||||
return
|
||||
|
||||
stdout = ''
|
||||
stderr = ''
|
||||
spawnedProcess.stdout.on 'data', (data) -> stdout += data
|
||||
spawnedProcess.stderr.on 'data', (data) -> stderr += data
|
||||
|
||||
errorEmitted = false
|
||||
spawnedProcess.on 'error', (error) ->
|
||||
errorEmitted = true
|
||||
callback error
|
||||
spawnedProcess.on 'exit', (code, signal) ->
|
||||
# We may have already emitted an error.
|
||||
return if errorEmitted
|
||||
|
||||
# Process terminated with error.
|
||||
if code isnt 0
|
||||
return callback "Command failed: #{signal ? code}\n#{stderr}"
|
||||
|
||||
# Success.
|
||||
callback null, stdout
|
||||
|
||||
# Start an instance of the installed app.
|
||||
exports.processStart = (callback) ->
|
||||
spawnUpdate ['--processStart', exeName], true, ->
|
||||
|
||||
# Download the releases specified by the URL and write new results to stdout.
|
||||
exports.download = (updateUrl, callback) ->
|
||||
spawnUpdate ['--download', updateUrl], false, (error, stdout) ->
|
||||
return callback(error) if error?
|
||||
|
||||
try
|
||||
# Last line of output is the JSON details about the releases
|
||||
json = stdout.trim().split('\n').pop()
|
||||
update = JSON.parse(json)?.releasesToApply?.pop?()
|
||||
catch
|
||||
return callback "Invalid result:\n#{stdout}"
|
||||
|
||||
callback null, update
|
||||
|
||||
# Update the application to the latest remote version specified by URL.
|
||||
exports.update = (updateUrl, callback) ->
|
||||
spawnUpdate ['--update', updateUrl], false, callback
|
||||
|
||||
# Is the Update.exe installed with the current application?
|
||||
exports.supported = ->
|
||||
try
|
||||
fs.accessSync updateExe, fs.R_OK
|
||||
return true
|
||||
catch
|
||||
return false
|
|
@ -62,17 +62,15 @@ void AtomBrowserMainParts::PreEarlyInitialization() {
|
|||
void AtomBrowserMainParts::PostEarlyInitialization() {
|
||||
brightray::BrowserMainParts::PostEarlyInitialization();
|
||||
|
||||
{
|
||||
// Temporary set the bridge_task_runner_ as current thread's task runner,
|
||||
// so we can fool gin::PerIsolateData to use it as its task runner, instead
|
||||
// of getting current message loop's task runner, which is null for now.
|
||||
bridge_task_runner_ = new BridgeTaskRunner;
|
||||
base::ThreadTaskRunnerHandle handle(bridge_task_runner_);
|
||||
// Temporary set the bridge_task_runner_ as current thread's task runner,
|
||||
// so we can fool gin::PerIsolateData to use it as its task runner, instead
|
||||
// of getting current message loop's task runner, which is null for now.
|
||||
bridge_task_runner_ = new BridgeTaskRunner;
|
||||
base::ThreadTaskRunnerHandle handle(bridge_task_runner_);
|
||||
|
||||
// The ProxyResolverV8 has setup a complete V8 environment, in order to
|
||||
// avoid conflicts we only initialize our V8 environment after that.
|
||||
js_env_.reset(new JavascriptEnvironment);
|
||||
}
|
||||
// The ProxyResolverV8 has setup a complete V8 environment, in order to
|
||||
// avoid conflicts we only initialize our V8 environment after that.
|
||||
js_env_.reset(new JavascriptEnvironment);
|
||||
|
||||
node_bindings_->Initialize();
|
||||
|
||||
|
@ -107,6 +105,7 @@ void AtomBrowserMainParts::PreMainMessageLoopRun() {
|
|||
1000));
|
||||
|
||||
brightray::BrowserMainParts::PreMainMessageLoopRun();
|
||||
BridgeTaskRunner::MessageLoopIsReady();
|
||||
|
||||
#if defined(USE_X11)
|
||||
libgtk2ui::GtkInitFromCommandLine(*base::CommandLine::ForCurrentProcess());
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include "atom/browser/atom_resource_dispatcher_host_delegate.h"
|
||||
|
||||
#include "atom/browser/login_handler.h"
|
||||
#include "atom/common/platform_util.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "net/base/escape.h"
|
||||
|
@ -29,4 +30,11 @@ bool AtomResourceDispatcherHostDelegate::HandleExternalProtocol(
|
|||
return true;
|
||||
}
|
||||
|
||||
content::ResourceDispatcherHostLoginDelegate*
|
||||
AtomResourceDispatcherHostDelegate::CreateLoginDelegate(
|
||||
net::AuthChallengeInfo* auth_info,
|
||||
net::URLRequest* request) {
|
||||
return new LoginHandler(auth_info, request);
|
||||
}
|
||||
|
||||
} // namespace atom
|
||||
|
|
|
@ -21,6 +21,9 @@ class AtomResourceDispatcherHostDelegate
|
|||
bool is_main_frame,
|
||||
ui::PageTransition transition,
|
||||
bool has_user_gesture) override;
|
||||
content::ResourceDispatcherHostLoginDelegate* CreateLoginDelegate(
|
||||
net::AuthChallengeInfo* auth_info,
|
||||
net::URLRequest* request) override;
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
|
|
@ -6,22 +6,25 @@
|
|||
|
||||
namespace auto_updater {
|
||||
|
||||
AutoUpdaterDelegate* AutoUpdater::delegate_ = NULL;
|
||||
Delegate* AutoUpdater::delegate_ = nullptr;
|
||||
|
||||
AutoUpdaterDelegate* AutoUpdater::GetDelegate() {
|
||||
Delegate* AutoUpdater::GetDelegate() {
|
||||
return delegate_;
|
||||
}
|
||||
|
||||
void AutoUpdater::SetDelegate(AutoUpdaterDelegate* delegate) {
|
||||
void AutoUpdater::SetDelegate(Delegate* delegate) {
|
||||
delegate_ = delegate;
|
||||
}
|
||||
|
||||
#if defined(OS_MACOSX) && defined(MAS_BUILD)
|
||||
#if !defined(OS_MACOSX) || defined(MAS_BUILD)
|
||||
void AutoUpdater::SetFeedURL(const std::string& url) {
|
||||
}
|
||||
|
||||
void AutoUpdater::CheckForUpdates() {
|
||||
}
|
||||
|
||||
void AutoUpdater::QuitAndInstall() {
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace auto_updater
|
||||
|
|
|
@ -9,21 +9,48 @@
|
|||
|
||||
#include "base/basictypes.h"
|
||||
|
||||
namespace base {
|
||||
class Time;
|
||||
}
|
||||
|
||||
namespace auto_updater {
|
||||
|
||||
class AutoUpdaterDelegate;
|
||||
class Delegate {
|
||||
public:
|
||||
// An error happened.
|
||||
virtual void OnError(const std::string& error) {}
|
||||
|
||||
// Checking to see if there is an update
|
||||
virtual void OnCheckingForUpdate() {}
|
||||
|
||||
// There is an update available and it is being downloaded
|
||||
virtual void OnUpdateAvailable() {}
|
||||
|
||||
// There is no available update.
|
||||
virtual void OnUpdateNotAvailable() {}
|
||||
|
||||
// There is a new update which has been downloaded.
|
||||
virtual void OnUpdateDownloaded(const std::string& release_notes,
|
||||
const std::string& release_name,
|
||||
const base::Time& release_date,
|
||||
const std::string& update_url) {}
|
||||
|
||||
protected:
|
||||
virtual ~Delegate() {}
|
||||
};
|
||||
|
||||
class AutoUpdater {
|
||||
public:
|
||||
// Gets/Sets the delegate.
|
||||
static AutoUpdaterDelegate* GetDelegate();
|
||||
static void SetDelegate(AutoUpdaterDelegate* delegate);
|
||||
static Delegate* GetDelegate();
|
||||
static void SetDelegate(Delegate* delegate);
|
||||
|
||||
static void SetFeedURL(const std::string& url);
|
||||
static void CheckForUpdates();
|
||||
static void QuitAndInstall();
|
||||
|
||||
private:
|
||||
static AutoUpdaterDelegate* delegate_;
|
||||
static Delegate* delegate_;
|
||||
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(AutoUpdater);
|
||||
};
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
// Copyright (c) 2013 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_AUTO_UPDATER_DELEGATE_H_
|
||||
#define ATOM_BROWSER_AUTO_UPDATER_DELEGATE_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/callback_forward.h"
|
||||
|
||||
namespace base {
|
||||
class Time;
|
||||
}
|
||||
|
||||
namespace auto_updater {
|
||||
|
||||
class AutoUpdaterDelegate {
|
||||
public:
|
||||
// An error happened.
|
||||
virtual void OnError(const std::string& error) {}
|
||||
|
||||
// Checking to see if there is an update
|
||||
virtual void OnCheckingForUpdate() {}
|
||||
|
||||
// There is an update available and it is being downloaded
|
||||
virtual void OnUpdateAvailable() {}
|
||||
|
||||
// There is no available update.
|
||||
virtual void OnUpdateNotAvailable() {}
|
||||
|
||||
// There is a new update which has been downloaded.
|
||||
virtual void OnUpdateDownloaded(const std::string& release_notes,
|
||||
const std::string& release_name,
|
||||
const base::Time& release_date,
|
||||
const std::string& update_url,
|
||||
const base::Closure& quit_and_install) {}
|
||||
|
||||
protected:
|
||||
virtual ~AutoUpdaterDelegate() {}
|
||||
};
|
||||
|
||||
} // namespace auto_updater
|
||||
|
||||
#endif // ATOM_BROWSER_AUTO_UPDATER_DELEGATE_H_
|
|
@ -1,17 +0,0 @@
|
|||
// Copyright (c) 2013 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/auto_updater.h"
|
||||
|
||||
namespace auto_updater {
|
||||
|
||||
// static
|
||||
void AutoUpdater::SetFeedURL(const std::string& url) {
|
||||
}
|
||||
|
||||
// static
|
||||
void AutoUpdater::CheckForUpdates() {
|
||||
}
|
||||
|
||||
} // namespace auto_updater
|
|
@ -12,9 +12,6 @@
|
|||
#include "base/bind.h"
|
||||
#include "base/time/time.h"
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#include "atom/browser/auto_updater_delegate.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace auto_updater {
|
||||
|
||||
|
@ -23,20 +20,12 @@ namespace {
|
|||
// The gloal SQRLUpdater object.
|
||||
SQRLUpdater* g_updater = nil;
|
||||
|
||||
void RelaunchToInstallUpdate() {
|
||||
[[g_updater relaunchToInstallUpdate] subscribeError:^(NSError* error) {
|
||||
AutoUpdaterDelegate* delegate = AutoUpdater::GetDelegate();
|
||||
if (delegate)
|
||||
delegate->OnError(base::SysNSStringToUTF8(error.localizedDescription));
|
||||
}];
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
void AutoUpdater::SetFeedURL(const std::string& feed) {
|
||||
if (g_updater == nil) {
|
||||
AutoUpdaterDelegate* delegate = GetDelegate();
|
||||
Delegate* delegate = GetDelegate();
|
||||
if (!delegate)
|
||||
return;
|
||||
|
||||
|
@ -67,7 +56,7 @@ void AutoUpdater::SetFeedURL(const std::string& feed) {
|
|||
|
||||
// static
|
||||
void AutoUpdater::CheckForUpdates() {
|
||||
AutoUpdaterDelegate* delegate = GetDelegate();
|
||||
Delegate* delegate = GetDelegate();
|
||||
if (!delegate)
|
||||
return;
|
||||
|
||||
|
@ -86,8 +75,7 @@ void AutoUpdater::CheckForUpdates() {
|
|||
base::SysNSStringToUTF8(update.releaseNotes),
|
||||
base::SysNSStringToUTF8(update.releaseName),
|
||||
base::Time::FromDoubleT(update.releaseDate.timeIntervalSince1970),
|
||||
base::SysNSStringToUTF8(update.updateURL.absoluteString),
|
||||
base::Bind(RelaunchToInstallUpdate));
|
||||
base::SysNSStringToUTF8(update.updateURL.absoluteString));
|
||||
} else {
|
||||
// When the completed event is sent with no update, then we know there
|
||||
// is no update available.
|
||||
|
@ -100,4 +88,12 @@ void AutoUpdater::CheckForUpdates() {
|
|||
}];
|
||||
}
|
||||
|
||||
void AutoUpdater::QuitAndInstall() {
|
||||
[[g_updater relaunchToInstallUpdate] subscribeError:^(NSError* error) {
|
||||
Delegate* delegate = AutoUpdater::GetDelegate();
|
||||
if (delegate)
|
||||
delegate->OnError(base::SysNSStringToUTF8(error.localizedDescription));
|
||||
}];
|
||||
}
|
||||
|
||||
} // namespace auto_updater
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
// Copyright (c) 2013 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/auto_updater.h"
|
||||
|
||||
namespace auto_updater {
|
||||
|
||||
// static
|
||||
void AutoUpdater::SetFeedURL(const std::string& url) {
|
||||
}
|
||||
|
||||
// static
|
||||
void AutoUpdater::CheckForUpdates() {
|
||||
}
|
||||
|
||||
} // namespace auto_updater
|
|
@ -8,13 +8,33 @@
|
|||
|
||||
namespace atom {
|
||||
|
||||
// static
|
||||
std::vector<BridgeTaskRunner::TaskPair> BridgeTaskRunner::tasks_;
|
||||
std::vector<BridgeTaskRunner::TaskPair> BridgeTaskRunner::non_nestable_tasks_;
|
||||
|
||||
// static
|
||||
void BridgeTaskRunner::MessageLoopIsReady() {
|
||||
auto message_loop = base::MessageLoop::current();
|
||||
CHECK(message_loop);
|
||||
for (const TaskPair& task : tasks_) {
|
||||
message_loop->task_runner()->PostDelayedTask(
|
||||
base::get<0>(task), base::get<1>(task), base::get<2>(task));
|
||||
}
|
||||
for (const TaskPair& task : non_nestable_tasks_) {
|
||||
message_loop->task_runner()->PostNonNestableDelayedTask(
|
||||
base::get<0>(task), base::get<1>(task), base::get<2>(task));
|
||||
}
|
||||
}
|
||||
|
||||
bool BridgeTaskRunner::PostDelayedTask(
|
||||
const tracked_objects::Location& from_here,
|
||||
const base::Closure& task,
|
||||
base::TimeDelta delay) {
|
||||
auto message_loop = base::MessageLoop::current();
|
||||
if (!message_loop)
|
||||
return false;
|
||||
if (!message_loop) {
|
||||
tasks_.push_back(base::MakeTuple(from_here, task, delay));
|
||||
return true;
|
||||
}
|
||||
|
||||
return message_loop->task_runner()->PostDelayedTask(from_here, task, delay);
|
||||
}
|
||||
|
@ -22,7 +42,7 @@ bool BridgeTaskRunner::PostDelayedTask(
|
|||
bool BridgeTaskRunner::RunsTasksOnCurrentThread() const {
|
||||
auto message_loop = base::MessageLoop::current();
|
||||
if (!message_loop)
|
||||
return false;
|
||||
return true;
|
||||
|
||||
return message_loop->task_runner()->RunsTasksOnCurrentThread();
|
||||
}
|
||||
|
@ -32,8 +52,10 @@ bool BridgeTaskRunner::PostNonNestableDelayedTask(
|
|||
const base::Closure& task,
|
||||
base::TimeDelta delay) {
|
||||
auto message_loop = base::MessageLoop::current();
|
||||
if (!message_loop)
|
||||
return false;
|
||||
if (!message_loop) {
|
||||
non_nestable_tasks_.push_back(base::MakeTuple(from_here, task, delay));
|
||||
return true;
|
||||
}
|
||||
|
||||
return message_loop->task_runner()->PostNonNestableDelayedTask(
|
||||
from_here, task, delay);
|
||||
|
|
|
@ -5,17 +5,23 @@
|
|||
#ifndef ATOM_BROWSER_BRIDGE_TASK_RUNNER_H_
|
||||
#define ATOM_BROWSER_BRIDGE_TASK_RUNNER_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "base/single_thread_task_runner.h"
|
||||
#include "base/tuple.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
// Post all tasks to the current message loop's task runner if available,
|
||||
// otherwise fail silently.
|
||||
// otherwise delay the work until message loop is ready.
|
||||
class BridgeTaskRunner : public base::SingleThreadTaskRunner {
|
||||
public:
|
||||
BridgeTaskRunner() {}
|
||||
~BridgeTaskRunner() override {}
|
||||
|
||||
// Called when message loop is ready.
|
||||
static void MessageLoopIsReady();
|
||||
|
||||
// base::SingleThreadTaskRunner:
|
||||
bool PostDelayedTask(const tracked_objects::Location& from_here,
|
||||
const base::Closure& task,
|
||||
|
@ -27,6 +33,11 @@ class BridgeTaskRunner : public base::SingleThreadTaskRunner {
|
|||
base::TimeDelta delay) override;
|
||||
|
||||
private:
|
||||
using TaskPair = base::Tuple<
|
||||
tracked_objects::Location, base::Closure, base::TimeDelta>;
|
||||
static std::vector<TaskPair> tasks_;
|
||||
static std::vector<TaskPair> non_nestable_tasks_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(BridgeTaskRunner);
|
||||
};
|
||||
|
||||
|
|
|
@ -53,8 +53,14 @@ void Browser::Shutdown() {
|
|||
is_quiting_ = true;
|
||||
|
||||
FOR_EACH_OBSERVER(BrowserObserver, observers_, OnQuit());
|
||||
base::MessageLoop::current()->PostTask(
|
||||
FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
|
||||
|
||||
if (base::MessageLoop::current()) {
|
||||
base::MessageLoop::current()->PostTask(
|
||||
FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
|
||||
} else {
|
||||
// There is no message loop available so we are in early stage.
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
std::string Browser::GetVersion() const {
|
||||
|
@ -128,6 +134,10 @@ void Browser::ClientCertificateSelector(
|
|||
delegate.Pass()));
|
||||
}
|
||||
|
||||
void Browser::RequestLogin(LoginHandler* login_handler) {
|
||||
FOR_EACH_OBSERVER(BrowserObserver, observers_, OnLogin(login_handler));
|
||||
}
|
||||
|
||||
void Browser::NotifyAndShutdown() {
|
||||
if (is_shutdown_)
|
||||
return;
|
||||
|
|
|
@ -29,6 +29,8 @@ class MenuModel;
|
|||
|
||||
namespace atom {
|
||||
|
||||
class LoginHandler;
|
||||
|
||||
// This class is used for control application-wide operations.
|
||||
class Browser : public WindowListObserver {
|
||||
public:
|
||||
|
@ -122,6 +124,9 @@ class Browser : public WindowListObserver {
|
|||
net::SSLCertRequestInfo* cert_request_info,
|
||||
scoped_ptr<content::ClientCertificateDelegate> delegate);
|
||||
|
||||
// Request basic auth login.
|
||||
void RequestLogin(LoginHandler* login_handler);
|
||||
|
||||
void AddObserver(BrowserObserver* obs) {
|
||||
observers_.AddObserver(obs);
|
||||
}
|
||||
|
@ -130,6 +135,7 @@ class Browser : public WindowListObserver {
|
|||
observers_.RemoveObserver(obs);
|
||||
}
|
||||
|
||||
bool is_shutting_down() const { return is_shutdown_; }
|
||||
bool is_quiting() const { return is_quiting_; }
|
||||
bool is_ready() const { return is_ready_; }
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@ class SSLCertRequestInfo;
|
|||
|
||||
namespace atom {
|
||||
|
||||
class LoginHandler;
|
||||
|
||||
class BrowserObserver {
|
||||
public:
|
||||
// The browser is about to close all windows.
|
||||
|
@ -57,6 +59,9 @@ class BrowserObserver {
|
|||
net::SSLCertRequestInfo* cert_request_info,
|
||||
scoped_ptr<content::ClientCertificateDelegate> delegate) {}
|
||||
|
||||
// The browser requests HTTP login.
|
||||
virtual void OnLogin(LoginHandler* login_handler) {}
|
||||
|
||||
protected:
|
||||
virtual ~BrowserObserver() {}
|
||||
};
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include "atom/browser/javascript_environment.h"
|
||||
|
||||
#include "base/command_line.h"
|
||||
#include "gin/array_buffer.h"
|
||||
#include "gin/v8_initializer.h"
|
||||
|
||||
|
@ -20,6 +21,12 @@ JavascriptEnvironment::JavascriptEnvironment()
|
|||
}
|
||||
|
||||
bool JavascriptEnvironment::Initialize() {
|
||||
auto cmd = base::CommandLine::ForCurrentProcess();
|
||||
if (cmd->HasSwitch("debug-brk")) {
|
||||
// Need to be called before v8::Initialize().
|
||||
const char expose_debug_as[] = "--expose_debug_as=v8debug";
|
||||
v8::V8::SetFlagsFromString(expose_debug_as, sizeof(expose_debug_as) - 1);
|
||||
}
|
||||
gin::IsolateHolder::Initialize(gin::IsolateHolder::kNonStrictMode,
|
||||
gin::ArrayBufferAllocator::SharedInstance());
|
||||
return true;
|
||||
|
|
|
@ -10,6 +10,8 @@ valueToMeta = (sender, value, optimizeSimpleObject=false) ->
|
|||
meta.type = 'buffer' if Buffer.isBuffer value
|
||||
meta.type = 'value' if value is null
|
||||
meta.type = 'array' if Array.isArray value
|
||||
meta.type = 'error' if value instanceof Error
|
||||
meta.type = 'date' if value instanceof Date
|
||||
meta.type = 'promise' if value? and value.constructor.name is 'Promise'
|
||||
|
||||
# Treat simple objects as value.
|
||||
|
@ -36,6 +38,10 @@ valueToMeta = (sender, value, optimizeSimpleObject=false) ->
|
|||
meta.value = Array::slice.call value, 0
|
||||
else if meta.type is 'promise'
|
||||
meta.then = valueToMeta(sender, value.then.bind(value))
|
||||
else if meta.type is 'error'
|
||||
meta.message = value.message
|
||||
else if meta.type is 'date'
|
||||
meta.value = value.getTime()
|
||||
else
|
||||
meta.type = 'value'
|
||||
meta.value = value
|
||||
|
@ -43,8 +49,8 @@ valueToMeta = (sender, value, optimizeSimpleObject=false) ->
|
|||
meta
|
||||
|
||||
# Convert Error into meta data.
|
||||
errorToMeta = (error) ->
|
||||
type: 'error', message: error.message, stack: (error.stack || error)
|
||||
exceptionToMeta = (error) ->
|
||||
type: 'exception', message: error.message, stack: (error.stack || error)
|
||||
|
||||
# Convert array of meta data from renderer into array of real values.
|
||||
unwrapArgs = (sender, args) ->
|
||||
|
@ -100,19 +106,19 @@ ipc.on 'ATOM_BROWSER_REQUIRE', (event, module) ->
|
|||
try
|
||||
event.returnValue = valueToMeta event.sender, process.mainModule.require(module)
|
||||
catch e
|
||||
event.returnValue = errorToMeta e
|
||||
event.returnValue = exceptionToMeta e
|
||||
|
||||
ipc.on 'ATOM_BROWSER_GLOBAL', (event, name) ->
|
||||
try
|
||||
event.returnValue = valueToMeta event.sender, global[name]
|
||||
catch e
|
||||
event.returnValue = errorToMeta e
|
||||
event.returnValue = exceptionToMeta e
|
||||
|
||||
ipc.on 'ATOM_BROWSER_CURRENT_WINDOW', (event) ->
|
||||
try
|
||||
event.returnValue = valueToMeta event.sender, event.sender.getOwnerBrowserWindow()
|
||||
catch e
|
||||
event.returnValue = errorToMeta e
|
||||
event.returnValue = exceptionToMeta e
|
||||
|
||||
ipc.on 'ATOM_BROWSER_CURRENT_WEB_CONTENTS', (event) ->
|
||||
event.returnValue = valueToMeta event.sender, event.sender
|
||||
|
@ -126,7 +132,7 @@ ipc.on 'ATOM_BROWSER_CONSTRUCTOR', (event, id, args) ->
|
|||
obj = new (Function::bind.apply(constructor, [null].concat(args)))
|
||||
event.returnValue = valueToMeta event.sender, obj
|
||||
catch e
|
||||
event.returnValue = errorToMeta e
|
||||
event.returnValue = exceptionToMeta e
|
||||
|
||||
ipc.on 'ATOM_BROWSER_FUNCTION_CALL', (event, id, args) ->
|
||||
try
|
||||
|
@ -134,7 +140,7 @@ ipc.on 'ATOM_BROWSER_FUNCTION_CALL', (event, id, args) ->
|
|||
func = objectsRegistry.get id
|
||||
callFunction event, func, global, args
|
||||
catch e
|
||||
event.returnValue = errorToMeta e
|
||||
event.returnValue = exceptionToMeta e
|
||||
|
||||
ipc.on 'ATOM_BROWSER_MEMBER_CONSTRUCTOR', (event, id, method, args) ->
|
||||
try
|
||||
|
@ -144,7 +150,7 @@ ipc.on 'ATOM_BROWSER_MEMBER_CONSTRUCTOR', (event, id, method, args) ->
|
|||
obj = new (Function::bind.apply(constructor, [null].concat(args)))
|
||||
event.returnValue = valueToMeta event.sender, obj
|
||||
catch e
|
||||
event.returnValue = errorToMeta e
|
||||
event.returnValue = exceptionToMeta e
|
||||
|
||||
ipc.on 'ATOM_BROWSER_MEMBER_CALL', (event, id, method, args) ->
|
||||
try
|
||||
|
@ -152,7 +158,7 @@ ipc.on 'ATOM_BROWSER_MEMBER_CALL', (event, id, method, args) ->
|
|||
obj = objectsRegistry.get id
|
||||
callFunction event, obj[method], obj, args
|
||||
catch e
|
||||
event.returnValue = errorToMeta e
|
||||
event.returnValue = exceptionToMeta e
|
||||
|
||||
ipc.on 'ATOM_BROWSER_MEMBER_SET', (event, id, name, value) ->
|
||||
try
|
||||
|
@ -160,14 +166,14 @@ ipc.on 'ATOM_BROWSER_MEMBER_SET', (event, id, name, value) ->
|
|||
obj[name] = value
|
||||
event.returnValue = null
|
||||
catch e
|
||||
event.returnValue = errorToMeta e
|
||||
event.returnValue = exceptionToMeta e
|
||||
|
||||
ipc.on 'ATOM_BROWSER_MEMBER_GET', (event, id, name) ->
|
||||
try
|
||||
obj = objectsRegistry.get id
|
||||
event.returnValue = valueToMeta event.sender, obj[name]
|
||||
catch e
|
||||
event.returnValue = errorToMeta e
|
||||
event.returnValue = exceptionToMeta e
|
||||
|
||||
ipc.on 'ATOM_BROWSER_DEREFERENCE', (event, id) ->
|
||||
objectsRegistry.remove event.sender.getId(), id
|
||||
|
@ -177,4 +183,4 @@ ipc.on 'ATOM_BROWSER_GUEST_WEB_CONTENTS', (event, guestInstanceId) ->
|
|||
guestViewManager = require './guest-view-manager'
|
||||
event.returnValue = valueToMeta event.sender, guestViewManager.getGuest(guestInstanceId)
|
||||
catch e
|
||||
event.returnValue = errorToMeta e
|
||||
event.returnValue = exceptionToMeta e
|
||||
|
|
109
atom/browser/login_handler.cc
Normal file
109
atom/browser/login_handler.cc
Normal file
|
@ -0,0 +1,109 @@
|
|||
// Copyright (c) 2015 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/login_handler.h"
|
||||
|
||||
#include "atom/browser/browser.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/browser/render_frame_host.h"
|
||||
#include "content/public/browser/resource_dispatcher_host.h"
|
||||
#include "content/public/browser/resource_request_info.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "net/base/auth.h"
|
||||
#include "net/url_request/url_request.h"
|
||||
|
||||
using content::BrowserThread;
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace {
|
||||
|
||||
// Helper to remove the ref from an net::URLRequest to the LoginHandler.
|
||||
// Should only be called from the IO thread, since it accesses an
|
||||
// net::URLRequest.
|
||||
void ResetLoginHandlerForRequest(net::URLRequest* request) {
|
||||
content::ResourceDispatcherHost::Get()->ClearLoginDelegateForRequest(request);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
LoginHandler::LoginHandler(net::AuthChallengeInfo* auth_info,
|
||||
net::URLRequest* request)
|
||||
: handled_auth_(false),
|
||||
auth_info_(auth_info),
|
||||
request_(request),
|
||||
render_process_host_id_(0),
|
||||
render_frame_id_(0) {
|
||||
content::ResourceRequestInfo::ForRequest(request_)->GetAssociatedRenderFrame(
|
||||
&render_process_host_id_, &render_frame_id_);
|
||||
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
|
||||
base::Bind(&Browser::RequestLogin,
|
||||
base::Unretained(Browser::Get()),
|
||||
make_scoped_refptr(this)));
|
||||
}
|
||||
|
||||
LoginHandler::~LoginHandler() {
|
||||
}
|
||||
|
||||
content::WebContents* LoginHandler::GetWebContents() const {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
|
||||
content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(
|
||||
render_process_host_id_, render_frame_id_);
|
||||
return content::WebContents::FromRenderFrameHost(rfh);
|
||||
}
|
||||
|
||||
void LoginHandler::Login(const base::string16& username,
|
||||
const base::string16& password) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
if (TestAndSetAuthHandled())
|
||||
return;
|
||||
BrowserThread::PostTask(
|
||||
BrowserThread::IO, FROM_HERE,
|
||||
base::Bind(&LoginHandler::DoLogin, this, username, password));
|
||||
}
|
||||
|
||||
void LoginHandler::CancelAuth() {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
if (TestAndSetAuthHandled())
|
||||
return;
|
||||
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
|
||||
base::Bind(&LoginHandler::DoCancelAuth, this));
|
||||
}
|
||||
|
||||
void LoginHandler::OnRequestCancelled() {
|
||||
TestAndSetAuthHandled();
|
||||
request_ = nullptr;
|
||||
}
|
||||
|
||||
// Marks authentication as handled and returns the previous handled state.
|
||||
bool LoginHandler::TestAndSetAuthHandled() {
|
||||
base::AutoLock lock(handled_auth_lock_);
|
||||
bool was_handled = handled_auth_;
|
||||
handled_auth_ = true;
|
||||
return was_handled;
|
||||
}
|
||||
|
||||
void LoginHandler::DoCancelAuth() {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
||||
|
||||
if (request_) {
|
||||
request_->CancelAuth();
|
||||
// Verify that CancelAuth doesn't destroy the request via our delegate.
|
||||
DCHECK(request_ != nullptr);
|
||||
ResetLoginHandlerForRequest(request_);
|
||||
}
|
||||
}
|
||||
|
||||
void LoginHandler::DoLogin(const base::string16& username,
|
||||
const base::string16& password) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
||||
|
||||
if (request_) {
|
||||
request_->SetAuth(net::AuthCredentials(username, password));
|
||||
ResetLoginHandlerForRequest(request_);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace atom
|
76
atom/browser/login_handler.h
Normal file
76
atom/browser/login_handler.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
// Copyright (c) 2015 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_LOGIN_HANDLER_H_
|
||||
#define ATOM_BROWSER_LOGIN_HANDLER_H_
|
||||
|
||||
#include "base/strings/string16.h"
|
||||
#include "base/synchronization/lock.h"
|
||||
#include "content/public/browser/resource_dispatcher_host_login_delegate.h"
|
||||
|
||||
namespace content {
|
||||
class WebContents;
|
||||
}
|
||||
|
||||
namespace net {
|
||||
class AuthChallengeInfo;
|
||||
class URLRequest;
|
||||
}
|
||||
|
||||
namespace atom {
|
||||
|
||||
// Handles the HTTP basic auth, must be created on IO thread.
|
||||
class LoginHandler : public content::ResourceDispatcherHostLoginDelegate {
|
||||
public:
|
||||
LoginHandler(net::AuthChallengeInfo* auth_info, net::URLRequest* request);
|
||||
|
||||
// Returns the WebContents associated with the request, must be called on UI
|
||||
// thread.
|
||||
content::WebContents* GetWebContents() const;
|
||||
|
||||
// The auth is cancelled, must be called on UI thread.
|
||||
void CancelAuth();
|
||||
|
||||
// Login with |username| and |password|, must be called on UI thread.
|
||||
void Login(const base::string16& username, const base::string16& password);
|
||||
|
||||
const net::AuthChallengeInfo* auth_info() const { return auth_info_.get(); }
|
||||
const net::URLRequest* request() const { return request_; }
|
||||
|
||||
protected:
|
||||
~LoginHandler() override;
|
||||
|
||||
// content::ResourceDispatcherHostLoginDelegate:
|
||||
void OnRequestCancelled() override;
|
||||
|
||||
private:
|
||||
// Must be called on IO thread.
|
||||
void DoCancelAuth();
|
||||
void DoLogin(const base::string16& username, const base::string16& password);
|
||||
|
||||
// Marks authentication as handled and returns the previous handled
|
||||
// state.
|
||||
bool TestAndSetAuthHandled();
|
||||
|
||||
// True if we've handled auth (Login or CancelAuth has been called).
|
||||
bool handled_auth_;
|
||||
mutable base::Lock handled_auth_lock_;
|
||||
|
||||
// Who/where/what asked for the authentication.
|
||||
scoped_refptr<net::AuthChallengeInfo> auth_info_;
|
||||
|
||||
// The request that wants login data.
|
||||
// This should only be accessed on the IO loop.
|
||||
net::URLRequest* request_;
|
||||
|
||||
// Cached from the net::URLRequest, in case it goes NULL on us.
|
||||
int render_process_host_id_;
|
||||
int render_frame_id_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(LoginHandler);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_LOGIN_HANDLER_H_
|
|
@ -139,6 +139,10 @@ void NativeWindow::InitFromOptions(const mate::Dictionary& options) {
|
|||
if (options.Get(switches::kKiosk, &kiosk) && kiosk) {
|
||||
SetKiosk(kiosk);
|
||||
}
|
||||
std::string color;
|
||||
if (options.Get(switches::kBackgroundColor, &color)) {
|
||||
SetBackgroundColor(color);
|
||||
}
|
||||
std::string title("Electron");
|
||||
options.Get(switches::kTitle, &title);
|
||||
SetTitle(title);
|
||||
|
@ -460,6 +464,14 @@ void NativeWindow::NotifyWindowExecuteWindowsCommand(
|
|||
OnExecuteWindowsCommand(command));
|
||||
}
|
||||
|
||||
#if defined(OS_WIN)
|
||||
void NativeWindow::NotifyWindowMessage(
|
||||
UINT message, WPARAM w_param, LPARAM l_param) {
|
||||
FOR_EACH_OBSERVER(NativeWindowObserver, observers_,
|
||||
OnWindowMessage(message, w_param, l_param));
|
||||
}
|
||||
#endif
|
||||
|
||||
scoped_ptr<SkRegion> NativeWindow::DraggableRegionsToSkRegion(
|
||||
const std::vector<DraggableRegion>& regions) {
|
||||
scoped_ptr<SkRegion> sk_region(new SkRegion);
|
||||
|
|
|
@ -134,6 +134,7 @@ class NativeWindow : public base::SupportsUserData,
|
|||
virtual void SetSkipTaskbar(bool skip) = 0;
|
||||
virtual void SetKiosk(bool kiosk) = 0;
|
||||
virtual bool IsKiosk() = 0;
|
||||
virtual void SetBackgroundColor(const std::string& color_name) = 0;
|
||||
virtual void SetRepresentedFilename(const std::string& filename);
|
||||
virtual std::string GetRepresentedFilename();
|
||||
virtual void SetDocumentEdited(bool edited);
|
||||
|
@ -209,6 +210,10 @@ class NativeWindow : public base::SupportsUserData,
|
|||
void NotifyWindowLeaveHtmlFullScreen();
|
||||
void NotifyWindowExecuteWindowsCommand(const std::string& command);
|
||||
|
||||
#if defined(OS_WIN)
|
||||
void NotifyWindowMessage(UINT message, WPARAM w_param, LPARAM l_param);
|
||||
#endif
|
||||
|
||||
void AddObserver(NativeWindowObserver* obs) {
|
||||
observers_.AddObserver(obs);
|
||||
}
|
||||
|
|
|
@ -57,6 +57,7 @@ class NativeWindowMac : public NativeWindow {
|
|||
void SetSkipTaskbar(bool skip) override;
|
||||
void SetKiosk(bool kiosk) override;
|
||||
bool IsKiosk() override;
|
||||
void SetBackgroundColor(const std::string& color_name) override;
|
||||
void SetRepresentedFilename(const std::string& filename) override;
|
||||
std::string GetRepresentedFilename() override;
|
||||
void SetDocumentEdited(bool edited) override;
|
||||
|
|
|
@ -635,6 +635,9 @@ bool NativeWindowMac::IsKiosk() {
|
|||
return is_kiosk_;
|
||||
}
|
||||
|
||||
void NativeWindowMac::SetBackgroundColor(const std::string& color_name) {
|
||||
}
|
||||
|
||||
void NativeWindowMac::SetRepresentedFilename(const std::string& filename) {
|
||||
[window_ setRepresentedFilename:base::SysUTF8ToNSString(filename)];
|
||||
}
|
||||
|
|
|
@ -11,6 +11,10 @@
|
|||
#include "ui/base/window_open_disposition.h"
|
||||
#include "url/gurl.h"
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
namespace atom {
|
||||
|
||||
class NativeWindowObserver {
|
||||
|
@ -55,6 +59,11 @@ class NativeWindowObserver {
|
|||
virtual void OnWindowEnterHtmlFullScreen() {}
|
||||
virtual void OnWindowLeaveHtmlFullScreen() {}
|
||||
|
||||
// Called when window message received
|
||||
#if defined(OS_WIN)
|
||||
virtual void OnWindowMessage(UINT message, WPARAM w_param, LPARAM l_param) {}
|
||||
#endif
|
||||
|
||||
// Called when renderer is hung.
|
||||
virtual void OnRendererUnresponsive() {}
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#elif defined(OS_WIN)
|
||||
#include "atom/browser/ui/views/win_frame_view.h"
|
||||
#include "atom/browser/ui/win/atom_desktop_window_tree_host_win.h"
|
||||
#include "skia/ext/skia_utils_win.h"
|
||||
#include "ui/base/win/shell.h"
|
||||
#include "ui/gfx/win/dpi.h"
|
||||
#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
|
||||
|
@ -77,6 +78,29 @@ bool IsAltModifier(const content::NativeWebKeyboardEvent& event) {
|
|||
(modifiers == (Modifiers::AltKey | Modifiers::IsRight));
|
||||
}
|
||||
|
||||
SkColor ParseHexColor(const std::string& name) {
|
||||
SkColor result = 0xFF000000;
|
||||
unsigned value = 0;
|
||||
auto color = name.substr(1);
|
||||
unsigned length = color.size();
|
||||
if (length != 3 && length != 6)
|
||||
return result;
|
||||
for (unsigned i = 0; i < length; ++i) {
|
||||
if (!base::IsHexDigit(color[i]))
|
||||
return result;
|
||||
value <<= 4;
|
||||
value |= (color[i] < 'A' ? color[i] - '0' : (color[i] - 'A' + 10) & 0xF);
|
||||
}
|
||||
if (length == 6) {
|
||||
result |= value;
|
||||
return result;
|
||||
}
|
||||
result |= (value & 0xF00) << 12 | (value & 0xF00) << 8
|
||||
| (value & 0xF0) << 8 | (value & 0xF0) << 4
|
||||
| (value & 0xF) << 4 | (value & 0xF);
|
||||
return result;
|
||||
}
|
||||
|
||||
class NativeWindowClientView : public views::ClientView {
|
||||
public:
|
||||
NativeWindowClientView(views::Widget* widget,
|
||||
|
@ -205,7 +229,7 @@ NativeWindowViews::NativeWindowViews(
|
|||
|
||||
// Add web view.
|
||||
SetLayoutManager(new MenuLayout(this, kMenuBarHeight));
|
||||
set_background(views::Background::CreateStandardPanelBackground());
|
||||
|
||||
AddChildView(web_view_);
|
||||
|
||||
#if defined(OS_WIN)
|
||||
|
@ -496,6 +520,21 @@ bool NativeWindowViews::IsKiosk() {
|
|||
return IsFullscreen();
|
||||
}
|
||||
|
||||
void NativeWindowViews::SetBackgroundColor(const std::string& color_name) {
|
||||
// web views' background color.
|
||||
SkColor background_color = ParseHexColor(color_name);
|
||||
set_background(views::Background::CreateSolidBackground(background_color));
|
||||
|
||||
#if defined(OS_WIN)
|
||||
// Set the background color of native window.
|
||||
HBRUSH brush = CreateSolidBrush(skia::SkColorToCOLORREF(background_color));
|
||||
ULONG_PTR previous_brush = SetClassLongPtr(
|
||||
GetAcceleratedWidget(), GCLP_HBRBACKGROUND, (LONG)brush);
|
||||
if (previous_brush)
|
||||
DeleteObject((HBRUSH)previous_brush);
|
||||
#endif
|
||||
}
|
||||
|
||||
void NativeWindowViews::SetMenu(ui::MenuModel* menu_model) {
|
||||
if (menu_model == nullptr) {
|
||||
// Remove accelerators
|
||||
|
|
|
@ -77,6 +77,7 @@ class NativeWindowViews : public NativeWindow,
|
|||
void SetSkipTaskbar(bool skip) override;
|
||||
void SetKiosk(bool kiosk) override;
|
||||
bool IsKiosk() override;
|
||||
void SetBackgroundColor(const std::string& color_name) override;
|
||||
void SetMenu(ui::MenuModel* menu_model) override;
|
||||
gfx::NativeWindow GetNativeWindow() override;
|
||||
void SetOverlayIcon(const gfx::Image& overlay,
|
||||
|
|
|
@ -80,6 +80,8 @@ bool NativeWindowViews::ExecuteWindowsCommand(int command_id) {
|
|||
|
||||
bool NativeWindowViews::PreHandleMSG(
|
||||
UINT message, WPARAM w_param, LPARAM l_param, LRESULT* result) {
|
||||
NotifyWindowMessage(message, w_param, l_param);
|
||||
|
||||
switch (message) {
|
||||
case WM_COMMAND:
|
||||
// Handle thumbar button click message.
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
#include "atom/common/native_mate_converters/callback.h"
|
||||
#include "atom/common/native_mate_converters/v8_value_converter.h"
|
||||
#include "native_mate/function_template.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
|
@ -16,34 +15,14 @@ namespace internal {
|
|||
|
||||
namespace {
|
||||
|
||||
struct CallbackHolder {
|
||||
ResponseCallback callback;
|
||||
};
|
||||
|
||||
// Cached JavaScript version of |HandlerCallback|.
|
||||
v8::Persistent<v8::FunctionTemplate> g_handler_callback_;
|
||||
|
||||
// The callback which is passed to |handler|.
|
||||
void HandlerCallback(v8::Isolate* isolate,
|
||||
v8::Local<v8::External> external,
|
||||
v8::Local<v8::Object> state,
|
||||
mate::Arguments* args) {
|
||||
// Check if the callback has already been called.
|
||||
v8::Local<v8::String> called_symbol = mate::StringToSymbol(isolate, "called");
|
||||
if (state->Has(called_symbol))
|
||||
return; // no nothing
|
||||
else
|
||||
state->Set(called_symbol, v8::Boolean::New(isolate, true));
|
||||
|
||||
void HandlerCallback(const ResponseCallback& callback, mate::Arguments* args) {
|
||||
// If there is no argument passed then we failed.
|
||||
scoped_ptr<CallbackHolder> holder(
|
||||
static_cast<CallbackHolder*>(external->Value()));
|
||||
CHECK(holder);
|
||||
v8::Local<v8::Value> value;
|
||||
if (!args->GetNext(&value)) {
|
||||
content::BrowserThread::PostTask(
|
||||
content::BrowserThread::IO, FROM_HERE,
|
||||
base::Bind(holder->callback, false, nullptr));
|
||||
base::Bind(callback, false, nullptr));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -53,42 +32,7 @@ void HandlerCallback(v8::Isolate* isolate,
|
|||
scoped_ptr<base::Value> options(converter.FromV8Value(value, context));
|
||||
content::BrowserThread::PostTask(
|
||||
content::BrowserThread::IO, FROM_HERE,
|
||||
base::Bind(holder->callback, true, base::Passed(&options)));
|
||||
}
|
||||
|
||||
// func.bind(func, arg1, arg2).
|
||||
// NB(zcbenz): Using C++11 version crashes VS.
|
||||
v8::Local<v8::Value> BindFunctionWith(v8::Isolate* isolate,
|
||||
v8::Local<v8::Context> context,
|
||||
v8::Local<v8::Function> func,
|
||||
v8::Local<v8::Value> arg1,
|
||||
v8::Local<v8::Value> arg2) {
|
||||
v8::MaybeLocal<v8::Value> bind = func->Get(mate::StringToV8(isolate, "bind"));
|
||||
CHECK(!bind.IsEmpty());
|
||||
v8::Local<v8::Function> bind_func =
|
||||
v8::Local<v8::Function>::Cast(bind.ToLocalChecked());
|
||||
v8::Local<v8::Value> converted[] = { func, arg1, arg2 };
|
||||
return bind_func->Call(
|
||||
context, func, arraysize(converted), converted).ToLocalChecked();
|
||||
}
|
||||
|
||||
// Generate the callback that will be passed to |handler|.
|
||||
v8::MaybeLocal<v8::Value> GenerateCallback(v8::Isolate* isolate,
|
||||
v8::Local<v8::Context> context,
|
||||
const ResponseCallback& callback) {
|
||||
// The FunctionTemplate is cached.
|
||||
if (g_handler_callback_.IsEmpty())
|
||||
g_handler_callback_.Reset(
|
||||
isolate,
|
||||
mate::CreateFunctionTemplate(isolate, base::Bind(&HandlerCallback)));
|
||||
|
||||
v8::Local<v8::FunctionTemplate> handler_callback =
|
||||
v8::Local<v8::FunctionTemplate>::New(isolate, g_handler_callback_);
|
||||
CallbackHolder* holder = new CallbackHolder;
|
||||
holder->callback = callback;
|
||||
return BindFunctionWith(isolate, context, handler_callback->GetFunction(),
|
||||
v8::External::New(isolate, holder),
|
||||
v8::Object::New(isolate));
|
||||
base::Bind(callback, true, base::Passed(&options)));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -102,16 +46,9 @@ void AskForOptions(v8::Isolate* isolate,
|
|||
v8::HandleScope handle_scope(isolate);
|
||||
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
||||
v8::Context::Scope context_scope(context);
|
||||
// We don't convert the callback to C++ directly because creating
|
||||
// FunctionTemplate will cause memory leak since V8 never releases it. So we
|
||||
// have to create the function object in JavaScript to work around it.
|
||||
v8::MaybeLocal<v8::Value> wrapped_callback = GenerateCallback(
|
||||
isolate, context, callback);
|
||||
if (wrapped_callback.IsEmpty()) {
|
||||
callback.Run(false, nullptr);
|
||||
return;
|
||||
}
|
||||
handler.Run(request, wrapped_callback.ToLocalChecked());
|
||||
handler.Run(request,
|
||||
mate::ConvertToV8(isolate,
|
||||
base::Bind(&HandlerCallback, callback)));
|
||||
}
|
||||
|
||||
bool IsErrorOptions(base::Value* value, int* error) {
|
||||
|
|
|
@ -35,17 +35,14 @@ NodeDebugger::NodeDebugger(v8::Isolate* isolate)
|
|||
weak_factory_(this) {
|
||||
bool use_debug_agent = false;
|
||||
int port = 5858;
|
||||
bool wait_for_connection = false;
|
||||
|
||||
std::string port_str;
|
||||
base::CommandLine* cmd = base::CommandLine::ForCurrentProcess();
|
||||
if (cmd->HasSwitch("debug")) {
|
||||
use_debug_agent = true;
|
||||
port_str = cmd->GetSwitchValueASCII("debug");
|
||||
}
|
||||
if (cmd->HasSwitch("debug-brk")) {
|
||||
} else if (cmd->HasSwitch("debug-brk")) {
|
||||
use_debug_agent = true;
|
||||
wait_for_connection = true;
|
||||
port_str = cmd->GetSwitchValueASCII("debug-brk");
|
||||
}
|
||||
|
||||
|
@ -56,9 +53,6 @@ NodeDebugger::NodeDebugger(v8::Isolate* isolate)
|
|||
isolate_->SetData(kIsolateSlot, this);
|
||||
v8::Debug::SetMessageHandler(DebugMessageHandler);
|
||||
|
||||
if (wait_for_connection)
|
||||
v8::Debug::DebugBreak(isolate_);
|
||||
|
||||
uv_async_init(uv_default_loop(), &weak_up_ui_handle_, ProcessMessageInUI);
|
||||
|
||||
// Start a new IO thread.
|
||||
|
|
|
@ -17,9 +17,9 @@
|
|||
<key>CFBundleIconFile</key>
|
||||
<string>atom.icns</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>0.34.0</string>
|
||||
<string>0.34.1</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.34.0</string>
|
||||
<string>0.34.1</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.developer-tools</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
|
|
|
@ -56,8 +56,8 @@ END
|
|||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 0,34,0,0
|
||||
PRODUCTVERSION 0,34,0,0
|
||||
FILEVERSION 0,34,1,0
|
||||
PRODUCTVERSION 0,34,1,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
|
@ -74,12 +74,12 @@ BEGIN
|
|||
BEGIN
|
||||
VALUE "CompanyName", "GitHub, Inc."
|
||||
VALUE "FileDescription", "Electron"
|
||||
VALUE "FileVersion", "0.34.0"
|
||||
VALUE "FileVersion", "0.34.1"
|
||||
VALUE "InternalName", "electron.exe"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved."
|
||||
VALUE "OriginalFilename", "electron.exe"
|
||||
VALUE "ProductName", "Electron"
|
||||
VALUE "ProductVersion", "0.34.0"
|
||||
VALUE "ProductVersion", "0.34.1"
|
||||
VALUE "SquirrelAwareVersion", "1"
|
||||
END
|
||||
END
|
||||
|
|
|
@ -79,8 +79,25 @@ class FileDialog {
|
|||
if (!title.empty())
|
||||
GetPtr()->SetTitle(base::UTF8ToUTF16(title).c_str());
|
||||
|
||||
if (!filterspec.empty())
|
||||
GetPtr()->SetDefaultExtension(filterspec.front().pszSpec);
|
||||
// By default, *.* will be added to the file name if file type is "*.*". In
|
||||
// Electron, we disable it to make a better experience.
|
||||
//
|
||||
// From MSDN: https://msdn.microsoft.com/en-us/library/windows/desktop/
|
||||
// bb775970(v=vs.85).aspx
|
||||
//
|
||||
// If SetDefaultExtension is not called, the dialog will not update
|
||||
// automatically when user choose a new file type in the file dialog.
|
||||
//
|
||||
// We set file extension to the first none-wildcard extension to make
|
||||
// sure the dialog will update file extension automatically.
|
||||
for (size_t i = 0; i < filterspec.size(); ++i) {
|
||||
if (std::wstring(filterspec[i].pszSpec) != L"*.*") {
|
||||
// SetFileTypeIndex is regarded as one-based index.
|
||||
GetPtr()->SetFileTypeIndex(i+1);
|
||||
GetPtr()->SetDefaultExtension(filterspec[i].pszSpec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SetDefaultFolder(default_path);
|
||||
}
|
||||
|
@ -255,7 +272,8 @@ bool ShowSaveDialog(atom::NativeWindow* parent_window,
|
|||
|
||||
bool matched = false;
|
||||
for (size_t i = 0; i < filter.second.size(); ++i) {
|
||||
if (base::EndsWith(file_name, filter.second[i], false)) {
|
||||
if (filter.second[i] == "*" ||
|
||||
base::EndsWith(file_name, filter.second[i], false)) {
|
||||
matched = true;
|
||||
break;;
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ void FatalErrorCallback(const char* location, const char* message) {
|
|||
}
|
||||
|
||||
void Log(const base::string16& message) {
|
||||
std::cout << message;
|
||||
std::cout << message << std::flush;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
module.exports = process.atomBinding 'shell'
|
||||
|
||||
if process.platform is 'win32' and process.type is 'renderer'
|
||||
module.exports.showItemInFolder = require('remote').process.atomBinding('shell').showItemInFolder
|
||||
module.exports.showItemInFolder = (item) ->
|
||||
require('remote').require('shell').showItemInFolder item
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
#define ATOM_MAJOR_VERSION 0
|
||||
#define ATOM_MINOR_VERSION 34
|
||||
#define ATOM_PATCH_VERSION 0
|
||||
#define ATOM_PATCH_VERSION 1
|
||||
|
||||
#define ATOM_VERSION_IS_RELEASE 1
|
||||
|
||||
|
|
78
atom/common/native_mate_converters/callback.cc
Normal file
78
atom/common/native_mate_converters/callback.cc
Normal file
|
@ -0,0 +1,78 @@
|
|||
// Copyright (c) 2015 GitHub, Inc. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/common/native_mate_converters/callback.h"
|
||||
|
||||
namespace mate {
|
||||
|
||||
namespace internal {
|
||||
|
||||
namespace {
|
||||
|
||||
struct TranslaterHolder {
|
||||
Translater translater;
|
||||
};
|
||||
|
||||
// Cached JavaScript version of |CallTranslater|.
|
||||
v8::Persistent<v8::FunctionTemplate> g_call_translater;
|
||||
|
||||
void CallTranslater(v8::Local<v8::External> external,
|
||||
v8::Local<v8::Object> state,
|
||||
mate::Arguments* args) {
|
||||
v8::Isolate* isolate = args->isolate();
|
||||
|
||||
// Check if the callback has already been called.
|
||||
v8::Local<v8::String> called_symbol = mate::StringToSymbol(isolate, "called");
|
||||
if (state->Has(called_symbol)) {
|
||||
args->ThrowError("callback can only be called for once");
|
||||
return;
|
||||
} else {
|
||||
state->Set(called_symbol, v8::Boolean::New(isolate, true));
|
||||
}
|
||||
|
||||
TranslaterHolder* holder = static_cast<TranslaterHolder*>(external->Value());
|
||||
holder->translater.Run(args);
|
||||
delete holder;
|
||||
}
|
||||
|
||||
// func.bind(func, arg1).
|
||||
// NB(zcbenz): Using C++11 version crashes VS.
|
||||
v8::Local<v8::Value> BindFunctionWith(v8::Isolate* isolate,
|
||||
v8::Local<v8::Context> context,
|
||||
v8::Local<v8::Function> func,
|
||||
v8::Local<v8::Value> arg1,
|
||||
v8::Local<v8::Value> arg2) {
|
||||
v8::MaybeLocal<v8::Value> bind = func->Get(mate::StringToV8(isolate, "bind"));
|
||||
CHECK(!bind.IsEmpty());
|
||||
v8::Local<v8::Function> bind_func =
|
||||
v8::Local<v8::Function>::Cast(bind.ToLocalChecked());
|
||||
v8::Local<v8::Value> converted[] = { func, arg1, arg2 };
|
||||
return bind_func->Call(
|
||||
context, func, arraysize(converted), converted).ToLocalChecked();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
v8::Local<v8::Value> CreateFunctionFromTranslater(
|
||||
v8::Isolate* isolate, const Translater& translater) {
|
||||
// The FunctionTemplate is cached.
|
||||
if (g_call_translater.IsEmpty())
|
||||
g_call_translater.Reset(
|
||||
isolate,
|
||||
mate::CreateFunctionTemplate(isolate, base::Bind(&CallTranslater)));
|
||||
|
||||
v8::Local<v8::FunctionTemplate> call_translater =
|
||||
v8::Local<v8::FunctionTemplate>::New(isolate, g_call_translater);
|
||||
TranslaterHolder* holder = new TranslaterHolder;
|
||||
holder->translater = translater;
|
||||
return BindFunctionWith(isolate,
|
||||
isolate->GetCurrentContext(),
|
||||
call_translater->GetFunction(),
|
||||
v8::External::New(isolate, holder),
|
||||
v8::Object::New(isolate));
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
} // namespace mate
|
|
@ -20,6 +20,7 @@ namespace internal {
|
|||
|
||||
typedef scoped_refptr<RefCountedPersistent<v8::Function> > SafeV8Function;
|
||||
|
||||
// Helper to invoke a V8 function with C++ parameters.
|
||||
template <typename Sig>
|
||||
struct V8FunctionInvoker {};
|
||||
|
||||
|
@ -70,21 +71,47 @@ struct V8FunctionInvoker<ReturnType(ArgTypes...)> {
|
|||
v8::Local<v8::Function> holder = function->NewHandle();
|
||||
v8::Local<v8::Context> context = holder->CreationContext();
|
||||
v8::Context::Scope context_scope(context);
|
||||
ReturnType ret;
|
||||
ReturnType ret = ReturnType();
|
||||
std::vector<v8::Local<v8::Value>> args = { ConvertToV8(isolate, raw)... };
|
||||
v8::Local<v8::Value> val(holder->Call(holder, args.size(), &args.front()));
|
||||
Converter<ReturnType>::FromV8(isolate, val, &ret);
|
||||
v8::Local<v8::Value> result;
|
||||
auto maybe_result =
|
||||
holder->Call(context, holder, args.size(), &args.front());
|
||||
if (maybe_result.ToLocal(&result))
|
||||
Converter<ReturnType>::FromV8(isolate, result, &ret);
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
// Helper to pass a C++ funtion to JavaScript.
|
||||
using Translater = base::Callback<void(Arguments* args)>;
|
||||
v8::Local<v8::Value> CreateFunctionFromTranslater(
|
||||
v8::Isolate* isolate, const Translater& translater);
|
||||
|
||||
// Calls callback with Arguments.
|
||||
template <typename Sig>
|
||||
struct NativeFunctionInvoker {};
|
||||
|
||||
template <typename ReturnType, typename... ArgTypes>
|
||||
struct NativeFunctionInvoker<ReturnType(ArgTypes...)> {
|
||||
static void Go(base::Callback<ReturnType(ArgTypes...)> val, Arguments* args) {
|
||||
using Indices = typename IndicesGenerator<sizeof...(ArgTypes)>::type;
|
||||
Invoker<Indices, ArgTypes...> invoker(args, 0);
|
||||
if (invoker.IsOK())
|
||||
invoker.DispatchToCallback(val);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
template<typename Sig>
|
||||
struct Converter<base::Callback<Sig> > {
|
||||
struct Converter<base::Callback<Sig>> {
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
const base::Callback<Sig>& val) {
|
||||
return CreateFunctionTemplate(isolate, val)->GetFunction();
|
||||
const base::Callback<Sig>& val) {
|
||||
// We don't use CreateFunctionTemplate here because it creates a new
|
||||
// FunctionTemplate everytime, which is cached by V8 and causes leaks.
|
||||
internal::Translater translater = base::Bind(
|
||||
&internal::NativeFunctionInvoker<Sig>::Go, val);
|
||||
return internal::CreateFunctionFromTranslater(isolate, translater);
|
||||
}
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
|
|
34
atom/common/native_mate_converters/content_converter.cc
Normal file
34
atom/common/native_mate_converters/content_converter.cc
Normal file
|
@ -0,0 +1,34 @@
|
|||
// Copyright (c) 2015 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/common/native_mate_converters/content_converter.h"
|
||||
|
||||
#include "native_mate/dictionary.h"
|
||||
#include "net/url_request/url_request.h"
|
||||
|
||||
namespace mate {
|
||||
|
||||
// static
|
||||
v8::Local<v8::Value> Converter<const net::URLRequest*>::ToV8(
|
||||
v8::Isolate* isolate, const net::URLRequest* val) {
|
||||
mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
|
||||
dict.Set("method", val->method());
|
||||
dict.Set("url", val->url().spec());
|
||||
dict.Set("referrer", val->referrer());
|
||||
return mate::ConvertToV8(isolate, dict);
|
||||
}
|
||||
|
||||
// static
|
||||
v8::Local<v8::Value> Converter<const net::AuthChallengeInfo*>::ToV8(
|
||||
v8::Isolate* isolate, const net::AuthChallengeInfo* val) {
|
||||
mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
|
||||
dict.Set("isProxy", val->is_proxy);
|
||||
dict.Set("scheme", val->scheme);
|
||||
dict.Set("host", val->challenger.host());
|
||||
dict.Set("port", static_cast<uint32_t>(val->challenger.port()));
|
||||
dict.Set("realm", val->realm);
|
||||
return mate::ConvertToV8(isolate, dict);
|
||||
}
|
||||
|
||||
} // namespace mate
|
31
atom/common/native_mate_converters/content_converter.h
Normal file
31
atom/common/native_mate_converters/content_converter.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
// Copyright (c) 2015 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_COMMON_NATIVE_MATE_CONVERTERS_CONTENT_CONVERTER_H_
|
||||
#define ATOM_COMMON_NATIVE_MATE_CONVERTERS_CONTENT_CONVERTER_H_
|
||||
|
||||
#include "native_mate/converter.h"
|
||||
|
||||
namespace net {
|
||||
class AuthChallengeInfo;
|
||||
class URLRequest;
|
||||
}
|
||||
|
||||
namespace mate {
|
||||
|
||||
template<>
|
||||
struct Converter<const net::URLRequest*> {
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
const net::URLRequest* val);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Converter<const net::AuthChallengeInfo*> {
|
||||
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
|
||||
const net::AuthChallengeInfo* val);
|
||||
};
|
||||
|
||||
} // namespace mate
|
||||
|
||||
#endif // ATOM_COMMON_NATIVE_MATE_CONVERTERS_CONTENT_CONVERTER_H_
|
|
@ -93,6 +93,9 @@ const char kDisableAutoHideCursor[] = "disable-auto-hide-cursor";
|
|||
// Use the OS X's standard window instead of the textured window.
|
||||
const char kStandardWindow[] = "standard-window";
|
||||
|
||||
// Default browser window background color.
|
||||
const char kBackgroundColor[] = "background-color";
|
||||
|
||||
// Path to client certificate.
|
||||
const char kClientCertificate[] = "client-certificate";
|
||||
|
||||
|
|
|
@ -47,6 +47,7 @@ extern const char kTransparent[];
|
|||
extern const char kType[];
|
||||
extern const char kDisableAutoHideCursor[];
|
||||
extern const char kStandardWindow[];
|
||||
extern const char kBackgroundColor[];
|
||||
extern const char kClientCertificate[];
|
||||
|
||||
extern const char kExperimentalFeatures[];
|
||||
|
|
|
@ -46,7 +46,9 @@ metaToValue = (meta) ->
|
|||
when 'array' then (metaToValue(el) for el in meta.members)
|
||||
when 'buffer' then new Buffer(meta.value)
|
||||
when 'promise' then Promise.resolve(then: metaToValue(meta.then))
|
||||
when 'error'
|
||||
when 'error' then new Error(meta.message)
|
||||
when 'date' then new Date(meta.value)
|
||||
when 'exception'
|
||||
throw new Error("#{meta.message}\n#{meta.stack}")
|
||||
else
|
||||
if meta.type is 'function'
|
||||
|
|
|
@ -39,16 +39,8 @@ namespace atom {
|
|||
namespace {
|
||||
|
||||
bool IsSwitchEnabled(base::CommandLine* command_line,
|
||||
const char* switch_string,
|
||||
bool* enabled) {
|
||||
std::string value = command_line->GetSwitchValueASCII(switch_string);
|
||||
if (value == "true")
|
||||
*enabled = true;
|
||||
else if (value == "false")
|
||||
*enabled = false;
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
const char* switch_string) {
|
||||
return command_line->GetSwitchValueASCII(switch_string) == "true";
|
||||
}
|
||||
|
||||
// Helper class to forward the messages to the client.
|
||||
|
@ -216,10 +208,8 @@ bool AtomRendererClient::ShouldOverridePageVisibilityState(
|
|||
const content::RenderFrame* render_frame,
|
||||
blink::WebPageVisibilityState* override_state) {
|
||||
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
|
||||
bool b;
|
||||
|
||||
if (IsSwitchEnabled(command_line, switches::kPageVisibility, &b)
|
||||
&& b) {
|
||||
if (IsSwitchEnabled(command_line, switches::kPageVisibility)) {
|
||||
*override_state = blink::WebPageVisibilityStateVisible;
|
||||
return true;
|
||||
}
|
||||
|
@ -229,17 +219,17 @@ bool AtomRendererClient::ShouldOverridePageVisibilityState(
|
|||
|
||||
void AtomRendererClient::EnableWebRuntimeFeatures() {
|
||||
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
|
||||
bool b;
|
||||
if (IsSwitchEnabled(command_line, switches::kExperimentalFeatures, &b))
|
||||
blink::WebRuntimeFeatures::enableExperimentalFeatures(b);
|
||||
if (IsSwitchEnabled(command_line, switches::kExperimentalCanvasFeatures, &b))
|
||||
blink::WebRuntimeFeatures::enableExperimentalCanvasFeatures(b);
|
||||
if (IsSwitchEnabled(command_line, switches::kOverlayScrollbars, &b))
|
||||
blink::WebRuntimeFeatures::enableOverlayScrollbars(b);
|
||||
if (IsSwitchEnabled(command_line, switches::kOverlayFullscreenVideo, &b))
|
||||
blink::WebRuntimeFeatures::enableOverlayFullscreenVideo(b);
|
||||
if (IsSwitchEnabled(command_line, switches::kSharedWorker, &b))
|
||||
blink::WebRuntimeFeatures::enableSharedWorker(b);
|
||||
|
||||
if (IsSwitchEnabled(command_line, switches::kExperimentalFeatures))
|
||||
blink::WebRuntimeFeatures::enableExperimentalFeatures(true);
|
||||
if (IsSwitchEnabled(command_line, switches::kExperimentalCanvasFeatures))
|
||||
blink::WebRuntimeFeatures::enableExperimentalCanvasFeatures(true);
|
||||
if (IsSwitchEnabled(command_line, switches::kOverlayScrollbars))
|
||||
blink::WebRuntimeFeatures::enableOverlayScrollbars(true);
|
||||
if (IsSwitchEnabled(command_line, switches::kOverlayFullscreenVideo))
|
||||
blink::WebRuntimeFeatures::enableOverlayFullscreenVideo(true);
|
||||
if (IsSwitchEnabled(command_line, switches::kSharedWorker))
|
||||
blink::WebRuntimeFeatures::enableSharedWorker(true);
|
||||
}
|
||||
|
||||
} // namespace atom
|
||||
|
|
86
chromium_src/chrome/browser/chrome_process_finder_win.cc
Normal file
86
chromium_src/chrome/browser/chrome_process_finder_win.cc
Normal file
|
@ -0,0 +1,86 @@
|
|||
// Copyright 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "chrome/browser/chrome_process_finder_win.h"
|
||||
|
||||
#include <shellapi.h>
|
||||
#include <string>
|
||||
|
||||
#include "base/command_line.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/process/process.h"
|
||||
#include "base/process/process_info.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/win/message_window.h"
|
||||
#include "base/win/scoped_handle.h"
|
||||
#include "base/win/win_util.h"
|
||||
#include "base/win/windows_version.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
int timeout_in_milliseconds = 20 * 1000;
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace chrome {
|
||||
|
||||
HWND FindRunningChromeWindow(const base::FilePath& user_data_dir) {
|
||||
return base::win::MessageWindow::FindWindow(user_data_dir.value());
|
||||
}
|
||||
|
||||
NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window,
|
||||
bool fast_start) {
|
||||
DCHECK(remote_window);
|
||||
DWORD process_id = 0;
|
||||
DWORD thread_id = GetWindowThreadProcessId(remote_window, &process_id);
|
||||
if (!thread_id || !process_id)
|
||||
return NOTIFY_FAILED;
|
||||
|
||||
// Send the command line to the remote chrome window.
|
||||
// Format is "START\0<<<current directory>>>\0<<<commandline>>>".
|
||||
std::wstring to_send(L"START\0", 6); // want the NULL in the string.
|
||||
base::FilePath cur_dir;
|
||||
if (!base::GetCurrentDirectory(&cur_dir))
|
||||
return NOTIFY_FAILED;
|
||||
to_send.append(cur_dir.value());
|
||||
to_send.append(L"\0", 1); // Null separator.
|
||||
to_send.append(::GetCommandLineW());
|
||||
to_send.append(L"\0", 1); // Null separator.
|
||||
|
||||
// Allow the current running browser window to make itself the foreground
|
||||
// window (otherwise it will just flash in the taskbar).
|
||||
::AllowSetForegroundWindow(process_id);
|
||||
|
||||
COPYDATASTRUCT cds;
|
||||
cds.dwData = 0;
|
||||
cds.cbData = static_cast<DWORD>((to_send.length() + 1) * sizeof(wchar_t));
|
||||
cds.lpData = const_cast<wchar_t*>(to_send.c_str());
|
||||
DWORD_PTR result = 0;
|
||||
if (::SendMessageTimeout(remote_window, WM_COPYDATA, NULL,
|
||||
reinterpret_cast<LPARAM>(&cds), SMTO_ABORTIFHUNG,
|
||||
timeout_in_milliseconds, &result)) {
|
||||
return result ? NOTIFY_SUCCESS : NOTIFY_FAILED;
|
||||
}
|
||||
|
||||
// It is possible that the process owning this window may have died by now.
|
||||
if (!::IsWindow(remote_window))
|
||||
return NOTIFY_FAILED;
|
||||
|
||||
// If the window couldn't be notified but still exists, assume it is hung.
|
||||
return NOTIFY_WINDOW_HUNG;
|
||||
}
|
||||
|
||||
base::TimeDelta SetNotificationTimeoutForTesting(base::TimeDelta new_timeout) {
|
||||
base::TimeDelta old_timeout =
|
||||
base::TimeDelta::FromMilliseconds(timeout_in_milliseconds);
|
||||
timeout_in_milliseconds = new_timeout.InMilliseconds();
|
||||
return old_timeout;
|
||||
}
|
||||
|
||||
} // namespace chrome
|
39
chromium_src/chrome/browser/chrome_process_finder_win.h
Normal file
39
chromium_src/chrome/browser/chrome_process_finder_win.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
// Copyright 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef CHROME_BROWSER_CHROME_PROCESS_FINDER_WIN_H_
|
||||
#define CHROME_BROWSER_CHROME_PROCESS_FINDER_WIN_H_
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "base/time/time.h"
|
||||
|
||||
namespace base {
|
||||
class FilePath;
|
||||
}
|
||||
|
||||
namespace chrome {
|
||||
|
||||
enum NotifyChromeResult {
|
||||
NOTIFY_SUCCESS,
|
||||
NOTIFY_FAILED,
|
||||
NOTIFY_WINDOW_HUNG,
|
||||
};
|
||||
|
||||
// Finds an already running Chrome window if it exists.
|
||||
HWND FindRunningChromeWindow(const base::FilePath& user_data_dir);
|
||||
|
||||
// Attempts to send the current command line to an already running instance of
|
||||
// Chrome via a WM_COPYDATA message.
|
||||
// Returns true if a running Chrome is found and successfully notified.
|
||||
// |fast_start| is true when this is being called on the window fast start path.
|
||||
NotifyChromeResult AttemptToNotifyRunningChrome(HWND remote_window,
|
||||
bool fast_start);
|
||||
|
||||
// Changes the notification timeout to |new_timeout|, returns the old timeout.
|
||||
base::TimeDelta SetNotificationTimeoutForTesting(base::TimeDelta new_timeout);
|
||||
|
||||
} // namespace chrome
|
||||
|
||||
#endif // CHROME_BROWSER_CHROME_PROCESS_FINDER_WIN_H_
|
182
chromium_src/chrome/browser/process_singleton.h
Normal file
182
chromium_src/chrome/browser/process_singleton.h
Normal file
|
@ -0,0 +1,182 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef CHROME_BROWSER_PROCESS_SINGLETON_H_
|
||||
#define CHROME_BROWSER_PROCESS_SINGLETON_H_
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include <windows.h>
|
||||
#endif // defined(OS_WIN)
|
||||
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "base/callback.h"
|
||||
#include "base/command_line.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "base/process/process.h"
|
||||
#include "base/threading/non_thread_safe.h"
|
||||
#include "ui/gfx/native_widget_types.h"
|
||||
|
||||
#if defined(OS_POSIX) && !defined(OS_ANDROID)
|
||||
#include "base/files/scoped_temp_dir.h"
|
||||
#endif
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include "base/win/message_window.h"
|
||||
#endif // defined(OS_WIN)
|
||||
|
||||
namespace base {
|
||||
class CommandLine;
|
||||
}
|
||||
|
||||
// ProcessSingleton ----------------------------------------------------------
|
||||
//
|
||||
// This class allows different browser processes to communicate with
|
||||
// each other. It is named according to the user data directory, so
|
||||
// we can be sure that no more than one copy of the application can be
|
||||
// running at once with a given data directory.
|
||||
//
|
||||
// Implementation notes:
|
||||
// - the Windows implementation uses an invisible global message window;
|
||||
// - the Linux implementation uses a Unix domain socket in the user data dir.
|
||||
|
||||
class ProcessSingleton : public base::NonThreadSafe {
|
||||
public:
|
||||
enum NotifyResult {
|
||||
PROCESS_NONE,
|
||||
PROCESS_NOTIFIED,
|
||||
PROFILE_IN_USE,
|
||||
LOCK_ERROR,
|
||||
};
|
||||
|
||||
// Implement this callback to handle notifications from other processes. The
|
||||
// callback will receive the command line and directory with which the other
|
||||
// Chrome process was launched. Return true if the command line will be
|
||||
// handled within the current browser instance or false if the remote process
|
||||
// should handle it (i.e., because the current process is shutting down).
|
||||
using NotificationCallback =
|
||||
base::Callback<bool(const base::CommandLine::StringVector& command_line,
|
||||
const base::FilePath& current_directory)>;
|
||||
|
||||
ProcessSingleton(const base::FilePath& user_data_dir,
|
||||
const NotificationCallback& notification_callback);
|
||||
~ProcessSingleton();
|
||||
|
||||
// Notify another process, if available. Otherwise sets ourselves as the
|
||||
// singleton instance. Returns PROCESS_NONE if we became the singleton
|
||||
// instance. Callers are guaranteed to either have notified an existing
|
||||
// process or have grabbed the singleton (unless the profile is locked by an
|
||||
// unreachable process).
|
||||
// TODO(brettw): Make the implementation of this method non-platform-specific
|
||||
// by making Linux re-use the Windows implementation.
|
||||
NotifyResult NotifyOtherProcessOrCreate();
|
||||
|
||||
// Sets ourself up as the singleton instance. Returns true on success. If
|
||||
// false is returned, we are not the singleton instance and the caller must
|
||||
// exit.
|
||||
// NOTE: Most callers should generally prefer NotifyOtherProcessOrCreate() to
|
||||
// this method, only callers for whom failure is preferred to notifying
|
||||
// another process should call this directly.
|
||||
bool Create();
|
||||
|
||||
// Clear any lock state during shutdown.
|
||||
void Cleanup();
|
||||
|
||||
#if defined(OS_POSIX) && !defined(OS_ANDROID)
|
||||
static void DisablePromptForTesting();
|
||||
#endif
|
||||
#if defined(OS_WIN)
|
||||
// Called to query whether to kill a hung browser process that has visible
|
||||
// windows. Return true to allow killing the hung process.
|
||||
using ShouldKillRemoteProcessCallback = base::Callback<bool()>;
|
||||
void OverrideShouldKillRemoteProcessCallbackForTesting(
|
||||
const ShouldKillRemoteProcessCallback& display_dialog_callback);
|
||||
#endif
|
||||
|
||||
protected:
|
||||
// Notify another process, if available.
|
||||
// Returns true if another process was found and notified, false if we should
|
||||
// continue with the current process.
|
||||
// On Windows, Create() has to be called before this.
|
||||
NotifyResult NotifyOtherProcess();
|
||||
|
||||
#if defined(OS_POSIX) && !defined(OS_ANDROID)
|
||||
// Exposed for testing. We use a timeout on Linux, and in tests we want
|
||||
// this timeout to be short.
|
||||
NotifyResult NotifyOtherProcessWithTimeout(
|
||||
const base::CommandLine& command_line,
|
||||
int retry_attempts,
|
||||
const base::TimeDelta& timeout,
|
||||
bool kill_unresponsive);
|
||||
NotifyResult NotifyOtherProcessWithTimeoutOrCreate(
|
||||
const base::CommandLine& command_line,
|
||||
int retry_attempts,
|
||||
const base::TimeDelta& timeout);
|
||||
void OverrideCurrentPidForTesting(base::ProcessId pid);
|
||||
void OverrideKillCallbackForTesting(
|
||||
const base::Callback<void(int)>& callback);
|
||||
#endif
|
||||
|
||||
private:
|
||||
NotificationCallback notification_callback_; // Handler for notifications.
|
||||
|
||||
#if defined(OS_WIN)
|
||||
HWND remote_window_; // The HWND_MESSAGE of another browser.
|
||||
base::win::MessageWindow window_; // The message-only window.
|
||||
bool is_virtualized_; // Stuck inside Microsoft Softricity VM environment.
|
||||
HANDLE lock_file_;
|
||||
base::FilePath user_data_dir_;
|
||||
ShouldKillRemoteProcessCallback should_kill_remote_process_callback_;
|
||||
#elif defined(OS_POSIX) && !defined(OS_ANDROID)
|
||||
// Start listening to the socket.
|
||||
void StartListening(int sock);
|
||||
|
||||
// Return true if the given pid is one of our child processes.
|
||||
// Assumes that the current pid is the root of all pids of the current
|
||||
// instance.
|
||||
bool IsSameChromeInstance(pid_t pid);
|
||||
|
||||
// Extract the process's pid from a symbol link path and if it is on
|
||||
// the same host, kill the process, unlink the lock file and return true.
|
||||
// If the process is part of the same chrome instance, unlink the lock file
|
||||
// and return true without killing it.
|
||||
// If the process is on a different host, return false.
|
||||
bool KillProcessByLockPath();
|
||||
|
||||
// Default function to kill a process, overridable by tests.
|
||||
void KillProcess(int pid);
|
||||
|
||||
// Allow overriding for tests.
|
||||
base::ProcessId current_pid_;
|
||||
|
||||
// Function to call when the other process is hung and needs to be killed.
|
||||
// Allows overriding for tests.
|
||||
base::Callback<void(int)> kill_callback_;
|
||||
|
||||
// Path in file system to the socket.
|
||||
base::FilePath socket_path_;
|
||||
|
||||
// Path in file system to the lock.
|
||||
base::FilePath lock_path_;
|
||||
|
||||
// Path in file system to the cookie file.
|
||||
base::FilePath cookie_path_;
|
||||
|
||||
// Temporary directory to hold the socket.
|
||||
base::ScopedTempDir socket_dir_;
|
||||
|
||||
// Helper class for linux specific messages. LinuxWatcher is ref counted
|
||||
// because it posts messages between threads.
|
||||
class LinuxWatcher;
|
||||
scoped_refptr<LinuxWatcher> watcher_;
|
||||
#endif
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ProcessSingleton);
|
||||
};
|
||||
|
||||
#endif // CHROME_BROWSER_PROCESS_SINGLETON_H_
|
1061
chromium_src/chrome/browser/process_singleton_posix.cc
Normal file
1061
chromium_src/chrome/browser/process_singleton_posix.cc
Normal file
File diff suppressed because it is too large
Load diff
328
chromium_src/chrome/browser/process_singleton_win.cc
Normal file
328
chromium_src/chrome/browser/process_singleton_win.cc
Normal file
|
@ -0,0 +1,328 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "chrome/browser/process_singleton.h"
|
||||
|
||||
#include <shellapi.h>
|
||||
|
||||
#include "base/base_paths.h"
|
||||
#include "base/bind.h"
|
||||
#include "base/command_line.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/process/process.h"
|
||||
#include "base/process/process_info.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/time/time.h"
|
||||
#include "base/win/metro.h"
|
||||
#include "base/win/registry.h"
|
||||
#include "base/win/scoped_handle.h"
|
||||
#include "base/win/windows_version.h"
|
||||
#include "chrome/browser/chrome_process_finder_win.h"
|
||||
#include "content/public/common/result_codes.h"
|
||||
#include "net/base/escape.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
#include "ui/gfx/win/hwnd_util.h"
|
||||
|
||||
namespace {
|
||||
|
||||
const char kLockfile[] = "lockfile";
|
||||
|
||||
// A helper class that acquires the given |mutex| while the AutoLockMutex is in
|
||||
// scope.
|
||||
class AutoLockMutex {
|
||||
public:
|
||||
explicit AutoLockMutex(HANDLE mutex) : mutex_(mutex) {
|
||||
DWORD result = ::WaitForSingleObject(mutex_, INFINITE);
|
||||
DPCHECK(result == WAIT_OBJECT_0) << "Result = " << result;
|
||||
}
|
||||
|
||||
~AutoLockMutex() {
|
||||
BOOL released = ::ReleaseMutex(mutex_);
|
||||
DPCHECK(released);
|
||||
}
|
||||
|
||||
private:
|
||||
HANDLE mutex_;
|
||||
DISALLOW_COPY_AND_ASSIGN(AutoLockMutex);
|
||||
};
|
||||
|
||||
// A helper class that releases the given |mutex| while the AutoUnlockMutex is
|
||||
// in scope and immediately re-acquires it when going out of scope.
|
||||
class AutoUnlockMutex {
|
||||
public:
|
||||
explicit AutoUnlockMutex(HANDLE mutex) : mutex_(mutex) {
|
||||
BOOL released = ::ReleaseMutex(mutex_);
|
||||
DPCHECK(released);
|
||||
}
|
||||
|
||||
~AutoUnlockMutex() {
|
||||
DWORD result = ::WaitForSingleObject(mutex_, INFINITE);
|
||||
DPCHECK(result == WAIT_OBJECT_0) << "Result = " << result;
|
||||
}
|
||||
|
||||
private:
|
||||
HANDLE mutex_;
|
||||
DISALLOW_COPY_AND_ASSIGN(AutoUnlockMutex);
|
||||
};
|
||||
|
||||
// Checks the visibility of the enumerated window and signals once a visible
|
||||
// window has been found.
|
||||
BOOL CALLBACK BrowserWindowEnumeration(HWND window, LPARAM param) {
|
||||
bool* result = reinterpret_cast<bool*>(param);
|
||||
*result = ::IsWindowVisible(window) != 0;
|
||||
// Stops enumeration if a visible window has been found.
|
||||
return !*result;
|
||||
}
|
||||
|
||||
// Convert Command line string to argv.
|
||||
base::CommandLine::StringVector CommandLineStringToArgv(
|
||||
const std::wstring& command_line_string) {
|
||||
int num_args = 0;
|
||||
wchar_t** args = NULL;
|
||||
args = ::CommandLineToArgvW(command_line_string.c_str(), &num_args);
|
||||
base::CommandLine::StringVector argv;
|
||||
for (int i = 0; i < num_args; ++i)
|
||||
argv.push_back(std::wstring(args[i]));
|
||||
LocalFree(args);
|
||||
return argv;
|
||||
}
|
||||
|
||||
bool ParseCommandLine(const COPYDATASTRUCT* cds,
|
||||
base::CommandLine::StringVector* parsed_command_line,
|
||||
base::FilePath* current_directory) {
|
||||
// We should have enough room for the shortest command (min_message_size)
|
||||
// and also be a multiple of wchar_t bytes. The shortest command
|
||||
// possible is L"START\0\0" (empty current directory and command line).
|
||||
static const int min_message_size = 7;
|
||||
if (cds->cbData < min_message_size * sizeof(wchar_t) ||
|
||||
cds->cbData % sizeof(wchar_t) != 0) {
|
||||
LOG(WARNING) << "Invalid WM_COPYDATA, length = " << cds->cbData;
|
||||
return false;
|
||||
}
|
||||
|
||||
// We split the string into 4 parts on NULLs.
|
||||
DCHECK(cds->lpData);
|
||||
const std::wstring msg(static_cast<wchar_t*>(cds->lpData),
|
||||
cds->cbData / sizeof(wchar_t));
|
||||
const std::wstring::size_type first_null = msg.find_first_of(L'\0');
|
||||
if (first_null == 0 || first_null == std::wstring::npos) {
|
||||
// no NULL byte, don't know what to do
|
||||
LOG(WARNING) << "Invalid WM_COPYDATA, length = " << msg.length() <<
|
||||
", first null = " << first_null;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Decode the command, which is everything until the first NULL.
|
||||
if (msg.substr(0, first_null) == L"START") {
|
||||
// Another instance is starting parse the command line & do what it would
|
||||
// have done.
|
||||
VLOG(1) << "Handling STARTUP request from another process";
|
||||
const std::wstring::size_type second_null =
|
||||
msg.find_first_of(L'\0', first_null + 1);
|
||||
if (second_null == std::wstring::npos ||
|
||||
first_null == msg.length() - 1 || second_null == msg.length()) {
|
||||
LOG(WARNING) << "Invalid format for start command, we need a string in 4 "
|
||||
"parts separated by NULLs";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get current directory.
|
||||
*current_directory = base::FilePath(msg.substr(first_null + 1,
|
||||
second_null - first_null));
|
||||
|
||||
const std::wstring::size_type third_null =
|
||||
msg.find_first_of(L'\0', second_null + 1);
|
||||
if (third_null == std::wstring::npos ||
|
||||
third_null == msg.length()) {
|
||||
LOG(WARNING) << "Invalid format for start command, we need a string in 4 "
|
||||
"parts separated by NULLs";
|
||||
}
|
||||
|
||||
// Get command line.
|
||||
const std::wstring cmd_line =
|
||||
msg.substr(second_null + 1, third_null - second_null);
|
||||
*parsed_command_line = CommandLineStringToArgv(cmd_line);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ProcessLaunchNotification(
|
||||
const ProcessSingleton::NotificationCallback& notification_callback,
|
||||
UINT message,
|
||||
WPARAM wparam,
|
||||
LPARAM lparam,
|
||||
LRESULT* result) {
|
||||
if (message != WM_COPYDATA)
|
||||
return false;
|
||||
|
||||
// Handle the WM_COPYDATA message from another process.
|
||||
const COPYDATASTRUCT* cds = reinterpret_cast<COPYDATASTRUCT*>(lparam);
|
||||
|
||||
base::CommandLine::StringVector parsed_command_line;
|
||||
base::FilePath current_directory;
|
||||
if (!ParseCommandLine(cds, &parsed_command_line, ¤t_directory)) {
|
||||
*result = TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
*result = notification_callback.Run(parsed_command_line, current_directory) ?
|
||||
TRUE : FALSE;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TerminateAppWithError() {
|
||||
// TODO: This is called when the secondary process can't ping the primary
|
||||
// process. Need to find out what to do here.
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ProcessSingleton::ProcessSingleton(
|
||||
const base::FilePath& user_data_dir,
|
||||
const NotificationCallback& notification_callback)
|
||||
: notification_callback_(notification_callback),
|
||||
is_virtualized_(false),
|
||||
lock_file_(INVALID_HANDLE_VALUE),
|
||||
user_data_dir_(user_data_dir),
|
||||
should_kill_remote_process_callback_(
|
||||
base::Bind(&TerminateAppWithError)) {
|
||||
}
|
||||
|
||||
ProcessSingleton::~ProcessSingleton() {
|
||||
if (lock_file_ != INVALID_HANDLE_VALUE)
|
||||
::CloseHandle(lock_file_);
|
||||
}
|
||||
|
||||
// Code roughly based on Mozilla.
|
||||
ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
|
||||
if (is_virtualized_)
|
||||
return PROCESS_NOTIFIED; // We already spawned the process in this case.
|
||||
if (lock_file_ == INVALID_HANDLE_VALUE && !remote_window_) {
|
||||
return LOCK_ERROR;
|
||||
} else if (!remote_window_) {
|
||||
return PROCESS_NONE;
|
||||
}
|
||||
|
||||
switch (chrome::AttemptToNotifyRunningChrome(remote_window_, false)) {
|
||||
case chrome::NOTIFY_SUCCESS:
|
||||
return PROCESS_NOTIFIED;
|
||||
case chrome::NOTIFY_FAILED:
|
||||
remote_window_ = NULL;
|
||||
return PROCESS_NONE;
|
||||
case chrome::NOTIFY_WINDOW_HUNG:
|
||||
// Fall through and potentially terminate the hung browser.
|
||||
break;
|
||||
}
|
||||
|
||||
DWORD process_id = 0;
|
||||
DWORD thread_id = ::GetWindowThreadProcessId(remote_window_, &process_id);
|
||||
if (!thread_id || !process_id) {
|
||||
remote_window_ = NULL;
|
||||
return PROCESS_NONE;
|
||||
}
|
||||
base::Process process = base::Process::Open(process_id);
|
||||
|
||||
// The window is hung. Scan for every window to find a visible one.
|
||||
bool visible_window = false;
|
||||
::EnumThreadWindows(thread_id,
|
||||
&BrowserWindowEnumeration,
|
||||
reinterpret_cast<LPARAM>(&visible_window));
|
||||
|
||||
// If there is a visible browser window, ask the user before killing it.
|
||||
if (visible_window && !should_kill_remote_process_callback_.Run()) {
|
||||
// The user denied. Quit silently.
|
||||
return PROCESS_NOTIFIED;
|
||||
}
|
||||
|
||||
// Time to take action. Kill the browser process.
|
||||
process.Terminate(content::RESULT_CODE_HUNG, true);
|
||||
remote_window_ = NULL;
|
||||
return PROCESS_NONE;
|
||||
}
|
||||
|
||||
ProcessSingleton::NotifyResult
|
||||
ProcessSingleton::NotifyOtherProcessOrCreate() {
|
||||
ProcessSingleton::NotifyResult result = PROCESS_NONE;
|
||||
if (!Create()) {
|
||||
result = NotifyOtherProcess();
|
||||
if (result == PROCESS_NONE)
|
||||
result = PROFILE_IN_USE;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Look for a Chrome instance that uses the same profile directory. If there
|
||||
// isn't one, create a message window with its title set to the profile
|
||||
// directory path.
|
||||
bool ProcessSingleton::Create() {
|
||||
static const wchar_t kMutexName[] = L"Local\\AtomProcessSingletonStartup!";
|
||||
|
||||
remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_);
|
||||
if (!remote_window_) {
|
||||
// Make sure we will be the one and only process creating the window.
|
||||
// We use a named Mutex since we are protecting against multi-process
|
||||
// access. As documented, it's clearer to NOT request ownership on creation
|
||||
// since it isn't guaranteed we will get it. It is better to create it
|
||||
// without ownership and explicitly get the ownership afterward.
|
||||
base::win::ScopedHandle only_me(::CreateMutex(NULL, FALSE, kMutexName));
|
||||
if (!only_me.IsValid()) {
|
||||
DPLOG(FATAL) << "CreateMutex failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
AutoLockMutex auto_lock_only_me(only_me.Get());
|
||||
|
||||
// We now own the mutex so we are the only process that can create the
|
||||
// window at this time, but we must still check if someone created it
|
||||
// between the time where we looked for it above and the time the mutex
|
||||
// was given to us.
|
||||
remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_);
|
||||
if (!remote_window_) {
|
||||
// We have to make sure there is no Chrome instance running on another
|
||||
// machine that uses the same profile.
|
||||
base::FilePath lock_file_path = user_data_dir_.AppendASCII(kLockfile);
|
||||
lock_file_ = ::CreateFile(lock_file_path.value().c_str(),
|
||||
GENERIC_WRITE,
|
||||
FILE_SHARE_READ,
|
||||
NULL,
|
||||
CREATE_ALWAYS,
|
||||
FILE_ATTRIBUTE_NORMAL |
|
||||
FILE_FLAG_DELETE_ON_CLOSE,
|
||||
NULL);
|
||||
DWORD error = ::GetLastError();
|
||||
LOG_IF(WARNING, lock_file_ != INVALID_HANDLE_VALUE &&
|
||||
error == ERROR_ALREADY_EXISTS) << "Lock file exists but is writable.";
|
||||
LOG_IF(ERROR, lock_file_ == INVALID_HANDLE_VALUE)
|
||||
<< "Lock file can not be created! Error code: " << error;
|
||||
|
||||
if (lock_file_ != INVALID_HANDLE_VALUE) {
|
||||
// Set the window's title to the path of our user data directory so
|
||||
// other Chrome instances can decide if they should forward to us.
|
||||
bool result = window_.CreateNamed(
|
||||
base::Bind(&ProcessLaunchNotification, notification_callback_),
|
||||
user_data_dir_.value());
|
||||
|
||||
// NB: Ensure that if the primary app gets started as elevated
|
||||
// admin inadvertently, secondary windows running not as elevated
|
||||
// will still be able to send messages
|
||||
::ChangeWindowMessageFilterEx(window_.hwnd(), WM_COPYDATA, MSGFLT_ALLOW, NULL);
|
||||
CHECK(result && window_.hwnd());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return window_.hwnd() != NULL;
|
||||
}
|
||||
|
||||
void ProcessSingleton::Cleanup() {
|
||||
}
|
||||
|
||||
void ProcessSingleton::OverrideShouldKillRemoteProcessCallbackForTesting(
|
||||
const ShouldKillRemoteProcessCallback& display_dialog_callback) {
|
||||
should_kill_remote_process_callback_ = display_dialog_callback;
|
||||
}
|
|
@ -62,7 +62,7 @@
|
|||
## Desarrollo
|
||||
|
||||
* [Guía de Estilo](development/coding-style.md)
|
||||
* [Estructura de los directorios del Código Fuente](../../development/source-code-directory-structure.md)
|
||||
* [Estructura de los directorios del Código Fuente](development/source-code-directory-structure.md)
|
||||
* [Diferencias Técnicas con NW.js (anteriormente conocido como node-webkit)](../../development/atom-shell-vs-node-webkit.md)
|
||||
* [Repaso del Sistema de Compilación](../../development/build-system-overview.md)
|
||||
* [Instrucciones de Compilación (Mac)](../../development/build-instructions-osx.md)
|
||||
|
|
37
docs-translations/es/development/coding-style.md
Normal file
37
docs-translations/es/development/coding-style.md
Normal file
|
@ -0,0 +1,37 @@
|
|||
# Guía de estilo de código
|
||||
|
||||
Esta es la guía de estilo de código para Electron.
|
||||
|
||||
## C++ y Python
|
||||
|
||||
Para C++ y Python, nosotros seguimos la [guía de estilo](http://www.chromium.org/developers/coding-style) de Chromium.
|
||||
Además hay un script `script/cpplint.py` para verificar si todos los archivos
|
||||
siguen el estilo.
|
||||
|
||||
La versión de Python que estamos usando ahora es Python 2.7.
|
||||
|
||||
El código C++ usa muchas abstracciones y tipos de Chromium, por eso
|
||||
se recomienda familiarizarse con ellos. Un buen lugar para iniciar es
|
||||
el documento de Chromium sobre [Abstracciones importantes y estructras de datos](https://www.chromium.org/developers/coding-style/important-abstractions-and-data-structures). El documento menciona algunos tipos especiales, tipos por alcance (que
|
||||
automaticamente liberan su memoria cuando salen de su alcance), mecanismos de
|
||||
registro de eventos, etcétera.
|
||||
|
||||
## CoffeeScript
|
||||
|
||||
Para CoffeeScript, nosotros seguimos la [guía de estilo](https://github.com/styleguide/javascript) de Github y también las
|
||||
siguientes reglas:
|
||||
|
||||
* Los archivos **NO** deberían terminar con una nueva línea, por que se busca
|
||||
seguir los estilos que usa Google.
|
||||
* Los nombres de los archivos debén estar concatenados con `-` en vez de `_`,
|
||||
por ejemplo `nombre-de-archivo.coffee` en vez de `nombre_de_archivo.coffee`,
|
||||
esto es por que en [github/atom](https://github.com/github/atom)
|
||||
los nombres de los módulos usualmente estan en la forma `nombre-de-modulo`.
|
||||
Esta regla aplica únicamente a los archivos `.coffee`.
|
||||
|
||||
## Nombres de las API
|
||||
|
||||
Al crear una nueva API, nosotros deberíamos preferir usar metodos `get` y `set`
|
||||
en vez de usar el estilo de jQuery que utiliza una sola función. Por ejemplo,
|
||||
se prefiere `.getText()` y `.setText()` por sobre `.text([text])`. Hay una
|
||||
[discusión](https://github.com/atom/electron/issues/46) sobre esto.
|
|
@ -0,0 +1,62 @@
|
|||
# Estructura de los directorios del código fuente
|
||||
|
||||
El código fuente de electron es separado en pocas partes, en su mayoría
|
||||
siguiendo las especificaciones para separar archivos que usa Chromium.
|
||||
|
||||
Quizá necesites familiarizarte con la [arquitectura multiprocesos](http://dev.chromium.org/developers/design-documents/multi-process-architecture) de Chromium para comprender mejor el código fuente.
|
||||
|
||||
## Estructura del código fuente
|
||||
|
||||
```
|
||||
Electron
|
||||
├──atom - Código fuente de Electron.
|
||||
| ├── app - Código de arranque.
|
||||
| ├── browser - La interfaz incluyendo la ventana principal, UI,
|
||||
| | y todas las cosas del proceso principal. Este le habla al renderizador
|
||||
| | para manejar las páginas web.
|
||||
| | ├── lib - Código Javascript para inicializar el proceso principal.
|
||||
| | ├── ui - Implementaciones de UI para distintas plataformas.
|
||||
| | | ├── cocoa - Código fuente específico para Cocoa.
|
||||
| | | ├── gtk - Código fuente específico para GTK+.
|
||||
| | | └── win - Código fuente específico para Windows GUI.
|
||||
| | ├── default_app - La página por defecto para mostrar cuando Electron
|
||||
| | | es iniciado sin proveer una app.
|
||||
| | ├── api - La implementación de las APIs para el proceso principal.
|
||||
| | | └── lib - Código Javascript parte de la implementación de la API.
|
||||
| | ├── net - Código relacionado a la red.
|
||||
| | ├── mac - Código fuente de Objective-C específico para Mac.
|
||||
| | └── resources - Iconos, archivos específicos de plataforma, etc.
|
||||
| ├── renderer - Código que se ejecuta en el proceso de renderizado.
|
||||
| | ├── lib - Código Javascript del proceso de inicio del renderizador.
|
||||
| | └── api - La implementación de las APIs para el proceso de renderizado.
|
||||
| | └── lib - Código Javascript parte de la implementación de la API.
|
||||
| └── common - Código que se utiliza en ambos procesos, el principal y el de
|
||||
| renderizado. Incluye algunas funciones de utilidad y código para integrar
|
||||
| el ciclo de mensajes de Node en el ciclo de mensajes de Chromium.
|
||||
| ├── lib - Código Javascript común para la inicialización.
|
||||
| └── api - La implementación de APIs comunes, y los fundamentos de
|
||||
| los módulos integrados de Electron.
|
||||
| └── lib - Código Javascript parte de la implementación de la API.
|
||||
├── chromium_src - Código fuente copiado de Chromium.
|
||||
├── docs - Documentación.
|
||||
├── spec - Pruebas automaticas.
|
||||
├── atom.gyp - Reglas de compilado de Electron.
|
||||
└── common.gypi - Configuración específica para compilar y reglas
|
||||
de empaquetado para otros componentes como `node` y `breakpad`.
|
||||
```
|
||||
|
||||
## Estructura de otros directorios
|
||||
|
||||
* **script** - Scripts usados para propositos de desarrollo
|
||||
como compilar, empaquetar, realizar pruebas, etc.
|
||||
* **tools** - Scripts de ayuda usados por los archivos gyp, contrario a la
|
||||
carpeta `scripts`, estos scripts nunca deberían ser llamados por los usuarios.
|
||||
* **vendor** - Código fuente de dependencias externas, no usamos `third_party`
|
||||
como nombre por que se podría confundir con el mismo directorio
|
||||
en las carpetas del código fuente de Chromium.
|
||||
* **node_modules** - Módulos de node usados para la compilación.
|
||||
* **out** - Directorio temporal de salida usado por `ninja`.
|
||||
* **dist** - Directorio temporal creado por `script/create-dist.py` cuando
|
||||
se esta creando una distribución.
|
||||
* **external_binaries** - Binarios descargados de frameworks externos que no
|
||||
soportan la compilación con `gyp`.
|
|
@ -65,7 +65,6 @@ a ser ejecutado por el proceso principal. Un ejemplo de `package.json` podría v
|
|||
```
|
||||
|
||||
El `main.js` debería crear las ventanas y gestionar los eventos del sistema, un ejemplo típico sería:
|
||||
example being:
|
||||
|
||||
```javascript
|
||||
var app = require('app'); // Módulo para controlar el ciclo de vida de la aplicación.
|
||||
|
|
30
docs-translations/es/tutorial/supported-platforms.md
Normal file
30
docs-translations/es/tutorial/supported-platforms.md
Normal file
|
@ -0,0 +1,30 @@
|
|||
# Plataformas soportadas
|
||||
|
||||
Las siguientes plataformas son soportadas por Electron:
|
||||
|
||||
### OS X
|
||||
|
||||
Sólo se proveen binarios de 64 bit para OS X.
|
||||
La versión mínima soportada es OS X 10.8.
|
||||
|
||||
### Windows
|
||||
|
||||
Windows 7 y posteriores son soportados, las versiones antiguas no son soportadas (y no funcionan).
|
||||
|
||||
Se proveen binarios para las arquitecturas `x86` y `amd64` (x64).
|
||||
Nota: La versión para `ARM` de Windows no está soportada aún.
|
||||
|
||||
### Linux
|
||||
|
||||
Los binarios preconstruidos para `ia32`(`i686`) y `x64`(`amd64`) son construidos sobre
|
||||
Ubuntu 12.04, el binario para `arm` es construido sobre ARM v7 con la ABI hard-float
|
||||
y NEON para Debian Wheezy.
|
||||
|
||||
La posibilidad de que un binario preconstruido se ejecute en una distribución determinada
|
||||
depende de las librerías contra las que fue enlazado Electron.
|
||||
Por ahora sólo se garantiza la ejecución en Ubuntu 12.04, aunque también se ha verificado
|
||||
el funcionamiento de los binarios preconstruidos en las siguientes plataformas:
|
||||
|
||||
* Ubuntu 12.04 and later
|
||||
* Fedora 21
|
||||
* Debian 8
|
73
docs-translations/th-TH/README.md
Normal file
73
docs-translations/th-TH/README.md
Normal file
|
@ -0,0 +1,73 @@
|
|||
## คู่มือ
|
||||
|
||||
* [แพลตฟอร์มที่รองรับ](tutorial/supported-platforms.md)
|
||||
* [การเผยแพร่แอปพลิเคชัน](tutorial/application-distribution.md)
|
||||
* [แนวทางการส่งแอปเข้า Mac App Store](tutorial/mac-app-store-submission-guide.md)
|
||||
* [การบรรจุแอปพลิเคชัน](tutorial/application-packaging.md)
|
||||
* [การใช้โมดูลของ Node](tutorial/using-native-node-modules.md)
|
||||
* [การหาข้อผิดพลาดในกระบวนการหลัก](tutorial/debugging-main-process.md)
|
||||
* [การใช้งาน Selenium และ WebDriver](tutorial/using-selenium-and-webdriver.md)
|
||||
* [ส่วนเสริมของ DevTools](tutorial/devtools-extension.md)
|
||||
* [การใช้งานส่วนเสริม Pepper Flash](tutorial/using-pepper-flash-plugin.md)
|
||||
|
||||
## แนะนำ
|
||||
|
||||
* [เริ่มต้นอย่างคราวๆ](tutorial/quick-start.md)
|
||||
* [การร่วมกันของสภาพแวดล้อมบนเดสทอป](tutorial/desktop-environment-integration.md)
|
||||
* [การตรวจจับเหตุการณ์ออนไลน์หรือออฟไลน์](tutorial/online-offline-events.md)
|
||||
|
||||
## แหล่งอ้างอิงของ API
|
||||
|
||||
* [สรุปความ](api/synopsis.md)
|
||||
* [โปรเซสออบเจค](api/process.md)
|
||||
* [คำสั่งสำหรับเปลี่ยนแปลงค่าของ Chrome ที่รองรับ](api/chrome-command-line-switches.md)
|
||||
|
||||
### การปรับแต่ง DOM:
|
||||
|
||||
* [วัตถุ `File`](api/file-object.md)
|
||||
* [แท็ก `<webview>`](api/web-view-tag.md)
|
||||
* [ฟังก์ชัน `window.open`](api/window-open.md)
|
||||
|
||||
### โมดูลสำหรับกระบวนการหลัก :
|
||||
|
||||
* [app](api/app.md)
|
||||
* [auto-updater](api/auto-updater.md)
|
||||
* [browser-window](api/browser-window.md)
|
||||
* [content-tracing](api/content-tracing.md)
|
||||
* [dialog](api/dialog.md)
|
||||
* [global-shortcut](api/global-shortcut.md)
|
||||
* [ipc (main process)](api/ipc-main-process.md)
|
||||
* [menu](api/menu.md)
|
||||
* [menu-item](api/menu-item.md)
|
||||
* [power-monitor](api/power-monitor.md)
|
||||
* [power-save-blocker](api/power-save-blocker.md)
|
||||
* [protocol](api/protocol.md)
|
||||
* [session](api/session.md)
|
||||
* [web-contents](api/web-contents.md)
|
||||
* [tray](api/tray.md)
|
||||
|
||||
### โมดูลสำหรับกระบวนการ Renderer (เว็บเพจ):
|
||||
|
||||
* [ipc (renderer)](api/ipc-renderer.md)
|
||||
* [remote](api/remote.md)
|
||||
* [web-frame](api/web-frame.md)
|
||||
|
||||
### Modules for Both Processes:
|
||||
|
||||
* [clipboard](api/clipboard.md)
|
||||
* [crash-reporter](api/crash-reporter.md)
|
||||
* [native-image](api/native-image.md)
|
||||
* [screen](api/screen.md)
|
||||
* [shell](api/shell.md)
|
||||
|
||||
## การพัฒนา
|
||||
|
||||
* [ลักษณะการเขียนโค้ด](development/coding-style.md)
|
||||
* [โครงสร้างไดเรคทอรี่ของซอร์สโค้ด](development/source-code-directory-structure.md)
|
||||
* [ความแตกต่างทางเทคนิคจาก NW.js (หรือ node-webkit)](development/atom-shell-vs-node-webkit.md)
|
||||
* [ภาพรวมการสร้างระบบ](development/build-system-overview.md)
|
||||
* [ขั้นตอนการสร้าง (OS X)](development/build-instructions-osx.md)
|
||||
* [ขั้นตอนการสร้าง (Windows)](development/build-instructions-windows.md)
|
||||
* [ขั้นตอนการสร้าง (Linux)](development/build-instructions-linux.md)
|
||||
* [Setting Up Symbol Server in debugger](development/setting-up-symbol-server.md)
|
||||
* [การติดตั้งเซิร์ฟเวอร์ Symbol Server ใน debugger](development/setting-up-symbol-server.md)
|
|
@ -30,7 +30,7 @@ Electron 的用戶擁有在網頁中呼叫 Node.js APIs 的能力,允許低級
|
|||
在網頁中,是不允許呼叫原生 GUI 相關 APIs 因為管理原生 GUI 資源在網頁上是非常危險而且容易造成資源洩露。
|
||||
如果你想要在網頁中呼叫 GUI 相關的 APIs 的操作,網頁的渲染行程必須與主行程進行通訊,請求主行程進行相關的操作。
|
||||
|
||||
在 Electron ,我們提供用於在主行程與渲染行程之間通訊的 [ipc][1] 模組。並且也有一個遠端模使用 RPC 通訊方式 [remote][2]。
|
||||
在 Electron,我們提供用於在主行程與渲染行程之間通訊的 [ipc](../api/ipc-renderer.md) 模組。並且也有一個遠端模組使用 RPC 通訊方式 [remote](../api/remote.md)。
|
||||
|
||||
# 打造你第一個 Electron 應用
|
||||
|
||||
|
@ -43,7 +43,7 @@ your-app/
|
|||
└── index.html
|
||||
```
|
||||
|
||||
`package.json ` 的格式與 Node 的模組完全一樣,並且有個腳本被指定為 `main` 是用來啟動你的應用程式,它運行在主行程上。
|
||||
`package.json` 的格式與 Node 的模組完全一樣,並且有個腳本被指定為 `main` 是用來啟動你的應用程式,它運行在主行程上。
|
||||
你應用裡的 一個範例在你的 `package.json` 看起來可能像這樣:
|
||||
|
||||
```json
|
||||
|
@ -88,7 +88,7 @@ app.on('ready', function() {
|
|||
mainWindow.loadUrl('file://' + __dirname + '/index.html');
|
||||
|
||||
// 打開開發者工具
|
||||
mainWindow.openDevTools();
|
||||
mainWindow.webContents.openDevTools();
|
||||
|
||||
// 當window 被關閉,這個事件會被觸發
|
||||
mainWindow.on('closed', function() {
|
||||
|
@ -110,8 +110,9 @@ app.on('ready', function() {
|
|||
</head>
|
||||
<body>
|
||||
<h1>Hello World!</h1>
|
||||
We are using Node.js <script>document.write(process.version)</script>
|
||||
and Electron <script>document.write(process.versions['electron'])</script>.
|
||||
We are using node <script>document.write(process.versions.node)</script>,
|
||||
Chrome <script>document.write(process.versions.chrome)</script>,
|
||||
and Electron <script>document.write(process.versions.electron)</script>.
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
@ -160,6 +161,17 @@ $ ./Electron.app/Contents/MacOS/Electron your-app/
|
|||
# 作為版本發行
|
||||
在你完成了你的應用程式後,你可以依照 [應用部署](https://github.com/atom/electron/blob/master/docs/tutorial/application-distribution.md) 指南發布一個版本,並且運行已經打包好的應用程式。
|
||||
|
||||
[1]: https://github.com/atom/electron/blob/master/docs/api/ipc-renderer.md
|
||||
# 試試這個範例
|
||||
|
||||
[2]: https://github.com/atom/electron/blob/master/docs/api/remote.md
|
||||
Clone 與執行本篇教學的程式碼,它們都放在 [`atom/electron-quick-start`](https://github.com/atom/electron-quick-start) 這個 repository。
|
||||
|
||||
**Note**: 執行這個範例需要 [Git](https://git-scm.com) 以及 [Node.js](https://nodejs.org/en/download/) (其中包括 [npm](https://npmjs.org)) 在你的作業系統。
|
||||
|
||||
```bash
|
||||
# Clone the repository
|
||||
$ git clone https://github.com/atom/electron-quick-start
|
||||
# Go into the repository
|
||||
$ cd electron-quick-start
|
||||
# Install dependencies and run the app
|
||||
$ npm install && npm start
|
||||
```
|
105
docs/api/app.md
105
docs/api/app.md
|
@ -133,18 +133,23 @@ Emitted when a new [browserWindow](browser-window.md) is created.
|
|||
|
||||
### Event: 'select-certificate'
|
||||
|
||||
Emitted when a client certificate is requested.
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `webContents` [WebContents](browser-window.md#class-webcontents)
|
||||
* `url` String
|
||||
* `webContents` [WebContents](web-contents.md)
|
||||
* `url` URL
|
||||
* `certificateList` [Objects]
|
||||
* `data` PEM encoded data
|
||||
* `issuerName` Issuer's Common Name
|
||||
* `callback` Function
|
||||
|
||||
Emitted when a client certificate is requested.
|
||||
|
||||
The `url` corresponds to the navigation entry requesting the client certificate
|
||||
and `callback` needs to be called with an entry filtered from the list. Using
|
||||
`event.preventDefault()` prevents the application from using the first
|
||||
certificate from the store.
|
||||
|
||||
```javascript
|
||||
app.on('select-certificate', function(event, host, url, list, callback) {
|
||||
event.preventDefault();
|
||||
|
@ -152,10 +157,36 @@ app.on('select-certificate', function(event, host, url, list, callback) {
|
|||
})
|
||||
```
|
||||
|
||||
The `url` corresponds to the navigation entry requesting the client certificate
|
||||
and `callback` needs to be called with an entry filtered from the list. Using
|
||||
`event.preventDefault()` prevents the application from using the first
|
||||
certificate from the store.
|
||||
### Event: 'login'
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `webContents` [WebContents](web-contents.md)
|
||||
* `request` Object
|
||||
* `method` String
|
||||
* `url` URL
|
||||
* `referrer` URL
|
||||
* `authInfo` Object
|
||||
* `isProxy` Boolean
|
||||
* `scheme` String
|
||||
* `host` String
|
||||
* `port` Integer
|
||||
* `realm` String
|
||||
* `callback` Function
|
||||
|
||||
Emitted when `webContents` wants to do basic auth.
|
||||
|
||||
The default behavior is to cancel all authentications, to override this you
|
||||
should prevent the default behavior with `event.preventDefault()` and call
|
||||
`callback(username, password)` with the credentials.
|
||||
|
||||
```javascript
|
||||
app.on('login', function(event, webContents, request, authInfo, callback) {
|
||||
event.preventDefault();
|
||||
callback('username', 'secret');
|
||||
})
|
||||
```
|
||||
|
||||
### Event: 'gpu-process-crashed'
|
||||
|
||||
|
@ -245,7 +276,7 @@ Returns the current application locale.
|
|||
Resolves the proxy information for `url`. The `callback` will be called with
|
||||
`callback(proxy)` when the request is performed.
|
||||
|
||||
### `app.addRecentDocument(path)`
|
||||
### `app.addRecentDocument(path)` _OS X_ _Windows_
|
||||
|
||||
* `path` String
|
||||
|
||||
|
@ -254,7 +285,7 @@ Adds `path` to the recent documents list.
|
|||
This list is managed by the OS. On Windows you can visit the list from the task
|
||||
bar, and on OS X you can visit it from dock menu.
|
||||
|
||||
### `app.clearRecentDocuments()`
|
||||
### `app.clearRecentDocuments()` _OS X_ _Windows_
|
||||
|
||||
Clears the recent documents list.
|
||||
|
||||
|
@ -290,6 +321,60 @@ URLs that fall under "Local Intranet" sites (i.e. are in the same domain as you)
|
|||
However, this detection often fails when corporate networks are badly configured,
|
||||
so this lets you co-opt this behavior and enable it for all URLs.
|
||||
|
||||
### `app.makeSingleInstance(callback)`
|
||||
|
||||
* `callback` Function
|
||||
|
||||
This method makes your application a Single Instance Application - instead of
|
||||
allowing multiple instances of your app to run, this will ensure that only a
|
||||
single instance of your app is running, and other instances signal this
|
||||
instance and exit.
|
||||
|
||||
`callback` will be called with `callback(argv, workingDirectory)` when a second
|
||||
instance has been executed. `argv` is an Array of the second instance's command
|
||||
line arguments, and `workingDirectory` is its current working directory. Usually
|
||||
applications respond to this by making their primary window focused and
|
||||
non-minimized.
|
||||
|
||||
The `callback` is guaranteed to be executed after the `ready` event of `app`
|
||||
gets emitted.
|
||||
|
||||
This method returns `false` if your process is the primary instance of the
|
||||
application and your app should continue loading. And returns `true` if your
|
||||
process has sent its parameters to another instance, and you should immediately
|
||||
quit.
|
||||
|
||||
On OS X the system enforces single instance automatically when users try to open
|
||||
a second instance of your app in Finder, and the `open-file` and `open-url`
|
||||
events will be emitted for that. However when users start your app in command
|
||||
line the system's single instance machanism will be bypassed and you have to
|
||||
use this method to ensure single instance.
|
||||
|
||||
An example of activating the window of primary instance when a second instance
|
||||
starts:
|
||||
|
||||
```js
|
||||
var myWindow = null;
|
||||
|
||||
var shouldQuit = app.makeSingleInstance(function(commandLine, workingDirectory) {
|
||||
// Someone tried to run a second instance, we should focus our window
|
||||
if (myWindow) {
|
||||
if (myWindow.isMinimized()) myWindow.restore();
|
||||
myWindow.focus();
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (shouldQuit) {
|
||||
app.quit();
|
||||
return;
|
||||
}
|
||||
|
||||
// Create myWindow, load the rest of the app, etc...
|
||||
app.on('ready', function() {
|
||||
});
|
||||
```
|
||||
|
||||
### `app.commandLine.appendSwitch(switch[, value])`
|
||||
|
||||
Append a switch (with optional `value`) to Chromium's command line.
|
||||
|
|
|
@ -1,106 +1,31 @@
|
|||
# autoUpdater
|
||||
|
||||
**This module has only been implemented for OS X.**
|
||||
This module provides an interface for the `Squirrel` auto-updater framework.
|
||||
|
||||
Check out [atom/grunt-electron-installer](https://github.com/atom/grunt-electron-installer)
|
||||
to build a Windows installer for your app.
|
||||
## Platform notices
|
||||
|
||||
The `auto-updater` module is a simple wrapper around the
|
||||
[Squirrel.Mac](https://github.com/Squirrel/Squirrel.Mac) framework.
|
||||
Though `autoUpdater` provides an uniform API for different platforms, there are
|
||||
still some subtle differences on each platform.
|
||||
|
||||
Squirrel.Mac requires that your `.app` folder is signed using the
|
||||
[codesign](https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man1/codesign.1.html)
|
||||
utility for updates to be installed.
|
||||
### OS X
|
||||
|
||||
## Squirrel
|
||||
On OS X the `autoUpdater` module is built upon [Squirrel.Mac][squirrel-mac], you
|
||||
don't need any special setup to make it work. For server-side requirements, you
|
||||
can read [Server Support][server-support].
|
||||
|
||||
Squirrel is an OS X framework focused on making application updates **as safe
|
||||
and transparent as updates to a website**.
|
||||
### Windows
|
||||
|
||||
Instead of publishing a feed of versions from which your app must select,
|
||||
Squirrel updates to the version your server tells it to. This allows you to
|
||||
intelligently update your clients based on the request you give to Squirrel.
|
||||
On Windows you have to install your app into user's machine before you can use
|
||||
the auto-updater, it is recommended to use [grunt-electron-installer][installer]
|
||||
module to generate a Windows installer.
|
||||
|
||||
Your request can include authentication details, custom headers or a request
|
||||
body so that your server has the context it needs in order to supply the most
|
||||
suitable update.
|
||||
The server-side setup is also different from OS X, you can read the documents of
|
||||
[Squirrel.Windows][squirrel-windows] to get more details.
|
||||
|
||||
The update JSON Squirrel requests should be dynamically generated based on
|
||||
criteria in the request and whether an update is required. Squirrel relies
|
||||
on server-side support to determine whether an update is required. See
|
||||
[Server Support](#server-support).
|
||||
### Linux
|
||||
|
||||
Squirrel's installer is designed to be fault tolerant and ensures that any
|
||||
updates installed are valid.
|
||||
|
||||
## Update Requests
|
||||
|
||||
Squirrel is indifferent to the request the client application provides for
|
||||
update checking. `Accept: application/json` is added to the request headers
|
||||
because Squirrel is responsible for parsing the response.
|
||||
|
||||
For the requirements imposed on the responses and the body format of an update
|
||||
response, see [Server Support](#server-support).
|
||||
|
||||
Your update request must *at least* include a version identifier so that the
|
||||
server can determine whether an update for this specific version is required. It
|
||||
may also include other identifying criteria, such as operating system version or
|
||||
username, to allow the server to deliver as fine grained an update as you
|
||||
would like.
|
||||
|
||||
How you include the version identifier or other criteria is specific to the
|
||||
server that you are requesting updates from. A common approach is to use query
|
||||
parameters, like this:
|
||||
|
||||
```javascript
|
||||
// In the main process
|
||||
var app = require('app');
|
||||
var autoUpdater = require('auto-updater');
|
||||
autoUpdater.setFeedUrl('http://mycompany.com/myapp/latest?version=' + app.getVersion());
|
||||
```
|
||||
|
||||
## Server Support
|
||||
|
||||
Your server should determine whether an update is required based on the
|
||||
[Update Request](#update-requests) your client issues.
|
||||
|
||||
If an update is required, your server should respond with a status code of
|
||||
[200 OK](http://tools.ietf.org/html/rfc2616#section-10.2.1) and include the
|
||||
[update JSON](#update-json-format) in the body. Squirrel **will** download and
|
||||
install this update, even if the version of the update is the same as the
|
||||
currently running version. To save redundantly downloading the same version
|
||||
multiple times your server must not inform the client to update.
|
||||
|
||||
If no update is required your server must respond with a status code of
|
||||
[204 No Content](http://tools.ietf.org/html/rfc2616#section-10.2.5). Squirrel
|
||||
will check for an update again at the interval you specify.
|
||||
|
||||
## Update JSON Format
|
||||
|
||||
When an update is available, Squirrel expects the following schema in response
|
||||
to the update request provided:
|
||||
|
||||
```json
|
||||
{
|
||||
"url": "http://mycompany.com/myapp/releases/myrelease",
|
||||
"name": "My Release Name",
|
||||
"notes": "Theses are some release notes innit",
|
||||
"pub_date": "2013-09-18T12:29:53+01:00"
|
||||
}
|
||||
```
|
||||
|
||||
The only required key is "url"; the others are optional.
|
||||
|
||||
Squirrel will request "url" with `Accept: application/zip` and only supports
|
||||
installing ZIP updates. If future update formats are supported their MIME type
|
||||
will be added to the `Accept` header so that your server can return the
|
||||
appropriate format.
|
||||
|
||||
`pub_date` (if present) must be formatted according to ISO 8601.
|
||||
|
||||
## Update server implementations
|
||||
|
||||
[Nuts](https://github.com/GitbookIO/nuts) is an open source implementation of the update server described above, it integrates beautifully with GitHub releases. Nuts manages downloads and updates, it’s compatible with `Squirrel.Mac` and `Squirrel.Windows` so you get cross-platform support out of the box.
|
||||
There is not built-in support for auto-updater on Linux, it is recommended to
|
||||
use the distribution's package manager to update your app.
|
||||
|
||||
## Events
|
||||
|
||||
|
@ -110,8 +35,7 @@ The `autoUpdater` object emits the following events:
|
|||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `message` String
|
||||
* `error` Error
|
||||
|
||||
Emitted when there is an error while updating.
|
||||
|
||||
|
@ -137,10 +61,10 @@ Returns:
|
|||
* `releaseName` String
|
||||
* `releaseDate` Date
|
||||
* `updateUrl` String
|
||||
* `quitAndUpdate` Function
|
||||
|
||||
Emitted when an update has been downloaded. Calling `quitAndUpdate()` will
|
||||
restart the application and install the update.
|
||||
Emitted when an update has been downloaded.
|
||||
|
||||
On Windows only `releaseName` is available.
|
||||
|
||||
## Methods
|
||||
|
||||
|
@ -150,10 +74,20 @@ The `autoUpdater` object has the following methods:
|
|||
|
||||
* `url` String
|
||||
|
||||
Set the `url` and initialize the auto updater. The `url` cannot be changed
|
||||
Sets the `url` and initialize the auto updater. The `url` cannot be changed
|
||||
once it is set.
|
||||
|
||||
### `autoUpdater.checkForUpdates()`
|
||||
|
||||
Ask the server whether there is an update. You must call `setFeedUrl` before
|
||||
Asks the server whether there is an update. You must call `setFeedUrl` before
|
||||
using this API.
|
||||
|
||||
### `autoUpdater.quitAndUpdate()`
|
||||
|
||||
Restarts the app and install the update after it has been downloaded. It should
|
||||
only be called after `update-downloaded` has been emitted.
|
||||
|
||||
[squirrel-mac]: https://github.com/Squirrel/Squirrel.Mac
|
||||
[server-support]: https://github.com/Squirrel/Squirrel.Mac#server-support
|
||||
[squirrel-windows]: https://github.com/Squirrel/Squirrel.Windows
|
||||
[installer]: https://github.com/atom/grunt-electron-installer
|
||||
|
|
|
@ -61,6 +61,8 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
|
|||
key is pressed.
|
||||
* `enable-larger-than-screen` Boolean - Enable the window to be resized larger
|
||||
than screen.
|
||||
* `background-color` String - Window's background color as Hexadecimal value,
|
||||
like `#66CD00` or `#FFF`. This is only implemented on Linux and Windows.
|
||||
* `dark-theme` Boolean - Forces using dark theme for the window, only works on
|
||||
some GTK+3 desktop environments.
|
||||
* `transparent` Boolean - Makes the window [transparent](frameless-window.md).
|
||||
|
@ -550,6 +552,30 @@ Enters or leaves the kiosk mode.
|
|||
|
||||
Returns whether the window is in kiosk mode.
|
||||
|
||||
### `win.hookWindowMessage(message, callback)` _WINDOWS_
|
||||
|
||||
* `message` Integer
|
||||
* `callback` Function
|
||||
|
||||
Hooks a windows message. The `callback` is called when
|
||||
the message is received in the WndProc.
|
||||
|
||||
### `win.isWindowMessageHooked(message)` _WINDOWS_
|
||||
|
||||
* `message` Integer
|
||||
|
||||
Returns `true` or `false` depending on whether the message is hooked.
|
||||
|
||||
### `win.unhookWindowMessage(message)` _WINDOWS_
|
||||
|
||||
* `message` Integer
|
||||
|
||||
Unhook the window message.
|
||||
|
||||
### `win.unhookAllWindowMessages()` _WINDOWS_
|
||||
|
||||
Unhooks all of the window messages.
|
||||
|
||||
### `win.setRepresentedFilename(filename)` _OS X_
|
||||
|
||||
* `filename` String
|
||||
|
|
|
@ -59,11 +59,10 @@ ID.
|
|||
|
||||
The crash reporter will send the following data to the `submitUrl` as `POST`:
|
||||
|
||||
* `rept` String - e.g. 'electron-crash-service'.
|
||||
* `ver` String - The version of Electron.
|
||||
* `platform` String - e.g. 'win32'.
|
||||
* `process_type` String - e.g. 'renderer'.
|
||||
* `ptime` Number
|
||||
* `guid` String - e.g. '5e1286fc-da97-479e-918b-6bfb0c3d1c72'
|
||||
* `_version` String - The version in `package.json`.
|
||||
* `_productName` String - The product name in the `crashReporter` `options`
|
||||
object.
|
||||
|
|
|
@ -3,12 +3,7 @@
|
|||
The `remote` module provides a simple way to do inter-process communication
|
||||
(IPC) between the renderer process (web page) and the main process.
|
||||
|
||||
In Electron, only GUI-unrelated modules are available in the renderer process.
|
||||
Without the `remote` module, users who want to call a main process API in
|
||||
the renderer process will have to explicitly send inter-process messages
|
||||
to the main process. With the `remote` module, you can invoke methods of the
|
||||
main process object without explicitly sending inter-process messages, similar
|
||||
to Java's [RMI](http://en.wikipedia.org/wiki/Java_remote_method_invocation).
|
||||
In Electron, GUI-related modules (such as `dialog`, `menu` etc.) are only available in the main process, not in the renderer process. In order to use them from the renderer process, the `ipc` module is necessary to send inter-process messages to the main process. With the `remote` module, you can invoke methods of the main process object without explicitly sending inter-process messages, similar to Java's [RMI](http://en.wikipedia.org/wiki/Java_remote_method_invocation).
|
||||
|
||||
An example of creating a browser window from a renderer process:
|
||||
|
||||
|
|
|
@ -154,7 +154,9 @@ Clears the data of web storages.
|
|||
* `config` String
|
||||
* `callback` Function - Called when operation is done.
|
||||
|
||||
Parses the `config` indicating which proxies to use for the session.
|
||||
If `config` is a PAC url, it is used directly otherwise
|
||||
`config` is parsed based on the following rules indicating which
|
||||
proxies to use for the session.
|
||||
|
||||
```
|
||||
config = scheme-proxies[";"<scheme-proxies>]
|
||||
|
|
|
@ -36,6 +36,7 @@ Returns:
|
|||
|
||||
This event is like `did-finish-load` but emitted when the load failed or was
|
||||
cancelled, e.g. `window.stop()` is invoked.
|
||||
The full list of error codes and their meaning is available [here](https://code.google.com/p/chromium/codesearch#chromium/src/net/base/net_error_list.h).
|
||||
|
||||
### Event: 'did-frame-finish-load'
|
||||
|
||||
|
@ -166,6 +167,27 @@ Emitted when DevTools is closed.
|
|||
|
||||
Emitted when DevTools is focused / opened.
|
||||
|
||||
### Event: 'login'
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `request` Object
|
||||
* `method` String
|
||||
* `url` URL
|
||||
* `referrer` URL
|
||||
* `authInfo` Object
|
||||
* `isProxy` Boolean
|
||||
* `scheme` String
|
||||
* `host` String
|
||||
* `port` Integer
|
||||
* `realm` String
|
||||
* `callback` Function
|
||||
|
||||
Emitted when `webContents` wants to do basic auth.
|
||||
|
||||
The usage is the same with [the `login` event of `app`](app.md#event-login).
|
||||
|
||||
## Instance Methods
|
||||
|
||||
The `webContents` object has the following instance methods:
|
||||
|
@ -182,6 +204,7 @@ See [session documentation](session.md) for this object's methods.
|
|||
* `options` Object (optional), properties:
|
||||
* `httpReferrer` String - A HTTP Referrer url.
|
||||
* `userAgent` String - A user agent originating the request.
|
||||
* `extraHeaders` String - Extra headers separated by "\n"
|
||||
|
||||
Loads the `url` in the window, the `url` must contain the protocol prefix,
|
||||
e.g. the `http://` or `file://`.
|
||||
|
|
|
@ -1,26 +1,26 @@
|
|||
# Mac App Store Submission Guide
|
||||
|
||||
Since v0.34.0, Electron allows submitting packaged apps to the Mac App Store
|
||||
(MAS). This guide provides information on: how to submit your app and the
|
||||
Since v0.34.0, Electron allows submitting packaged apps to the Mac App Store
|
||||
(MAS). This guide provides information on: how to submit your app and the
|
||||
limitations of the MAS build.
|
||||
|
||||
## How to Submit Your App
|
||||
|
||||
The following steps introduce a simple way to submit your app to Mac App Store.
|
||||
However, these steps do not ensure sure your app will be approved by Apple; you
|
||||
still need to read Apple's [Submitting Your App][submitting-your-app] guide on
|
||||
The following steps introduce a simple way to submit your app to Mac App Store.
|
||||
However, these steps do not ensure sure your app will be approved by Apple; you
|
||||
still need to read Apple's [Submitting Your App][submitting-your-app] guide on
|
||||
how to meet the Mac App Store requirements.
|
||||
|
||||
### Get Certificate
|
||||
|
||||
To submit your app to the Mac App Store, you first must get a certificate from
|
||||
To submit your app to the Mac App Store, you first must get a certificate from
|
||||
Apple. You can follow these [existing guides][nwjs-guide] on web.
|
||||
|
||||
### Sign Your App
|
||||
|
||||
After getting the certificate from Apple, you can package your app by following
|
||||
[Application Distribution](application-distribution.md), and then proceed to
|
||||
signing your app. This step is basically the same with other programs, but the
|
||||
[Application Distribution](application-distribution.md), and then proceed to
|
||||
signing your app. This step is basically the same with other programs, but the
|
||||
key is to sign every dependency of Electron one by one.
|
||||
|
||||
First, you need to prepare two entitlements files.
|
||||
|
@ -79,9 +79,9 @@ codesign --deep -fs "$APP_KEY" --entitlements child.plist "$FRAMEWORKS_PATH/$APP
|
|||
codesign -fs "$APP_KEY" --entitlements parent.plist "$APP_PATH"
|
||||
productbuild --component "$APP_PATH" /Applications --sign "$INSTALLER_KEY" "$APP_PATH"
|
||||
```
|
||||
a
|
||||
|
||||
If you are new to app sandboxing under OS X, you should also read through
|
||||
Apple's [Enabling App Sandbox][enable-app-sandbox] to have a basic idea, then
|
||||
Apple's [Enabling App Sandbox][enable-app-sandbox] to have a basic idea, then
|
||||
add keys for the permissions needed by your app to the entitlements files.
|
||||
|
||||
### Upload Your App and Submit for Review
|
||||
|
@ -92,7 +92,7 @@ before uploading. Then you can [submit your app for review][submit-for-review].
|
|||
|
||||
## Limitations of MAS Build
|
||||
|
||||
In order to satisfy all requirements for app sandboxing, the following modules
|
||||
In order to satisfy all requirements for app sandboxing, the following modules
|
||||
have been disabled in the MAS build:
|
||||
|
||||
* `crash-reporter`
|
||||
|
|
|
@ -31,8 +31,8 @@ which handles the manual steps of downloading headers and building native module
|
|||
```sh
|
||||
npm install --save-dev electron-rebuild
|
||||
|
||||
# Every time you run npm install, run this
|
||||
node ./node_modules/.bin/electron-rebuild
|
||||
# Every time you run "npm install", run this
|
||||
./node_modules/.bin/electron-rebuild
|
||||
```
|
||||
|
||||
### The npm Way
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
'atom/browser/api/lib/app.coffee',
|
||||
'atom/browser/api/lib/atom-delegate.coffee',
|
||||
'atom/browser/api/lib/auto-updater.coffee',
|
||||
'atom/browser/api/lib/auto-updater/auto-updater-mac.coffee',
|
||||
'atom/browser/api/lib/auto-updater/auto-updater-win.coffee',
|
||||
'atom/browser/api/lib/auto-updater/squirrel-update-win.coffee',
|
||||
'atom/browser/api/lib/browser-window.coffee',
|
||||
'atom/browser/api/lib/content-tracing.coffee',
|
||||
'atom/browser/api/lib/dialog.coffee',
|
||||
|
@ -112,10 +115,7 @@
|
|||
'atom/browser/api/save_page_handler.h',
|
||||
'atom/browser/auto_updater.cc',
|
||||
'atom/browser/auto_updater.h',
|
||||
'atom/browser/auto_updater_delegate.h',
|
||||
'atom/browser/auto_updater_linux.cc',
|
||||
'atom/browser/auto_updater_mac.mm',
|
||||
'atom/browser/auto_updater_win.cc',
|
||||
'atom/browser/atom_access_token_store.cc',
|
||||
'atom/browser/atom_access_token_store.h',
|
||||
'atom/browser/atom_browser_client.cc',
|
||||
|
@ -150,6 +150,8 @@
|
|||
'atom/browser/common_web_contents_delegate.h',
|
||||
'atom/browser/javascript_environment.cc',
|
||||
'atom/browser/javascript_environment.h',
|
||||
'atom/browser/login_handler.cc',
|
||||
'atom/browser/login_handler.h',
|
||||
'atom/browser/mac/atom_application.h',
|
||||
'atom/browser/mac/atom_application.mm',
|
||||
'atom/browser/mac/atom_application_delegate.h',
|
||||
|
@ -302,7 +304,10 @@
|
|||
'atom/common/native_mate_converters/accelerator_converter.h',
|
||||
'atom/common/native_mate_converters/blink_converter.cc',
|
||||
'atom/common/native_mate_converters/blink_converter.h',
|
||||
'atom/common/native_mate_converters/callback.cc',
|
||||
'atom/common/native_mate_converters/callback.h',
|
||||
'atom/common/native_mate_converters/content_converter.cc',
|
||||
'atom/common/native_mate_converters/content_converter.h',
|
||||
'atom/common/native_mate_converters/file_path_converter.h',
|
||||
'atom/common/native_mate_converters/gfx_converter.cc',
|
||||
'atom/common/native_mate_converters/gfx_converter.h',
|
||||
|
@ -346,6 +351,8 @@
|
|||
'atom/utility/atom_content_utility_client.h',
|
||||
'chromium_src/chrome/browser/browser_process.cc',
|
||||
'chromium_src/chrome/browser/browser_process.h',
|
||||
'chromium_src/chrome/browser/chrome_process_finder_win.cc',
|
||||
'chromium_src/chrome/browser/chrome_process_finder_win.h',
|
||||
'chromium_src/chrome/browser/chrome_notification_types.h',
|
||||
'chromium_src/chrome/browser/extensions/global_shortcut_listener.cc',
|
||||
'chromium_src/chrome/browser/extensions/global_shortcut_listener.h',
|
||||
|
@ -374,6 +381,9 @@
|
|||
'chromium_src/chrome/browser/printing/printing_message_filter.h',
|
||||
'chromium_src/chrome/browser/printing/print_preview_message_handler.cc',
|
||||
'chromium_src/chrome/browser/printing/print_preview_message_handler.h',
|
||||
'chromium_src/chrome/browser/process_singleton_posix.cc',
|
||||
'chromium_src/chrome/browser/process_singleton_win.cc',
|
||||
'chromium_src/chrome/browser/process_singleton.h',
|
||||
'chromium_src/chrome/browser/renderer_host/pepper/chrome_browser_pepper_host_factory.cc',
|
||||
'chromium_src/chrome/browser/renderer_host/pepper/chrome_browser_pepper_host_factory.h',
|
||||
'chromium_src/chrome/browser/renderer_host/pepper/pepper_broker_message_filter.cc',
|
||||
|
|
|
@ -176,7 +176,8 @@ def create_dist_zip():
|
|||
zip_file = os.path.join(SOURCE_ROOT, 'dist', dist_name)
|
||||
|
||||
with scoped_cwd(DIST_DIR):
|
||||
files = TARGET_BINARIES[PLATFORM] + ['LICENSE', 'version']
|
||||
files = TARGET_BINARIES[PLATFORM] + ['LICENSE', 'LICENSES.chromium.html',
|
||||
'version']
|
||||
if PLATFORM == 'linux':
|
||||
files += [lib for lib in SYSTEM_LIBRARIES if os.path.exists(lib)]
|
||||
dirs = TARGET_DIRECTORIES[PLATFORM]
|
||||
|
@ -189,7 +190,7 @@ def create_chrome_binary_zip(binary, version):
|
|||
zip_file = os.path.join(SOURCE_ROOT, 'dist', dist_name)
|
||||
|
||||
with scoped_cwd(DIST_DIR):
|
||||
files = ['LICENSE']
|
||||
files = ['LICENSE', 'LICENSES.chromium.html']
|
||||
if PLATFORM == 'win32':
|
||||
files += [binary + '.exe']
|
||||
else:
|
||||
|
@ -205,7 +206,7 @@ def create_symbols_zip():
|
|||
zip_file = os.path.join(SOURCE_ROOT, 'dist', dist_name)
|
||||
|
||||
with scoped_cwd(DIST_DIR):
|
||||
files = ['LICENSE', 'version']
|
||||
files = ['LICENSE', 'LICENSES.chromium.html', 'version']
|
||||
dirs = ['{0}.breakpad.syms'.format(PROJECT_NAME)]
|
||||
make_zip(zip_file, files, dirs)
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ import sys
|
|||
|
||||
BASE_URL = os.getenv('LIBCHROMIUMCONTENT_MIRROR') or \
|
||||
'http://gh-contractor-zcbenz.s3.amazonaws.com/libchromiumcontent'
|
||||
LIBCHROMIUMCONTENT_COMMIT = '78e54bc39a04b758ed5167cd980cc4d9951bd629'
|
||||
LIBCHROMIUMCONTENT_COMMIT = '464aff2398f619b1d4d91b9187de69803919dca2'
|
||||
|
||||
PLATFORM = {
|
||||
'cygwin': 'win32',
|
||||
|
|
|
@ -23,9 +23,12 @@ describe 'protocol module', ->
|
|||
|
||||
it 'does not crash when handler is called twice', (done) ->
|
||||
doubleHandler = (request, callback) ->
|
||||
callback(text)
|
||||
callback()
|
||||
try
|
||||
callback(text)
|
||||
callback()
|
||||
catch
|
||||
protocol.registerStringProtocol protocolName, doubleHandler, (error) ->
|
||||
return done(error) if error
|
||||
$.ajax
|
||||
url: "#{protocolName}://fake-host"
|
||||
success: (data) ->
|
||||
|
@ -36,6 +39,7 @@ describe 'protocol module', ->
|
|||
|
||||
it 'sends error when callback is called with nothing', (done) ->
|
||||
protocol.registerBufferProtocol protocolName, emptyHandler, (error) ->
|
||||
return done(error) if error
|
||||
$.ajax
|
||||
url: "#{protocolName}://fake-host"
|
||||
success: (data) ->
|
||||
|
@ -48,6 +52,7 @@ describe 'protocol module', ->
|
|||
handler = (request, callback) ->
|
||||
setImmediate -> callback(text)
|
||||
protocol.registerStringProtocol protocolName, handler, (error) ->
|
||||
return done(error) if error
|
||||
$.ajax
|
||||
url: "#{protocolName}://fake-host"
|
||||
success: (data) ->
|
||||
|
@ -66,6 +71,7 @@ describe 'protocol module', ->
|
|||
it 'sends string as response', (done) ->
|
||||
handler = (request, callback) -> callback(text)
|
||||
protocol.registerStringProtocol protocolName, handler, (error) ->
|
||||
return done(error) if error
|
||||
$.ajax
|
||||
url: "#{protocolName}://fake-host"
|
||||
success: (data) ->
|
||||
|
@ -77,6 +83,7 @@ describe 'protocol module', ->
|
|||
it 'sends object as response', (done) ->
|
||||
handler = (request, callback) -> callback(data: text, mimeType: 'text/html')
|
||||
protocol.registerStringProtocol protocolName, handler, (error) ->
|
||||
return done(error) if error
|
||||
$.ajax
|
||||
url: "#{protocolName}://fake-host"
|
||||
success: (data, statux, request) ->
|
||||
|
@ -88,6 +95,7 @@ describe 'protocol module', ->
|
|||
it 'fails when sending object other than string', (done) ->
|
||||
handler = (request, callback) -> callback(new Date)
|
||||
protocol.registerBufferProtocol protocolName, handler, (error) ->
|
||||
return done(error) if error
|
||||
$.ajax
|
||||
url: "#{protocolName}://fake-host"
|
||||
success: (data) ->
|
||||
|
@ -102,6 +110,7 @@ describe 'protocol module', ->
|
|||
it 'sends Buffer as response', (done) ->
|
||||
handler = (request, callback) -> callback(buffer)
|
||||
protocol.registerBufferProtocol protocolName, handler, (error) ->
|
||||
return done(error) if error
|
||||
$.ajax
|
||||
url: "#{protocolName}://fake-host"
|
||||
success: (data) ->
|
||||
|
@ -113,6 +122,7 @@ describe 'protocol module', ->
|
|||
it 'sends object as response', (done) ->
|
||||
handler = (request, callback) -> callback(data: buffer, mimeType: 'text/html')
|
||||
protocol.registerBufferProtocol protocolName, handler, (error) ->
|
||||
return done(error) if error
|
||||
$.ajax
|
||||
url: "#{protocolName}://fake-host"
|
||||
success: (data, statux, request) ->
|
||||
|
@ -124,6 +134,7 @@ describe 'protocol module', ->
|
|||
it 'fails when sending string', (done) ->
|
||||
handler = (request, callback) -> callback(text)
|
||||
protocol.registerBufferProtocol protocolName, handler, (error) ->
|
||||
return done(error) if error
|
||||
$.ajax
|
||||
url: "#{protocolName}://fake-host"
|
||||
success: (data) ->
|
||||
|
@ -142,6 +153,7 @@ describe 'protocol module', ->
|
|||
it 'sends file path as response', (done) ->
|
||||
handler = (request, callback) -> callback(filePath)
|
||||
protocol.registerFileProtocol protocolName, handler, (error) ->
|
||||
return done(error) if error
|
||||
$.ajax
|
||||
url: "#{protocolName}://fake-host"
|
||||
success: (data) ->
|
||||
|
@ -153,6 +165,7 @@ describe 'protocol module', ->
|
|||
it 'sends object as response', (done) ->
|
||||
handler = (request, callback) -> callback(path: filePath)
|
||||
protocol.registerFileProtocol protocolName, handler, (error) ->
|
||||
return done(error) if error
|
||||
$.ajax
|
||||
url: "#{protocolName}://fake-host"
|
||||
success: (data, statux, request) ->
|
||||
|
@ -164,6 +177,7 @@ describe 'protocol module', ->
|
|||
it 'can send normal file', (done) ->
|
||||
handler = (request, callback) -> callback(normalPath)
|
||||
protocol.registerFileProtocol protocolName, handler, (error) ->
|
||||
return done(error) if error
|
||||
$.ajax
|
||||
url: "#{protocolName}://fake-host"
|
||||
success: (data) ->
|
||||
|
@ -176,6 +190,7 @@ describe 'protocol module', ->
|
|||
fakeFilePath = path.join __dirname, 'fixtures', 'asar', 'a.asar', 'not-exist'
|
||||
handler = (request, callback) -> callback(fakeFilePath)
|
||||
protocol.registerBufferProtocol protocolName, handler, (error) ->
|
||||
return done(error) if error
|
||||
$.ajax
|
||||
url: "#{protocolName}://fake-host"
|
||||
success: (data) ->
|
||||
|
@ -187,6 +202,7 @@ describe 'protocol module', ->
|
|||
it 'fails when sending unsupported content', (done) ->
|
||||
handler = (request, callback) -> callback(new Date)
|
||||
protocol.registerBufferProtocol protocolName, handler, (error) ->
|
||||
return done(error) if error
|
||||
$.ajax
|
||||
url: "#{protocolName}://fake-host"
|
||||
success: (data) ->
|
||||
|
@ -206,6 +222,7 @@ describe 'protocol module', ->
|
|||
url = "http://127.0.0.1:#{port}"
|
||||
handler = (request, callback) -> callback({url})
|
||||
protocol.registerHttpProtocol protocolName, handler, (error) ->
|
||||
return done(error) if error
|
||||
$.ajax
|
||||
url: "#{protocolName}://fake-host"
|
||||
success: (data) ->
|
||||
|
@ -217,6 +234,7 @@ describe 'protocol module', ->
|
|||
it 'fails when sending invalid url', (done) ->
|
||||
handler = (request, callback) -> callback({url: 'url'})
|
||||
protocol.registerHttpProtocol protocolName, handler, (error) ->
|
||||
return done(error) if error
|
||||
$.ajax
|
||||
url: "#{protocolName}://fake-host"
|
||||
success: (data) ->
|
||||
|
@ -228,6 +246,7 @@ describe 'protocol module', ->
|
|||
it 'fails when sending unsupported content', (done) ->
|
||||
handler = (request, callback) -> callback(new Date)
|
||||
protocol.registerHttpProtocol protocolName, handler, (error) ->
|
||||
return done(error) if error
|
||||
$.ajax
|
||||
url: "#{protocolName}://fake-host"
|
||||
success: (data) ->
|
||||
|
@ -285,9 +304,12 @@ describe 'protocol module', ->
|
|||
|
||||
it 'does not crash when handler is called twice', (done) ->
|
||||
doubleHandler = (request, callback) ->
|
||||
callback(text)
|
||||
callback()
|
||||
try
|
||||
callback(text)
|
||||
callback()
|
||||
catch
|
||||
protocol.interceptStringProtocol 'http', doubleHandler, (error) ->
|
||||
return done(error) if error
|
||||
$.ajax
|
||||
url: 'http://fake-host'
|
||||
success: (data) ->
|
||||
|
@ -298,6 +320,7 @@ describe 'protocol module', ->
|
|||
|
||||
it 'sends error when callback is called with nothing', (done) ->
|
||||
protocol.interceptBufferProtocol 'http', emptyHandler, (error) ->
|
||||
return done(error) if error
|
||||
$.ajax
|
||||
url: 'http://fake-host'
|
||||
success: (data) ->
|
||||
|
@ -310,6 +333,7 @@ describe 'protocol module', ->
|
|||
it 'can intercept http protocol', (done) ->
|
||||
handler = (request, callback) -> callback(text)
|
||||
protocol.interceptStringProtocol 'http', handler, (error) ->
|
||||
return done(error) if error
|
||||
$.ajax
|
||||
url: 'http://fake-host'
|
||||
success: (data) ->
|
||||
|
@ -322,6 +346,7 @@ describe 'protocol module', ->
|
|||
handler = (request, callback) ->
|
||||
callback({mimeType: 'application/json', data: '{"value": 1}'})
|
||||
protocol.interceptStringProtocol 'http', handler, (error) ->
|
||||
return done(error) if error
|
||||
$.ajax
|
||||
url: 'http://fake-host'
|
||||
success: (data) ->
|
||||
|
@ -335,6 +360,7 @@ describe 'protocol module', ->
|
|||
it 'can intercept http protocol', (done) ->
|
||||
handler = (request, callback) -> callback(new Buffer(text))
|
||||
protocol.interceptBufferProtocol 'http', handler, (error) ->
|
||||
return done(error) if error
|
||||
$.ajax
|
||||
url: 'http://fake-host'
|
||||
success: (data) ->
|
||||
|
|
Loading…
Reference in a new issue