summaryrefslogtreecommitdiffstats
path: root/WebCore/inspector/front-end/InjectedFakeWorker.js
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/inspector/front-end/InjectedFakeWorker.js')
-rw-r--r--WebCore/inspector/front-end/InjectedFakeWorker.js345
1 files changed, 345 insertions, 0 deletions
diff --git a/WebCore/inspector/front-end/InjectedFakeWorker.js b/WebCore/inspector/front-end/InjectedFakeWorker.js
new file mode 100644
index 0000000..7176844
--- /dev/null
+++ b/WebCore/inspector/front-end/InjectedFakeWorker.js
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+var InjectedFakeWorker = function(InjectedScriptHost, inspectedWindow, injectedScriptId)
+{
+
+Worker = function(url)
+{
+ var impl = new FakeWorker(this, url);
+ if (impl === null)
+ return null;
+
+ this.isFake = true;
+ this.postMessage = bind(impl.postMessage, impl);
+ this.terminate = bind(impl.terminate, impl);
+
+ function onmessageGetter()
+ {
+ return impl.channel.port1.onmessage;
+ }
+ function onmessageSetter(callback)
+ {
+ impl.channel.port1.onmessage = callback;
+ }
+ this.__defineGetter__("onmessage", onmessageGetter);
+ this.__defineSetter__("onmessage", onmessageSetter);
+ this.addEventListener = bind(impl.channel.port1.addEventListener, impl.channel.port1);
+ this.removeEventListener = bind(impl.channel.port1.removeEventListener, impl.channel.port1);
+ this.dispatchEvent = bind(impl.channel.port1.dispatchEvent, impl.channel.port1);
+}
+
+function FakeWorker(worker, url)
+{
+ var scriptURL = this._expandURLAndCheckOrigin(document.baseURI, location.href, url);
+
+ this._worker = worker;
+ this._id = InjectedScriptHost.nextWorkerId();
+ this.channel = new MessageChannel();
+ this._listeners = [];
+ this._buildWorker(scriptURL);
+
+ InjectedScriptHost.didCreateWorker(this._id, scriptURL.url, false);
+}
+
+FakeWorker.prototype = {
+ postMessage: function(msg, opt_ports)
+ {
+ if (this._frame != null)
+ this.channel.port1.postMessage.apply(this.channel.port1, arguments);
+ else if (this._pendingMessages)
+ this._pendingMessages.push(arguments)
+ else
+ this._pendingMessages = [ arguments ];
+ },
+
+ terminate: function()
+ {
+ InjectedScriptHost.didDestroyWorker(this._id);
+
+ this.channel.port1.close();
+ this.channel.port2.close();
+ if (this._frame != null)
+ this._frame.frameElement.parentNode.removeChild(this._frame.frameElement);
+ this._frame = null;
+ this._worker = null; // Break reference loop.
+ },
+
+ _buildWorker: function(url)
+ {
+ var code = this._loadScript(url.url);
+ var iframeElement = document.createElement("iframe");
+ iframeElement.style.display = "none";
+
+ this._document = document;
+ iframeElement.onload = bind(this._onWorkerFrameLoaded, this, iframeElement, url, code);
+
+ if (document.body)
+ this._attachWorkerFrameToDocument(iframeElement, url, code);
+ else
+ window.addEventListener("load", bind(this._attachWorkerFrameToDocument, this, iframeElement), false);
+ },
+
+ _attachWorkerFrameToDocument: function(iframeElement)
+ {
+ document.body.appendChild(iframeElement);
+ },
+
+ _onWorkerFrameLoaded: function(iframeElement, url, code)
+ {
+ var frame = iframeElement.contentWindow;
+ this._frame = frame;
+ this._setupWorkerContext(frame, url);
+
+ var frameContents = '(function() { var location = __devtools.location; var window; ' + code + '})();\n' + '//@ sourceURL=' + url.url;
+
+ frame.eval(frameContents);
+ if (this._pendingMessages) {
+ for (var msg = 0; msg < this._pendingMessages.length; ++msg)
+ this.postMessage.apply(this, this._pendingMessages[msg]);
+ delete this._pendingMessages;
+ }
+ },
+
+ _setupWorkerContext: function(workerFrame, url)
+ {
+ workerFrame.__devtools = {
+ handleException: bind(this._handleException, this),
+ location: url.mockLocation()
+ };
+
+ var self = this;
+
+ function onmessageGetter()
+ {
+ return self.channel.port2.onmessage ? self.channel.port2.onmessage.originalCallback : null;
+ }
+
+ function onmessageSetter(callback)
+ {
+ var wrappedCallback = bind(self._callbackWrapper, self, callback);
+ wrappedCallback.originalCallback = callback;
+ self.channel.port2.onmessage = wrappedCallback;
+ }
+
+ workerFrame.__defineGetter__("onmessage", onmessageGetter);
+ workerFrame.__defineSetter__("onmessage", onmessageSetter);
+ workerFrame.addEventListener = bind(this._addEventListener, this);
+ workerFrame.removeEventListener = bind(this._removeEventListener, this);
+ workerFrame.dispatchEvent = bind(this.channel.port2.dispatchEvent, this.channel.port2);
+ workerFrame.postMessage = bind(this.channel.port2.postMessage, this.channel.port2);
+ workerFrame.importScripts = bind(this._importScripts, this, workerFrame);
+ workerFrame.close = bind(this.terminate, this);
+ },
+
+ _addEventListener: function(type, callback, useCapture)
+ {
+ var wrappedCallback = bind(this._callbackWrapper, this, callback);
+ wrappedCallback.originalCallback = callback;
+ wrappedCallback.type = type;
+ wrappedCallback.useCapture = Boolean(useCapture);
+
+ this.channel.port2.addEventListener(type, wrappedCallback, useCapture);
+ this._listeners.push(wrappedCallback);
+ },
+
+ _removeEventListener: function(type, callback, useCapture)
+ {
+ var listeners = this._listeners;
+ for (var i = 0; i < listeners.length; ++i) {
+ if (listeners[i].originalCallback === callback &&
+ listeners[i].type === type &&
+ listeners[i].useCapture === Boolean(useCapture)) {
+ this.channel.port2.removeEventListener(type, listeners[i], useCapture);
+ listeners[i] = listeners[listeners.length - 1];
+ listeners.pop();
+ break;
+ }
+ }
+ },
+
+ _callbackWrapper: function(callback, msg)
+ {
+ // Shortcut -- if no exception handlers installed, avoid try/catch so as not to obscure line number.
+ if (!this._frame.onerror && !this._worker.onerror) {
+ callback(msg);
+ return;
+ }
+
+ try {
+ callback(msg);
+ } catch (e) {
+ this._handleException(e, this._frame.onerror, this._worker.onerror);
+ }
+ },
+
+ _handleException: function(e)
+ {
+ // NB: it should be an ErrorEvent, but creating it from script is not
+ // currently supported, so emulate it on top of plain vanilla Event.
+ var errorEvent = this._document.createEvent("Event");
+ errorEvent.initEvent("Event", false, false);
+ errorEvent.message = "Uncaught exception";
+
+ for (var i = 1; i < arguments.length; ++i) {
+ if (arguments[i] && arguments[i](errorEvent))
+ return;
+ }
+
+ throw e;
+ },
+
+ _importScripts: function(targetFrame)
+ {
+ for (var i = 1; i < arguments.length; ++i) {
+ var workerOrigin = targetFrame.__devtools.location.href;
+ var url = this._expandURLAndCheckOrigin(workerOrigin, workerOrigin, arguments[i]);
+ targetFrame.eval(this._loadScript(url.url) + "\n//@ sourceURL= " + url.url);
+ }
+ },
+
+ _loadScript: function(url)
+ {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", url, false);
+ xhr.send(null);
+
+ var text = xhr.responseText;
+ if (xhr.status != 0 && xhr.status/100 !== 2) { // We're getting status === 0 when using file://.
+ console.error("Failed to load worker: " + url + "[" + xhr.status + "]");
+ text = ""; // We've got error message, not worker code.
+ }
+ return text;
+ },
+
+ _expandURLAndCheckOrigin: function(baseURL, origin, url)
+ {
+ var scriptURL = new URL(baseURL).completeWith(url);
+
+ if (!scriptURL.sameOrigin(origin))
+ throw new DOMCoreException("SECURITY_ERR",18);
+ return scriptURL;
+ }
+};
+
+function URL(url)
+{
+ this.url = url;
+ this.split();
+}
+
+URL.prototype = {
+ urlRegEx: (/^(http[s]?|file):\/\/([^\/:]*)(:[\d]+)?(?:(\/[^#?]*)(\?[^#]*)?(?:#(.*))?)?$/i),
+
+ split: function()
+ {
+ function emptyIfNull(str)
+ {
+ return str == null ? "" : str;
+ }
+ var parts = this.urlRegEx.exec(this.url);
+
+ this.schema = parts[1];
+ this.host = parts[2];
+ this.port = emptyIfNull(parts[3]);
+ this.path = emptyIfNull(parts[4]);
+ this.query = emptyIfNull(parts[5]);
+ this.fragment = emptyIfNull(parts[6]);
+ },
+
+ mockLocation: function()
+ {
+ var host = this.host.replace(/^[^@]*@/, "");
+
+ return {
+ href: this.url,
+ protocol: this.schema + ":",
+ host: host,
+ hostname: host,
+ port: this.port,
+ pathname: this.path,
+ search: this.query,
+ hash: this.fragment
+ };
+ },
+
+ completeWith: function(url)
+ {
+ if (url === "" || /^[^/]*:/.exec(url)) // If given absolute url, return as is now.
+ return new URL(url);
+
+ var relParts = /^([^#?]*)(.*)$/.exec(url); // => [ url, path, query-andor-fragment ]
+
+ var path = (relParts[1].slice(0, 1) === "/" ? "" : this.path.replace(/[^/]*$/, "")) + relParts[1];
+ path = path.replace(/(\/\.)+(\/|$)/g, "/").replace(/[^/]*\/\.\.(\/|$)/g, "");
+
+ return new URL(this.schema + "://" + this.host + this.port + path + relParts[2]);
+ },
+
+ sameOrigin: function(url)
+ {
+ function normalizePort(schema, port)
+ {
+ var portNo = port.slice(1);
+ return (schema === "https" && portNo == 443 || schema === "http" && portNo == 80) ? "" : port;
+ }
+
+ var other = new URL(url);
+
+ return this.schema === other.schema &&
+ this.host === other.host &&
+ normalizePort(this.schema, this.port) === normalizePort(other.schema, other.port);
+ }
+};
+
+function DOMCoreException(name, code)
+{
+ function formatError()
+ {
+ return "Error: " + this.message;
+ }
+
+ this.name = name;
+ this.message = name + ": DOM Exception " + code;
+ this.code = code;
+ this.toString = bind(formatError, this);
+}
+
+function bind(func, thisObject)
+{
+ var args = Array.prototype.slice.call(arguments, 2);
+ return function() { return func.apply(thisObject, args.concat(Array.prototype.slice.call(arguments, 0))); };
+}
+
+function noop()
+{
+}
+
+}