Have the chrome debugger run javascript within a web worker, to remove the global document.

Summary: To make the chrome debugger environment consisten with the JSC executer environment,
where there is no `window.document`, make the chrome debugger run the javascript inside a web worker.

This fixes #1473
Closes https://github.com/facebook/react-native/pull/1632

Reviewed By: @martinbigio

Differential Revision: D2471710

Pulled By: @vjeux
This commit is contained in:
oveddan 2015-09-23 20:02:22 -07:00 committed by facebook-github-bot-9
parent c7ec04b253
commit fd0a9c1557
3 changed files with 69 additions and 28 deletions

View File

@ -42,32 +42,28 @@ function setStatus(status) {
document.getElementById('status').innerHTML = status; document.getElementById('status').innerHTML = status;
} }
// This worker will run the application javascript code,
// making sure that it's run in an environment without a global
// document, to make it consistent with the JSC executor environment.
var worker = new Worker('debuggerWorker.js');
var messageHandlers = { var messageHandlers = {
// This method is a bit hacky. Catalyst asks for a new clean JS runtime. // This method is a bit hacky. Catalyst asks for a new clean JS runtime.
// The easiest way to do this is to reload this page. That also means that // The easiest way to do this is to reload this page. That also means that
// web socket connection will be lost. To send reply back we need to remember // web socket connection will be lost. To send reply back we need to remember
// message id // message id.
// This message also needs to be handled outside of the worker, since the worker
// doesn't have access to local storage.
'prepareJSRuntime': function(message) { 'prepareJSRuntime': function(message) {
window.onbeforeunload = undefined; window.onbeforeunload = undefined;
window.localStorage.setItem('sessionID', message.id); window.localStorage.setItem('sessionID', message.id);
window.location.reload(); window.location.reload();
}, },
'executeApplicationScript': function(message, sendReply) { 'executeApplicationScript': function(message) {
for (var key in message.inject) { worker.postMessage(message);
window[key] = JSON.parse(message.inject[key]);
}
loadScript(message.url, sendReply.bind(null, null));
}, },
'executeJSCall': function(message, sendReply) { 'executeJSCall': function(message) {
var returnValue = null; worker.postMessage(message);
try {
if (window && window.require) {
var module = window.require(message.moduleName);
returnValue = module[message.moduleMethod].apply(module, message.arguments);
}
} finally {
sendReply(JSON.stringify(returnValue));
}
} }
}; };
@ -89,12 +85,9 @@ function connectToDebuggerProxy() {
return; return;
} }
var sendReply = function(result) {
ws.send(JSON.stringify({replyID: object.id, result: result}));
};
var handler = messageHandlers[object.method]; var handler = messageHandlers[object.method];
if (handler) { if (handler) {
handler(object, sendReply); handler(object);
} else { } else {
console.warn('Unknown method: ' + object.method); console.warn('Unknown method: ' + object.method);
} }
@ -107,18 +100,14 @@ function connectToDebuggerProxy() {
window.localStorage.removeItem('sessionID'); window.localStorage.removeItem('sessionID');
debuggerSetTimeout(connectToDebuggerProxy, 100); debuggerSetTimeout(connectToDebuggerProxy, 100);
}; };
worker.onmessage = function(message) {
ws.send(JSON.stringify(message.data));
}
} }
connectToDebuggerProxy(); connectToDebuggerProxy();
function loadScript(src, callback) {
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = src;
script.onload = callback;
document.head.appendChild(script);
}
})(); })();
</script> </script>
<style type="text/css"> <style type="text/css">

46
debuggerWorker.js Normal file
View File

@ -0,0 +1,46 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/* global self, importScripts, postMessage, onmessage: true */
/* eslint no-unused-vars: 0 */
'use strict';
var messageHandlers = {
'executeApplicationScript': function(message, sendReply) {
for (var key in message.inject) {
self[key] = JSON.parse(message.inject[key]);
}
importScripts(message.url);
sendReply();
},
'executeJSCall': function(message, sendReply) {
var returnValue = [[], [], [], [], []];
try {
if (require) {
returnValue = require(message.moduleName)[message.moduleMethod].apply(null, message.arguments);
}
} finally {
sendReply(JSON.stringify(returnValue));
}
}
};
onmessage = function(message) {
var object = message.data;
var sendReply = function(result) {
postMessage({replyID: object.id, result: result});
};
var handler = messageHandlers[object.method];
if (handler) {
handler(object, sendReply);
} else {
console.warn('Unknown method: ' + object.method);
}
};

View File

@ -184,6 +184,12 @@ function getDevToolsLauncher(options) {
var debuggerPath = path.join(__dirname, 'debugger.html'); var debuggerPath = path.join(__dirname, 'debugger.html');
res.writeHead(200, {'Content-Type': 'text/html'}); res.writeHead(200, {'Content-Type': 'text/html'});
fs.createReadStream(debuggerPath).pipe(res); fs.createReadStream(debuggerPath).pipe(res);
} else if (req.url === '/debuggerWorker.js') {
var workerPath = path.join(__dirname, 'debuggerWorker.js');
res.writeHead(200, {'Content-Type': 'application/javascript'});
fs.createReadStream(workerPath).pipe(res);
} else if (req.url === '/launch-chrome-devtools') { } else if (req.url === '/launch-chrome-devtools') {
var debuggerURL = 'http://localhost:' + options.port + '/debugger-ui'; var debuggerURL = 'http://localhost:' + options.port + '/debugger-ui';
var script = 'launchChromeDevTools.applescript'; var script = 'launchChromeDevTools.applescript';