Release render view's remote objects when it's deleted.

Privously we release them when the window is unloaded, which is not
correct since a render view can have multiple windows (or js contexts)
and when the unload event is emitted the render view could already have
gone.

This PR does the cleaning work purely in browser, so here is no need to
worry about renderer's life time.
This commit is contained in:
Cheng Zhao 2013-12-06 14:44:25 +08:00
parent e9e90b481a
commit 623e0f3ae4
8 changed files with 28 additions and 11 deletions

View file

@ -76,6 +76,10 @@ void Window::OnRendererResponsive() {
Emit("responsive"); Emit("responsive");
} }
void Window::OnRenderViewDeleted() {
Emit("render-view-deleted");
}
void Window::OnRendererCrashed() { void Window::OnRendererCrashed() {
Emit("crashed"); Emit("crashed");
} }

View file

@ -43,6 +43,7 @@ class Window : public EventEmitter,
virtual void OnWindowBlur() OVERRIDE; virtual void OnWindowBlur() OVERRIDE;
virtual void OnRendererUnresponsive() OVERRIDE; virtual void OnRendererUnresponsive() OVERRIDE;
virtual void OnRendererResponsive() OVERRIDE; virtual void OnRendererResponsive() OVERRIDE;
virtual void OnRenderViewDeleted() OVERRIDE;
virtual void OnRendererCrashed() OVERRIDE; virtual void OnRendererCrashed() OVERRIDE;
private: private:

View file

@ -11,6 +11,11 @@ BrowserWindow::_init = ->
menu = app.getApplicationMenu() menu = app.getApplicationMenu()
@setMenu menu if menu? @setMenu menu if menu?
# Tell the rpc server that a render view has been deleted and we need to
# release all objects owned by it.
@on 'render-view-deleted', ->
process.emit 'ATOM_BROWSER_RELEASE_RENDER_VIEW', @getProcessId(), @getRoutingId()
BrowserWindow::toggleDevTools = -> BrowserWindow::toggleDevTools = ->
if @isDevToolsOpened() then @closeDevTools() else @openDevTools() if @isDevToolsOpened() then @closeDevTools() else @openDevTools()

View file

@ -78,6 +78,11 @@ callFunction = (event, processId, routingId, func, caller, args) ->
ret = func.apply caller, args ret = func.apply caller, args
event.returnValue = valueToMeta processId, routingId, ret event.returnValue = valueToMeta processId, routingId, ret
# Send by BrowserWindow when its render view is deleted.
process.on 'ATOM_BROWSER_RELEASE_RENDER_VIEW', (processId, routingId) ->
console.log 'ATOM_BROWSER_RELEASE_RENDER_VIEW', processId, routingId
objectsRegistry.clear processId, routingId
ipc.on 'ATOM_BROWSER_REQUIRE', (event, processId, routingId, module) -> ipc.on 'ATOM_BROWSER_REQUIRE', (event, processId, routingId, module) ->
try try
event.returnValue = valueToMeta processId, routingId, require(module) event.returnValue = valueToMeta processId, routingId, require(module)
@ -90,10 +95,6 @@ ipc.on 'ATOM_BROWSER_GLOBAL', (event, processId, routingId, name) ->
catch e catch e
event.returnValue = errorToMeta e event.returnValue = errorToMeta e
ipc.on 'ATOM_BROWSER_RELEASE_RENDER_VIEW', (event, processId, routingId) ->
objectsRegistry.clear processId, routingId
event.returnValue = null
ipc.on 'ATOM_BROWSER_CURRENT_WINDOW', (event, processId, routingId) -> ipc.on 'ATOM_BROWSER_CURRENT_WINDOW', (event, processId, routingId) ->
try try
BrowserWindow = require 'browser-window' BrowserWindow = require 'browser-window'

View file

@ -268,6 +268,11 @@ void NativeWindow::NotifyWindowClosed() {
if (is_closed_) if (is_closed_)
return; return;
// The OnRenderViewDeleted is not called when the WebContents is destroyed
// directly (e.g. when closing the window), so we make sure it's always
// emitted to users by sending it before window is closed..
FOR_EACH_OBSERVER(NativeWindowObserver, observers_, OnRenderViewDeleted());
is_closed_ = true; is_closed_ = true;
FOR_EACH_OBSERVER(NativeWindowObserver, observers_, OnWindowClosed()); FOR_EACH_OBSERVER(NativeWindowObserver, observers_, OnWindowClosed());
@ -393,6 +398,10 @@ bool NativeWindow::OnMessageReceived(const IPC::Message& message) {
return handled; return handled;
} }
void NativeWindow::RenderViewDeleted(content::RenderViewHost*) {
FOR_EACH_OBSERVER(NativeWindowObserver, observers_, OnRenderViewDeleted());
}
void NativeWindow::RenderViewGone(base::TerminationStatus status) { void NativeWindow::RenderViewGone(base::TerminationStatus status) {
FOR_EACH_OBSERVER(NativeWindowObserver, observers_, OnRendererCrashed()); FOR_EACH_OBSERVER(NativeWindowObserver, observers_, OnRendererCrashed());
} }

View file

@ -182,6 +182,7 @@ class NativeWindow : public brightray::DefaultWebContentsDelegate,
virtual void RendererResponsive(content::WebContents* source) OVERRIDE; virtual void RendererResponsive(content::WebContents* source) OVERRIDE;
// Implementations of content::WebContentsObserver. // Implementations of content::WebContentsObserver.
virtual void RenderViewDeleted(content::RenderViewHost*) OVERRIDE;
virtual void RenderViewGone(base::TerminationStatus status) OVERRIDE; virtual void RenderViewGone(base::TerminationStatus status) OVERRIDE;
virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;

View file

@ -35,6 +35,9 @@ class NativeWindowObserver {
// Called when renderer recovers. // Called when renderer recovers.
virtual void OnRendererResponsive() {} virtual void OnRendererResponsive() {}
// Called when a render view has been deleted.
virtual void OnRenderViewDeleted() {}
// Called when renderer has crashed. // Called when renderer has crashed.
virtual void OnRendererCrashed() {} virtual void OnRendererCrashed() {}
}; };

View file

@ -2,7 +2,6 @@ ipc = require 'ipc'
CallbacksRegistry = require 'callbacks-registry' CallbacksRegistry = require 'callbacks-registry'
v8Util = process.atomBinding 'v8_util' v8Util = process.atomBinding 'v8_util'
currentContextExist = true
callbacksRegistry = new CallbacksRegistry callbacksRegistry = new CallbacksRegistry
# Convert the arguments object into an array of meta data. # Convert the arguments object into an array of meta data.
@ -82,7 +81,6 @@ metaToValue = (meta) ->
# Track delegate object's life time, and tell the browser to clean up # Track delegate object's life time, and tell the browser to clean up
# when the object is GCed. # when the object is GCed.
v8Util.setDestructor ret, -> v8Util.setDestructor ret, ->
return unless currentContextExist
ipc.sendChannel 'ATOM_BROWSER_DEREFERENCE', meta.storeId ipc.sendChannel 'ATOM_BROWSER_DEREFERENCE', meta.storeId
# Remember object's id. # Remember object's id.
@ -98,11 +96,6 @@ ipc.on 'ATOM_RENDERER_CALLBACK', (id, args) ->
ipc.on 'ATOM_RENDERER_RELEASE_CALLBACK', (id) -> ipc.on 'ATOM_RENDERER_RELEASE_CALLBACK', (id) ->
callbacksRegistry.remove id callbacksRegistry.remove id
# Release all resources of current render view when it's going to be unloaded.
window.addEventListener 'unload', (event) ->
currentContextExist = false
ipc.sendChannelSync 'ATOM_BROWSER_RELEASE_RENDER_VIEW'
# Get remote module. # Get remote module.
# (Just like node's require, the modules are cached permanently, note that this # (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) # is safe leak since the object is not expected to get freed in browser)