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

View file

@ -12,12 +12,23 @@
namespace {
bool OpenExternal(const GURL& url, mate::Arguments* args) {
bool activate = true;
if (args->Length() == 2) {
mate::Dictionary options;
if (args->GetNext(&options)) {
options.Get("activate", &activate);
}
}
return platform_util::OpenExternal(url, activate);
}
void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
v8::Local<v8::Context> context, void* priv) {
mate::Dictionary dict(context->GetIsolate(), exports);
dict.SetMethod("showItemInFolder", &platform_util::ShowItemInFolder);
dict.SetMethod("openItem", &platform_util::OpenItem);
dict.SetMethod("openExternal", &platform_util::OpenExternal);
dict.SetMethod("openExternal", &OpenExternal);
dict.SetMethod("moveItemToTrash", &platform_util::MoveItemToTrash);
dict.SetMethod("beep", &platform_util::Beep);
}

View file

@ -2,49 +2,15 @@
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include <map>
#include <string>
#include "atom/common/api/object_life_monitor.h"
#include "atom/common/node_includes.h"
#include "base/stl_util.h"
#include "native_mate/dictionary.h"
#include "v8/include/v8-profiler.h"
namespace {
// A Persistent that can be copied and will not free itself.
template<class T>
struct LeakedPersistentTraits {
typedef v8::Persistent<T, LeakedPersistentTraits<T> > LeakedPersistent;
static const bool kResetInDestructor = false;
template<class S, class M>
static V8_INLINE void Copy(const v8::Persistent<S, M>& source,
LeakedPersistent* dest) {
// do nothing, just allow copy
}
};
// The handles are leaked on purpose.
using FunctionTemplateHandle =
LeakedPersistentTraits<v8::FunctionTemplate>::LeakedPersistent;
std::map<std::string, FunctionTemplateHandle> function_templates_;
v8::Local<v8::Object> CreateObjectWithName(v8::Isolate* isolate,
const std::string& name) {
if (name == "Object")
return v8::Object::New(isolate);
if (ContainsKey(function_templates_, name))
return v8::Local<v8::FunctionTemplate>::New(
isolate, function_templates_[name])->GetFunction()->NewInstance();
v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate);
t->SetClassName(mate::StringToV8(isolate, name));
function_templates_[name] = FunctionTemplateHandle(isolate, t);
return t->GetFunction()->NewInstance();
}
v8::Local<v8::Value> GetHiddenValue(v8::Local<v8::Object> object,
v8::Local<v8::String> key) {
return object->GetHiddenValue(key);
@ -78,7 +44,6 @@ void TakeHeapSnapshot(v8::Isolate* isolate) {
void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
v8::Local<v8::Context> context, void* priv) {
mate::Dictionary dict(context->GetIsolate(), exports);
dict.SetMethod("createObjectWithName", &CreateObjectWithName);
dict.SetMethod("getHiddenValue", &GetHiddenValue);
dict.SetMethod("setHiddenValue", &SetHiddenValue);
dict.SetMethod("deleteHiddenValue", &DeleteHiddenValue);

View file

@ -88,9 +88,13 @@ deprecate.warn = function(oldName, newName) {
return deprecate.log(oldName + " is deprecated. Use " + newName + " instead.");
};
var deprecationHandler = null;
// Print deprecation message.
deprecate.log = function(message) {
if (process.throwDeprecation) {
if (typeof deprecationHandler === 'function') {
deprecationHandler(message);
} else if (process.throwDeprecation) {
throw new Error(message);
} else if (process.traceDeprecation) {
return console.trace(message);
@ -99,4 +103,12 @@ deprecate.log = function(message) {
}
};
deprecate.setHandler = function(handler) {
deprecationHandler = handler;
};
deprecate.getHandler = function() {
return deprecationHandler;
};
module.exports = deprecate;

View file

@ -0,0 +1,11 @@
'use strict';
const deprecate = require('electron').deprecate;
exports.setHandler = function (deprecationHandler) {
deprecate.setHandler(deprecationHandler);
};
exports.getHandler = function () {
return deprecate.getHandler();
};

View file

@ -31,6 +31,12 @@ exports.defineProperties = function(exports) {
return require('../crash-reporter');
}
},
deprecations: {
enumerable: true,
get: function() {
return require('../deprecations');
}
},
nativeImage: {
enumerable: true,
get: function() {

View file

@ -7,7 +7,7 @@
#define ATOM_MAJOR_VERSION 0
#define ATOM_MINOR_VERSION 36
#define ATOM_PATCH_VERSION 7
#define ATOM_PATCH_VERSION 8
#define ATOM_VERSION_IS_RELEASE 1

View file

@ -173,6 +173,8 @@ bool Converter<content::StopFindAction>::FromV8(
// static
v8::Local<v8::Value> Converter<content::WebContents*>::ToV8(
v8::Isolate* isolate, content::WebContents* val) {
if (!val)
return v8::Null(isolate);
return atom::api::WebContents::CreateFrom(isolate, val).ToV8();
}

View file

@ -27,7 +27,9 @@ v8::Local<v8::Value> Converter<const net::URLRequest*>::ToV8(
v8::Isolate* isolate, const net::URLRequest* val) {
scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
dict->SetString("method", val->method());
dict->SetStringWithoutPathExpansion("url", val->url().spec());
std::string url;
if (!val->url_chain().empty()) url = val->url().spec();
dict->SetStringWithoutPathExpansion("url", url);
dict->SetString("referrer", val->referrer());
scoped_ptr<base::ListValue> list(new base::ListValue);
atom::GetUploadData(list.get(), val);

View file

@ -23,7 +23,7 @@ void OpenItem(const base::FilePath& full_path);
// Open the given external protocol URL in the desktop's default manner.
// (For example, mailto: URLs in the default mail user agent.)
bool OpenExternal(const GURL& url);
bool OpenExternal(const GURL& url, bool activate);
// Move a file to trash.
bool MoveItemToTrash(const base::FilePath& full_path);

View file

@ -64,7 +64,7 @@ void OpenItem(const base::FilePath& full_path) {
XDGOpen(full_path.value());
}
bool OpenExternal(const GURL& url) {
bool OpenExternal(const GURL& url, bool activate) {
if (url.SchemeIs("mailto"))
return XDGEmail(url.spec());
else

View file

@ -119,7 +119,7 @@ void OpenItem(const base::FilePath& full_path) {
}
}
bool OpenExternal(const GURL& url) {
bool OpenExternal(const GURL& url, bool activate) {
DCHECK([NSThread isMainThread]);
NSURL* ns_url = net::NSURLWithGURL(url);
if (!ns_url) {
@ -136,7 +136,15 @@ bool OpenExternal(const GURL& url) {
}
CFRelease(openingApp); // NOT A BUG; LSGetApplicationForURL retains for us
return [[NSWorkspace sharedWorkspace] openURL:ns_url];
NSUInteger launchOptions = NSWorkspaceLaunchDefault;
if (!activate)
launchOptions |= NSWorkspaceLaunchWithoutActivation;
return [[NSWorkspace sharedWorkspace] openURLs: @[ns_url]
withAppBundleIdentifier: nil
options: launchOptions
additionalEventParamDescriptor: NULL
launchIdentifiers: NULL];
}
bool MoveItemToTrash(const base::FilePath& full_path) {

View file

@ -301,7 +301,7 @@ void OpenItem(const base::FilePath& full_path) {
ui::win::OpenFileViaShell(full_path);
}
bool OpenExternal(const GURL& url) {
bool OpenExternal(const GURL& url, bool activate) {
// Quote the input scheme to be sure that the command does not have
// parameters unexpected by the external program. This url should already
// have been escaped.

View file

@ -1,11 +1,16 @@
'use strict';
const ipcRenderer = require('electron').ipcRenderer;
const CallbacksRegistry = require('electron').CallbacksRegistry;
const v8Util = process.atomBinding('v8_util');
const IDWeakMap = process.atomBinding('id_weak_map').IDWeakMap;
const callbacksRegistry = new CallbacksRegistry;
var includes = [].includes;
var remoteObjectCache = new IDWeakMap;
// Check for circular reference.
var isCircular = function(field, visited) {
if (typeof field === 'object') {
@ -85,9 +90,59 @@ var wrapArgs = function(args, visited) {
return Array.prototype.slice.call(args).map(valueToMeta);
};
// Populate object's members from descriptors.
// This matches |getObjectMemebers| in rpc-server.
let setObjectMembers = function(object, metaId, members) {
for (let member of members) {
if (object.hasOwnProperty(member.name))
continue;
let descriptor = { enumerable: member.enumerable };
if (member.type === 'method') {
let remoteMemberFunction = function() {
if (this && this.constructor === remoteMemberFunction) {
// Constructor call.
let ret = ipcRenderer.sendSync('ATOM_BROWSER_MEMBER_CONSTRUCTOR', metaId, member.name, wrapArgs(arguments));
return metaToValue(ret);
} else {
// Call member function.
let ret = ipcRenderer.sendSync('ATOM_BROWSER_MEMBER_CALL', metaId, member.name, wrapArgs(arguments));
return metaToValue(ret);
}
};
descriptor.value = remoteMemberFunction;
} else if (member.type === 'get') {
descriptor.get = function() {
return metaToValue(ipcRenderer.sendSync('ATOM_BROWSER_MEMBER_GET', metaId, member.name));
};
// Only set setter when it is writable.
if (member.writable) {
descriptor.set = function(value) {
ipcRenderer.sendSync('ATOM_BROWSER_MEMBER_SET', metaId, member.name, value);
return value;
};
}
}
Object.defineProperty(object, member.name, descriptor);
}
};
// Populate object's prototype from descriptor.
// This matches |getObjectPrototype| in rpc-server.
let setObjectPrototype = function(object, metaId, descriptor) {
if (descriptor === null)
return;
let proto = {};
setObjectMembers(proto, metaId, descriptor.members);
setObjectPrototype(proto, metaId, descriptor.proto);
Object.setPrototypeOf(object, proto);
};
// Convert meta data from browser into real value.
var metaToValue = function(meta) {
var el, i, j, len, len1, member, ref1, ref2, results, ret;
let metaToValue = function(meta) {
var el, i, len, ref1, results, ret;
switch (meta.type) {
case 'value':
return meta.value;
@ -112,56 +167,47 @@ var metaToValue = function(meta) {
case 'exception':
throw new Error(meta.message + "\n" + meta.stack);
default:
if (remoteObjectCache.has(meta.id))
return remoteObjectCache.get(meta.id);
if (meta.type === 'function') {
// A shadow class to represent the remote function object.
ret = (function() {
function RemoteFunction() {
var obj;
if (this.constructor === RemoteFunction) {
// Constructor call.
obj = ipcRenderer.sendSync('ATOM_BROWSER_CONSTRUCTOR', meta.id, wrapArgs(arguments));
/*
Returning object in constructor will replace constructed object
with the returned object.
http://stackoverflow.com/questions/1978049/what-values-can-a-constructor-return-to-avoid-returning-this
*/
return metaToValue(obj);
} else {
// Function call.
obj = ipcRenderer.sendSync('ATOM_BROWSER_FUNCTION_CALL', meta.id, wrapArgs(arguments));
return metaToValue(obj);
}
let remoteFunction = function() {
if (this && this.constructor === remoteFunction) {
// Constructor call.
let obj = ipcRenderer.sendSync('ATOM_BROWSER_CONSTRUCTOR', meta.id, wrapArgs(arguments));
// Returning object in constructor will replace constructed object
// with the returned object.
// http://stackoverflow.com/questions/1978049/what-values-can-a-constructor-return-to-avoid-returning-this
return metaToValue(obj);
} else {
// Function call.
let obj = ipcRenderer.sendSync('ATOM_BROWSER_FUNCTION_CALL', meta.id, wrapArgs(arguments));
return metaToValue(obj);
}
return RemoteFunction;
})();
};
ret = remoteFunction;
} else {
ret = v8Util.createObjectWithName(meta.name);
ret = {};
}
// Polulate delegate members.
ref2 = meta.members;
for (j = 0, len1 = ref2.length; j < len1; j++) {
member = ref2[j];
if (member.type === 'function') {
ret[member.name] = createRemoteMemberFunction(meta.id, member.name);
} else {
Object.defineProperty(ret, member.name, createRemoteMemberProperty(meta.id, member.name));
}
}
// Populate delegate members.
setObjectMembers(ret, meta.id, meta.members);
// Populate delegate prototype.
setObjectPrototype(ret, meta.id, meta.proto);
// Set constructor.name to object's name.
Object.defineProperty(ret.constructor, 'name', { value: meta.name });
// Track delegate object's life time, and tell the browser to clean up
// when the object is GCed.
v8Util.setDestructor(ret, function() {
return ipcRenderer.send('ATOM_BROWSER_DEREFERENCE', meta.id);
ipcRenderer.send('ATOM_BROWSER_DEREFERENCE', meta.id);
});
// Remember object's id.
v8Util.setHiddenValue(ret, 'atomId', meta.id);
remoteObjectCache.set(meta.id, ret);
return ret;
}
};
@ -185,52 +231,6 @@ var metaToPlainObject = function(meta) {
return obj;
};
// Create a RemoteMemberFunction instance.
// This function's content should not be inlined into metaToValue, otherwise V8
// may consider it circular reference.
var createRemoteMemberFunction = function(metaId, name) {
return (function() {
function RemoteMemberFunction() {
var ret;
if (this.constructor === RemoteMemberFunction) {
// Constructor call.
ret = ipcRenderer.sendSync('ATOM_BROWSER_MEMBER_CONSTRUCTOR', metaId, name, wrapArgs(arguments));
return metaToValue(ret);
} else {
// Call member function.
ret = ipcRenderer.sendSync('ATOM_BROWSER_MEMBER_CALL', metaId, name, wrapArgs(arguments));
return metaToValue(ret);
}
}
return RemoteMemberFunction;
})();
};
// Create configuration for defineProperty.
// This function's content should not be inlined into metaToValue, otherwise V8
// may consider it circular reference.
var createRemoteMemberProperty = function(metaId, name) {
return {
enumerable: true,
configurable: false,
set: function(value) {
// Set member data.
ipcRenderer.sendSync('ATOM_BROWSER_MEMBER_SET', metaId, name, value);
return value;
},
get: function() {
// Get member data.
return metaToValue(ipcRenderer.sendSync('ATOM_BROWSER_MEMBER_GET', metaId, name));
}
};
};
// Browser calls a callback in renderer.
ipcRenderer.on('ATOM_RENDERER_CALLBACK', function(event, id, args) {
return callbacksRegistry.apply(id, metaToValue(args));
@ -257,73 +257,33 @@ for (var name in browserModules) {
}
// Get remote module.
// (Just like node's require, the modules are cached permanently, note that this
// is safe leak since the object is not expected to get freed in browser)
var moduleCache = {};
exports.require = function(module) {
var meta;
if (moduleCache[module] != null) {
return moduleCache[module];
}
meta = ipcRenderer.sendSync('ATOM_BROWSER_REQUIRE', module);
return moduleCache[module] = metaToValue(meta);
return metaToValue(ipcRenderer.sendSync('ATOM_BROWSER_REQUIRE', module));
};
// Optimize require('electron').
moduleCache.electron = exports;
// Alias to remote.require('electron').xxx.
var builtinCache = {};
exports.getBuiltin = function(module) {
var meta;
if (builtinCache[module] != null) {
return builtinCache[module];
}
meta = ipcRenderer.sendSync('ATOM_BROWSER_GET_BUILTIN', module);
return builtinCache[module] = metaToValue(meta);
return metaToValue(ipcRenderer.sendSync('ATOM_BROWSER_GET_BUILTIN', module));
};
// Get current BrowserWindow object.
var windowCache = null;
// Get current BrowserWindow.
exports.getCurrentWindow = function() {
var meta;
if (windowCache != null) {
return windowCache;
}
meta = ipcRenderer.sendSync('ATOM_BROWSER_CURRENT_WINDOW');
return windowCache = metaToValue(meta);
return metaToValue(ipcRenderer.sendSync('ATOM_BROWSER_CURRENT_WINDOW'));
};
// Get current WebContents object.
var webContentsCache = null;
exports.getCurrentWebContents = function() {
var meta;
if (webContentsCache != null) {
return webContentsCache;
}
meta = ipcRenderer.sendSync('ATOM_BROWSER_CURRENT_WEB_CONTENTS');
return webContentsCache = metaToValue(meta);
return metaToValue(ipcRenderer.sendSync('ATOM_BROWSER_CURRENT_WEB_CONTENTS'));
};
// Get a global object in browser.
exports.getGlobal = function(name) {
var meta;
meta = ipcRenderer.sendSync('ATOM_BROWSER_GLOBAL', name);
return metaToValue(meta);
return metaToValue(ipcRenderer.sendSync('ATOM_BROWSER_GLOBAL', name));
};
// Get the process object in browser.
var processCache = null;
exports.__defineGetter__('process', function() {
if (processCache == null) {
processCache = exports.getGlobal('process');
}
return processCache;
return exports.getGlobal('process');
});
// Create a funtion that will return the specifed value when called in browser.

View file

@ -388,7 +388,7 @@ var registerWebViewElement = function() {
'downloadURL',
'inspectServiceWorker',
'print',
'printToPDF'
'printToPDF',
];
nonblockMethods = [
'executeJavaScript',
@ -430,6 +430,12 @@ var registerWebViewElement = function() {
proto[m] = createNonBlockHandler(m);
}
// WebContents associated with this webview.
proto.getWebContents = function() {
var internal = v8Util.getHiddenValue(this, 'internal');
return internal.webContents;
};
// Deprecated.
deprecate.rename(proto, 'getUrl', 'getURL');
window.WebView = webFrame.registerEmbedderCustomElement('webview', {