home *** CD-ROM | disk | FTP | other *** search
- /*
- * 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()
- {
- }
-
- }
-