Merge branch 'master' into chrome-storage-sync
This commit is contained in:
commit
f121f46a24
50 changed files with 605 additions and 192 deletions
|
@ -110,6 +110,8 @@ int GetPathConstant(const std::string& name) {
|
|||
return chrome::DIR_USER_PICTURES;
|
||||
else if (name == "videos")
|
||||
return chrome::DIR_USER_VIDEOS;
|
||||
else if (name == "pepperFlashSystemPlugin")
|
||||
return chrome::FILE_PEPPER_FLASH_SYSTEM_PLUGIN;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
@ -261,13 +263,14 @@ void App::OnContinueUserActivity(
|
|||
}
|
||||
#endif
|
||||
|
||||
void App::OnLogin(LoginHandler* login_handler) {
|
||||
void App::OnLogin(LoginHandler* login_handler,
|
||||
const base::DictionaryValue& request_details) {
|
||||
v8::Locker locker(isolate());
|
||||
v8::HandleScope handle_scope(isolate());
|
||||
bool prevent_default = Emit(
|
||||
"login",
|
||||
WebContents::CreateFrom(isolate(), login_handler->GetWebContents()),
|
||||
login_handler->request(),
|
||||
request_details,
|
||||
login_handler->auth_info(),
|
||||
base::Bind(&PassLoginInformation, make_scoped_refptr(login_handler)));
|
||||
|
||||
|
|
|
@ -70,7 +70,8 @@ class App : public AtomBrowserClient::Delegate,
|
|||
void OnActivate(bool has_visible_windows) override;
|
||||
void OnWillFinishLaunching() override;
|
||||
void OnFinishLaunching() override;
|
||||
void OnLogin(LoginHandler* login_handler) override;
|
||||
void OnLogin(LoginHandler* login_handler,
|
||||
const base::DictionaryValue& request_details) override;
|
||||
#if defined(OS_MACOSX)
|
||||
void OnContinueUserActivity(
|
||||
bool* prevent_default,
|
||||
|
|
|
@ -25,6 +25,9 @@ struct Converter<content::DownloadItem::DownloadState> {
|
|||
content::DownloadItem::DownloadState state) {
|
||||
std::string download_state;
|
||||
switch (state) {
|
||||
case content::DownloadItem::IN_PROGRESS:
|
||||
download_state = "progressing";
|
||||
break;
|
||||
case content::DownloadItem::COMPLETE:
|
||||
download_state = "completed";
|
||||
break;
|
||||
|
@ -85,7 +88,7 @@ void DownloadItem::OnDownloadUpdated(content::DownloadItem* item) {
|
|||
// Destroy the item once item is downloaded.
|
||||
base::MessageLoop::current()->PostTask(FROM_HERE, GetDestroyClosure());
|
||||
} else {
|
||||
Emit("updated");
|
||||
Emit("updated", item->GetState());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,10 +102,18 @@ void DownloadItem::Pause() {
|
|||
download_item_->Pause();
|
||||
}
|
||||
|
||||
bool DownloadItem::IsPaused() const {
|
||||
return download_item_->IsPaused();
|
||||
}
|
||||
|
||||
void DownloadItem::Resume() {
|
||||
download_item_->Resume();
|
||||
}
|
||||
|
||||
bool DownloadItem::CanResume() const {
|
||||
return download_item_->CanResume();
|
||||
}
|
||||
|
||||
void DownloadItem::Cancel() {
|
||||
download_item_->Cancel(true);
|
||||
download_item_->Remove();
|
||||
|
@ -141,6 +152,14 @@ const GURL& DownloadItem::GetURL() const {
|
|||
return download_item_->GetURL();
|
||||
}
|
||||
|
||||
content::DownloadItem::DownloadState DownloadItem::GetState() const {
|
||||
return download_item_->GetState();
|
||||
}
|
||||
|
||||
bool DownloadItem::IsDone() const {
|
||||
return download_item_->IsDone();
|
||||
}
|
||||
|
||||
void DownloadItem::SetSavePath(const base::FilePath& path) {
|
||||
save_path_ = path;
|
||||
}
|
||||
|
@ -155,7 +174,9 @@ void DownloadItem::BuildPrototype(v8::Isolate* isolate,
|
|||
mate::ObjectTemplateBuilder(isolate, prototype)
|
||||
.MakeDestroyable()
|
||||
.SetMethod("pause", &DownloadItem::Pause)
|
||||
.SetMethod("isPaused", &DownloadItem::IsPaused)
|
||||
.SetMethod("resume", &DownloadItem::Resume)
|
||||
.SetMethod("canResume", &DownloadItem::CanResume)
|
||||
.SetMethod("cancel", &DownloadItem::Cancel)
|
||||
.SetMethod("getReceivedBytes", &DownloadItem::GetReceivedBytes)
|
||||
.SetMethod("getTotalBytes", &DownloadItem::GetTotalBytes)
|
||||
|
@ -164,6 +185,8 @@ void DownloadItem::BuildPrototype(v8::Isolate* isolate,
|
|||
.SetMethod("getFilename", &DownloadItem::GetFilename)
|
||||
.SetMethod("getContentDisposition", &DownloadItem::GetContentDisposition)
|
||||
.SetMethod("getURL", &DownloadItem::GetURL)
|
||||
.SetMethod("getState", &DownloadItem::GetState)
|
||||
.SetMethod("isDone", &DownloadItem::IsDone)
|
||||
.SetMethod("setSavePath", &DownloadItem::SetSavePath)
|
||||
.SetMethod("getSavePath", &DownloadItem::GetSavePath);
|
||||
}
|
||||
|
|
|
@ -27,7 +27,9 @@ class DownloadItem : public mate::TrackableObject<DownloadItem>,
|
|||
v8::Local<v8::ObjectTemplate> prototype);
|
||||
|
||||
void Pause();
|
||||
bool IsPaused() const;
|
||||
void Resume();
|
||||
bool CanResume() const;
|
||||
void Cancel();
|
||||
int64_t GetReceivedBytes() const;
|
||||
int64_t GetTotalBytes() const;
|
||||
|
@ -36,6 +38,8 @@ class DownloadItem : public mate::TrackableObject<DownloadItem>,
|
|||
std::string GetFilename() const;
|
||||
std::string GetContentDisposition() const;
|
||||
const GURL& GetURL() const;
|
||||
content::DownloadItem::DownloadState GetState() const;
|
||||
bool IsDone() const;
|
||||
void SetSavePath(const base::FilePath& path);
|
||||
base::FilePath GetSavePath() const;
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#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/net_converter.h"
|
||||
#include "atom/common/native_mate_converters/value_converter.h"
|
||||
#include "atom/common/node_includes.h"
|
||||
#include "atom/common/options_switches.h"
|
||||
#include "base/command_line.h"
|
||||
|
@ -173,17 +173,10 @@ void RegisterStandardSchemes(
|
|||
base::JoinString(schemes, ","));
|
||||
}
|
||||
|
||||
mate::Handle<atom::api::Protocol> CreateProtocol(v8::Isolate* isolate) {
|
||||
auto browser_context = static_cast<atom::AtomBrowserContext*>(
|
||||
atom::AtomBrowserMainParts::Get()->browser_context());
|
||||
return atom::api::Protocol::Create(isolate, browser_context);
|
||||
}
|
||||
|
||||
void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
|
||||
v8::Local<v8::Context> context, void* priv) {
|
||||
v8::Isolate* isolate = context->GetIsolate();
|
||||
mate::Dictionary dict(isolate, exports);
|
||||
dict.SetMethod("createProtocolObject", base::Bind(&CreateProtocol, isolate));
|
||||
dict.SetMethod("registerStandardSchemes", &RegisterStandardSchemes);
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "atom/browser/api/trackable_object.h"
|
||||
#include "atom/browser/net/atom_url_request_job_factory.h"
|
||||
#include "base/callback.h"
|
||||
#include "base/containers/scoped_ptr_hash_map.h"
|
||||
|
@ -16,7 +17,10 @@
|
|||
#include "native_mate/arguments.h"
|
||||
#include "native_mate/dictionary.h"
|
||||
#include "native_mate/handle.h"
|
||||
#include "native_mate/wrappable.h"
|
||||
|
||||
namespace base {
|
||||
class DictionaryValue;
|
||||
}
|
||||
|
||||
namespace net {
|
||||
class URLRequest;
|
||||
|
@ -30,10 +34,10 @@ class AtomURLRequestJobFactory;
|
|||
|
||||
namespace api {
|
||||
|
||||
class Protocol : public mate::Wrappable<Protocol> {
|
||||
class Protocol : public mate::TrackableObject<Protocol> {
|
||||
public:
|
||||
using Handler =
|
||||
base::Callback<void(const net::URLRequest*, v8::Local<v8::Value>)>;
|
||||
base::Callback<void(const base::DictionaryValue&, v8::Local<v8::Value>)>;
|
||||
using CompletionCallback = base::Callback<void(v8::Local<v8::Value>)>;
|
||||
using BooleanCallback = base::Callback<void(bool)>;
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include "atom/browser/api/atom_api_cookies.h"
|
||||
#include "atom/browser/api/atom_api_download_item.h"
|
||||
#include "atom/browser/api/atom_api_protocol.h"
|
||||
#include "atom/browser/api/atom_api_web_request.h"
|
||||
#include "atom/browser/atom_browser_context.h"
|
||||
#include "atom/browser/atom_browser_main_parts.h"
|
||||
|
@ -462,6 +463,14 @@ v8::Local<v8::Value> Session::Cookies(v8::Isolate* isolate) {
|
|||
return v8::Local<v8::Value>::New(isolate, cookies_);
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> Session::Protocol(v8::Isolate* isolate) {
|
||||
if (protocol_.IsEmpty()) {
|
||||
auto handle = atom::api::Protocol::Create(isolate, browser_context());
|
||||
protocol_.Reset(isolate, handle.ToV8());
|
||||
}
|
||||
return v8::Local<v8::Value>::New(isolate, protocol_);
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> Session::WebRequest(v8::Isolate* isolate) {
|
||||
if (web_request_.IsEmpty()) {
|
||||
auto handle = atom::api::WebRequest::Create(isolate, browser_context());
|
||||
|
@ -512,6 +521,7 @@ void Session::BuildPrototype(v8::Isolate* isolate,
|
|||
.SetMethod("allowNTLMCredentialsForDomains",
|
||||
&Session::AllowNTLMCredentialsForDomains)
|
||||
.SetProperty("cookies", &Session::Cookies)
|
||||
.SetProperty("protocol", &Session::Protocol)
|
||||
.SetProperty("webRequest", &Session::WebRequest);
|
||||
}
|
||||
|
||||
|
|
|
@ -81,10 +81,12 @@ class Session: public mate::TrackableObject<Session>,
|
|||
void ClearHostResolverCache(mate::Arguments* args);
|
||||
void AllowNTLMCredentialsForDomains(const std::string& domains);
|
||||
v8::Local<v8::Value> Cookies(v8::Isolate* isolate);
|
||||
v8::Local<v8::Value> Protocol(v8::Isolate* isolate);
|
||||
v8::Local<v8::Value> WebRequest(v8::Isolate* isolate);
|
||||
|
||||
// Cached object.
|
||||
v8::Global<v8::Value> cookies_;
|
||||
v8::Global<v8::Value> protocol_;
|
||||
v8::Global<v8::Value> web_request_;
|
||||
|
||||
// The X-DevTools-Emulate-Network-Conditions-Client-Id.
|
||||
|
|
|
@ -744,6 +744,15 @@ int WebContents::GetID() const {
|
|||
return web_contents()->GetRenderProcessHost()->GetID();
|
||||
}
|
||||
|
||||
std::string WebContents::GetType() const {
|
||||
switch (type_) {
|
||||
case BROWSER_WINDOW: return "window";
|
||||
case WEB_VIEW: return "webview";
|
||||
case REMOTE: return "remote";
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
|
||||
bool WebContents::Equal(const WebContents* web_contents) const {
|
||||
return GetID() == web_contents->GetID();
|
||||
}
|
||||
|
@ -1287,6 +1296,7 @@ void WebContents::BuildPrototype(v8::Isolate* isolate,
|
|||
.SetMethod("endFrameSubscription", &WebContents::EndFrameSubscription)
|
||||
.SetMethod("setSize", &WebContents::SetSize)
|
||||
.SetMethod("isGuest", &WebContents::IsGuest)
|
||||
.SetMethod("getType", &WebContents::GetType)
|
||||
.SetMethod("getWebPreferences", &WebContents::GetWebPreferences)
|
||||
.SetMethod("getOwnerBrowserWindow", &WebContents::GetOwnerBrowserWindow)
|
||||
.SetMethod("hasServiceWorker", &WebContents::HasServiceWorker)
|
||||
|
@ -1365,6 +1375,8 @@ void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
|
|||
dict.SetMethod("_setWrapWebContents", &atom::api::SetWrapWebContents);
|
||||
dict.SetMethod("fromId",
|
||||
&mate::TrackableObject<atom::api::WebContents>::FromWeakMapID);
|
||||
dict.SetMethod("getAllWebContents",
|
||||
&mate::TrackableObject<atom::api::WebContents>::GetAll);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
|
@ -59,6 +59,7 @@ class WebContents : public mate::TrackableObject<WebContents>,
|
|||
v8::Local<v8::ObjectTemplate> prototype);
|
||||
|
||||
int GetID() const;
|
||||
std::string GetType() const;
|
||||
bool Equal(const WebContents* web_contents) const;
|
||||
void LoadURL(const GURL& url, const mate::Dictionary& options);
|
||||
void DownloadURL(const GURL& url);
|
||||
|
|
|
@ -31,10 +31,13 @@ void HandleExternalProtocolInUI(
|
|||
if (!web_contents)
|
||||
return;
|
||||
|
||||
GURL escaped_url(net::EscapeExternalHandlerValue(url.spec()));
|
||||
auto callback = base::Bind(&OnOpenExternal, escaped_url);
|
||||
auto permission_helper =
|
||||
WebContentsPermissionHelper::FromWebContents(web_contents);
|
||||
if (!permission_helper)
|
||||
return;
|
||||
|
||||
GURL escaped_url(net::EscapeExternalHandlerValue(url.spec()));
|
||||
auto callback = base::Bind(&OnOpenExternal, escaped_url);
|
||||
permission_helper->RequestOpenExternalPermission(callback, has_user_gesture);
|
||||
}
|
||||
|
||||
|
|
|
@ -151,8 +151,12 @@ void Browser::DidFinishLaunching() {
|
|||
FOR_EACH_OBSERVER(BrowserObserver, observers_, OnFinishLaunching());
|
||||
}
|
||||
|
||||
void Browser::RequestLogin(LoginHandler* login_handler) {
|
||||
FOR_EACH_OBSERVER(BrowserObserver, observers_, OnLogin(login_handler));
|
||||
void Browser::RequestLogin(
|
||||
LoginHandler* login_handler,
|
||||
std::unique_ptr<base::DictionaryValue> request_details) {
|
||||
FOR_EACH_OBSERVER(BrowserObserver,
|
||||
observers_,
|
||||
OnLogin(login_handler, *(request_details.get())));
|
||||
}
|
||||
|
||||
void Browser::NotifyAndShutdown() {
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#endif
|
||||
|
||||
namespace base {
|
||||
class DictionaryValue;
|
||||
class FilePath;
|
||||
}
|
||||
|
||||
|
@ -165,7 +166,8 @@ class Browser : public WindowListObserver {
|
|||
void DidFinishLaunching();
|
||||
|
||||
// Request basic auth login.
|
||||
void RequestLogin(LoginHandler* login_handler);
|
||||
void RequestLogin(LoginHandler* login_handler,
|
||||
std::unique_ptr<base::DictionaryValue> request_details);
|
||||
|
||||
void AddObserver(BrowserObserver* obs) {
|
||||
observers_.AddObserver(obs);
|
||||
|
|
|
@ -49,7 +49,8 @@ class BrowserObserver {
|
|||
virtual void OnFinishLaunching() {}
|
||||
|
||||
// The browser requests HTTP login.
|
||||
virtual void OnLogin(LoginHandler* login_handler) {}
|
||||
virtual void OnLogin(LoginHandler* login_handler,
|
||||
const base::DictionaryValue& request_details) {}
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
// The browser wants to resume a user activity via handoff. (OS X only)
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
#include "atom/browser/login_handler.h"
|
||||
|
||||
#include "atom/browser/browser.h"
|
||||
#include "atom/common/native_mate_converters/net_converter.h"
|
||||
#include "base/values.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/browser/render_frame_host.h"
|
||||
#include "content/public/browser/resource_dispatcher_host.h"
|
||||
|
@ -37,11 +39,18 @@ LoginHandler::LoginHandler(net::AuthChallengeInfo* auth_info,
|
|||
render_frame_id_(0) {
|
||||
content::ResourceRequestInfo::ForRequest(request_)->GetAssociatedRenderFrame(
|
||||
&render_process_host_id_, &render_frame_id_);
|
||||
|
||||
// Fill request details on IO thread.
|
||||
std::unique_ptr<base::DictionaryValue> request_details(
|
||||
new base::DictionaryValue);
|
||||
FillRequestDetails(request_details.get(), request_);
|
||||
|
||||
BrowserThread::PostTask(
|
||||
BrowserThread::UI, FROM_HERE,
|
||||
base::Bind(&Browser::RequestLogin,
|
||||
base::Unretained(Browser::Get()),
|
||||
base::RetainedRef(make_scoped_refptr(this))));
|
||||
base::RetainedRef(make_scoped_refptr(this)),
|
||||
base::Passed(&request_details)));
|
||||
}
|
||||
|
||||
LoginHandler::~LoginHandler() {
|
||||
|
|
|
@ -36,7 +36,6 @@ class LoginHandler : public content::ResourceDispatcherHostLoginDelegate {
|
|||
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;
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include "atom/browser/atom_browser_context.h"
|
||||
#include "atom/browser/atom_browser_main_parts.h"
|
||||
#include "atom/browser/browser.h"
|
||||
#include "atom/browser/window_list.h"
|
||||
#include "atom/common/api/api_messages.h"
|
||||
#include "atom/common/native_mate_converters/file_path_converter.h"
|
||||
|
@ -166,7 +167,7 @@ void NativeWindow::InitFromOptions(const mate::Dictionary& options) {
|
|||
// For normal window, use white as default background.
|
||||
SetBackgroundColor("#FFFF");
|
||||
}
|
||||
std::string title("Electron");
|
||||
std::string title(Browser::Get()->GetName());
|
||||
options.Get(options::kTitle, &title);
|
||||
SetTitle(title);
|
||||
|
||||
|
|
|
@ -524,10 +524,6 @@ NativeWindowMac::NativeWindowMac(
|
|||
options.Get(options::kDisableAutoHideCursor, &disableAutoHideCursor);
|
||||
[window_ setDisableAutoHideCursor:disableAutoHideCursor];
|
||||
|
||||
// Disable zoom button if window is not resizable.
|
||||
if (!maximizable)
|
||||
SetMaximizable(false);
|
||||
|
||||
NSView* view = inspectable_web_contents()->GetView()->GetNativeView();
|
||||
[view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
|
||||
|
||||
|
@ -555,6 +551,12 @@ NativeWindowMac::NativeWindowMac(
|
|||
}];
|
||||
|
||||
InstallView();
|
||||
|
||||
// Disable zoom button if window is not resizable.
|
||||
// Set maximizable state last to ensure zoom button does not get reset
|
||||
// by calls to other APIs.
|
||||
if (!maximizable)
|
||||
SetMaximizable(false);
|
||||
}
|
||||
|
||||
NativeWindowMac::~NativeWindowMac() {
|
||||
|
|
|
@ -71,18 +71,13 @@ bool MatchesFilterCondition(net::URLRequest* request,
|
|||
|
||||
// Overloaded by multiple types to fill the |details| object.
|
||||
void ToDictionary(base::DictionaryValue* details, net::URLRequest* request) {
|
||||
FillRequestDetails(details, request);
|
||||
details->SetInteger("id", request->identifier());
|
||||
details->SetString("url", request->url().spec());
|
||||
details->SetString("method", request->method());
|
||||
details->SetDouble("timestamp", base::Time::Now().ToDoubleT() * 1000);
|
||||
auto info = content::ResourceRequestInfo::ForRequest(request);
|
||||
details->SetString("resourceType",
|
||||
info ? ResourceTypeToString(info->GetResourceType())
|
||||
: "other");
|
||||
std::unique_ptr<base::ListValue> list(new base::ListValue);
|
||||
GetUploadData(list.get(), request);
|
||||
if (!list->empty())
|
||||
details->Set("uploadData", std::move(list));
|
||||
}
|
||||
|
||||
void ToDictionary(base::DictionaryValue* details,
|
||||
|
|
|
@ -44,7 +44,7 @@ void HandlerCallback(const BeforeStartCallback& before_start,
|
|||
|
||||
void AskForOptions(v8::Isolate* isolate,
|
||||
const JavaScriptHandler& handler,
|
||||
net::URLRequest* request,
|
||||
std::unique_ptr<base::DictionaryValue> request_details,
|
||||
const BeforeStartCallback& before_start,
|
||||
const ResponseCallback& callback) {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||
|
@ -53,7 +53,7 @@ void AskForOptions(v8::Isolate* isolate,
|
|||
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
||||
v8::Context::Scope context_scope(context);
|
||||
handler.Run(
|
||||
request,
|
||||
*(request_details.get()),
|
||||
mate::ConvertToV8(isolate,
|
||||
base::Bind(&HandlerCallback, before_start, callback)));
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#ifndef ATOM_BROWSER_NET_JS_ASKER_H_
|
||||
#define ATOM_BROWSER_NET_JS_ASKER_H_
|
||||
|
||||
#include "atom/common/native_mate_converters/net_converter.h"
|
||||
#include "base/callback.h"
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
|
@ -19,7 +20,7 @@
|
|||
namespace atom {
|
||||
|
||||
using JavaScriptHandler =
|
||||
base::Callback<void(const net::URLRequest*, v8::Local<v8::Value>)>;
|
||||
base::Callback<void(const base::DictionaryValue&, v8::Local<v8::Value>)>;
|
||||
|
||||
namespace internal {
|
||||
|
||||
|
@ -31,7 +32,7 @@ using ResponseCallback =
|
|||
// Ask handler for options in UI thread.
|
||||
void AskForOptions(v8::Isolate* isolate,
|
||||
const JavaScriptHandler& handler,
|
||||
net::URLRequest* request,
|
||||
std::unique_ptr<base::DictionaryValue> request_details,
|
||||
const BeforeStartCallback& before_start,
|
||||
const ResponseCallback& callback);
|
||||
|
||||
|
@ -67,12 +68,15 @@ class JsAsker : public RequestJob {
|
|||
private:
|
||||
// RequestJob:
|
||||
void Start() override {
|
||||
std::unique_ptr<base::DictionaryValue> request_details(
|
||||
new base::DictionaryValue);
|
||||
FillRequestDetails(request_details.get(), RequestJob::request());
|
||||
content::BrowserThread::PostTask(
|
||||
content::BrowserThread::UI, FROM_HERE,
|
||||
base::Bind(&internal::AskForOptions,
|
||||
isolate_,
|
||||
handler_,
|
||||
RequestJob::request(),
|
||||
base::Passed(&request_details),
|
||||
base::Bind(&JsAsker::BeforeStartInUI,
|
||||
weak_factory_.GetWeakPtr()),
|
||||
base::Bind(&JsAsker::OnResponse,
|
||||
|
|
|
@ -54,7 +54,7 @@ TaskbarHost::~TaskbarHost() {
|
|||
|
||||
bool TaskbarHost::SetThumbarButtons(
|
||||
HWND window, const std::vector<ThumbarButton>& buttons) {
|
||||
if (buttons.size() > kMaxButtonsCount || !InitailizeTaskbar())
|
||||
if (buttons.size() > kMaxButtonsCount || !InitializeTaskbar())
|
||||
return false;
|
||||
|
||||
callback_map_.clear();
|
||||
|
@ -118,7 +118,7 @@ bool TaskbarHost::SetThumbarButtons(
|
|||
}
|
||||
|
||||
bool TaskbarHost::SetProgressBar(HWND window, double value) {
|
||||
if (!InitailizeTaskbar())
|
||||
if (!InitializeTaskbar())
|
||||
return false;
|
||||
|
||||
HRESULT r;
|
||||
|
@ -133,7 +133,7 @@ bool TaskbarHost::SetProgressBar(HWND window, double value) {
|
|||
|
||||
bool TaskbarHost::SetOverlayIcon(
|
||||
HWND window, const gfx::Image& overlay, const std::string& text) {
|
||||
if (!InitailizeTaskbar())
|
||||
if (!InitializeTaskbar())
|
||||
return false;
|
||||
|
||||
base::win::ScopedHICON icon(
|
||||
|
@ -152,7 +152,7 @@ bool TaskbarHost::HandleThumbarButtonEvent(int button_id) {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool TaskbarHost::InitailizeTaskbar() {
|
||||
bool TaskbarHost::InitializeTaskbar() {
|
||||
if (FAILED(taskbar_.CreateInstance(CLSID_TaskbarList,
|
||||
nullptr,
|
||||
CLSCTX_INPROC_SERVER)) ||
|
||||
|
|
|
@ -44,8 +44,8 @@ class TaskbarHost {
|
|||
bool HandleThumbarButtonEvent(int button_id);
|
||||
|
||||
private:
|
||||
// Initailize the taskbar object.
|
||||
bool InitailizeTaskbar();
|
||||
// Initialize the taskbar object.
|
||||
bool InitializeTaskbar();
|
||||
|
||||
using CallbackMap = std::map<int, base::Closure>;
|
||||
CallbackMap callback_map_;
|
||||
|
|
|
@ -22,22 +22,6 @@
|
|||
|
||||
namespace mate {
|
||||
|
||||
// static
|
||||
v8::Local<v8::Value> Converter<const net::URLRequest*>::ToV8(
|
||||
v8::Isolate* isolate, const net::URLRequest* val) {
|
||||
std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
|
||||
dict->SetString("method", val->method());
|
||||
std::string url;
|
||||
if (!val->url_chain().empty()) url = val->url().spec();
|
||||
dict->SetStringWithoutPathExpansion("url", url);
|
||||
dict->SetString("referrer", val->referrer());
|
||||
std::unique_ptr<base::ListValue> list(new base::ListValue);
|
||||
atom::GetUploadData(list.get(), val);
|
||||
if (!list->empty())
|
||||
dict->Set("uploadData", std::move(list));
|
||||
return mate::ConvertToV8(isolate, *(dict.get()));
|
||||
}
|
||||
|
||||
// static
|
||||
v8::Local<v8::Value> Converter<const net::AuthChallengeInfo*>::ToV8(
|
||||
v8::Isolate* isolate, const net::AuthChallengeInfo* val) {
|
||||
|
@ -69,6 +53,19 @@ v8::Local<v8::Value> Converter<scoped_refptr<net::X509Certificate>>::ToV8(
|
|||
|
||||
namespace atom {
|
||||
|
||||
void FillRequestDetails(base::DictionaryValue* details,
|
||||
const net::URLRequest* request) {
|
||||
details->SetString("method", request->method());
|
||||
std::string url;
|
||||
if (!request->url_chain().empty()) url = request->url().spec();
|
||||
details->SetStringWithoutPathExpansion("url", url);
|
||||
details->SetString("referrer", request->referrer());
|
||||
std::unique_ptr<base::ListValue> list(new base::ListValue);
|
||||
GetUploadData(list.get(), request);
|
||||
if (!list->empty())
|
||||
details->Set("uploadData", std::move(list));
|
||||
}
|
||||
|
||||
void GetUploadData(base::ListValue* upload_data_list,
|
||||
const net::URLRequest* request) {
|
||||
const net::UploadDataStream* upload_data = request->get_upload();
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "native_mate/converter.h"
|
||||
|
||||
namespace base {
|
||||
class DictionaryValue;
|
||||
class ListValue;
|
||||
}
|
||||
|
||||
|
@ -20,12 +21,6 @@ class X509Certificate;
|
|||
|
||||
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,
|
||||
|
@ -42,6 +37,9 @@ struct Converter<scoped_refptr<net::X509Certificate>> {
|
|||
|
||||
namespace atom {
|
||||
|
||||
void FillRequestDetails(base::DictionaryValue* details,
|
||||
const net::URLRequest* request);
|
||||
|
||||
void GetUploadData(base::ListValue* upload_data_list,
|
||||
const net::URLRequest* request);
|
||||
|
||||
|
|
|
@ -120,6 +120,21 @@ productbuild --component "$APP_PATH" /Applications --sign "$INSTALLER_KEY" "$RES
|
|||
문서를 참고하여 기본적인 개념을 이해해야 합니다. 그리고 자격(plist) 파일에
|
||||
어플리케이션에서 요구하는 권한의 키를 추가합니다.
|
||||
|
||||
그 외에 [electron-osx-sign][electron-osx-sign] 모듈을 이용해서 직접 서명할 수도 있습니다.
|
||||
|
||||
#### 네이티브 모듈 서명하기
|
||||
|
||||
앱 내부에서 사용한 네이티브 모듈들도 서명이 필요합니다.
|
||||
electron-osx-sign 을 사용한다면, 앱 실행 인수 목록에 경로를 반드시 지정해야 합니다.
|
||||
|
||||
```bash
|
||||
electron-osx-sign YourApp.app YourApp.app/Contents/Resources/app/node_modules/nativemodule/build/release/nativemodule
|
||||
```
|
||||
|
||||
참고할 점은 네이티브 모듈이 의도하지 않았지만 오브젝트 파일(.o)을 포함하는 경우도 있습니다.
|
||||
이 경우 오브젝트 파일들의 서명을 해야할 수도 있습니다.
|
||||
[electron-packager][electron-packager]를 사용한다면, 빌드 과정에 `--ignore=.+\.o$` 코드를 추가해 해당 파일을 무시해줍시다.
|
||||
|
||||
### 어플리케이션 업로드
|
||||
|
||||
어플리케이션 서명을 완료한 후 iTunes Connect에 업로드하기 위해 Application Loader를
|
||||
|
@ -190,6 +205,8 @@ ERN의 승인을 얻는 방법은, 다음 글을 참고하는 것이 좋습니
|
|||
[nwjs-guide]: https://github.com/nwjs/nw.js/wiki/Mac-App-Store-%28MAS%29-Submission-Guideline#first-steps
|
||||
[enable-app-sandbox]: https://developer.apple.com/library/ios/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/EnablingAppSandbox.html
|
||||
[create-record]: https://developer.apple.com/library/ios/documentation/LanguagesUtilities/Conceptual/iTunesConnect_Guide/Chapters/CreatingiTunesConnectRecord.html
|
||||
[electron-osx-sign]: https://github.com/electron-userland/electron-osx-sign
|
||||
[electron-packager]: https://github.com/electron-userland/electron-packager
|
||||
[submit-for-review]: https://developer.apple.com/library/ios/documentation/LanguagesUtilities/Conceptual/iTunesConnect_Guide/Chapters/SubmittingTheApp.html
|
||||
[app-sandboxing]: https://developer.apple.com/app-sandboxing/
|
||||
[ern-tutorial]: https://carouselapps.com/2015/12/15/legally-submit-app-apples-app-store-uses-encryption-obtain-ern/
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
Пожалуйста, убедитесь, что вы используете документацию, которые соответствует вашей версии Electron.
|
||||
Номер версии должен быть частью адреса страницы. Если это не так, вы
|
||||
возможно, используете документацию ветки разработки, которая может содержать изменения api,
|
||||
Пожалуйста, убедитесь, что Вы используете документацию, которая соответствует вашей версии Electron.
|
||||
Номер версии должен быть частью адреса страницы. Если это не так, Вы
|
||||
возможно, используете документацию ветки разработки, которая может содержать изменения api,
|
||||
которые не совместимы с вашей версией Electron. Если это так,
|
||||
Вы можете переключиться на другую версию документации в списке
|
||||
[доступные версии](http://electron.atom.io/docs/) на atom.io, или
|
||||
если вы используете интерфейс GitHub, откройте список "переключение ветки/тега" и
|
||||
[доступные версии](http://electron.atom.io/docs/) на [atom.io](atom.io), или
|
||||
если Вы используете интерфейс GitHub, откройте список "переключение ветки/тега" и
|
||||
выберите тег, который соответствует вашей версии.
|
||||
|
||||
## Руководства
|
||||
|
@ -13,7 +13,7 @@
|
|||
* [Application Distribution](tutorial/application-distribution.md)
|
||||
* [Mac App Store Submission Guide](tutorial/mac-app-store-submission-guide.md)
|
||||
* [Application Packaging](tutorial/application-packaging.md)
|
||||
* [Using Native Node Modules](tutorial/using-native-node-modules.md)
|
||||
* [Использование нативных модулей NodeJS](tutorial/using-native-node-modules.md)
|
||||
* [Отладка главного процесса](tutorial/debugging-main-process.md)
|
||||
* [Использование Selenium и WebDriver](tutorial/using-selenium-and-webdriver.md)
|
||||
* [DevTools Extension](tutorial/devtools-extension.md)
|
||||
|
@ -22,8 +22,8 @@
|
|||
## Учебники
|
||||
|
||||
* [Быстрый старт](tutorial/quick-start.md)
|
||||
* [Desktop Environment Integration](tutorial/desktop-environment-integration.md)
|
||||
* [Online/Offline Event Detection](tutorial/online-offline-events.md)
|
||||
* [Интеграция рабочего окружения](tutorial/desktop-environment-integration.md)
|
||||
* [Определение Онлайн/Оффлайн состояния](tutorial/online-offline-events.md)
|
||||
|
||||
## API References
|
||||
|
||||
|
@ -37,7 +37,7 @@
|
|||
* [`<webview>` Tag](api/web-view-tag.md)
|
||||
* [`window.open` Function](api/window-open.md)
|
||||
|
||||
### Modules for the Main Process:
|
||||
### Модули для Main Process:
|
||||
|
||||
* [app](api/app.md)
|
||||
* [autoUpdater](api/auto-updater.md)
|
||||
|
@ -61,7 +61,7 @@
|
|||
* [remote](api/remote.md)
|
||||
* [webFrame](api/web-frame.md)
|
||||
|
||||
### Modules for Both Processes:
|
||||
### Модули для обоих процессов:
|
||||
|
||||
* [clipboard](api/clipboard.md)
|
||||
* [crashReporter](api/crash-reporter.md)
|
||||
|
@ -72,11 +72,11 @@
|
|||
## Разработка
|
||||
|
||||
* [Стиль кодирования](development/coding-style.md)
|
||||
* [Source Code Directory Structure](development/source-code-directory-structure.md)
|
||||
* [Структура папок с исходным кодом](development/source-code-directory-structure.md)
|
||||
* [Technical Differences to NW.js (formerly 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)
|
||||
* [Настройка сервера символов для отладчика](development/setting-up-symbol-server.md)
|
||||
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
# Быстрый старт
|
||||
|
||||
Electron позволяет вам делать приложения для рабочего стола на чистом JavaScript,
|
||||
предоставляя среду с богатым API. Можете представлять его как Node.js, который
|
||||
ориентирован на рабочий стол, а не веб сервера.
|
||||
Electron позволяет Вам делать приложения для рабочего стола на чистом JavaScript,
|
||||
предоставляя среду с богатым API. Можете представлять его как Node.js приложение, которое
|
||||
ориентировано для рабочего стола, а не для веб сервера.
|
||||
|
||||
Это, однако, не значит, что Electron — лишь привязки к GUI билиотекам. На деле
|
||||
Electron использует веб-страницы как интерфейс, так что вы можете считать его
|
||||
Однако это не значит, что Electron — лишь привязка к GUI билиотекам. На деле
|
||||
Electron использует веб-страницы как интерфейс, так что Вы можете считать его
|
||||
небольшим Chroumium браузером, который контролируется с помощью JavaScript.
|
||||
|
||||
### Главный процесс
|
||||
|
@ -18,11 +18,11 @@ __главным процессом__. Скрипт, который работа
|
|||
|
||||
Так как Electron использует Chromium для показа веб-страниц,
|
||||
мульти-процессовая архитектура показа страниц Chromium тоже используется.
|
||||
Каждая веб-страницы в Electron работает в своём собственном процессе,
|
||||
Каждая веб-страница в Electron работает в своём собственном процессе,
|
||||
который называется __процесс-рендерер__.
|
||||
|
||||
В обычных браузерах веб-страницы обычно запускаются в "песочнице" и им недоступны
|
||||
реальные ресурсы компьютера. Пользователи Electron же могут использовать API
|
||||
реальные ресурсы компьютера. Пользователи Electron напротив могут использовать API
|
||||
Node.js на страницах, что допускает более низкоуровневую работу с операционной системой.
|
||||
|
||||
### Разница мужду главным процессом и процессом-рендерером
|
||||
|
@ -43,11 +43,11 @@ Node.js на страницах, что допускает более низко
|
|||
В Electron есть несолько способов общения между процессам. Например, модули
|
||||
[`ipcRenderer`](../api/ipc-renderer.md) и [`ipcMain`](../api/ipc-main.md) используются
|
||||
для отправки сообщений, а [remote](../api/remote.md) - для коммуникации в RPC стиле.
|
||||
В ЧАВО также есть пункт о том, [как разделять информацию между страницами][share-data]
|
||||
В FAQ также есть пункт о том, [как разделять информацию между страницами][share-data]
|
||||
|
||||
## Первое приложение на Electron
|
||||
|
||||
Как правило, приложение Electron структурировано следующим образом::
|
||||
Как правило, приложение Electron структурировано следующим образом:
|
||||
|
||||
```text
|
||||
your-app/
|
||||
|
@ -56,9 +56,9 @@ your-app/
|
|||
└── index.html
|
||||
```
|
||||
|
||||
Формат `package.json` точно такой же, как у модулей Node и сприпт, объявленый
|
||||
Формат `package.json` точно такой же, как у модулей Node и скрипт, объявленый
|
||||
как `main`, будет выполняться при запуске вашего приложения, работая в
|
||||
главном процессе. Например, ваш `package.json` может выглядеть вот так:
|
||||
главном процессе. Например, Ваш `package.json` может выглядеть вот так:
|
||||
|
||||
```json
|
||||
{
|
||||
|
@ -82,12 +82,12 @@ const app = electron.app
|
|||
// Модуль, создающий окно приложения.
|
||||
const BrowserWindow = electron.BrowserWindow
|
||||
|
||||
// Удерживайте глобальное обращение к объекту окна, если вы так не сделаете, то
|
||||
// Удерживайте глобальное обращение к объекту окна, если Вы так не сделаете, то
|
||||
// окно само закроется после того, как объект будет собран сборщиком мусора.
|
||||
let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Создаём окно браузера.
|
||||
// Создаём окно браузера
|
||||
mainWindow = new BrowserWindow({width: 800, height: 600})
|
||||
|
||||
// и загружаем index.html приложения.
|
||||
|
@ -98,9 +98,9 @@ function createWindow () {
|
|||
|
||||
// Будет выполнено, когда пользователь закроет окно
|
||||
mainWindow.on('closed', function () {
|
||||
//Убрать обращение на объект окна, обычно стоит хранить окна в массиве,
|
||||
//если ваше приложение поддерживает несколько, сейчас стоит удалить
|
||||
//соответствующий элемент.
|
||||
// Убрать обращение на объект окна, обычно стоит хранить окна в массиве,
|
||||
// если ваше приложение поддерживает несколько, сейчас стоит удалить
|
||||
// соответствующий элемент.
|
||||
mainWindow = null
|
||||
})
|
||||
}
|
||||
|
@ -114,7 +114,7 @@ app.on('ready', createWindow)
|
|||
// Выйти, если все окна закрыты
|
||||
app.on('window-all-closed', function () {
|
||||
//На OS X приложение и его строка меню обычно остаются активными,
|
||||
//пока пользователь не завершит их с помощью Cmd + Q.
|
||||
//пока пользователь не завершит их с помощью `Cmd + Q`.
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
|
@ -129,12 +129,12 @@ app.on('activate', function () {
|
|||
}
|
||||
})
|
||||
|
||||
//В этот файл вы можете включить остальной код вашего главного процесса.
|
||||
//В этот файл Вы можете включить остальной код вашего главного процесса.
|
||||
//Вы также можете разложить его по отдельным файлам и подключить с помощью require.
|
||||
|
||||
```
|
||||
|
||||
Наконец, `index.html`, страница, которую вы хотите показать:
|
||||
Наконец, `index.html`, страница, которую Вы хотите показать:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
|
@ -154,22 +154,22 @@ app.on('activate', function () {
|
|||
|
||||
## Запуск вашего приложения
|
||||
|
||||
Когда вы создали `main.js`, `index.html` и `package.json` вас скорее всего захочется
|
||||
запустить ваше приложение, чтобы проверить, что оно работает так, как надо.
|
||||
После того как Вы создали `main.js`, `index.html` и `package.json` Вам скорее всего захочется
|
||||
запустить приложение, чтобы проверить, что оно работает так, как надо.
|
||||
|
||||
### electron-prebuilt
|
||||
|
||||
[`electron-prebuilt`](https://github.com/electron-userland/electron-prebuilt) — `npm` модуль,
|
||||
который содержит прекомпилированную версию Electron.
|
||||
|
||||
Если вы установили Electron глобально через `npm`, то вам нужно будет всего лишь
|
||||
Если вы установили Electron глобально через `npm`, то Вам нужно будет всего лишь
|
||||
запустить сдедующее в папке вашего проекта:
|
||||
|
||||
```bash
|
||||
electron .
|
||||
```
|
||||
|
||||
Если вы установили Electron локально, то выполните это:
|
||||
Если Вы установили Electron локально, то выполните это:
|
||||
|
||||
```bash
|
||||
./node_modules/.bin/electron .
|
||||
|
@ -177,7 +177,7 @@ electron .
|
|||
|
||||
### Исполняемые файлы Electron, скачанные вручную
|
||||
|
||||
Если вы скачали Electron вручную, то вы можете использовать
|
||||
Если Вы скачали Electron вручную, то Вы можете использовать
|
||||
исполняемые файлы прямо в папке вашего проекта.
|
||||
|
||||
#### Windows
|
||||
|
@ -198,27 +198,27 @@ $ ./electron/electron your-app/
|
|||
$ ./Electron.app/Contents/MacOS/Electron your-app/
|
||||
```
|
||||
|
||||
`Electron.app` — часть реализного пакета Electron, вы можете скачать его
|
||||
`Electron.app` — часть реализного пакета Electron, Вы можете скачать его
|
||||
[тут](https://github.com/electron/electron/releases).
|
||||
|
||||
### Запустить как дистрибутив
|
||||
|
||||
Когда вы закончили написание вашего приложения, вы можете создать
|
||||
Когда Вы закончили написание вашего приложения, Вы можете создать
|
||||
дистрибутив, следуя инструкциям [отсюда](./application-distribution.md) и
|
||||
затем запустить полученное приложение.
|
||||
|
||||
### Попробуйте этот пример
|
||||
|
||||
Скопируйте и запустите этот обучающий код, ичпользуя репозиторий [`atom/electron-quick-start`](https://github.com/electron/electron-quick-start)
|
||||
Скопируйте и запустите этот обучающий код, используя репозиторий [`atom/electron-quick-start`](https://github.com/electron/electron-quick-start)
|
||||
|
||||
**Заметка**: Для запуска требуется [Git](https://git-scm.com) и [Node.js](https://nodejs.org/en/download/) (который включает в себя [npm](https://npmjs.org)).
|
||||
|
||||
```bash
|
||||
# Склонируйте репозиторий
|
||||
# Клонируем репозиторий
|
||||
$ git clone https://github.com/electron/electron-quick-start
|
||||
# Перейдите в папку репозитория
|
||||
# Переходим в папку скачанного репозитория
|
||||
$ cd electron-quick-start
|
||||
# Установите зависимости и запустите
|
||||
# Устанавливаем зависимости и запускаем
|
||||
$ npm install && npm start
|
||||
```
|
||||
|
||||
|
|
|
@ -1,15 +1,24 @@
|
|||
Платформы поддерживаемые Electron:
|
||||
# Платформы поддерживаемые Electron:
|
||||
|
||||
#OS X
|
||||
Поддерживает только 64-ых битные OS X. Минимально поддерживаемой версией является OS X 10.9
|
||||
Следующие платформы поддерживаются Electron:
|
||||
|
||||
#Windows
|
||||
Поддерживаются операционные систем Windows 7 и выше, старые операционные системы не поддерживаются (и не работают).
|
||||
Поддерживаются платформы на x86 и amd64 (64-разрядная) для Windows. Будьте внимательны, что ARM Windows не поддерживается на данный момент.
|
||||
### OS X
|
||||
|
||||
Поддерживает только 64-x битные OS X. Минимально поддерживаемой версией является OS X 10.9
|
||||
|
||||
### Windows
|
||||
|
||||
Поддерживаются операционные системы Windows 7 и выше, старые операционные системы не поддерживаются (и не работают).
|
||||
|
||||
Поддерживаются бинарники `x86` и `amd64` (x64) для Windows. Будьте внимательны, `ARM` версия Windows не поддерживается на данный момент.
|
||||
|
||||
### Linux
|
||||
|
||||
Подготовленные бинарники Electron для `ia32`(`i686`) и `x64`(`amd64`) архитектур сделаны на Ubuntu 12.04,
|
||||
`arm` бинарник сделан на ARM v7 с hard-float ABI и NEON для Debian Wheezy.
|
||||
|
||||
#Linux
|
||||
Поддерживает архитектуры ia32(i686 в) и x64(amd64).
|
||||
Гарантированно будет работать в дистрибутивах:
|
||||
- Ubuntu 12.04 и выше
|
||||
- Fedora 21
|
||||
- Debian 8
|
||||
|
||||
* Ubuntu 12.04 и выше
|
||||
* Fedora 21
|
||||
* Debian 8
|
||||
|
|
|
@ -32,7 +32,7 @@ app.on('window-all-closed', function() {
|
|||
|
||||
当所有的窗口都被关闭时触发。
|
||||
|
||||
这个时间仅在应用还没有退出时才能触发。 如果用户按下了 `Cmd + Q`,
|
||||
这个事件仅在应用还没有退出时才能触发。 如果用户按下了 `Cmd + Q`,
|
||||
或者开发者调用了 `app.quit()` ,Electron 将会先尝试关闭所有的窗口再触发 `will-quit` 事件,
|
||||
在这种情况下 `window-all-closed` 不会被触发。
|
||||
|
||||
|
|
|
@ -49,24 +49,24 @@ const {BrowserWindow} = electron;
|
|||
|
||||
// 保持一个对于 window 对象的全局引用,如果你不这样做,
|
||||
// 当 JavaScript 对象被垃圾回收, window 会被自动地关闭
|
||||
let win;
|
||||
let mainWindow;
|
||||
|
||||
function createWindow() {
|
||||
// 创建浏览器窗口。
|
||||
win = new BrowserWindow({width: 800, height: 600});
|
||||
mainWindow = new BrowserWindow({width: 800, height: 600});
|
||||
|
||||
// 加载应用的 index.html。
|
||||
win.loadURL(`file://${__dirname}/index.html`);
|
||||
mainWindow.loadURL(`file://${__dirname}/index.html`);
|
||||
|
||||
// 启用开发工具。
|
||||
win.webContents.openDevTools();
|
||||
mainWindow.webContents.openDevTools();
|
||||
|
||||
// 当 window 被关闭,这个事件会被触发。
|
||||
win.on('closed', () => {
|
||||
mainWindow.on('closed', () => {
|
||||
// 取消引用 window 对象,如果你的应用支持多窗口的话,
|
||||
// 通常会把多个 window 对象存放在一个数组里面,
|
||||
// 与此同时,你应该删除相应的元素。
|
||||
win = null;
|
||||
mainWindow = null;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -87,7 +87,7 @@ app.on('window-all-closed', () => {
|
|||
app.on('activate', () => {
|
||||
// On OS X it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (win === null) {
|
||||
if (mainWindow === null) {
|
||||
createWindow();
|
||||
}
|
||||
});
|
||||
|
@ -146,7 +146,7 @@ $ ./Electron.app/Contents/MacOS/Electron your-app/
|
|||
在你完成了你的应用后,你可以按照 [应用部署][7] 指导发布一个版本,并且以已经打包好的形式运行应用。
|
||||
|
||||
# 参照下面例子
|
||||
复制并且运行这个库 [atom/electron-quick-start][8]。
|
||||
复制并且运行这个库 [electron/electron-quick-start][8]。
|
||||
|
||||
*注意:*运行时需要你的系统已经安装了 [Git][9] 和 [Node.js][10](包含 [npm][11])。
|
||||
|
||||
|
@ -168,4 +168,4 @@ $ npm install && npm start
|
|||
[8]: https://github.com/electron/electron-quick-start
|
||||
[9]: https://git-scm.com/
|
||||
[10]: https://nodejs.org/en/download/
|
||||
[11]: https://www.npmjs.com/
|
||||
[11]: https://www.npmjs.com/
|
||||
|
|
|
@ -346,6 +346,7 @@ You can request the following paths by the name:
|
|||
* `music` Directory for a user's music.
|
||||
* `pictures` Directory for a user's pictures.
|
||||
* `videos` Directory for a user's videos.
|
||||
* `pepperFlashSystemPlugin` Full path to the system version of the Pepper Flash plugin.
|
||||
|
||||
### `app.setPath(name, path)`
|
||||
|
||||
|
|
|
@ -2,49 +2,70 @@
|
|||
|
||||
> Control file downloads from remote sources.
|
||||
|
||||
`DownloadItem` is an EventEmitter that represents a download item in Electron.
|
||||
It is used in `will-download` event of `Session` module, and allows users to
|
||||
`DownloadItem` is an `EventEmitter` that represents a download item in Electron.
|
||||
It is used in `will-download` event of `Session` class, and allows users to
|
||||
control the download item.
|
||||
|
||||
```javascript
|
||||
// In the main process.
|
||||
win.webContents.session.on('will-download', (event, item, webContents) => {
|
||||
// Set the save path, making Electron not to prompt a save dialog.
|
||||
item.setSavePath('/tmp/save.pdf');
|
||||
console.log(item.getMimeType());
|
||||
console.log(item.getFilename());
|
||||
console.log(item.getTotalBytes());
|
||||
item.on('updated', () => {
|
||||
console.log('Received bytes: ' + item.getReceivedBytes());
|
||||
});
|
||||
item.on('done', (e, state) => {
|
||||
if (state === 'completed') {
|
||||
console.log('Download successfully');
|
||||
} else {
|
||||
console.log('Download is cancelled or interrupted that can\'t be resumed');
|
||||
item.setSavePath('/tmp/save.pdf')
|
||||
|
||||
item.on('updated', (event, state) => {
|
||||
if (state === 'interrupted') {
|
||||
console.log('Download is interrupted but can be resumed')
|
||||
} else if (state === 'progressing') {
|
||||
if (item.isPaused()) {
|
||||
console.log('Download is paused')
|
||||
} else {
|
||||
console.log(`Received bytes: ${item.getReceivedBytes()}`)
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
})
|
||||
item.once('done', (event, state) => {
|
||||
if (state === 'completed') {
|
||||
console.log('Download successfully')
|
||||
} else {
|
||||
console.log(`Download failed: ${state}`)
|
||||
}
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
## Events
|
||||
|
||||
### Event: 'updated'
|
||||
|
||||
Emits when the `downloadItem` gets updated.
|
||||
|
||||
### Event: 'done'
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `state` String
|
||||
* `completed` - The download completed successfully.
|
||||
* `cancelled` - The download has been cancelled.
|
||||
* `interrupted` - An error broke the connection with the file server.
|
||||
|
||||
Emits when the download is in a terminal state. This includes a completed
|
||||
Emitted when the download has been updated and is not done.
|
||||
|
||||
The `state` can be one of following:
|
||||
|
||||
* `progressing` - The download is in-progress.
|
||||
* `interrupted` - The download has interrupted and can be resumed.
|
||||
|
||||
### Event: 'done'
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `state` String
|
||||
|
||||
Emitted when the download is in a terminal state. This includes a completed
|
||||
download, a cancelled download(via `downloadItem.cancel()`), and interrupted
|
||||
download that can't be resumed.
|
||||
|
||||
The `state` can be one of following:
|
||||
|
||||
* `completed` - The download completed successfully.
|
||||
* `cancelled` - The download has been cancelled.
|
||||
* `interrupted` - The download has interrupted and can not resume.
|
||||
|
||||
## Methods
|
||||
|
||||
The `downloadItem` object has the following methods:
|
||||
|
@ -61,10 +82,18 @@ routine to determine the save path(Usually prompts a save dialog).
|
|||
|
||||
Pauses the download.
|
||||
|
||||
### `downloadItem.isPaused()`
|
||||
|
||||
Returns whether the download is paused.
|
||||
|
||||
### `downloadItem.resume()`
|
||||
|
||||
Resumes the download that has been paused.
|
||||
|
||||
### `downloadItem.canResume()`
|
||||
|
||||
Resumes whether the download can resume.
|
||||
|
||||
### `downloadItem.cancel()`
|
||||
|
||||
Cancels the download operation.
|
||||
|
@ -102,3 +131,14 @@ Returns a `Integer` represents the received bytes of the download item.
|
|||
|
||||
Returns a `String` represents the Content-Disposition field from the response
|
||||
header.
|
||||
|
||||
### `downloadItem.getState()`
|
||||
|
||||
Returns current state as `String`.
|
||||
|
||||
Possible values are:
|
||||
|
||||
* `progressing` - The download is in-progress.
|
||||
* `completed` - The download completed successfully.
|
||||
* `cancelled` - The download has been cancelled.
|
||||
* `interrupted` - The download has interrupted.
|
||||
|
|
|
@ -547,3 +547,22 @@ The `listener` will be called with `listener(details)` when an error occurs.
|
|||
* `timestamp` Double
|
||||
* `fromCache` Boolean
|
||||
* `error` String - The error description.
|
||||
|
||||
#### `ses.protocol`
|
||||
|
||||
Returns an instance of [protocol](protocol.md) module for this session.
|
||||
|
||||
```javascript
|
||||
const {app, session} = require('electron')
|
||||
const path = require('path')
|
||||
|
||||
app.on('ready', function () {
|
||||
const protocol = session.fromPartition(partitionName).protocol
|
||||
protocol.registerFileProtocol('atom', function (request, callback) {
|
||||
var url = request.url.substr(7)
|
||||
callback({path: path.normalize(__dirname + '/' + url)})
|
||||
}, function (error) {
|
||||
if (error)
|
||||
console.error('Failed to register protocol')
|
||||
})
|
||||
})
|
||||
|
|
|
@ -8,7 +8,7 @@ be found in the `.gyp` and `.gypi` files.
|
|||
|
||||
Following `gyp` files contain the main rules for building Electron:
|
||||
|
||||
* `atom.gyp` defines how Electron itself is built.
|
||||
* `electron.gyp` defines how Electron itself is built.
|
||||
* `common.gypi` adjusts the build configurations of Node to make it build
|
||||
together with Chromium.
|
||||
* `vendor/brightray/brightray.gyp` defines how `brightray` is built and
|
||||
|
|
|
@ -41,6 +41,8 @@ app.on('ready', () => {
|
|||
});
|
||||
```
|
||||
|
||||
The path to the system wide Pepper Flash plugin can also be obtained by calling `app.getPath('pepperFlashSystemPlugin')`.
|
||||
|
||||
## Enable Flash Plugin in a `<webview>` Tag
|
||||
|
||||
Add `plugins` attribute to `<webview>` tag.
|
||||
|
|
|
@ -63,7 +63,8 @@
|
|||
'lib/renderer/api/remote.js',
|
||||
'lib/renderer/api/screen.js',
|
||||
'lib/renderer/api/web-frame.js',
|
||||
'lib/renderer/extensions/storage.js'
|
||||
'lib/renderer/extensions/i18n.js',
|
||||
'lib/renderer/extensions/storage.js',
|
||||
],
|
||||
'js2c_sources': [
|
||||
'lib/common/asar.js',
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
const {app} = require('electron')
|
||||
const {createProtocolObject, registerStandardSchemes} = process.atomBinding('protocol')
|
||||
const {app, session} = require('electron')
|
||||
const {registerStandardSchemes} = process.atomBinding('protocol')
|
||||
|
||||
exports.registerStandardSchemes = function (schemes) {
|
||||
if (app.isReady()) {
|
||||
|
@ -10,7 +10,7 @@ exports.registerStandardSchemes = function (schemes) {
|
|||
}
|
||||
|
||||
app.once('ready', function () {
|
||||
let protocol = createProtocolObject()
|
||||
let protocol = session.defaultSession.protocol
|
||||
for (let method in protocol) {
|
||||
exports[method] = protocol[method].bind(protocol)
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ const electron = require('electron')
|
|||
const bindings = process.atomBinding('session')
|
||||
|
||||
const PERSIST_PREFIX = 'persist:'
|
||||
const Session = new EventEmitter()
|
||||
|
||||
// Wrapper of binding.fromPartition that checks for ready event.
|
||||
const fromPartition = function (partition, persist) {
|
||||
|
@ -14,7 +15,7 @@ const fromPartition = function (partition, persist) {
|
|||
}
|
||||
|
||||
// Returns the Session from |partition| string.
|
||||
exports.fromPartition = function (partition = '') {
|
||||
Session.fromPartition = function (partition = '') {
|
||||
if (partition === '') return exports.defaultSession
|
||||
|
||||
if (partition.startsWith(PERSIST_PREFIX)) {
|
||||
|
@ -25,7 +26,7 @@ exports.fromPartition = function (partition = '') {
|
|||
}
|
||||
|
||||
// Returns the default session.
|
||||
Object.defineProperty(exports, 'defaultSession', {
|
||||
Object.defineProperty(Session, 'defaultSession', {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return fromPartition('', false)
|
||||
|
@ -35,6 +36,9 @@ Object.defineProperty(exports, 'defaultSession', {
|
|||
const wrapSession = function (session) {
|
||||
// Session is an EventEmitter.
|
||||
Object.setPrototypeOf(session, EventEmitter.prototype)
|
||||
Session.emit('session-created', session)
|
||||
}
|
||||
|
||||
bindings._setWrapSession(wrapSession)
|
||||
|
||||
module.exports = Session
|
||||
|
|
|
@ -10,6 +10,14 @@ session
|
|||
const binding = process.atomBinding('web_contents')
|
||||
const debuggerBinding = process.atomBinding('debugger')
|
||||
|
||||
const WebContents = new EventEmitter()
|
||||
WebContents.create = (options = {}) => {
|
||||
return binding.create(options)
|
||||
}
|
||||
WebContents.fromId = (id) => {
|
||||
return binding.fromId(id)
|
||||
}
|
||||
|
||||
let nextId = 0
|
||||
const getNextId = function () {
|
||||
return ++nextId
|
||||
|
@ -223,6 +231,8 @@ const wrapWebContents = function (webContents) {
|
|||
|
||||
this._printToPDF(printingSetting, callback)
|
||||
}
|
||||
|
||||
WebContents.emit('web-contents-created', webContents)
|
||||
}
|
||||
|
||||
binding._setWrapWebContents(wrapWebContents)
|
||||
|
@ -235,12 +245,4 @@ const wrapDebugger = function (webContentsDebugger) {
|
|||
|
||||
debuggerBinding._setWrapDebugger(wrapDebugger)
|
||||
|
||||
module.exports = {
|
||||
create (options = {}) {
|
||||
return binding.create(options)
|
||||
},
|
||||
|
||||
fromId (id) {
|
||||
return binding.fromId(id)
|
||||
}
|
||||
}
|
||||
module.exports = WebContents
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
const {app, ipcMain, protocol, webContents, BrowserWindow} = require('electron')
|
||||
const {app, ipcMain, session, webContents, BrowserWindow} = require('electron')
|
||||
const {getAllWebContents} = process.atomBinding('web_contents')
|
||||
const renderProcessPreferences = process.atomBinding('render_process_preferences').forAllBrowserWindow()
|
||||
|
||||
const fs = require('fs')
|
||||
|
@ -81,13 +82,13 @@ const removeBackgroundPages = function (manifest) {
|
|||
}
|
||||
|
||||
// Dispatch tabs events.
|
||||
const hookWindowForTabEvents = function (win) {
|
||||
const tabId = win.webContents.id
|
||||
const hookWebContentsForTabEvents = function (webContents) {
|
||||
const tabId = webContents.id
|
||||
for (const page of objectValues(backgroundPages)) {
|
||||
page.webContents.sendToAll('CHROME_TABS_ONCREATED', tabId)
|
||||
}
|
||||
|
||||
win.once('closed', () => {
|
||||
webContents.once('destroyed', () => {
|
||||
for (const page of objectValues(backgroundPages)) {
|
||||
page.webContents.sendToAll('CHROME_TABS_ONREMOVED', tabId)
|
||||
}
|
||||
|
@ -114,6 +115,10 @@ ipcMain.on('CHROME_RUNTIME_CONNECT', function (event, extensionId, connectInfo)
|
|||
page.webContents.sendToAll(`CHROME_RUNTIME_ONCONNECT_${extensionId}`, event.sender.id, portId, connectInfo)
|
||||
})
|
||||
|
||||
ipcMain.on('CHROME_I18N_MANIFEST', function (event, extensionId) {
|
||||
event.returnValue = manifestMap[extensionId]
|
||||
})
|
||||
|
||||
ipcMain.on('CHROME_RUNTIME_SENDMESSAGE', function (event, extensionId, message) {
|
||||
const page = backgroundPages[extensionId]
|
||||
if (!page) {
|
||||
|
@ -221,6 +226,15 @@ const loadDevToolsExtensions = function (win, manifests) {
|
|||
win.devToolsWebContents.executeJavaScript(`DevToolsAPI.addExtensions(${JSON.stringify(extensionInfoArray)})`)
|
||||
}
|
||||
|
||||
webContents.on('web-contents-created', function (webContents) {
|
||||
if (webContents.getType() === 'remote') return
|
||||
|
||||
hookWebContentsForTabEvents(webContents)
|
||||
webContents.on('devtools-opened', function () {
|
||||
loadDevToolsExtensions(webContents, objectValues(manifestMap))
|
||||
})
|
||||
})
|
||||
|
||||
// The persistent path of "DevTools Extensions" preference file.
|
||||
let loadedExtensionsPath = null
|
||||
|
||||
|
@ -270,10 +284,12 @@ app.once('ready', function () {
|
|||
}
|
||||
})
|
||||
}
|
||||
protocol.registerBufferProtocol('chrome-extension', chromeExtensionHandler, function (error) {
|
||||
if (error) {
|
||||
console.error(`Unable to register chrome-extension protocol: ${error}`)
|
||||
}
|
||||
session.on('session-created', function (ses) {
|
||||
ses.protocol.registerBufferProtocol('chrome-extension', chromeExtensionHandler, function (error) {
|
||||
if (error) {
|
||||
console.error(`Unable to register chrome-extension protocol: ${error}`)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// Load persisted extensions.
|
||||
|
@ -295,12 +311,15 @@ app.once('ready', function () {
|
|||
BrowserWindow.addDevToolsExtension = function (srcDirectory) {
|
||||
const manifest = getManifestFromPath(srcDirectory)
|
||||
if (manifest) {
|
||||
for (const win of BrowserWindow.getAllWindows()) {
|
||||
loadDevToolsExtensions(win, [manifest])
|
||||
for (const webContents of getAllWebContents()) {
|
||||
if (webContents.getType() !== 'remote') {
|
||||
loadDevToolsExtensions(webContents, [manifest])
|
||||
}
|
||||
}
|
||||
return manifest.name
|
||||
}
|
||||
}
|
||||
|
||||
BrowserWindow.removeDevToolsExtension = function (name) {
|
||||
const manifest = manifestNameMap[name]
|
||||
if (!manifest) return
|
||||
|
@ -310,14 +329,4 @@ app.once('ready', function () {
|
|||
delete manifestMap[manifest.extensionId]
|
||||
delete manifestNameMap[name]
|
||||
}
|
||||
|
||||
// Load extensions automatically when devtools is opened.
|
||||
const init = BrowserWindow.prototype._init
|
||||
BrowserWindow.prototype._init = function () {
|
||||
init.call(this)
|
||||
hookWindowForTabEvents(this)
|
||||
this.webContents.on('devtools-opened', () => {
|
||||
loadDevToolsExtensions(this, objectValues(manifestMap))
|
||||
})
|
||||
}
|
||||
})
|
||||
|
|
|
@ -194,4 +194,6 @@ exports.injectTo = function (extensionId, isBackgroundPage, context) {
|
|||
setPopup () {},
|
||||
getPopup () {}
|
||||
}
|
||||
|
||||
chrome.i18n = require('./extensions/i18n.js').setup(extensionId)
|
||||
}
|
||||
|
|
84
lib/renderer/extensions/i18n.js
Normal file
84
lib/renderer/extensions/i18n.js
Normal file
|
@ -0,0 +1,84 @@
|
|||
// Implementation of chrome.i18n.getMessage
|
||||
// https://developer.chrome.com/extensions/i18n#method-getMessage
|
||||
//
|
||||
// Does not implement predefined messages:
|
||||
// https://developer.chrome.com/extensions/i18n#overview-predefined
|
||||
|
||||
const {ipcRenderer} = require('electron')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
let metadata
|
||||
|
||||
const getExtensionMetadata = (extensionId) => {
|
||||
if (!metadata) {
|
||||
metadata = ipcRenderer.sendSync('CHROME_I18N_MANIFEST', extensionId)
|
||||
}
|
||||
return metadata
|
||||
}
|
||||
|
||||
const getMessagesPath = (extensionId, language) => {
|
||||
const metadata = getExtensionMetadata(extensionId)
|
||||
const defaultLocale = metadata.default_locale || 'en'
|
||||
const localesDirectory = path.join(metadata.srcDirectory, '_locales')
|
||||
let messagesPath = path.join(localesDirectory, language, 'messages.json')
|
||||
if (!fs.statSyncNoException(messagesPath)) {
|
||||
messagesPath = path.join(localesDirectory, defaultLocale, 'messages.json')
|
||||
}
|
||||
return messagesPath
|
||||
}
|
||||
|
||||
const getMessages = (extensionId, language) => {
|
||||
try {
|
||||
const messagesPath = getMessagesPath(extensionId, language)
|
||||
return JSON.parse(fs.readFileSync(messagesPath)) || {}
|
||||
} catch (error) {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
const getLanguage = () => {
|
||||
return navigator.language.replace(/-.*$/, '').toLowerCase()
|
||||
}
|
||||
|
||||
const replaceNumberedSubstitutions = (message, substitutions) => {
|
||||
return message.replace(/\$(\d+)/, (_, number) => {
|
||||
const index = parseInt(number, 10) - 1
|
||||
return substitutions[index] || ''
|
||||
})
|
||||
}
|
||||
|
||||
const replacePlaceholders = (message, placeholders, substitutions) => {
|
||||
if (typeof substitutions === 'string') {
|
||||
substitutions = [substitutions]
|
||||
}
|
||||
if (!Array.isArray(substitutions)) {
|
||||
substitutions = []
|
||||
}
|
||||
|
||||
if (placeholders) {
|
||||
Object.keys(placeholders).forEach((name) => {
|
||||
let {content} = placeholders[name]
|
||||
content = replaceNumberedSubstitutions(content, substitutions)
|
||||
message = message.replace(new RegExp(`\\$${name}\\$`, 'gi'), content)
|
||||
})
|
||||
}
|
||||
|
||||
return replaceNumberedSubstitutions(message, substitutions)
|
||||
}
|
||||
|
||||
const getMessage = (extensionId, messageName, substitutions) => {
|
||||
const messages = getMessages(extensionId, getLanguage())
|
||||
if (messages.hasOwnProperty(messageName)) {
|
||||
const {message, placeholders} = messages[messageName]
|
||||
return replacePlaceholders(message, placeholders, substitutions)
|
||||
}
|
||||
}
|
||||
|
||||
exports.setup = (extensionId) => {
|
||||
return {
|
||||
getMessage (messageName, substitutions) {
|
||||
return getMessage(extensionId, messageName, substitutions)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -859,11 +859,13 @@ describe('browser-window module', function () {
|
|||
})
|
||||
|
||||
describe('when the devtools is docked', function () {
|
||||
it('creates the extension', function (done) {
|
||||
it.only('creates the extension', function (done) {
|
||||
w.webContents.openDevTools({mode: 'bottom'})
|
||||
|
||||
ipcMain.once('answer', function (event, message) {
|
||||
assert.equal(message.runtimeId, 'foo')
|
||||
assert.equal(message.tabId, w.webContents.id)
|
||||
assert.equal(message.i18nString, 'foo - bar (baz)')
|
||||
assert.deepEqual(message.storageItems, {foo: 'bar'})
|
||||
done()
|
||||
})
|
||||
|
@ -876,12 +878,52 @@ describe('browser-window module', function () {
|
|||
|
||||
ipcMain.once('answer', function (event, message, extensionId) {
|
||||
assert.equal(message.runtimeId, 'foo')
|
||||
assert.equal(message.tabId, w.webContents.id)
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('works when used with partitions', function (done) {
|
||||
this.timeout(10000)
|
||||
|
||||
if (w != null) {
|
||||
w.destroy()
|
||||
}
|
||||
w = new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences: {
|
||||
partition: 'temp'
|
||||
}
|
||||
})
|
||||
|
||||
var extensionPath = path.join(__dirname, 'fixtures', 'devtools-extensions', 'foo')
|
||||
BrowserWindow.removeDevToolsExtension('foo')
|
||||
BrowserWindow.addDevToolsExtension(extensionPath)
|
||||
|
||||
w.webContents.on('devtools-opened', function () {
|
||||
var showPanelIntevalId = setInterval(function () {
|
||||
if (w && w.devToolsWebContents) {
|
||||
w.devToolsWebContents.executeJavaScript('(' + (function () {
|
||||
var lastPanelId = WebInspector.inspectorView._tabbedPane._tabs.peekLast().id
|
||||
WebInspector.inspectorView.showPanel(lastPanelId)
|
||||
}).toString() + ')()')
|
||||
} else {
|
||||
clearInterval(showPanelIntevalId)
|
||||
}
|
||||
}, 100)
|
||||
})
|
||||
|
||||
w.loadURL('about:blank')
|
||||
w.webContents.openDevTools({mode: 'bottom'})
|
||||
|
||||
ipcMain.once('answer', function (event, message) {
|
||||
assert.equal(message.runtimeId, 'foo')
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('serializes the registered extensions on quit', function () {
|
||||
var extensionName = 'foo'
|
||||
var extensionPath = path.join(__dirname, 'fixtures', 'devtools-extensions', extensionName)
|
||||
|
|
|
@ -16,8 +16,15 @@ describe('session module', function () {
|
|||
var fixtures = path.resolve(__dirname, 'fixtures')
|
||||
var w = null
|
||||
var url = 'http://127.0.0.1'
|
||||
var partitionName = 'temp'
|
||||
var protocolName = 'sp'
|
||||
const tempProtocol = session.fromPartition(partitionName).protocol
|
||||
const protocol = session.defaultSession.protocol
|
||||
|
||||
beforeEach(function () {
|
||||
if (w != null) {
|
||||
w.destroy()
|
||||
}
|
||||
w = new BrowserWindow({
|
||||
show: false,
|
||||
width: 400,
|
||||
|
@ -26,7 +33,10 @@ describe('session module', function () {
|
|||
})
|
||||
|
||||
afterEach(function () {
|
||||
w.destroy()
|
||||
if (w != null) {
|
||||
w.destroy()
|
||||
}
|
||||
w = null
|
||||
})
|
||||
|
||||
describe('session.cookies', function () {
|
||||
|
@ -262,4 +272,43 @@ describe('session module', function () {
|
|||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('session.protocol', function () {
|
||||
beforeEach(function () {
|
||||
if (w != null) {
|
||||
w.destroy()
|
||||
}
|
||||
w = new BrowserWindow({
|
||||
show: false,
|
||||
width: 400,
|
||||
height: 400,
|
||||
webPreferences: {
|
||||
partition: partitionName
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('handles requests from a partition', function (done) {
|
||||
var handler = function (error, callback) {
|
||||
callback({
|
||||
data: 'test'
|
||||
})
|
||||
}
|
||||
tempProtocol.registerStringProtocol(protocolName, handler, function (error) {
|
||||
if (error) {
|
||||
return done(error)
|
||||
}
|
||||
protocol.isProtocolHandled(protocolName, function (result) {
|
||||
assert.equal(result, false)
|
||||
tempProtocol.isProtocolHandled(protocolName, function (result) {
|
||||
assert.equal(result, true)
|
||||
w.webContents.on('did-finish-load', function () {
|
||||
done()
|
||||
})
|
||||
w.loadURL(protocolName + "://fake-host")
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
10
spec/fixtures/devtools-extensions/foo/_locales/en/messages.json
vendored
Normal file
10
spec/fixtures/devtools-extensions/foo/_locales/en/messages.json
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"foo": {
|
||||
"message": "foo - $BAZ$ ($2)",
|
||||
"placeholders": {
|
||||
"baz": {
|
||||
"content": "$1"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,6 +13,8 @@
|
|||
testStorage(function (items) {
|
||||
var message = JSON.stringify({
|
||||
runtimeId: chrome.runtime.id,
|
||||
tabId: chrome.devtools.inspectedWindow.tabId,
|
||||
i18nString: chrome.i18n.getMessage('foo', ['bar', 'baz']),
|
||||
storageItems: items
|
||||
})
|
||||
var sendMessage = `require('electron').ipcRenderer.send('answer', ${message})`
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"name": "foo",
|
||||
"version": "1.0",
|
||||
"devtools_page": "foo.html"
|
||||
"devtools_page": "foo.html",
|
||||
"default_locale": "en"
|
||||
}
|
||||
|
|
30
spec/fixtures/pages/webview-devtools.html
vendored
Normal file
30
spec/fixtures/pages/webview-devtools.html
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
<webview nodeintegration src="./a.html"></webview>
|
||||
<script>
|
||||
var wv = document.querySelector('webview')
|
||||
wv.addEventListener('dom-ready', () => {
|
||||
const webContents = wv.getWebContents()
|
||||
webContents.on('devtools-opened', function () {
|
||||
var showPanelIntevalId = setInterval(function () {
|
||||
if (webContents.devToolsWebContents) {
|
||||
webContents.devToolsWebContents.executeJavaScript('(' + (function () {
|
||||
var lastPanelId = WebInspector.inspectorView._tabbedPane._tabs.peekLast().id
|
||||
WebInspector.inspectorView.showPanel(lastPanelId)
|
||||
}).toString() + ')()')
|
||||
} else {
|
||||
clearInterval(showPanelIntevalId)
|
||||
}
|
||||
}, 100)
|
||||
})
|
||||
|
||||
wv.openDevTools()
|
||||
})
|
||||
</script>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -912,4 +912,25 @@ describe('<webview> tag', function () {
|
|||
|
||||
w.loadURL('file://' + fixtures + '/pages/webview-visibilitychange.html')
|
||||
})
|
||||
|
||||
it('loads devtools extensions registered on the parent window', function (done) {
|
||||
this.timeout(10000)
|
||||
|
||||
w = new BrowserWindow({
|
||||
show: false
|
||||
})
|
||||
|
||||
BrowserWindow.removeDevToolsExtension('foo')
|
||||
|
||||
var extensionPath = path.join(__dirname, 'fixtures', 'devtools-extensions', 'foo')
|
||||
BrowserWindow.addDevToolsExtension(extensionPath)
|
||||
|
||||
w.loadURL('file://' + fixtures + '/pages/webview-devtools.html')
|
||||
|
||||
ipcMain.once('answer', function (event, message) {
|
||||
assert.equal(message.runtimeId, 'foo')
|
||||
assert.notEqual(message.tabId, w.webContents.id)
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue