Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revise the docs on remote module #147

Merged
merged 3 commits into from Dec 31, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/api/browser/browser-window.md
Expand Up @@ -309,6 +309,10 @@ visible page.
You can write received `image` directly to a `.png` file, or you can base64
encode it and use data URL to embed the image in HTML.

**Note:** Be sure to read documents on remote buffer in
[remote](../renderer/remote.md) if you are going to use this API in renderer
process.

### BrowserWindow.getPageTitle()

Returns the title of web page.
Expand Down
144 changes: 107 additions & 37 deletions docs/api/renderer/remote.md
@@ -1,12 +1,17 @@
# remote

It's common that the developers want to use modules in browsers from the
renderer, like closing current window, opening file dialogs, etc. Instead of
writing IPC code for every operation you want to do, atom-shell provides the
`remote` module to let you do RPC call just like using normal javascript
objects.
The `remote` module provides a simple way to do inter-process communication
between renderer process and browser process.

An example of creating a window in renderer:
In atom-shell, all GUI related modules are only available in the browser
process, if users want to call an browser side API in the renderer process
, they usually would have to explicitly send inter-process messages to the
browser process. But with the `remote` module, users can invoke methods of
objects living in browser process without sending inter-process messages
directly, like Java's
[RMI](http://en.wikipedia.org/wiki/Java_remote_method_invocation).

An example of creating a browser window in renderer process:

```javascript
var remote = require('remote');
Expand All @@ -15,31 +20,50 @@ var win = new BrowserWindow({ width: 800, height: 600 });
win.loadUrl('https://github.com');
```

## Remote objects

Each object (including function) returned by `remote` module represents an
object in browser process (we call it remote object or remote function), when
you invoke methods of a remote object, or call a remote function, or even create
a new object with the remote constructor (function), you are actually sending
synchronous inter-process messages.

In the example above, both `BrowserWindow` and `win` were remote objects. And
`new BrowserWindow` didn't create a `BrowserWindow` object in renderer process,
instead it created a `BrowserWindow` object in browser process, and returned the
corresponding remote object in renderer process, namely the `win` object.

## Lifetime of remote objects

Every object returned by `remote` module represents an object in browser (e.g.
a remote object), so when you call methods of an object, or call a returned
function, or even create a object with the returned constructor, you are
indeed making a synchronous RPC call. And when the renderer releases the last
reference to the remote object, the browser would release the corresponding
reference too.
Atom-shell makes sure that as long as the remote object in renderer process
lives (in other words, has not been garbage collected), the corresponding object
in browser process would never be released. And when the remote object has been
garbage collected, the corresponding object in browser process would be
dereferenced.

This also means that, if the renderer keeps a reference to an object in
browser, the object would never be released. So be careful to never leak the
But it also means that, if the remote object is leaked in renderer process, like
being stored in a map but never got freed, the corresponding object in browser
process would also be leaked too. So you should be very careful not to leak
remote objects.

## Passing callbacks
Primary value types like strings and numbers, however, are sent by copy.

## Passing callbacks to browser

Some APIs in browser process accepts callbacks, and it would be attempting to
pass callbacks when calling a remote function. Yes `remote` module does support
doing this, but you should also be extremely careful on this.

Many APIs in browser accepts callbacks, so the `remote` module also supports
passing callbacks when calling remote functions, and the callbacks passed
would become remote functions in the browser.
First, in order to avoid dead locks, the callbacks passed to browser process
would be called asynchronously, so you should not expect the browser process to
get the return value of the passed callbacks.

But in order to avoid possible dead locks, the callbacks passed to browser
would be called asynchronously in browser, so you should never expect the
browser to get the return value of the passed callback.
Second, the callbacks passed to browser process would not get released
automatically after they were called, instead they would persistent until the
browser process garbage collected them.

Another thing is the lifetime of the remote callbacks in browser, it might be
very tempting to do things like following:
For example, following code seems innocent at first glance, It installed a
callback for the `close` event on a remote object:

```javascript
var remote = require('remote');
Expand All @@ -48,35 +72,81 @@ remote.getCurrentWindow().on('close', function() {
});
```

Yes it will work correctly, but when you reload the window, the callback you
setup on the object in browser will not be erased, resources are leaked and
there is no magic in javascript to release a referenced object.
But the callback would be stored in the browser process persistently until you
explicitly uninstall it! So each time you reload your window, the callback would
be installed for once and previous callbacks were just leak. To make things
worse, since the context of previously installed callbacks have been released,
when `close` event was emitted exceptions would happen in browser process.

So generally, unless you are clear what you are doing, you should always avoid
passing callbacks to browser process.

## Remote buffer

An instance of node's `Buffer` is an object, so when you got a `Buffer` from
browser process, what you got was indeed a remote object (let's call it remote
buffer), and everything would just follow the rules of remote objects.

However you should remember that though a remote buffer behaves like the real
`Buffer`, it's not a `Buffer` at all. If you pass a remote buffer to node APIs
that accepting `Buffer`, you should assume the remote buffer would be treated
like a normal object, instead of a `Buffer`.

For example you can call `BrowserWindow.capturePage` in renderer process, which
returns a `Buffer` by calling passed callback:

```javascript
var remote = require('remote');
var fs = require('fs');
remote.getCurrentWindow().capturePage(function(buf) {
fs.writeFile('/tmp/screenshot.png', buf, function(err) {
console.log(err);
});
});
```

But you may be surprised to find that the file written was corrupted. This is
because when you called `fs.writeFile`, you thought `buf` was a `Buffer`, but
indeed it was a remote buffer, and it would be converted to string before it was
written to file. Since `buf` contained binary data and could not be represented
by UTF-8 encoded string, the written file would be corrupted.

The workaround is to write the `buf` in browser process, where it is a real
`Buffer`:

So if you really need to keep a reference of callbacks in browser, you should
write the callback in browser and send messages to renderer. And also make use
of DOM's events like `unload` and `beforeunload`, they will work perfectly.
```javascript
var remote = require('remote');
remote.getCurrentWindow().capturePage(function(buf) {
remote.require('fs').writeFile('/tmp/screenshot.png', buf, function(err) {
console.log(err);
});
});
```

The same thing could happen for all native types, but usually it would just
throw a type error. The `Buffer` deserves your special attention because it
can be converted to string and APIs accepting `Buffer` usually accept string
too, and data corruption only happens when it contains binary data.

## remote.require(module)

* `module` String

Return a module in browser.
Returns the object returned by `require(module)` in the browser process.

## remote.getCurrentWindow()

Return the `BrowserWindow` object that represents current window.

`Note:` it doesn't return the `window` object which represents the global
scope, instead it returns an instance of the `BrowserWindow` class which is
created with `browser-window` module in browser.
Returns the [BrowserWindow](../browser/browser-window.md) object which
represents current window.

## remote.getGlobal(name)

* `name` String

Return the `global[name]` value in browser.
Returns the global variable of `name` (e.g. `global[name]`) in the browser
process.

## remote.process

Getter to return the `process` object in browser, this is the same with
Returns the `process` object in the browser process, this is the same with
`remote.getGlobal('process')` but gets cached.