home *** CD-ROM | disk | FTP | other *** search
/ Computer Active Guide 2009 July / CAG7.ISO / Internetas / SafariSetup.exe / AppleApplicationSupport.msi / WebKit.resources_inspector_InjectedFakeWorker.js < prev    next >
Encoding:
JavaScript  |  2010-06-03  |  11.5 KB  |  346 lines

  1. /*
  2.  * Copyright (C) 2010 Google Inc. All rights reserved.
  3.  *
  4.  * Redistribution and use in source and binary forms, with or without
  5.  * modification, are permitted provided that the following conditions are
  6.  * met:
  7.  *
  8.  *     * Redistributions of source code must retain the above copyright
  9.  * notice, this list of conditions and the following disclaimer.
  10.  *     * Redistributions in binary form must reproduce the above
  11.  * copyright notice, this list of conditions and the following disclaimer
  12.  * in the documentation and/or other materials provided with the
  13.  * distribution.
  14.  *     * Neither the name of Google Inc. nor the names of its
  15.  * contributors may be used to endorse or promote products derived from
  16.  * this software without specific prior written permission.
  17.  *
  18.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  19.  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  20.  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  21.  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  22.  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  23.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  24.  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  25.  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  26.  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  27.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  28.  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  29.  */
  30.  
  31. var InjectedFakeWorker = function(InjectedScriptHost, inspectedWindow, injectedScriptId)
  32. {
  33.  
  34. Worker = function(url)
  35. {
  36.     var impl = new FakeWorker(this, url);
  37.     if (impl === null)
  38.         return null;
  39.  
  40.     this.isFake = true;
  41.     this.postMessage = bind(impl.postMessage, impl);
  42.     this.terminate = bind(impl.terminate, impl);
  43.  
  44.     function onmessageGetter()
  45.     {
  46.         return impl.channel.port1.onmessage;
  47.     }
  48.     function onmessageSetter(callback)
  49.     {
  50.         impl.channel.port1.onmessage = callback;
  51.     }
  52.     this.__defineGetter__("onmessage", onmessageGetter);
  53.     this.__defineSetter__("onmessage", onmessageSetter);
  54.     this.addEventListener = bind(impl.channel.port1.addEventListener, impl.channel.port1);
  55.     this.removeEventListener = bind(impl.channel.port1.removeEventListener, impl.channel.port1);
  56.     this.dispatchEvent = bind(impl.channel.port1.dispatchEvent, impl.channel.port1);
  57. }
  58.  
  59. function FakeWorker(worker, url)
  60. {
  61.     var scriptURL = this._expandURLAndCheckOrigin(document.baseURI, location.href, url);
  62.  
  63.     this._worker = worker;
  64.     this._id = InjectedScriptHost.nextWorkerId();
  65.     this.channel = new MessageChannel();
  66.     this._listeners = [];
  67.     this._buildWorker(scriptURL);
  68.  
  69.     InjectedScriptHost.didCreateWorker(this._id, scriptURL.url, false);
  70. }
  71.  
  72. FakeWorker.prototype = {
  73.     postMessage: function(msg, opt_ports)
  74.     {
  75.         if (this._frame != null)
  76.             this.channel.port1.postMessage.apply(this.channel.port1, arguments);
  77.         else if (this._pendingMessages)
  78.             this._pendingMessages.push(arguments)
  79.         else
  80.             this._pendingMessages = [ arguments ];
  81.     },
  82.  
  83.     terminate: function()
  84.     {
  85.         InjectedScriptHost.didDestroyWorker(this._id);
  86.  
  87.         this.channel.port1.close();
  88.         this.channel.port2.close();
  89.         if (this._frame != null)
  90.             this._frame.frameElement.parentNode.removeChild(this._frame.frameElement);
  91.         this._frame = null;
  92.         this._worker = null; // Break reference loop.
  93.     },
  94.  
  95.     _buildWorker: function(url)
  96.     {
  97.         var code = this._loadScript(url.url);
  98.         var iframeElement = document.createElement("iframe");
  99.         iframeElement.style.display = "none";
  100.  
  101.         this._document = document;
  102.         iframeElement.onload = bind(this._onWorkerFrameLoaded, this, iframeElement, url, code);
  103.  
  104.         if (document.body)
  105.             this._attachWorkerFrameToDocument(iframeElement, url, code);
  106.         else
  107.             window.addEventListener("load", bind(this._attachWorkerFrameToDocument, this, iframeElement), false);
  108.     },
  109.  
  110.     _attachWorkerFrameToDocument: function(iframeElement)
  111.     {
  112.         document.body.appendChild(iframeElement);
  113.     },
  114.  
  115.     _onWorkerFrameLoaded: function(iframeElement, url, code)
  116.     {
  117.         var frame = iframeElement.contentWindow;
  118.         this._frame = frame;
  119.         this._setupWorkerContext(frame, url);
  120.  
  121.         var frameContents = '(function() { var location = __devtools.location; var window; ' + code + '})();\n' + '//@ sourceURL=' + url.url;
  122.  
  123.         frame.eval(frameContents);
  124.         if (this._pendingMessages) {
  125.             for (var msg = 0; msg < this._pendingMessages.length; ++msg)
  126.                 this.postMessage.apply(this, this._pendingMessages[msg]);
  127.             delete this._pendingMessages;
  128.         }
  129.     },
  130.  
  131.     _setupWorkerContext: function(workerFrame, url)
  132.     {
  133.         workerFrame.__devtools = {
  134.             handleException: bind(this._handleException, this),
  135.             location: url.mockLocation()
  136.         };
  137.  
  138.         var self = this;
  139.  
  140.         function onmessageGetter()
  141.         {
  142.             return self.channel.port2.onmessage ? self.channel.port2.onmessage.originalCallback : null;
  143.         }
  144.  
  145.         function onmessageSetter(callback)
  146.         {
  147.             var wrappedCallback = bind(self._callbackWrapper, self, callback);
  148.             wrappedCallback.originalCallback = callback;
  149.             self.channel.port2.onmessage = wrappedCallback;
  150.         }
  151.  
  152.         workerFrame.__defineGetter__("onmessage", onmessageGetter);
  153.         workerFrame.__defineSetter__("onmessage", onmessageSetter);
  154.         workerFrame.addEventListener = bind(this._addEventListener, this);
  155.         workerFrame.removeEventListener = bind(this._removeEventListener, this);
  156.         workerFrame.dispatchEvent = bind(this.channel.port2.dispatchEvent, this.channel.port2);
  157.         workerFrame.postMessage = bind(this.channel.port2.postMessage, this.channel.port2);
  158.         workerFrame.importScripts = bind(this._importScripts, this, workerFrame);
  159.         workerFrame.close = bind(this.terminate, this);
  160.     },
  161.  
  162.     _addEventListener: function(type, callback, useCapture)
  163.     {
  164.         var wrappedCallback = bind(this._callbackWrapper, this, callback);
  165.         wrappedCallback.originalCallback = callback;
  166.         wrappedCallback.type = type;
  167.         wrappedCallback.useCapture = Boolean(useCapture);
  168.  
  169.         this.channel.port2.addEventListener(type, wrappedCallback, useCapture);
  170.         this._listeners.push(wrappedCallback);
  171.     },
  172.  
  173.     _removeEventListener: function(type, callback, useCapture)
  174.     {
  175.         var listeners = this._listeners;
  176.         for (var i = 0; i < listeners.length; ++i) {
  177.             if (listeners[i].originalCallback === callback &&
  178.                 listeners[i].type === type && 
  179.                 listeners[i].useCapture === Boolean(useCapture)) {
  180.                 this.channel.port2.removeEventListener(type, listeners[i], useCapture);
  181.                 listeners[i] = listeners[listeners.length - 1];
  182.                 listeners.pop();
  183.                 break;
  184.             }
  185.         }
  186.     },
  187.  
  188.     _callbackWrapper: function(callback, msg)
  189.     {
  190.         // Shortcut -- if no exception handlers installed, avoid try/catch so as not to obscure line number.
  191.         if (!this._frame.onerror && !this._worker.onerror) {
  192.             callback(msg);
  193.             return;
  194.         }
  195.  
  196.         try {
  197.             callback(msg);
  198.         } catch (e) {
  199.             this._handleException(e, this._frame.onerror, this._worker.onerror);
  200.         }
  201.     },
  202.  
  203.     _handleException: function(e)
  204.     {
  205.         // NB: it should be an ErrorEvent, but creating it from script is not
  206.         // currently supported, so emulate it on top of plain vanilla Event.
  207.         var errorEvent = this._document.createEvent("Event");
  208.         errorEvent.initEvent("Event", false, false);
  209.         errorEvent.message = "Uncaught exception";
  210.  
  211.         for (var i = 1; i < arguments.length; ++i) {
  212.             if (arguments[i] && arguments[i](errorEvent))
  213.                 return;
  214.         }
  215.  
  216.         throw e;
  217.     },
  218.  
  219.     _importScripts: function(targetFrame)
  220.     {
  221.         for (var i = 1; i < arguments.length; ++i) {
  222.             var workerOrigin = targetFrame.__devtools.location.href;
  223.             var url = this._expandURLAndCheckOrigin(workerOrigin, workerOrigin, arguments[i]);
  224.             targetFrame.eval(this._loadScript(url.url) + "\n//@ sourceURL= " + url.url);
  225.         }
  226.     },
  227.  
  228.     _loadScript: function(url)
  229.     {
  230.         var xhr = new XMLHttpRequest();
  231.         xhr.open("GET", url, false);
  232.         xhr.send(null);
  233.  
  234.         var text = xhr.responseText;
  235.         if (xhr.status != 0 && xhr.status/100 !== 2) { // We're getting status === 0 when using file://.
  236.             console.error("Failed to load worker: " + url + "[" + xhr.status + "]");
  237.             text = ""; // We've got error message, not worker code.
  238.         }
  239.         return text;
  240.     },
  241.  
  242.     _expandURLAndCheckOrigin: function(baseURL, origin, url)
  243.     {
  244.         var scriptURL = new URL(baseURL).completeWith(url);
  245.  
  246.         if (!scriptURL.sameOrigin(origin))
  247.             throw new DOMCoreException("SECURITY_ERR",18);
  248.         return scriptURL;
  249.     }
  250. };
  251.  
  252. function URL(url)
  253. {
  254.     this.url = url;
  255.     this.split();
  256. }
  257.  
  258. URL.prototype = {
  259.     urlRegEx: (/^(http[s]?|file):\/\/([^\/:]*)(:[\d]+)?(?:(\/[^#?]*)(\?[^#]*)?(?:#(.*))?)?$/i),
  260.  
  261.     split: function()
  262.     {
  263.         function emptyIfNull(str)
  264.         {
  265.             return str == null ? "" : str;
  266.         }
  267.         var parts = this.urlRegEx.exec(this.url);
  268.  
  269.         this.schema = parts[1];
  270.         this.host = parts[2];
  271.         this.port = emptyIfNull(parts[3]);
  272.         this.path = emptyIfNull(parts[4]);
  273.         this.query = emptyIfNull(parts[5]);
  274.         this.fragment = emptyIfNull(parts[6]);
  275.     },
  276.  
  277.     mockLocation: function()
  278.     {
  279.         var host = this.host.replace(/^[^@]*@/, "");
  280.  
  281.         return {
  282.             href:     this.url,
  283.             protocol: this.schema + ":",
  284.             host:     host,
  285.             hostname: host,
  286.             port:     this.port,
  287.             pathname: this.path,
  288.             search:   this.query,
  289.             hash:     this.fragment
  290.         };
  291.     },
  292.  
  293.     completeWith: function(url)
  294.     {
  295.         if (url === "" || /^[^/]*:/.exec(url)) // If given absolute url, return as is now.
  296.             return new URL(url);
  297.  
  298.         var relParts = /^([^#?]*)(.*)$/.exec(url); // => [ url, path, query-andor-fragment ]
  299.  
  300.         var path = (relParts[1].slice(0, 1) === "/" ? "" : this.path.replace(/[^/]*$/, "")) + relParts[1];
  301.         path = path.replace(/(\/\.)+(\/|$)/g, "/").replace(/[^/]*\/\.\.(\/|$)/g, "");
  302.  
  303.         return new URL(this.schema + "://" + this.host + this.port + path + relParts[2]);
  304.     },
  305.  
  306.     sameOrigin: function(url)
  307.     {
  308.         function normalizePort(schema, port)
  309.         {
  310.             var portNo = port.slice(1);
  311.             return (schema === "https" && portNo == 443 || schema === "http" && portNo == 80) ? "" : port;
  312.         }
  313.  
  314.         var other = new URL(url);
  315.  
  316.         return this.schema === other.schema &&
  317.             this.host === other.host &&
  318.             normalizePort(this.schema, this.port) === normalizePort(other.schema, other.port);
  319.     }
  320. };
  321.  
  322. function DOMCoreException(name, code)
  323. {
  324.     function formatError()
  325.     {
  326.         return "Error: " + this.message;
  327.     }
  328.  
  329.     this.name = name;
  330.     this.message = name + ": DOM Exception " + code;
  331.     this.code = code;
  332.     this.toString = bind(formatError, this);
  333. }
  334.  
  335. function bind(func, thisObject)
  336. {
  337.     var args = Array.prototype.slice.call(arguments, 2);
  338.     return function() { return func.apply(thisObject, args.concat(Array.prototype.slice.call(arguments, 0))); };
  339. }
  340.  
  341. function noop()
  342. {
  343. }
  344.  
  345. }
  346.