OLD | NEW |
(Empty) | |
| 1 # Using the Chrome Devtools JavaScript preprocessing feature |
| 2 |
| 3 The Chrome Devtools JavaScript preprocessor intercepts JavaScript just before it
enters V8, the Chrome JS system, allowing the JS to be transcoded before compil
ation. In combination with page injected JavaScript, the preprocessor allows a
complete synthetic runtime to be constructed in JavaScript. Combined with other
functions in the `chrome.devtools` extension API, the preprocessor allows new mo
re sophisticated JavaScript-related developer tools to be created. |
| 4 |
| 5 ## API |
| 6 |
| 7 To use the script preprocessor, write a [chrome devtools extension](http://devel
oper.chrome.com/extensions/devtools.inspectedWindow.html#method-reload) that rel
oads the Web page with the preprocessor installed: |
| 8 ``` |
| 9 chrome.devtools.inspectedWindow.reload({ |
| 10 ignoreCache: true, |
| 11 injectedScript: runThisFirst, |
| 12 preprocessorScript: preprocessor |
| 13 }); |
| 14 ``` |
| 15 where `preprocessorScript` is source code (string) for a JavaScript function tak
ing three string arguments, the source to preprocess, the URL of the source, and
a function name if the source is an DOM event handler. The preprocessorerScript
function should return a string to be compiled by Chrome in place of the input
source. In the case that the source is a DOM event handler, the returned source
must compile to a single JS function. |
| 16 |
| 17 The [Chrome Preprocessor Example](http://developer.chrome.com/extensions/samples
.html) illustrates the API call in a simple chrome devtools extension. Download
and unpack the .zip file, use `chrome://extensions` in Developer Mode and load t
he unpacked extension. Then open or reopen devtools. The Preprocessor panel has
a **reload** button that triggers a simple preprocessor. |
| 18 |
| 19 The preprocessor runs in an isolated world similar to the environment of Chrome
content scripts. A `window` object is available but it shares no properties with
the Web page `window` object. DOM calls in the preprocessor environment will o
perate on the Web page, but developers should be cautious about operating on the
DOM in the preprocessor. We do not test such operations though we expect the re
sult to resemble calls from the outer function of `<script>` tags. |
| 20 |
| 21 In some applications the developer may coordinate runtime initialization using t
he `injectedScript` property in the object passed to the `reload()` call. This i
s also JavaScript source code; it is compiled into the page ahead of any Web pag
e scripts and thus before any JavaScript is preprocessed. |
| 22 |
| 23 The preprocessor is compiled once just before the first JavaScript appears. It r
emains active until the page is reloaded or otherwise navigated. Navigating the
Web page back and then forward will result in no preprocessing. Closing devtool
s will leave the preprocessor in place. |
| 24 |
| 25 ## Use Cases |
| 26 |
| 27 The script preprocessor supports transcoding input source to JavaScript. Use cas
es include: |
| 28 * Adding write barriers for Querypoint debugging, |
| 29 * Supporting feature-specific debugging of next generation EcmaScript using eg
Traceur, |
| 30 * Integration of development tools like coverage analysis. |
| 31 * Analysis of call sequences for performance tuning. |
| 32 Several JavaScript compilers support transcoding, including [Traceur](https://gi
thub.com/google/traceur-compiler#readme) and [Esprima](http://esprima.org/). |
| 33 |
| 34 ## Implementation |
| 35 |
| 36 The implementation relies on the Devtools front-end hosting an extension supplyi
ng the preprocessor script; the front end communicates with the browser backend
over eg web sockets. |
| 37 |
| 38 The devtools extension function call issues a postMessage() event from the devto
ols extension iframe to the devtools main frame. The event is handled in Extensi
onServer.js which forwards it over the [devtools remote debug protocol](https://
developers.google.com/chrome-developer-tools/docs/protocol/1.0/page#command-relo
ad). (See [Bug 229971](https://code.google.com/p/chromium/issues/detail?id=2299
71) for this part of the implementation and its status). |
| 39 |
| 40 When the preprocessor script arrives in the back end, `InspectorPageAgent::reloa
d` stores the preprocessor script in `m_pendingScriptPreprocessor`. After the br
owser begins the reload operation, it calls `PageDebuggerAgent::didClearWindowOb
jectInWorld` which moves the processor source into the `scriptDebugServer()`. |
| 41 |
| 42 Next the browser prepares the page environment and calls `PageDebuggerAgent::did
ClearWindowObjectInWorld`. This function clears the preprocessor object pointer
and if it is not recreated during the page load, no scripts will be preprocessed
. At this point we only store the preprocessor source, delaying the compilation
of the preprocessor until just before its first use. This helps ensure that the
JS environment we use is fully initialized. |
| 43 |
| 44 Source to be preprocessed comes from three different places: |
| 45 1. Web page `<script>` tags, |
| 46 1. DOM event-listener attributes, eg `onload`, |
| 47 1. JS `eval()` or `new Function()` calls. |
| 48 |
| 49 When the browser encounters either a `<script>` tag (`ScriptController::executeS
criptInMainWorld`) or an element attribute script (`V8LazyEventListener::prepar
eListenerObject`) we call a corresponding function in InspectorInstrumentation.
This function has a fast inlined return path in the case that the debugger is no
t attached. |
| 50 |
| 51 If the debugger is attached, InspectorInstrumentation will call the matching fun
ction in PageDebuggerAgent (see core/inspector/InspectorInstrumentation.idl). It
checks to see if the preprocessor is installed. If not, it returns. |
| 52 |
| 53 The preprocessor source is stored in PageScriptDebugServer. |
| 54 If the preprocessor is installed, we check to see if it is compiled. If not, we
create a new `ScriptPreprocessor` object. The constructor uses `ScriptControll
er::executeScriptInIsolatedWorld` to compile the preprocessor in a new isolated
world associated with the Web page's main world. If the compilation and outer s
cript execution succeed and if the result is a JavaScript function, we store the
resulting function as a `ScopedPersistent<v8::Function>` member of the preproce
ssor. |
| 55 |
| 56 If the `PageScriptDebugServer::preprocess()` has a value for the preprocessor fu
nction, it applies the function to the web page source using `V8ScriptRunner::ca
llAsFunction()`. This calls the compiled JS function in the ScriptPreprocessor'
s isolated world and retrieves the resulting string. |
| 57 |
| 58 When the preprocessed JavaScript source runs it may call `eval()` or `new Functi
on()`. These calls cause the V8 runtime to compile source. Immediately before c
ompiling, V8 issues a beforeCompile event which triggers `ScriptDebugServer::han
dleV8DebugEvent()`. This code is only called if the debugger is active. In the h
andler we call `ScriptDebugServer::preprocessEval()` to examine the ScriptCompil
ationTypeInfo, a marker set by V8, to see if we are compiling dynamic code. Only
dynamic code is preprocessed in this function and only if we are not executing
the preprocessor itself. |
| 59 |
| 60 During the browser operation, API generation code, debugger console initializati
on code, injected page script code, debugger information extraction code, and re
gular web page code enter this function. There is currently no way to distingui
sh internal or system code from the web page code. However the internal code is
all static. By limiting our preprocessing to dynamic code in the beforeCompile h
andler, we know we are only operating on Web page code. The static Web page code
is preprocessed as described above. |
| 61 |
| 62 ## Limitations |
| 63 |
| 64 We currently do not support preprocessing of WebWorker source code. |
OLD | NEW |