Merge atom/master
This commit is contained in:
commit
3f0d598a59
137 changed files with 3700 additions and 2419 deletions
|
@ -218,7 +218,8 @@ WebContents::WebContents(content::WebContents* web_contents)
|
|||
|
||||
WebContents::WebContents(v8::Isolate* isolate,
|
||||
const mate::Dictionary& options)
|
||||
: request_id_(0) {
|
||||
: embedder_(nullptr),
|
||||
request_id_(0) {
|
||||
// Whether it is a guest WebContents.
|
||||
bool is_guest = false;
|
||||
options.Get("isGuest", &is_guest);
|
||||
|
@ -273,10 +274,10 @@ WebContents::WebContents(v8::Isolate* isolate,
|
|||
guest_delegate_->Initialize(this);
|
||||
|
||||
NativeWindow* owner_window = nullptr;
|
||||
WebContents* embedder = nullptr;
|
||||
if (options.Get("embedder", &embedder) && embedder) {
|
||||
if (options.Get("embedder", &embedder_) && embedder_) {
|
||||
// New WebContents's owner_window is the embedder's owner_window.
|
||||
auto relay = NativeWindowRelay::FromWebContents(embedder->web_contents());
|
||||
auto relay =
|
||||
NativeWindowRelay::FromWebContents(embedder_->web_contents());
|
||||
if (relay)
|
||||
owner_window = relay->window.get();
|
||||
}
|
||||
|
@ -296,6 +297,7 @@ WebContents::~WebContents() {
|
|||
// The WebContentsDestroyed will not be called automatically because we
|
||||
// unsubscribe from webContents before destroying it. So we have to manually
|
||||
// call it here to make sure "destroyed" event is emitted.
|
||||
RenderViewDeleted(web_contents()->GetRenderViewHost());
|
||||
WebContentsDestroyed();
|
||||
}
|
||||
}
|
||||
|
@ -485,17 +487,7 @@ void WebContents::BeforeUnloadFired(const base::TimeTicks& proceed_time) {
|
|||
}
|
||||
|
||||
void WebContents::RenderViewDeleted(content::RenderViewHost* render_view_host) {
|
||||
int process_id = render_view_host->GetProcess()->GetID();
|
||||
Emit("render-view-deleted", process_id);
|
||||
|
||||
// process.emit('ATOM_BROWSER_RELEASE_RENDER_VIEW', processId);
|
||||
// Tell the rpc server that a render view has been deleted and we need to
|
||||
// release all objects owned by it.
|
||||
v8::Locker locker(isolate());
|
||||
v8::HandleScope handle_scope(isolate());
|
||||
node::Environment* env = node::Environment::GetCurrent(isolate());
|
||||
mate::EmitEvent(isolate(), env->process_object(),
|
||||
"ATOM_BROWSER_RELEASE_RENDER_VIEW", process_id);
|
||||
Emit("render-view-deleted", render_view_host->GetProcess()->GetID());
|
||||
}
|
||||
|
||||
void WebContents::RenderProcessGone(base::TerminationStatus status) {
|
||||
|
@ -675,9 +667,6 @@ bool WebContents::OnMessageReceived(const IPC::Message& message) {
|
|||
// be destroyed on close, and WebContentsDestroyed would be called for it, so
|
||||
// we need to make sure the api::WebContents is also deleted.
|
||||
void WebContents::WebContentsDestroyed() {
|
||||
// The RenderViewDeleted was not called when the WebContents is destroyed.
|
||||
RenderViewDeleted(web_contents()->GetRenderViewHost());
|
||||
|
||||
// This event is only for internal use, which is emitted when WebContents is
|
||||
// being destroyed.
|
||||
Emit("will-destroy");
|
||||
|
@ -710,6 +699,14 @@ bool WebContents::Equal(const WebContents* web_contents) const {
|
|||
}
|
||||
|
||||
void WebContents::LoadURL(const GURL& url, const mate::Dictionary& options) {
|
||||
if (!url.is_valid()) {
|
||||
Emit("did-fail-load",
|
||||
static_cast<int>(net::ERR_INVALID_URL),
|
||||
net::ErrorToShortString(net::ERR_INVALID_URL),
|
||||
url.possibly_invalid_spec());
|
||||
return;
|
||||
}
|
||||
|
||||
content::NavigationController::LoadURLParams params(url);
|
||||
|
||||
GURL http_referrer;
|
||||
|
@ -1064,11 +1061,9 @@ void WebContents::BeginFrameSubscription(
|
|||
const FrameSubscriber::FrameCaptureCallback& callback) {
|
||||
const auto view = web_contents()->GetRenderWidgetHostView();
|
||||
if (view) {
|
||||
FrameSubscriber* frame_subscriber = new FrameSubscriber(
|
||||
isolate(), view, callback);
|
||||
scoped_ptr<FrameSubscriber::Subscriber> del_frame_subscriber(
|
||||
frame_subscriber->GetSubscriber());
|
||||
view->BeginFrameSubscription(del_frame_subscriber.Pass());
|
||||
scoped_ptr<FrameSubscriber> frame_subscriber(new FrameSubscriber(
|
||||
isolate(), view, callback));
|
||||
view->BeginFrameSubscription(frame_subscriber.Pass());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1122,6 +1117,12 @@ v8::Local<v8::Value> WebContents::Session(v8::Isolate* isolate) {
|
|||
return v8::Local<v8::Value>::New(isolate, session_);
|
||||
}
|
||||
|
||||
content::WebContents* WebContents::HostWebContents() {
|
||||
if (!embedder_)
|
||||
return nullptr;
|
||||
return embedder_->web_contents();
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> WebContents::DevToolsWebContents(v8::Isolate* isolate) {
|
||||
if (devtools_web_contents_.IsEmpty())
|
||||
return v8::Null(isolate);
|
||||
|
@ -1205,6 +1206,7 @@ void WebContents::BuildPrototype(v8::Isolate* isolate,
|
|||
.SetMethod("addWorkSpace", &WebContents::AddWorkSpace)
|
||||
.SetMethod("removeWorkSpace", &WebContents::RemoveWorkSpace)
|
||||
.SetProperty("session", &WebContents::Session)
|
||||
.SetProperty("hostWebContents", &WebContents::HostWebContents)
|
||||
.SetProperty("devToolsWebContents", &WebContents::DevToolsWebContents)
|
||||
.SetProperty("debugger", &WebContents::Debugger);
|
||||
}
|
||||
|
|
|
@ -147,6 +147,7 @@ class WebContents : public mate::TrackableObject<WebContents>,
|
|||
|
||||
// Properties.
|
||||
v8::Local<v8::Value> Session(v8::Isolate* isolate);
|
||||
content::WebContents* HostWebContents();
|
||||
v8::Local<v8::Value> DevToolsWebContents(v8::Isolate* isolate);
|
||||
v8::Local<v8::Value> Debugger(v8::Isolate* isolate);
|
||||
|
||||
|
@ -287,6 +288,9 @@ class WebContents : public mate::TrackableObject<WebContents>,
|
|||
|
||||
scoped_ptr<WebViewGuestDelegate> guest_delegate_;
|
||||
|
||||
// The host webcontents that may contain this webcontents.
|
||||
WebContents* embedder_;
|
||||
|
||||
// The type of current WebContents.
|
||||
Type type_;
|
||||
|
||||
|
|
|
@ -109,7 +109,7 @@ void TranslateOldOptions(v8::Isolate* isolate, v8::Local<v8::Object> options) {
|
|||
|
||||
// Converts binary data to Buffer.
|
||||
v8::Local<v8::Value> ToBuffer(v8::Isolate* isolate, void* val, int size) {
|
||||
auto buffer = node::Buffer::New(isolate, static_cast<char*>(val), size);
|
||||
auto buffer = node::Buffer::Copy(isolate, static_cast<char*>(val), size);
|
||||
if (buffer.IsEmpty())
|
||||
return v8::Null(isolate);
|
||||
else
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
|
||||
#include "base/bind.h"
|
||||
#include "atom/common/node_includes.h"
|
||||
#include "media/base/video_frame.h"
|
||||
#include "media/base/yuv_convert.h"
|
||||
#include "ui/gfx/screen.h"
|
||||
|
||||
#include "content/public/browser/render_widget_host.h"
|
||||
|
@ -16,75 +14,36 @@ namespace atom {
|
|||
|
||||
namespace api {
|
||||
|
||||
using Subscriber = FrameSubscriber::Subscriber;
|
||||
|
||||
FrameSubscriber::FrameSubscriber(v8::Isolate* isolate,
|
||||
content::RenderWidgetHostView* view,
|
||||
const FrameCaptureCallback& callback)
|
||||
: isolate_(isolate), callback_(callback), pending_frames(0), view_(view) {
|
||||
subscriber_ = new Subscriber(this);
|
||||
: isolate_(isolate), view_(view), callback_(callback), weak_factory_(this) {
|
||||
}
|
||||
|
||||
Subscriber::Subscriber(
|
||||
FrameSubscriber* frame_subscriber) : frame_subscriber_(frame_subscriber) {
|
||||
}
|
||||
|
||||
Subscriber::~Subscriber() {
|
||||
frame_subscriber_->subscriber_ = NULL;
|
||||
frame_subscriber_->RequestDestruct();
|
||||
}
|
||||
|
||||
bool Subscriber::ShouldCaptureFrame(
|
||||
bool FrameSubscriber::ShouldCaptureFrame(
|
||||
const gfx::Rect& damage_rect,
|
||||
base::TimeTicks present_time,
|
||||
scoped_refptr<media::VideoFrame>* storage,
|
||||
DeliverFrameCallback* callback) {
|
||||
const auto view = frame_subscriber_->view_;
|
||||
const auto host = view ? view->GetRenderWidgetHost() : nullptr;
|
||||
if (!view || !host)
|
||||
const auto host = view_ ? view_->GetRenderWidgetHost() : nullptr;
|
||||
if (!view_ || !host)
|
||||
return false;
|
||||
|
||||
const gfx::Size view_size = view->GetViewBounds().size();
|
||||
|
||||
gfx::Size bitmap_size = view_size;
|
||||
const gfx::NativeView native_view = view->GetNativeView();
|
||||
gfx::Screen* const screen = gfx::Screen::GetScreenFor(native_view);
|
||||
const float scale =
|
||||
screen->GetDisplayNearestWindow(native_view).device_scale_factor();
|
||||
if (scale > 1.0f)
|
||||
bitmap_size = gfx::ScaleToCeiledSize(view_size, scale);
|
||||
const auto size = view_->GetVisibleViewportSize();
|
||||
|
||||
host->CopyFromBackingStore(
|
||||
gfx::Rect(view_size),
|
||||
bitmap_size,
|
||||
gfx::Rect(size),
|
||||
size,
|
||||
base::Bind(&FrameSubscriber::OnFrameDelivered,
|
||||
base::Unretained(frame_subscriber_),
|
||||
frame_subscriber_->callback_),
|
||||
weak_factory_.GetWeakPtr(), callback_),
|
||||
kBGRA_8888_SkColorType);
|
||||
|
||||
frame_subscriber_->pending_frames++;
|
||||
return false;
|
||||
}
|
||||
|
||||
Subscriber* FrameSubscriber::GetSubscriber() {
|
||||
return subscriber_;
|
||||
}
|
||||
|
||||
bool FrameSubscriber::RequestDestruct() {
|
||||
bool deletable = (subscriber_ == NULL && pending_frames == 0);
|
||||
// Destruct FrameSubscriber if we're not waiting for frames and the
|
||||
// subscription has ended
|
||||
if (deletable)
|
||||
delete this;
|
||||
|
||||
return deletable;
|
||||
}
|
||||
|
||||
void FrameSubscriber::OnFrameDelivered(const FrameCaptureCallback& callback,
|
||||
const SkBitmap& bitmap, content::ReadbackResponse response) {
|
||||
pending_frames--;
|
||||
|
||||
if (RequestDestruct() || subscriber_ == NULL || bitmap.computeSize64() == 0)
|
||||
if (bitmap.computeSize64() == 0)
|
||||
return;
|
||||
|
||||
v8::Locker locker(isolate_);
|
||||
|
|
|
@ -6,55 +6,40 @@
|
|||
#define ATOM_BROWSER_API_FRAME_SUBSCRIBER_H_
|
||||
|
||||
#include "base/callback.h"
|
||||
#include "v8/include/v8.h"
|
||||
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "content/public/browser/render_widget_host_view.h"
|
||||
#include "content/public/browser/render_widget_host_view_frame_subscriber.h"
|
||||
#include "content/public/browser/readback_types.h"
|
||||
#include "third_party/skia/include/core/SkBitmap.h"
|
||||
#include "ui/gfx/geometry/size.h"
|
||||
#include "v8/include/v8.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace api {
|
||||
|
||||
class FrameSubscriber {
|
||||
class FrameSubscriber : public content::RenderWidgetHostViewFrameSubscriber {
|
||||
public:
|
||||
using FrameCaptureCallback = base::Callback<void(v8::Local<v8::Value>)>;
|
||||
|
||||
// Inner class that is the actual subscriber sent to chromium
|
||||
class Subscriber :
|
||||
public content::RenderWidgetHostViewFrameSubscriber {
|
||||
public:
|
||||
explicit Subscriber(FrameSubscriber* frame_subscriber);
|
||||
|
||||
bool ShouldCaptureFrame(const gfx::Rect& damage_rect,
|
||||
base::TimeTicks present_time,
|
||||
scoped_refptr<media::VideoFrame>* storage,
|
||||
DeliverFrameCallback* callback) override;
|
||||
|
||||
~Subscriber();
|
||||
private:
|
||||
FrameSubscriber* frame_subscriber_;
|
||||
};
|
||||
|
||||
FrameSubscriber(v8::Isolate* isolate,
|
||||
content::RenderWidgetHostView* view,
|
||||
const FrameCaptureCallback& callback);
|
||||
|
||||
Subscriber* GetSubscriber();
|
||||
bool ShouldCaptureFrame(const gfx::Rect& damage_rect,
|
||||
base::TimeTicks present_time,
|
||||
scoped_refptr<media::VideoFrame>* storage,
|
||||
DeliverFrameCallback* callback) override;
|
||||
|
||||
private:
|
||||
void OnFrameDelivered(const FrameCaptureCallback& callback,
|
||||
const SkBitmap& bitmap, content::ReadbackResponse response);
|
||||
|
||||
bool RequestDestruct();
|
||||
|
||||
v8::Isolate* isolate_;
|
||||
content::RenderWidgetHostView* view_;
|
||||
FrameCaptureCallback callback_;
|
||||
Subscriber* subscriber_;
|
||||
int pending_frames;
|
||||
|
||||
base::WeakPtrFactory<FrameSubscriber> weak_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(FrameSubscriber);
|
||||
};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const EventEmitter = require('events').EventEmitter;
|
||||
const bindings = process.atomBinding('session');
|
||||
const PERSIST_PERFIX = 'persist:';
|
||||
const PERSIST_PREFIX = 'persist:';
|
||||
|
||||
// Returns the Session from |partition| string.
|
||||
exports.fromPartition = function(partition) {
|
||||
|
@ -10,8 +10,8 @@ exports.fromPartition = function(partition) {
|
|||
if (partition === '') {
|
||||
return exports.defaultSession;
|
||||
}
|
||||
if (partition.startsWith(PERSIST_PERFIX)) {
|
||||
return bindings.fromPartition(partition.substr(PERSIST_PERFIX.length), false);
|
||||
if (partition.startsWith(PERSIST_PREFIX)) {
|
||||
return bindings.fromPartition(partition.substr(PERSIST_PREFIX.length), false);
|
||||
} else {
|
||||
return bindings.fromPartition(partition, true);
|
||||
}
|
||||
|
|
|
@ -26,7 +26,8 @@ bool AtomResourceDispatcherHostDelegate::HandleExternalProtocol(
|
|||
bool has_user_gesture) {
|
||||
GURL escaped_url(net::EscapeExternalHandlerValue(url.spec()));
|
||||
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
|
||||
base::Bind(base::IgnoreResult(platform_util::OpenExternal), escaped_url));
|
||||
base::Bind(
|
||||
base::IgnoreResult(platform_util::OpenExternal), escaped_url, true));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,13 +9,15 @@ app.on('window-all-closed', function() {
|
|||
app.quit();
|
||||
});
|
||||
|
||||
app.on('ready', function() {
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
autoHideMenuBar: true,
|
||||
useContentSize: true,
|
||||
exports.load = function(appUrl) {
|
||||
app.on('ready', function() {
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
autoHideMenuBar: true,
|
||||
useContentSize: true,
|
||||
});
|
||||
mainWindow.loadURL(appUrl);
|
||||
mainWindow.focus();
|
||||
});
|
||||
mainWindow.loadURL('file://' + __dirname + '/index.html');
|
||||
mainWindow.focus();
|
||||
});
|
||||
};
|
||||
|
|
|
@ -3,31 +3,39 @@
|
|||
<title>Electron</title>
|
||||
<style>
|
||||
body {
|
||||
color: #555;
|
||||
font-family: 'Open Sans',Helvetica,Arial,sans-serif;
|
||||
padding: 30px;
|
||||
color: #45828E;
|
||||
background-color: #A5ECFA;
|
||||
font-family: 'Helvetica Neue', 'Open Sans', Helvetica, Arial, sans-serif;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 15px 30px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: #2b6cc2;
|
||||
font-family: "Crimson Text",Georgia,serif;
|
||||
background-color: #76C7D7;
|
||||
color: #FAF7F3;
|
||||
font-weight: 400;
|
||||
line-height: 1.1;
|
||||
letter-spacing: -0.015em;
|
||||
padding: 15px 30px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #2b6cc2;
|
||||
color: #39AEC6;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
pre, code {
|
||||
font-family: "Menlo","Lucida Console",monospace;
|
||||
border: 1px solid #ddd;
|
||||
background-color: #f8f8f8;
|
||||
border: 1px solid #076274;
|
||||
background-color: #076274;
|
||||
color: #C5F3FC;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
|
@ -39,19 +47,24 @@
|
|||
padding: 6px 10px;
|
||||
}
|
||||
|
||||
code {
|
||||
padding: 1px 4px 1px 4px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
#holder {
|
||||
border: 4px dashed #ccc;
|
||||
border: 2px dashed #448691;
|
||||
margin: 0 auto;
|
||||
height: 300px;
|
||||
color: #ccc;
|
||||
color: #45828E;
|
||||
font-size: 40px;
|
||||
line-height: 300px;
|
||||
text-align: center;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
#holder.hover {
|
||||
border: 4px dashed #999;
|
||||
color: #eee;
|
||||
background-color: #7BDCEF;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
@ -78,40 +91,47 @@
|
|||
|
||||
<h2>
|
||||
<script>
|
||||
document.write(`Welcome to Electron (v${process.versions.electron})`)
|
||||
document.write(`Welcome to Electron ${process.versions.electron}`)
|
||||
</script>
|
||||
</h2>
|
||||
|
||||
<p>
|
||||
To run your app with Electron, execute the following command under your
|
||||
Console (or Terminal):
|
||||
</p>
|
||||
<div class="container">
|
||||
|
||||
<script>document.write('<pre>' + command + '</pre>')</script>
|
||||
<p>
|
||||
To run your app with Electron, execute the following command in your
|
||||
Console (or Terminal):
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The <code>path-to-your-app</code> should be the path to your own Electron
|
||||
app, you can read the
|
||||
<script>
|
||||
document.write(
|
||||
`<a href='https://github.com/atom/electron/blob/v${process.versions.electron}/docs/tutorial/quick-start.md'>quick start</a>`
|
||||
);
|
||||
</script>
|
||||
guide in Electron's
|
||||
<script>
|
||||
document.write(
|
||||
`<a href='https://github.com/atom/electron/tree/v${process.versions.electron}/docs#readme'>docs</a>`
|
||||
);
|
||||
</script>
|
||||
on how to write one.
|
||||
</p>
|
||||
<script>document.write('<pre>' + command + '</pre>')</script>
|
||||
|
||||
<p>
|
||||
Or you can just drag your app here to run it:
|
||||
</p>
|
||||
<p>
|
||||
The <code>path-to-your-app</code> should be the path to your own Electron
|
||||
app.
|
||||
</p>
|
||||
|
||||
<p>You can read the
|
||||
<script>
|
||||
document.write(
|
||||
`<a href='https://github.com/atom/electron/blob/v${process.versions.electron}/docs/tutorial/quick-start.md'>quick start</a>`
|
||||
);
|
||||
</script>
|
||||
guide in Electron's
|
||||
<script>
|
||||
document.write(
|
||||
`<a href='https://github.com/atom/electron/tree/v${process.versions.electron}/docs#readme'>docs</a>`
|
||||
);
|
||||
</script>
|
||||
to learn how to write one.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Or you can just drag your app here to run it:
|
||||
</p>
|
||||
|
||||
<div id="holder">
|
||||
Drag your app here to run it
|
||||
</div>
|
||||
|
||||
<div id="holder">
|
||||
Drag your app here to run it
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
|
|
@ -4,8 +4,9 @@ const dialog = electron.dialog;
|
|||
const shell = electron.shell;
|
||||
const Menu = electron.Menu;
|
||||
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const url = require('url');
|
||||
|
||||
// Quit when all windows are closed and no other one is listening to this.
|
||||
app.on('window-all-closed', function() {
|
||||
|
@ -234,12 +235,10 @@ if (option.modules.length > 0) {
|
|||
require('module')._preloadModules(option.modules);
|
||||
}
|
||||
|
||||
// Start the specified app if there is one specified in command line, otherwise
|
||||
// start the default app.
|
||||
if (option.file && !option.webdriver) {
|
||||
function loadApplicationPackage(packagePath) {
|
||||
try {
|
||||
// Override app name and version.
|
||||
var packagePath = path.resolve(option.file);
|
||||
packagePath = path.resolve(packagePath);
|
||||
var packageJsonPath = path.join(packagePath, 'package.json');
|
||||
if (fs.existsSync(packageJsonPath)) {
|
||||
var packageJson = JSON.parse(fs.readFileSync(packageJsonPath));
|
||||
|
@ -270,20 +269,44 @@ if (option.file && !option.webdriver) {
|
|||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function loadApplicationByUrl(appUrl) {
|
||||
require('./default_app').load(appUrl);
|
||||
}
|
||||
|
||||
// Start the specified app if there is one specified in command line, otherwise
|
||||
// start the default app.
|
||||
if (option.file && !option.webdriver) {
|
||||
var file = option.file;
|
||||
var protocol = url.parse(file).protocol;
|
||||
var extension = path.extname(file);
|
||||
if (protocol === 'http:' || protocol === 'https:' || protocol === 'file:') {
|
||||
loadApplicationByUrl(file);
|
||||
} else if (extension === '.html' || extension === '.htm') {
|
||||
loadApplicationByUrl('file://' + path.resolve(file));
|
||||
} else {
|
||||
loadApplicationPackage(file);
|
||||
}
|
||||
} else if (option.version) {
|
||||
console.log('v' + process.versions.electron);
|
||||
process.exit(0);
|
||||
} else if (option.help) {
|
||||
var helpMessage = "Electron v" + process.versions.electron + " - Cross Platform Desktop Application Shell\n\n";
|
||||
helpMessage += "Usage: electron [options] [path]\n\n";
|
||||
helpMessage += "A path to an Electron application may be specified. The path must be to \n";
|
||||
helpMessage += "an index.js file or to a folder containing a package.json or index.js file.\n\n";
|
||||
helpMessage += "Options:\n";
|
||||
helpMessage += "A path to an Electron application may be specified.\n";
|
||||
helpMessage += "The path must be one of the following:\n\n";
|
||||
helpMessage += " - index.js file.\n";
|
||||
helpMessage += " - Folder containing a package.json file.\n";
|
||||
helpMessage += " - Folder containing an index.js file.\n";
|
||||
helpMessage += " - .html/.htm file.\n";
|
||||
helpMessage += " - http://, https://, or file:// URL.\n";
|
||||
helpMessage += "\nOptions:\n";
|
||||
helpMessage += " -r, --require Module to preload (option can be repeated)\n";
|
||||
helpMessage += " -h, --help Print this usage message.\n";
|
||||
helpMessage += " -v, --version Print the version.";
|
||||
console.log(helpMessage);
|
||||
process.exit(0);
|
||||
} else {
|
||||
require('./default_app');
|
||||
loadApplicationByUrl('file://' + __dirname + '/index.html');
|
||||
}
|
||||
|
|
|
@ -54,12 +54,16 @@ app.on('will-quit', function() {
|
|||
loadedExtensions = Object.keys(extensionInfoMap).map(function(key) {
|
||||
return extensionInfoMap[key].srcDirectory;
|
||||
});
|
||||
try {
|
||||
fs.mkdirSync(path.dirname(loadedExtensionsPath));
|
||||
} catch (error) {
|
||||
// Ignore error
|
||||
if (loadedExtensions.length > 0) {
|
||||
try {
|
||||
fs.mkdirSync(path.dirname(loadedExtensionsPath));
|
||||
} catch (error) {
|
||||
// Ignore error
|
||||
}
|
||||
fs.writeFileSync(loadedExtensionsPath, JSON.stringify(loadedExtensions));
|
||||
} else {
|
||||
fs.unlinkSync(loadedExtensionsPath);
|
||||
}
|
||||
return fs.writeFileSync(loadedExtensionsPath, JSON.stringify(loadedExtensions));
|
||||
} catch (error) {
|
||||
// Ignore error
|
||||
}
|
||||
|
@ -70,7 +74,7 @@ app.once('ready', function() {
|
|||
var BrowserWindow, chromeExtensionHandler, i, init, len, protocol, srcDirectory;
|
||||
protocol = electron.protocol, BrowserWindow = electron.BrowserWindow;
|
||||
|
||||
// Load persistented extensions.
|
||||
// Load persisted extensions.
|
||||
loadedExtensionsPath = path.join(app.getPath('userData'), 'DevTools Extensions');
|
||||
try {
|
||||
loadedExtensions = JSON.parse(fs.readFileSync(loadedExtensionsPath));
|
||||
|
|
|
@ -2,6 +2,7 @@ const fs = require('fs');
|
|||
const path = require('path');
|
||||
const util = require('util');
|
||||
const Module = require('module');
|
||||
const v8 = require('v8');
|
||||
|
||||
var slice = [].slice;
|
||||
|
||||
|
@ -129,6 +130,11 @@ if (packageJson.desktopName != null) {
|
|||
app.setDesktopName((app.getName()) + ".desktop");
|
||||
}
|
||||
|
||||
// Set v8 flags
|
||||
if (packageJson.v8Flags != null) {
|
||||
v8.setFlagsFromString(packageJson.v8Flags);
|
||||
}
|
||||
|
||||
// Chrome 42 disables NPAPI plugins by default, reenable them here
|
||||
app.commandLine.appendSwitch('enable-npapi');
|
||||
|
||||
|
|
|
@ -1,13 +1,9 @@
|
|||
'use strict';
|
||||
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
const v8Util = process.atomBinding('v8_util');
|
||||
|
||||
class ObjectsRegistry extends EventEmitter {
|
||||
class ObjectsRegistry {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.setMaxListeners(Number.MAX_VALUE);
|
||||
this.nextId = 0;
|
||||
|
||||
// Stores all objects by ref-counting.
|
||||
|
@ -15,70 +11,61 @@ class ObjectsRegistry extends EventEmitter {
|
|||
this.storage = {};
|
||||
|
||||
// Stores the IDs of objects referenced by WebContents.
|
||||
// (webContentsId) => {(id) => (count)}
|
||||
// (webContentsId) => [id]
|
||||
this.owners = {};
|
||||
}
|
||||
|
||||
// Register a new object, the object would be kept referenced until you release
|
||||
// it explicitly.
|
||||
add(webContentsId, obj) {
|
||||
var base, base1, id;
|
||||
id = this.saveToStorage(obj);
|
||||
// Register a new object and return its assigned ID. If the object is already
|
||||
// registered then the already assigned ID would be returned.
|
||||
add(webContents, obj) {
|
||||
// Get or assign an ID to the object.
|
||||
let id = this.saveToStorage(obj);
|
||||
|
||||
// Remember the owner.
|
||||
if ((base = this.owners)[webContentsId] == null) {
|
||||
base[webContentsId] = {};
|
||||
// Add object to the set of referenced objects.
|
||||
let webContentsId = webContents.getId();
|
||||
let owner = this.owners[webContentsId];
|
||||
if (!owner) {
|
||||
owner = this.owners[webContentsId] = new Set();
|
||||
// Clear the storage when webContents is reloaded/navigated.
|
||||
webContents.once('render-view-deleted', (event, id) => {
|
||||
this.clear(id);
|
||||
});
|
||||
}
|
||||
if ((base1 = this.owners[webContentsId])[id] == null) {
|
||||
base1[id] = 0;
|
||||
if (!owner.has(id)) {
|
||||
owner.add(id);
|
||||
// Increase reference count if not referenced before.
|
||||
this.storage[id].count++;
|
||||
}
|
||||
this.owners[webContentsId][id]++;
|
||||
|
||||
// Returns object's id
|
||||
return id;
|
||||
}
|
||||
|
||||
// Get an object according to its ID.
|
||||
get(id) {
|
||||
var ref;
|
||||
return (ref = this.storage[id]) != null ? ref.object : void 0;
|
||||
return this.storage[id].object;
|
||||
}
|
||||
|
||||
// Dereference an object according to its ID.
|
||||
remove(webContentsId, id) {
|
||||
var pointer;
|
||||
this.dereference(id, 1);
|
||||
// Dereference from the storage.
|
||||
this.dereference(id);
|
||||
|
||||
// Also reduce the count in owner.
|
||||
pointer = this.owners[webContentsId];
|
||||
if (pointer == null) {
|
||||
return;
|
||||
}
|
||||
--pointer[id];
|
||||
if (pointer[id] === 0) {
|
||||
return delete pointer[id];
|
||||
}
|
||||
// Also remove the reference in owner.
|
||||
this.owners[webContentsId].delete(id);
|
||||
}
|
||||
|
||||
// Clear all references to objects refrenced by the WebContents.
|
||||
clear(webContentsId) {
|
||||
var count, id, ref;
|
||||
this.emit("clear-" + webContentsId);
|
||||
if (this.owners[webContentsId] == null) {
|
||||
let owner = this.owners[webContentsId];
|
||||
if (!owner)
|
||||
return;
|
||||
}
|
||||
ref = this.owners[webContentsId];
|
||||
for (id in ref) {
|
||||
count = ref[id];
|
||||
this.dereference(id, count);
|
||||
}
|
||||
return delete this.owners[webContentsId];
|
||||
for (let id of owner)
|
||||
this.dereference(id);
|
||||
delete this.owners[webContentsId];
|
||||
}
|
||||
|
||||
// Private: Saves the object into storage and assigns an ID for it.
|
||||
saveToStorage(object) {
|
||||
var id;
|
||||
id = v8Util.getHiddenValue(object, 'atomId');
|
||||
let id = v8Util.getHiddenValue(object, 'atomId');
|
||||
if (!id) {
|
||||
id = ++this.nextId;
|
||||
this.storage[id] = {
|
||||
|
@ -87,18 +74,16 @@ class ObjectsRegistry extends EventEmitter {
|
|||
};
|
||||
v8Util.setHiddenValue(object, 'atomId', id);
|
||||
}
|
||||
++this.storage[id].count;
|
||||
return id;
|
||||
}
|
||||
|
||||
// Private: Dereference the object from store.
|
||||
dereference(id, count) {
|
||||
var pointer;
|
||||
pointer = this.storage[id];
|
||||
dereference(id) {
|
||||
let pointer = this.storage[id];
|
||||
if (pointer == null) {
|
||||
return;
|
||||
}
|
||||
pointer.count -= count;
|
||||
pointer.count -= 1;
|
||||
if (pointer.count === 0) {
|
||||
v8Util.deleteHiddenValue(pointer.object, 'atomId');
|
||||
return delete this.storage[id];
|
||||
|
|
|
@ -6,11 +6,54 @@ const objectsRegistry = require('./objects-registry');
|
|||
const v8Util = process.atomBinding('v8_util');
|
||||
const IDWeakMap = process.atomBinding('id_weak_map').IDWeakMap;
|
||||
|
||||
var slice = [].slice;
|
||||
// The internal properties of Function.
|
||||
const FUNCTION_PROPERTIES = [
|
||||
'length', 'name', 'arguments', 'caller', 'prototype',
|
||||
];
|
||||
|
||||
// The remote functions in renderer processes.
|
||||
// (webContentsId) => {id: Function}
|
||||
let rendererFunctions = {};
|
||||
|
||||
// Return the description of object's members:
|
||||
let getObjectMemebers = function(object) {
|
||||
let names = Object.getOwnPropertyNames(object);
|
||||
// For Function, we should not override following properties even though they
|
||||
// are "own" properties.
|
||||
if (typeof object === 'function') {
|
||||
names = names.filter((name) => {
|
||||
return !FUNCTION_PROPERTIES.includes(name);
|
||||
});
|
||||
}
|
||||
// Map properties to descriptors.
|
||||
return names.map((name) => {
|
||||
let descriptor = Object.getOwnPropertyDescriptor(object, name);
|
||||
let member = {name, enumerable: descriptor.enumerable, writable: false};
|
||||
if (descriptor.get === undefined && typeof object[name] === 'function') {
|
||||
member.type = 'method';
|
||||
} else {
|
||||
if (descriptor.set || descriptor.writable)
|
||||
member.writable = true;
|
||||
member.type = 'get';
|
||||
}
|
||||
return member;
|
||||
});
|
||||
};
|
||||
|
||||
// Return the description of object's prototype.
|
||||
let getObjectPrototype = function(object) {
|
||||
let proto = Object.getPrototypeOf(object);
|
||||
if (proto === null || proto === Object.prototype)
|
||||
return null;
|
||||
return {
|
||||
members: getObjectMemebers(proto),
|
||||
proto: getObjectPrototype(proto),
|
||||
};
|
||||
};
|
||||
|
||||
// Convert a real value into meta data.
|
||||
var valueToMeta = function(sender, value, optimizeSimpleObject) {
|
||||
var el, field, i, len, meta, name;
|
||||
var el, i, len, meta;
|
||||
if (optimizeSimpleObject == null) {
|
||||
optimizeSimpleObject = false;
|
||||
}
|
||||
|
@ -57,19 +100,9 @@ var valueToMeta = function(sender, value, optimizeSimpleObject) {
|
|||
// Reference the original value if it's an object, because when it's
|
||||
// passed to renderer we would assume the renderer keeps a reference of
|
||||
// it.
|
||||
meta.id = objectsRegistry.add(sender.getId(), value);
|
||||
meta.members = (function() {
|
||||
var results;
|
||||
results = [];
|
||||
for (name in value) {
|
||||
field = value[name];
|
||||
results.push({
|
||||
name: name,
|
||||
type: typeof field
|
||||
});
|
||||
}
|
||||
return results;
|
||||
})();
|
||||
meta.id = objectsRegistry.add(sender, value);
|
||||
meta.members = getObjectMemebers(value);
|
||||
meta.proto = getObjectPrototype(value);
|
||||
} else if (meta.type === 'buffer') {
|
||||
meta.value = Array.prototype.slice.call(value, 0);
|
||||
} else if (meta.type === 'promise') {
|
||||
|
@ -114,7 +147,7 @@ var exceptionToMeta = function(error) {
|
|||
var unwrapArgs = function(sender, args) {
|
||||
var metaToValue;
|
||||
metaToValue = function(meta) {
|
||||
var i, len, member, ref, rendererReleased, returnValue;
|
||||
var i, len, member, ref, returnValue;
|
||||
switch (meta.type) {
|
||||
case 'value':
|
||||
return meta.value;
|
||||
|
@ -130,48 +163,50 @@ var unwrapArgs = function(sender, args) {
|
|||
return Promise.resolve({
|
||||
then: metaToValue(meta.then)
|
||||
});
|
||||
case 'object':
|
||||
let ret = v8Util.createObjectWithName(meta.name);
|
||||
case 'object': {
|
||||
let ret = {};
|
||||
Object.defineProperty(ret.constructor, 'name', { value: meta.name });
|
||||
|
||||
ref = meta.members;
|
||||
for (i = 0, len = ref.length; i < len; i++) {
|
||||
member = ref[i];
|
||||
ret[member.name] = metaToValue(member.value);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
case 'function-with-return-value':
|
||||
returnValue = metaToValue(meta.value);
|
||||
return function() {
|
||||
return returnValue;
|
||||
};
|
||||
case 'function':
|
||||
case 'function': {
|
||||
// Cache the callbacks in renderer.
|
||||
if (!sender.callbacks) {
|
||||
sender.callbacks = new IDWeakMap;
|
||||
sender.on('render-view-deleted', function() {
|
||||
return this.callbacks.clear();
|
||||
let webContentsId = sender.getId();
|
||||
let callbacks = rendererFunctions[webContentsId];
|
||||
if (!callbacks) {
|
||||
callbacks = rendererFunctions[webContentsId] = new IDWeakMap;
|
||||
sender.once('render-view-deleted', function(event, id) {
|
||||
callbacks.clear();
|
||||
delete rendererFunctions[id];
|
||||
});
|
||||
}
|
||||
|
||||
if (sender.callbacks.has(meta.id))
|
||||
return sender.callbacks.get(meta.id);
|
||||
|
||||
// Prevent the callback from being called when its page is gone.
|
||||
rendererReleased = false;
|
||||
sender.once('render-view-deleted', function() {
|
||||
rendererReleased = true;
|
||||
});
|
||||
if (callbacks.has(meta.id))
|
||||
return callbacks.get(meta.id);
|
||||
|
||||
let callIntoRenderer = function(...args) {
|
||||
if (rendererReleased || sender.isDestroyed())
|
||||
if ((webContentsId in rendererFunctions) && !sender.isDestroyed())
|
||||
sender.send('ATOM_RENDERER_CALLBACK', meta.id, valueToMeta(sender, args));
|
||||
else
|
||||
throw new Error(`Attempting to call a function in a renderer window that has been closed or released. Function provided here: ${meta.location}.`);
|
||||
sender.send('ATOM_RENDERER_CALLBACK', meta.id, valueToMeta(sender, args));
|
||||
};
|
||||
v8Util.setDestructor(callIntoRenderer, function() {
|
||||
if (!rendererReleased && !sender.isDestroyed())
|
||||
if ((webContentsId in rendererFunctions) && !sender.isDestroyed())
|
||||
sender.send('ATOM_RENDERER_RELEASE_CALLBACK', meta.id);
|
||||
});
|
||||
sender.callbacks.set(meta.id, callIntoRenderer);
|
||||
callbacks.set(meta.id, callIntoRenderer);
|
||||
return callIntoRenderer;
|
||||
}
|
||||
default:
|
||||
throw new TypeError("Unknown type: " + meta.type);
|
||||
}
|
||||
|
@ -204,11 +239,6 @@ var callFunction = function(event, func, caller, args) {
|
|||
}
|
||||
};
|
||||
|
||||
// Send by BrowserWindow when its render view is deleted.
|
||||
process.on('ATOM_BROWSER_RELEASE_RENDER_VIEW', function(id) {
|
||||
return objectsRegistry.clear(id);
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_BROWSER_REQUIRE', function(event, module) {
|
||||
try {
|
||||
return event.returnValue = valueToMeta(event.sender, process.mainModule.require(module));
|
||||
|
@ -246,14 +276,13 @@ ipcMain.on('ATOM_BROWSER_CURRENT_WEB_CONTENTS', function(event) {
|
|||
});
|
||||
|
||||
ipcMain.on('ATOM_BROWSER_CONSTRUCTOR', function(event, id, args) {
|
||||
var constructor, obj;
|
||||
try {
|
||||
args = unwrapArgs(event.sender, args);
|
||||
constructor = objectsRegistry.get(id);
|
||||
let constructor = objectsRegistry.get(id);
|
||||
|
||||
// Call new with array of arguments.
|
||||
// http://stackoverflow.com/questions/1606797/use-of-apply-with-new-operator-is-this-possible
|
||||
obj = new (Function.prototype.bind.apply(constructor, [null].concat(args)));
|
||||
let obj = new (Function.prototype.bind.apply(constructor, [null].concat(args)));
|
||||
return event.returnValue = valueToMeta(event.sender, obj);
|
||||
} catch (error) {
|
||||
return event.returnValue = exceptionToMeta(error);
|
||||
|
@ -261,10 +290,9 @@ ipcMain.on('ATOM_BROWSER_CONSTRUCTOR', function(event, id, args) {
|
|||
});
|
||||
|
||||
ipcMain.on('ATOM_BROWSER_FUNCTION_CALL', function(event, id, args) {
|
||||
var func;
|
||||
try {
|
||||
args = unwrapArgs(event.sender, args);
|
||||
func = objectsRegistry.get(id);
|
||||
let func = objectsRegistry.get(id);
|
||||
return callFunction(event, func, global, args);
|
||||
} catch (error) {
|
||||
return event.returnValue = exceptionToMeta(error);
|
||||
|
@ -272,13 +300,12 @@ ipcMain.on('ATOM_BROWSER_FUNCTION_CALL', function(event, id, args) {
|
|||
});
|
||||
|
||||
ipcMain.on('ATOM_BROWSER_MEMBER_CONSTRUCTOR', function(event, id, method, args) {
|
||||
var constructor, obj;
|
||||
try {
|
||||
args = unwrapArgs(event.sender, args);
|
||||
constructor = objectsRegistry.get(id)[method];
|
||||
let constructor = objectsRegistry.get(id)[method];
|
||||
|
||||
// Call new with array of arguments.
|
||||
obj = new (Function.prototype.bind.apply(constructor, [null].concat(args)));
|
||||
let obj = new (Function.prototype.bind.apply(constructor, [null].concat(args)));
|
||||
return event.returnValue = valueToMeta(event.sender, obj);
|
||||
} catch (error) {
|
||||
return event.returnValue = exceptionToMeta(error);
|
||||
|
@ -286,10 +313,9 @@ ipcMain.on('ATOM_BROWSER_MEMBER_CONSTRUCTOR', function(event, id, method, args)
|
|||
});
|
||||
|
||||
ipcMain.on('ATOM_BROWSER_MEMBER_CALL', function(event, id, method, args) {
|
||||
var obj;
|
||||
try {
|
||||
args = unwrapArgs(event.sender, args);
|
||||
obj = objectsRegistry.get(id);
|
||||
let obj = objectsRegistry.get(id);
|
||||
return callFunction(event, obj[method], obj, args);
|
||||
} catch (error) {
|
||||
return event.returnValue = exceptionToMeta(error);
|
||||
|
@ -297,9 +323,8 @@ ipcMain.on('ATOM_BROWSER_MEMBER_CALL', function(event, id, method, args) {
|
|||
});
|
||||
|
||||
ipcMain.on('ATOM_BROWSER_MEMBER_SET', function(event, id, name, value) {
|
||||
var obj;
|
||||
try {
|
||||
obj = objectsRegistry.get(id);
|
||||
let obj = objectsRegistry.get(id);
|
||||
obj[name] = value;
|
||||
return event.returnValue = null;
|
||||
} catch (error) {
|
||||
|
@ -308,9 +333,8 @@ ipcMain.on('ATOM_BROWSER_MEMBER_SET', function(event, id, name, value) {
|
|||
});
|
||||
|
||||
ipcMain.on('ATOM_BROWSER_MEMBER_GET', function(event, id, name) {
|
||||
var obj;
|
||||
try {
|
||||
obj = objectsRegistry.get(id);
|
||||
let obj = objectsRegistry.get(id);
|
||||
return event.returnValue = valueToMeta(event.sender, obj[name]);
|
||||
} catch (error) {
|
||||
return event.returnValue = exceptionToMeta(error);
|
||||
|
@ -322,21 +346,18 @@ ipcMain.on('ATOM_BROWSER_DEREFERENCE', function(event, id) {
|
|||
});
|
||||
|
||||
ipcMain.on('ATOM_BROWSER_GUEST_WEB_CONTENTS', function(event, guestInstanceId) {
|
||||
var guestViewManager;
|
||||
try {
|
||||
guestViewManager = require('./guest-view-manager');
|
||||
let guestViewManager = require('./guest-view-manager');
|
||||
return event.returnValue = valueToMeta(event.sender, guestViewManager.getGuest(guestInstanceId));
|
||||
} catch (error) {
|
||||
return event.returnValue = exceptionToMeta(error);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('ATOM_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', function() {
|
||||
var args, event, guest, guestInstanceId, guestViewManager, method;
|
||||
event = arguments[0], guestInstanceId = arguments[1], method = arguments[2], args = 4 <= arguments.length ? slice.call(arguments, 3) : [];
|
||||
ipcMain.on('ATOM_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', function(event, guestInstanceId, method, ...args) {
|
||||
try {
|
||||
guestViewManager = require('./guest-view-manager');
|
||||
guest = guestViewManager.getGuest(guestInstanceId);
|
||||
let guestViewManager = require('./guest-view-manager');
|
||||
let guest = guestViewManager.getGuest(guestInstanceId);
|
||||
return guest[method].apply(guest, args);
|
||||
} catch (error) {
|
||||
return event.returnValue = exceptionToMeta(error);
|
||||
|
|
|
@ -892,12 +892,25 @@ void NativeWindowMac::HandleKeyboardEvent(
|
|||
return;
|
||||
|
||||
BOOL handled = [[NSApp mainMenu] performKeyEquivalent:event.os_event];
|
||||
if (!handled && event.os_event.window != window_.get()) {
|
||||
// The event comes from detached devtools view, and it has already been
|
||||
if (!handled && (event.os_event.modifierFlags & NSCommandKeyMask) &&
|
||||
(event.os_event.keyCode == 50 /* ~ key */)) {
|
||||
// Handle the cmd+~ shortcut.
|
||||
Focus(true);
|
||||
if (!handled && event.os_event.window) {
|
||||
// Handle the cmd+~ shortcut.
|
||||
if ((event.os_event.modifierFlags & NSCommandKeyMask) /* cmd */ &&
|
||||
(event.os_event.keyCode == 50 /* ~ */)) {
|
||||
// Switch to next visible window.
|
||||
NSArray* windows = [NSApp windows];
|
||||
NSIndexSet* indexes = [windows indexesOfObjectsPassingTest:
|
||||
^BOOL(id window, NSUInteger idx, BOOL* stop) {
|
||||
return [window isVisible];
|
||||
}];
|
||||
if ([indexes count] == 0)
|
||||
return;
|
||||
NSUInteger current = [windows indexOfObject:event.os_event.window];
|
||||
if (current == NSNotFound) // Some faked event.
|
||||
return;
|
||||
NSUInteger next = [indexes indexGreaterThanIndex:current];
|
||||
if (next == NSNotFound)
|
||||
next = [indexes firstIndex];
|
||||
[[windows objectAtIndex:next] makeKeyAndOrderFront:nil];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -300,14 +300,29 @@ bool NativeWindowViews::IsFocused() {
|
|||
|
||||
void NativeWindowViews::Show() {
|
||||
window_->native_widget_private()->ShowWithWindowState(GetRestoredState());
|
||||
|
||||
#if defined(USE_X11)
|
||||
if (global_menu_bar_)
|
||||
global_menu_bar_->OnWindowMapped();
|
||||
#endif
|
||||
}
|
||||
|
||||
void NativeWindowViews::ShowInactive() {
|
||||
window_->ShowInactive();
|
||||
|
||||
#if defined(USE_X11)
|
||||
if (global_menu_bar_)
|
||||
global_menu_bar_->OnWindowMapped();
|
||||
#endif
|
||||
}
|
||||
|
||||
void NativeWindowViews::Hide() {
|
||||
window_->Hide();
|
||||
|
||||
#if defined(USE_X11)
|
||||
if (global_menu_bar_)
|
||||
global_menu_bar_->OnWindowUnmapped();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool NativeWindowViews::IsVisible() {
|
||||
|
|
|
@ -17,9 +17,9 @@
|
|||
<key>CFBundleIconFile</key>
|
||||
<string>atom.icns</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>0.36.7</string>
|
||||
<string>0.36.8</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.36.7</string>
|
||||
<string>0.36.8</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,36,7,0
|
||||
PRODUCTVERSION 0,36,7,0
|
||||
FILEVERSION 0,36,8,0
|
||||
PRODUCTVERSION 0,36,8,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
|
@ -74,12 +74,12 @@ BEGIN
|
|||
BEGIN
|
||||
VALUE "CompanyName", "GitHub, Inc."
|
||||
VALUE "FileDescription", "Electron"
|
||||
VALUE "FileVersion", "0.36.7"
|
||||
VALUE "FileVersion", "0.36.8"
|
||||
VALUE "InternalName", "electron.exe"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved."
|
||||
VALUE "OriginalFilename", "electron.exe"
|
||||
VALUE "ProductName", "Electron"
|
||||
VALUE "ProductVersion", "0.36.7"
|
||||
VALUE "ProductVersion", "0.36.8"
|
||||
VALUE "SquirrelAwareVersion", "1"
|
||||
END
|
||||
END
|
||||
|
|
|
@ -8,7 +8,9 @@
|
|||
|
||||
#include "atom/browser/native_window.h"
|
||||
#include "base/callback.h"
|
||||
#include "base/mac/mac_util.h"
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#include "skia/ext/skia_utils_mac.h"
|
||||
|
||||
@interface ModalDelegate : NSObject {
|
||||
@private
|
||||
|
@ -57,7 +59,8 @@ NSAlert* CreateNSAlert(NativeWindow* parent_window,
|
|||
int default_id,
|
||||
const std::string& title,
|
||||
const std::string& message,
|
||||
const std::string& detail) {
|
||||
const std::string& detail,
|
||||
const gfx::ImageSkia& icon) {
|
||||
// Ignore the title; it's the window title on other platforms and ignorable.
|
||||
NSAlert* alert = [[NSAlert alloc] init];
|
||||
[alert setMessageText:base::SysUTF8ToNSString(message)];
|
||||
|
@ -92,6 +95,12 @@ NSAlert* CreateNSAlert(NativeWindow* parent_window,
|
|||
[[ns_buttons objectAtIndex:default_id] setKeyEquivalent:@"\r"];
|
||||
}
|
||||
|
||||
if (!icon.isNull()) {
|
||||
NSImage* image = gfx::SkBitmapToNSImageWithColorSpace(
|
||||
*icon.bitmap(), base::mac::GetGenericRGBColorSpace());
|
||||
[alert setIcon:image];
|
||||
}
|
||||
|
||||
return alert;
|
||||
}
|
||||
|
||||
|
@ -113,7 +122,7 @@ int ShowMessageBox(NativeWindow* parent_window,
|
|||
const gfx::ImageSkia& icon) {
|
||||
NSAlert* alert = CreateNSAlert(
|
||||
parent_window, type, buttons, default_id, title, message,
|
||||
detail);
|
||||
detail, icon);
|
||||
|
||||
// Use runModal for synchronous alert without parent, since we don't have a
|
||||
// window to wait for.
|
||||
|
@ -149,7 +158,7 @@ void ShowMessageBox(NativeWindow* parent_window,
|
|||
const MessageBoxCallback& callback) {
|
||||
NSAlert* alert = CreateNSAlert(
|
||||
parent_window, type, buttons, default_id, title, message,
|
||||
detail);
|
||||
detail, icon);
|
||||
ModalDelegate* delegate = [[ModalDelegate alloc] initWithCallback:callback
|
||||
andAlert:alert
|
||||
callEndModal:false];
|
||||
|
|
|
@ -210,6 +210,14 @@ void GlobalMenuBarX11::InitServer(gfx::AcceleratedWidget xid) {
|
|||
server_ = server_new(path.c_str());
|
||||
}
|
||||
|
||||
void GlobalMenuBarX11::OnWindowMapped() {
|
||||
GlobalMenuBarRegistrarX11::GetInstance()->OnWindowMapped(xid_);
|
||||
}
|
||||
|
||||
void GlobalMenuBarX11::OnWindowUnmapped() {
|
||||
GlobalMenuBarRegistrarX11::GetInstance()->OnWindowUnmapped(xid_);
|
||||
}
|
||||
|
||||
void GlobalMenuBarX11::BuildMenuFromModel(ui::MenuModel* model,
|
||||
DbusmenuMenuitem* parent) {
|
||||
for (int i = 0; i < model->GetItemCount(); ++i) {
|
||||
|
|
|
@ -46,6 +46,10 @@ class GlobalMenuBarX11 {
|
|||
void SetMenu(ui::MenuModel* menu_model);
|
||||
bool IsServerStarted() const;
|
||||
|
||||
// Called by NativeWindow when it show/hides.
|
||||
void OnWindowMapped();
|
||||
void OnWindowUnmapped();
|
||||
|
||||
private:
|
||||
// Creates a DbusmenuServer.
|
||||
void InitServer(gfx::AcceleratedWidget xid);
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace atom {
|
|||
namespace {
|
||||
|
||||
// Filter out the "&" in menu label.
|
||||
base::string16 FilterAccecelator(const base::string16& label) {
|
||||
base::string16 FilterAccelerator(const base::string16& label) {
|
||||
base::string16 out;
|
||||
base::RemoveChars(label, base::ASCIIToUTF16("&").c_str(), &out);
|
||||
return out;
|
||||
|
@ -26,7 +26,7 @@ base::string16 FilterAccecelator(const base::string16& label) {
|
|||
SubmenuButton::SubmenuButton(views::ButtonListener* listener,
|
||||
const base::string16& title,
|
||||
views::MenuButtonListener* menu_button_listener)
|
||||
: views::MenuButton(listener, FilterAccecelator(title),
|
||||
: views::MenuButton(listener, FilterAccelerator(title),
|
||||
menu_button_listener, false),
|
||||
accelerator_(0),
|
||||
show_underline_(false),
|
||||
|
|
|
@ -23,7 +23,7 @@ void MediaAccessAllowed(
|
|||
bool allowed) {
|
||||
brightray::MediaStreamDevicesController controller(request, callback);
|
||||
if (allowed)
|
||||
controller.Accept();
|
||||
controller.TakeAction();
|
||||
else
|
||||
controller.Deny(content::MEDIA_DEVICE_PERMISSION_DENIED);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue