Merge atom/master

This commit is contained in:
Heilig Benedek 2016-02-26 02:05:57 +01:00
commit 3f0d598a59
137 changed files with 3700 additions and 2419 deletions

View file

@ -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);
}

View file

@ -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_;

View file

@ -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

View file

@ -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_);

View file

@ -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);
};

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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();
});
};

View file

@ -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>

View file

@ -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');
}

View file

@ -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));

View file

@ -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');

View file

@ -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];

View file

@ -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);

View file

@ -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];
}
}
}

View file

@ -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() {

View file

@ -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>

View file

@ -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

View file

@ -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];

View file

@ -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) {

View file

@ -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);

View file

@ -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),

View file

@ -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);
}