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

  1. /*
  2.  * Copyright (C) 2007 Apple 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
  6.  * are met:
  7.  *
  8.  * 1.  Redistributions of source code must retain the above copyright
  9.  *     notice, this list of conditions and the following disclaimer.
  10.  * 2.  Redistributions in binary form must reproduce the above copyright
  11.  *     notice, this list of conditions and the following disclaimer in the
  12.  *     documentation and/or other materials provided with the distribution.
  13.  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
  14.  *     its contributors may be used to endorse or promote products derived
  15.  *     from this software without specific prior written permission.
  16.  *
  17.  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
  18.  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  19.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  20.  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
  21.  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  22.  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  23.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  24.  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  26.  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27.  */
  28.  
  29. var injectedScriptConstructor = (function (InjectedScriptHost, inspectedWindow, injectedScriptId, jsEngine) {
  30.  
  31. var InjectedScript = {};
  32.  
  33. InjectedScript.lastBoundObjectId = 1;
  34. InjectedScript.idToWrappedObject = {};
  35. InjectedScript.objectGroups = {};
  36.  
  37. InjectedScript.wrapObjectForConsole = function(object, canAccessInspectedWindow)
  38. {
  39.     if (canAccessInspectedWindow)
  40.         return InjectedScript.wrapObject(object, "console");
  41.     var result = {};
  42.     result.type = typeof object;
  43.     result.description = InjectedScript._toString(object);
  44.     return result;
  45. }
  46.  
  47. InjectedScript.wrapObject = function(object, objectGroupName)
  48. {
  49.     try {
  50.         var objectId;
  51.         if (typeof object === "object" || typeof object === "function" || InjectedScript._isHTMLAllCollection(object)) {
  52.             var id = InjectedScript.lastBoundObjectId++;
  53.             objectId = "object#" + id;
  54.             InjectedScript.idToWrappedObject[objectId] = object;
  55.  
  56.             var group = InjectedScript.objectGroups[objectGroupName];
  57.             if (!group) {
  58.                 group = [];
  59.                 InjectedScript.objectGroups[objectGroupName] = group;
  60.             }
  61.             group.push(objectId);
  62.         }
  63.         return InjectedScript.createProxyObject(object, objectId);
  64.     } catch (e) {
  65.         return InjectedScript.createProxyObject("[ Exception: " + e.toString() + " ]");
  66.     }
  67.     return InjectedScript.createProxyObject(object, objectId);
  68. };
  69.  
  70. InjectedScript.unwrapObject = function(objectId) {
  71.     return InjectedScript.idToWrappedObject[objectId];
  72. };
  73.  
  74. InjectedScript.releaseWrapperObjectGroup = function(objectGroupName) {
  75.     var group = InjectedScript.objectGroups[objectGroupName];
  76.     if (!group)
  77.         return;
  78.     for (var i = 0; i < group.length; i++)
  79.         delete InjectedScript.idToWrappedObject[group[i]];
  80.     delete InjectedScript.objectGroups[objectGroupName];
  81. };
  82.  
  83. // Called from within InspectorController on the 'inspected page' side.
  84. InjectedScript.reset = function()
  85. {
  86.     InjectedScript._searchResults = [];
  87.     InjectedScript._includedInSearchResultsPropertyName = "__includedInInspectorSearchResults";
  88. }
  89.  
  90. InjectedScript.reset();
  91.  
  92. InjectedScript.dispatch = function(methodName, args, callId)
  93. {
  94.     var argsArray = eval("(" + args + ")");
  95.     if (callId)
  96.         argsArray.splice(0, 0, callId);  // Methods that run asynchronously have a call back id parameter.
  97.     var result = InjectedScript[methodName].apply(InjectedScript, argsArray);
  98.     if (typeof result === "undefined") {
  99.         inspectedWindow.console.error("Web Inspector error: InjectedScript.%s returns undefined", methodName);
  100.         result = null;
  101.     }
  102.     return result;
  103. }
  104.  
  105. InjectedScript.getPrototypes = function(nodeId)
  106. {
  107.     var node = InjectedScript._nodeForId(nodeId);
  108.     if (!node)
  109.         return false;
  110.  
  111.     var result = [];
  112.     for (var prototype = node; prototype; prototype = prototype.__proto__) {
  113.         var title = InjectedScript._describe(prototype, true);
  114.         if (title.match(/Prototype$/)) {
  115.             title = title.replace(/Prototype$/, "");
  116.         }
  117.         result.push(title);
  118.     }
  119.     return result;
  120. }
  121.  
  122. InjectedScript.getProperties = function(objectProxy, ignoreHasOwnProperty, abbreviate)
  123. {
  124.     var object = InjectedScript._resolveObject(objectProxy);
  125.     if (!InjectedScript._isDefined(object))
  126.         return false;
  127.     var properties = [];
  128.     
  129.     var propertyNames = ignoreHasOwnProperty ? InjectedScript._getPropertyNames(object) : Object.getOwnPropertyNames(object);
  130.     if (!ignoreHasOwnProperty && object.__proto__)
  131.         propertyNames.push("__proto__");
  132.  
  133.     if (jsEngine === "v8") {
  134.         // Check if the object is a scope.
  135.         if (InjectedScript._isScopeProxy(objectProxy)) {
  136.             propertyNames = [];
  137.             for (var name in object)
  138.                 propertyNames.push(name);
  139.         } else {
  140.             // FIXME(http://crbug.com/41243): Object.getOwnPropertyNames may return duplicated names.
  141.             var a = [];
  142.             propertyNames.sort();
  143.             var prev;
  144.             for (var i = 0; i < propertyNames.length; i++) {
  145.                 var n = propertyNames[i];
  146.                 if (n != prev)
  147.                     a.push(n);
  148.                 prev = n;
  149.             }
  150.             propertyNames = a;
  151.         }
  152.     }
  153.     
  154.     // Go over properties, prepare results.
  155.     for (var i = 0; i < propertyNames.length; ++i) {
  156.         var propertyName = propertyNames[i];
  157.  
  158.         var property = {};
  159.         property.name = propertyName + "";
  160.         property.parentObjectProxy = objectProxy;
  161.         var isGetter = object["__lookupGetter__"] && object.__lookupGetter__(propertyName);
  162.         if (!isGetter) {
  163.             try {
  164.                 var childObject = object[propertyName];
  165.                 var childObjectProxy = new InjectedScript.createProxyObject(childObject, objectProxy.objectId, abbreviate);
  166.                 childObjectProxy.path = objectProxy.path ? objectProxy.path.slice() : [];
  167.                 childObjectProxy.path.push(propertyName);
  168.                 property.value = childObjectProxy;
  169.             } catch(e) {
  170.                 property.value = { description: e.toString() };
  171.                 property.isError = true;
  172.             }
  173.         } else {
  174.             // FIXME: this should show something like "getter" (bug 16734).
  175.             property.value = { description: "\u2014" }; // em dash
  176.             property.isGetter = true;
  177.         }
  178.         properties.push(property);
  179.     }
  180.     return properties;
  181. }
  182.  
  183. InjectedScript._isScopeProxy = function(objectProxy)
  184. {
  185.     var objectId = objectProxy.objectId;
  186.     return typeof objectId === "object" && !objectId.thisObject;
  187.  
  188. InjectedScript.setPropertyValue = function(objectProxy, propertyName, expression)
  189. {
  190.     var object = InjectedScript._resolveObject(objectProxy);
  191.     if (!InjectedScript._isDefined(object))
  192.         return false;
  193.  
  194.     var expressionLength = expression.length;
  195.     if (!expressionLength) {
  196.         delete object[propertyName];
  197.         return !(propertyName in object);
  198.     }
  199.  
  200.     try {
  201.         // Surround the expression in parenthesis so the result of the eval is the result
  202.         // of the whole expression not the last potential sub-expression.
  203.  
  204.         // There is a regression introduced here: eval is now happening against global object,
  205.         // not call frame while on a breakpoint.
  206.         // TODO: bring evaluation against call frame back.
  207.         var result = inspectedWindow.eval("(" + expression + ")");
  208.         // Store the result in the property.
  209.         object[propertyName] = result;
  210.         return true;
  211.     } catch(e) {
  212.         try {
  213.             var result = inspectedWindow.eval("\"" + InjectedScript._escapeCharacters(expression, "\"") + "\"");
  214.             object[propertyName] = result;
  215.             return true;
  216.         } catch(e) {
  217.             return false;
  218.         }
  219.     }
  220. }
  221.  
  222. InjectedScript.getNodePropertyValue = function(nodeId, propertyName)
  223. {
  224.     var node = InjectedScript._nodeForId(nodeId);
  225.     if (!node)
  226.         return false;
  227.     var result = node[propertyName];
  228.     return result !== undefined ? result : false;
  229. }
  230.  
  231. InjectedScript.setOuterHTML = function(nodeId, value, expanded)
  232. {
  233.     var node = InjectedScript._nodeForId(nodeId);
  234.     if (!node)
  235.         return false;
  236.  
  237.     var parent = node.parentNode;
  238.     var prevSibling = node.previousSibling;
  239.     node.outerHTML = value;
  240.     var newNode = prevSibling ? prevSibling.nextSibling : parent.firstChild;
  241.  
  242.     return InjectedScriptHost.pushNodePathToFrontend(newNode, expanded, false);
  243. }
  244.  
  245. InjectedScript._populatePropertyNames = function(object, resultSet)
  246. {
  247.     for (var o = object; o; o = o.__proto__) {
  248.         try {
  249.             var names = Object.getOwnPropertyNames(o);
  250.             for (var i = 0; i < names.length; ++i)
  251.                 resultSet[names[i] + ""] = true;
  252.         } catch (e) {
  253.         }
  254.     }
  255. }
  256.  
  257. InjectedScript._getPropertyNames = function(object, resultSet)
  258. {
  259.     var propertyNameSet = {};
  260.     InjectedScript._populatePropertyNames(object, propertyNameSet);
  261.     return Object.keys(propertyNameSet);
  262. }
  263.  
  264. InjectedScript.getCompletions = function(expression, includeInspectorCommandLineAPI, callFrameId)
  265. {
  266.     var props = {};
  267.     try {
  268.         var expressionResult;
  269.         // Evaluate on call frame if call frame id is available.
  270.         if (typeof callFrameId === "number") {
  271.             var callFrame = InjectedScript._callFrameForId(callFrameId);
  272.             if (!callFrame)
  273.                 return props;
  274.             if (expression)
  275.                 expressionResult = InjectedScript._evaluateOn(callFrame.evaluate, callFrame, expression, true);
  276.             else {
  277.                 // Evaluate into properties in scope of the selected call frame.
  278.                 var scopeChain = callFrame.scopeChain;
  279.                 for (var i = 0; i < scopeChain.length; ++i)
  280.                     InjectedScript._populatePropertyNames(scopeChain[i], props);
  281.             }
  282.         } else {
  283.             if (!expression)
  284.                 expression = "this";
  285.             expressionResult = InjectedScript._evaluateOn(inspectedWindow.eval, inspectedWindow, expression);
  286.         }
  287.         if (typeof expressionResult == "object")
  288.             InjectedScript._populatePropertyNames(expressionResult, props);
  289.         if (includeInspectorCommandLineAPI)
  290.             for (var prop in inspectedWindow.console._inspectorCommandLineAPI)
  291.                 if (prop.charAt(0) !== '_')
  292.                     props[prop] = true;
  293.     } catch(e) {
  294.     }
  295.     return props;
  296. }
  297.  
  298. InjectedScript.evaluate = function(expression, objectGroup)
  299. {
  300.     return InjectedScript._evaluateAndWrap(inspectedWindow.eval, inspectedWindow, expression, objectGroup);
  301. }
  302.  
  303. InjectedScript._evaluateAndWrap = function(evalFunction, object, expression, objectGroup, dontUseCommandLineAPI)
  304. {
  305.     var result = {};
  306.     try {
  307.         result.value = InjectedScript.wrapObject(InjectedScript._evaluateOn(evalFunction, object, expression, dontUseCommandLineAPI), objectGroup);
  308.  
  309.         // Handle error that might have happened while describing result.
  310.         if (result.value.errorText) {
  311.             result.value = result.value.errorText;
  312.             result.isException = true;
  313.         }
  314.     } catch (e) {
  315.         result.value = e.toString();
  316.         result.isException = true;
  317.     }
  318.     return result;
  319. }
  320.  
  321. InjectedScript._evaluateOn = function(evalFunction, object, expression, dontUseCommandLineAPI)
  322. {
  323.     InjectedScript._ensureCommandLineAPIInstalled(evalFunction, object);
  324.     // Surround the expression in with statements to inject our command line API so that
  325.     // the window object properties still take more precedent than our API functions.
  326.     if (!dontUseCommandLineAPI)
  327.         expression = "with (window.console._inspectorCommandLineAPI) { with (window) {\n" + expression + "\n} }";
  328.  
  329.     var value = evalFunction.call(object, expression);
  330.  
  331.     // When evaluating on call frame error is not thrown, but returned as a value.
  332.     if (InjectedScript._type(value) === "error")
  333.         throw value.toString();
  334.  
  335.     return value;
  336. }
  337.  
  338. InjectedScript.addInspectedNode = function(nodeId)
  339. {
  340.     var node = InjectedScript._nodeForId(nodeId);
  341.     if (!node)
  342.         return false;
  343.  
  344.     InjectedScript._ensureCommandLineAPIInstalled(inspectedWindow.eval, inspectedWindow);
  345.     var inspectedNodes = inspectedWindow.console._inspectorCommandLineAPI._inspectedNodes;
  346.     inspectedNodes.unshift(node);
  347.     if (inspectedNodes.length >= 5)
  348.         inspectedNodes.pop();
  349.     return true;
  350. }
  351.  
  352. InjectedScript.getNodeId = function(node)
  353. {
  354.     return InjectedScriptHost.pushNodePathToFrontend(node, false, false);
  355. }
  356.  
  357. InjectedScript.performSearch = function(whitespaceTrimmedQuery, runSynchronously)
  358. {
  359.     // FIXME: Few things are missing here:
  360.     // 1) Search works with node granularity - number of matches within node is not calculated.
  361.     // 2) Search does not work outside main documents' domain - we need to use specific InjectedScript instances
  362.     //    for other domains.
  363.     // 3) There is no need to push all search results to the front-end at a time, pushing next / previous result
  364.     //    is sufficient.
  365.     var tagNameQuery = whitespaceTrimmedQuery;
  366.     var attributeNameQuery = whitespaceTrimmedQuery;
  367.     var startTagFound = (tagNameQuery.indexOf("<") === 0);
  368.     var endTagFound = (tagNameQuery.lastIndexOf(">") === (tagNameQuery.length - 1));
  369.  
  370.     if (startTagFound || endTagFound) {
  371.         var tagNameQueryLength = tagNameQuery.length;
  372.         tagNameQuery = tagNameQuery.substring((startTagFound ? 1 : 0), (endTagFound ? (tagNameQueryLength - 1) : tagNameQueryLength));
  373.     }
  374.  
  375.     // Check the tagNameQuery is it is a possibly valid tag name.
  376.     if (!/^[a-zA-Z0-9\-_:]+$/.test(tagNameQuery))
  377.         tagNameQuery = null;
  378.  
  379.     // Check the attributeNameQuery is it is a possibly valid tag name.
  380.     if (!/^[a-zA-Z0-9\-_:]+$/.test(attributeNameQuery))
  381.         attributeNameQuery = null;
  382.  
  383.     const escapedQuery = InjectedScript._escapeCharacters(whitespaceTrimmedQuery, "'");
  384.     const escapedTagNameQuery = (tagNameQuery ?  InjectedScript._escapeCharacters(tagNameQuery, "'") : null);
  385.     const escapedWhitespaceTrimmedQuery = InjectedScript._escapeCharacters(whitespaceTrimmedQuery, "'");
  386.     const searchResultsProperty = InjectedScript._includedInSearchResultsPropertyName;
  387.  
  388.     function addNodesToResults(nodes, length, getItem)
  389.     {
  390.         if (!length)
  391.             return;
  392.  
  393.         var nodeIds = [];
  394.         for (var i = 0; i < length; ++i) {
  395.             var node = getItem.call(nodes, i);
  396.             // Skip this node if it already has the property.
  397.             if (searchResultsProperty in node)
  398.                 continue;
  399.  
  400.             if (!InjectedScript._searchResults.length) {
  401.                 InjectedScript._currentSearchResultIndex = 0;
  402.             }
  403.  
  404.             node[searchResultsProperty] = true;
  405.             InjectedScript._searchResults.push(node);
  406.             var nodeId = InjectedScriptHost.pushNodePathToFrontend(node, false, false);
  407.             nodeIds.push(nodeId);
  408.         }
  409.         InjectedScriptHost.addNodesToSearchResult(nodeIds.join(","));
  410.     }
  411.  
  412.     function matchExactItems(doc)
  413.     {
  414.         matchExactId.call(this, doc);
  415.         matchExactClassNames.call(this, doc);
  416.         matchExactTagNames.call(this, doc);
  417.         matchExactAttributeNames.call(this, doc);
  418.     }
  419.  
  420.     function matchExactId(doc)
  421.     {
  422.         const result = doc.__proto__.getElementById.call(doc, whitespaceTrimmedQuery);
  423.         addNodesToResults.call(this, result, (result ? 1 : 0), function() { return this });
  424.     }
  425.  
  426.     function matchExactClassNames(doc)
  427.     {
  428.         const result = doc.__proto__.getElementsByClassName.call(doc, whitespaceTrimmedQuery);
  429.         addNodesToResults.call(this, result, result.length, result.item);
  430.     }
  431.  
  432.     function matchExactTagNames(doc)
  433.     {
  434.         if (!tagNameQuery)
  435.             return;
  436.         const result = doc.__proto__.getElementsByTagName.call(doc, tagNameQuery);
  437.         addNodesToResults.call(this, result, result.length, result.item);
  438.     }
  439.  
  440.     function matchExactAttributeNames(doc)
  441.     {
  442.         if (!attributeNameQuery)
  443.             return;
  444.         const result = doc.__proto__.querySelectorAll.call(doc, "[" + attributeNameQuery + "]");
  445.         addNodesToResults.call(this, result, result.length, result.item);
  446.     }
  447.  
  448.     function matchPartialTagNames(doc)
  449.     {
  450.         if (!tagNameQuery)
  451.             return;
  452.         const result = doc.__proto__.evaluate.call(doc, "//*[contains(name(), '" + escapedTagNameQuery + "')]", doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE);
  453.         addNodesToResults.call(this, result, result.snapshotLength, result.snapshotItem);
  454.     }
  455.  
  456.     function matchStartOfTagNames(doc)
  457.     {
  458.         if (!tagNameQuery)
  459.             return;
  460.         const result = doc.__proto__.evaluate.call(doc, "//*[starts-with(name(), '" + escapedTagNameQuery + "')]", doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE);
  461.         addNodesToResults.call(this, result, result.snapshotLength, result.snapshotItem);
  462.     }
  463.  
  464.     function matchPartialTagNamesAndAttributeValues(doc)
  465.     {
  466.         if (!tagNameQuery) {
  467.             matchPartialAttributeValues.call(this, doc);
  468.             return;
  469.         }
  470.  
  471.         const result = doc.__proto__.evaluate.call(doc, "//*[contains(name(), '" + escapedTagNameQuery + "') or contains(@*, '" + escapedQuery + "')]", doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE);
  472.         addNodesToResults.call(this, result, result.snapshotLength, result.snapshotItem);
  473.     }
  474.  
  475.     function matchPartialAttributeValues(doc)
  476.     {
  477.         const result = doc.__proto__.evaluate.call(doc, "//*[contains(@*, '" + escapedQuery + "')]", doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE);
  478.         addNodesToResults.call(this, result, result.snapshotLength, result.snapshotItem);
  479.     }
  480.  
  481.     function matchStyleSelector(doc)
  482.     {
  483.         const result = doc.__proto__.querySelectorAll.call(doc, whitespaceTrimmedQuery);
  484.         addNodesToResults.call(this, result, result.length, result.item);
  485.     }
  486.  
  487.     function matchPlainText(doc)
  488.     {
  489.         const result = doc.__proto__.evaluate.call(doc, "//text()[contains(., '" + escapedQuery + "')] | //comment()[contains(., '" + escapedQuery + "')]", doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE);
  490.         addNodesToResults.call(this, result, result.snapshotLength, result.snapshotItem);
  491.     }
  492.  
  493.     function matchXPathQuery(doc)
  494.     {
  495.         const result = doc.__proto__.evaluate.call(doc, whitespaceTrimmedQuery, doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE);
  496.         addNodesToResults.call(this, result, result.snapshotLength, result.snapshotItem);
  497.     }
  498.  
  499.     function finishedSearching()
  500.     {
  501.         // Remove the searchResultsProperty now that the search is finished.
  502.         for (var i = 0; i < InjectedScript._searchResults.length; ++i)
  503.             delete InjectedScript._searchResults[i][searchResultsProperty];
  504.     }
  505.  
  506.     const mainFrameDocument = inspectedWindow.document;
  507.     const searchDocuments = [mainFrameDocument];
  508.     var searchFunctions;
  509.     if (tagNameQuery && startTagFound && endTagFound)
  510.         searchFunctions = [matchExactTagNames, matchPlainText];
  511.     else if (tagNameQuery && startTagFound)
  512.         searchFunctions = [matchStartOfTagNames, matchPlainText];
  513.     else if (tagNameQuery && endTagFound) {
  514.         // FIXME: we should have a matchEndOfTagNames search function if endTagFound is true but not startTagFound.
  515.         // This requires ends-with() support in XPath, WebKit only supports starts-with() and contains().
  516.         searchFunctions = [matchPartialTagNames, matchPlainText];
  517.     } else if (whitespaceTrimmedQuery === "//*" || whitespaceTrimmedQuery === "*") {
  518.         // These queries will match every node. Matching everything isn't useful and can be slow for large pages,
  519.         // so limit the search functions list to plain text and attribute matching.
  520.         searchFunctions = [matchPartialAttributeValues, matchPlainText];
  521.     } else
  522.         searchFunctions = [matchExactItems, matchStyleSelector, matchPartialTagNamesAndAttributeValues, matchPlainText, matchXPathQuery];
  523.  
  524.     // Find all frames, iframes and object elements to search their documents.
  525.     const subdocumentResult = mainFrameDocument.querySelectorAll("iframe, frame, object");
  526.  
  527.     for (var i = 0; i < subdocumentResult.length; ++i) {
  528.         var element = subdocumentResult.item(i);
  529.         if (element.contentDocument)
  530.             searchDocuments.push(element.contentDocument);
  531.     }
  532.  
  533.     const panel = InjectedScript;
  534.     var documentIndex = 0;
  535.     var searchFunctionIndex = 0;
  536.     var chunkIntervalIdentifier = null;
  537.  
  538.     // Split up the work into chunks so we don't block the UI thread while processing.
  539.  
  540.     function processChunk()
  541.     {
  542.         var searchDocument = searchDocuments[documentIndex];
  543.         var searchFunction = searchFunctions[searchFunctionIndex];
  544.  
  545.         if (++searchFunctionIndex > searchFunctions.length) {
  546.             searchFunction = searchFunctions[0];
  547.             searchFunctionIndex = 0;
  548.  
  549.             if (++documentIndex > searchDocuments.length) {
  550.                 if (panel._currentSearchChunkIntervalIdentifier === chunkIntervalIdentifier)
  551.                     delete panel._currentSearchChunkIntervalIdentifier;
  552.                 clearInterval(chunkIntervalIdentifier);
  553.                 finishedSearching.call(panel);
  554.                 return false;
  555.             }
  556.  
  557.             searchDocument = searchDocuments[documentIndex];
  558.         }
  559.  
  560.         try {
  561.             searchFunction.call(panel, searchDocument);
  562.         } catch(err) {
  563.             // ignore any exceptions. the query might be malformed, but we allow that.
  564.         }
  565.         return true;
  566.     }
  567.  
  568.     if (runSynchronously)
  569.         while (processChunk()) {}
  570.     else {
  571.         processChunk();
  572.         chunkIntervalIdentifier = setInterval(processChunk, 25);
  573.         InjectedScript._currentSearchChunkIntervalIdentifier = chunkIntervalIdentifier;
  574.     }
  575.     return true;
  576. }
  577.  
  578. InjectedScript.searchCanceled = function()
  579. {
  580.     if (InjectedScript._searchResults) {
  581.         const searchResultsProperty = InjectedScript._includedInSearchResultsPropertyName;
  582.         for (var i = 0; i < this._searchResults.length; ++i) {
  583.             var node = this._searchResults[i];
  584.  
  585.             // Remove the searchResultsProperty since there might be an unfinished search.
  586.             delete node[searchResultsProperty];
  587.         }
  588.     }
  589.  
  590.     if (InjectedScript._currentSearchChunkIntervalIdentifier) {
  591.         clearInterval(InjectedScript._currentSearchChunkIntervalIdentifier);
  592.         delete InjectedScript._currentSearchChunkIntervalIdentifier;
  593.     }
  594.     InjectedScript._searchResults = [];
  595.     return true;
  596. }
  597.  
  598. InjectedScript.openInInspectedWindow = function(url)
  599. {
  600.     // Don't call window.open on wrapper - popup blocker mutes it.
  601.     // URIs should have no double quotes.
  602.     inspectedWindow.eval("window.open(\"" + url + "\")");
  603.     return true;
  604. }
  605.  
  606. InjectedScript.callFrames = function()
  607. {
  608.     var callFrame = InjectedScriptHost.currentCallFrame();
  609.     if (!callFrame)
  610.         return false;
  611.  
  612.     var result = [];
  613.     var depth = 0;
  614.     do {
  615.         result.push(new InjectedScript.CallFrameProxy(depth++, callFrame));
  616.         callFrame = callFrame.caller;
  617.     } while (callFrame);
  618.     return result;
  619. }
  620.  
  621. InjectedScript.evaluateInCallFrame = function(callFrameId, code, objectGroup)
  622. {
  623.     var callFrame = InjectedScript._callFrameForId(callFrameId);
  624.     if (!callFrame)
  625.         return false;
  626.     return InjectedScript._evaluateAndWrap(callFrame.evaluate, callFrame, code, objectGroup, true);
  627. }
  628.  
  629. InjectedScript._callFrameForId = function(id)
  630. {
  631.     var callFrame = InjectedScriptHost.currentCallFrame();
  632.     while (--id >= 0 && callFrame)
  633.         callFrame = callFrame.caller;
  634.     return callFrame;
  635. }
  636.  
  637. InjectedScript.clearConsoleMessages = function()
  638. {
  639.     InjectedScriptHost.clearConsoleMessages();
  640.     return true;
  641. }
  642.  
  643. InjectedScript._inspectObject = function(o)
  644. {
  645.     if (arguments.length === 0)
  646.         return;
  647.  
  648.     inspectedWindow.console.log(o);
  649.     if (InjectedScript._type(o) === "node") {
  650.         InjectedScriptHost.pushNodePathToFrontend(o, false, true);
  651.     } else {
  652.         switch (InjectedScript._describe(o)) {
  653.             case "Database":
  654.                 InjectedScriptHost.selectDatabase(o);
  655.                 break;
  656.             case "Storage":
  657.                 InjectedScriptHost.selectDOMStorage(o);
  658.                 break;
  659.         }
  660.     }
  661. }
  662.  
  663. InjectedScript._copy = function(o)
  664. {
  665.     if (InjectedScript._type(o) === "node") {
  666.         var nodeId = InjectedScriptHost.pushNodePathToFrontend(o, false, false);
  667.         InjectedScriptHost.copyNode(nodeId);
  668.     } else {
  669.         InjectedScriptHost.copyText(o);
  670.     }
  671. }
  672.  
  673. InjectedScript._ensureCommandLineAPIInstalled = function(evalFunction, evalObject)
  674. {
  675.     if (evalFunction.call(evalObject, "window.console._inspectorCommandLineAPI"))
  676.         return;
  677.     var inspectorCommandLineAPI = evalFunction.call(evalObject, "window.console._inspectorCommandLineAPI = { \n\
  678.         $: function() { return document.getElementById.apply(document, arguments) }, \n\
  679.         $$: function() { return document.querySelectorAll.apply(document, arguments) }, \n\
  680.         $x: function(xpath, context) \n\
  681.         { \n\
  682.             var nodes = []; \n\
  683.             try { \n\
  684.                 var doc = context || document; \n\
  685.                 var results = doc.evaluate(xpath, doc, null, XPathResult.ANY_TYPE, null); \n\
  686.                 var node; \n\
  687.                 while (node = results.iterateNext()) nodes.push(node); \n\
  688.             } catch (e) {} \n\
  689.             return nodes; \n\
  690.         }, \n\
  691.         dir: function() { return console.dir.apply(console, arguments) }, \n\
  692.         dirxml: function() { return console.dirxml.apply(console, arguments) }, \n\
  693.         keys: function(o) { var a = []; for (var k in o) a.push(k); return a; }, \n\
  694.         values: function(o) { var a = []; for (var k in o) a.push(o[k]); return a; }, \n\
  695.         profile: function() { return console.profile.apply(console, arguments) }, \n\
  696.         profileEnd: function() { return console.profileEnd.apply(console, arguments) }, \n\
  697.         _logEvent: function _inspectorCommandLineAPI_logEvent(e) { console.log(e.type, e); }, \n\
  698.         _allEventTypes: [\"mouse\", \"key\", \"load\", \"unload\", \"abort\", \"error\", \n\
  699.             \"select\", \"change\", \"submit\", \"reset\", \"focus\", \"blur\", \n\
  700.             \"resize\", \"scroll\"], \n\
  701.         _normalizeEventTypes: function(t) \n\
  702.         { \n\
  703.             if (typeof t === \"undefined\") \n\
  704.                 t = console._inspectorCommandLineAPI._allEventTypes; \n\
  705.             else if (typeof t === \"string\") \n\
  706.                 t = [t]; \n\
  707.             var i, te = []; \n\
  708.             for (i = 0; i < t.length; i++) { \n\
  709.                 if (t[i] === \"mouse\") \n\
  710.                     te.splice(0, 0, \"mousedown\", \"mouseup\", \"click\", \"dblclick\", \n\
  711.                         \"mousemove\", \"mouseover\", \"mouseout\"); \n\
  712.                 else if (t[i] === \"key\") \n\
  713.                     te.splice(0, 0, \"keydown\", \"keyup\", \"keypress\"); \n\
  714.                 else \n\
  715.                     te.push(t[i]); \n\
  716.             } \n\
  717.             return te; \n\
  718.         }, \n\
  719.         monitorEvents: function(o, t) \n\
  720.         { \n\
  721.             if (!o || !o.addEventListener || !o.removeEventListener) \n\
  722.                 return; \n\
  723.             t = console._inspectorCommandLineAPI._normalizeEventTypes(t); \n\
  724.             for (i = 0; i < t.length; i++) { \n\
  725.                 o.removeEventListener(t[i], console._inspectorCommandLineAPI._logEvent, false); \n\
  726.                 o.addEventListener(t[i], console._inspectorCommandLineAPI._logEvent, false); \n\
  727.             } \n\
  728.         }, \n\
  729.         unmonitorEvents: function(o, t) \n\
  730.         { \n\
  731.             if (!o || !o.removeEventListener) \n\
  732.                 return; \n\
  733.             t = console._inspectorCommandLineAPI._normalizeEventTypes(t); \n\
  734.             for (i = 0; i < t.length; i++) { \n\
  735.                 o.removeEventListener(t[i], console._inspectorCommandLineAPI._logEvent, false); \n\
  736.             } \n\
  737.         }, \n\
  738.         _inspectedNodes: [], \n\
  739.         get $0() { return console._inspectorCommandLineAPI._inspectedNodes[0] }, \n\
  740.         get $1() { return console._inspectorCommandLineAPI._inspectedNodes[1] }, \n\
  741.         get $2() { return console._inspectorCommandLineAPI._inspectedNodes[2] }, \n\
  742.         get $3() { return console._inspectorCommandLineAPI._inspectedNodes[3] }, \n\
  743.         get $4() { return console._inspectorCommandLineAPI._inspectedNodes[4] }, \n\
  744.     };");
  745.  
  746.     inspectorCommandLineAPI.clear = InjectedScript.clearConsoleMessages;
  747.     inspectorCommandLineAPI.inspect = InjectedScript._inspectObject;
  748.     inspectorCommandLineAPI.copy = InjectedScript._copy;
  749. }
  750.  
  751. InjectedScript._resolveObject = function(objectProxy)
  752. {
  753.     var object = InjectedScript._objectForId(objectProxy.objectId);
  754.     var path = objectProxy.path;
  755.     var protoDepth = objectProxy.protoDepth;
  756.  
  757.     // Follow the property path.
  758.     for (var i = 0; InjectedScript._isDefined(object) && path && i < path.length; ++i)
  759.         object = object[path[i]];
  760.  
  761.     return object;
  762. }
  763.  
  764. InjectedScript._nodeForId = function(nodeId)
  765. {
  766.     if (!nodeId)
  767.         return null;
  768.     return InjectedScriptHost.nodeForId(nodeId);
  769. }
  770.  
  771. InjectedScript._objectForId = function(objectId)
  772. {
  773.     // There are three types of object ids used:
  774.     // - numbers point to DOM Node via the InspectorDOMAgent mapping
  775.     // - strings point to console objects cached in InspectorController for lazy evaluation upon them
  776.     // - objects contain complex ids and are currently used for scoped objects
  777.     if (typeof objectId === "number") {
  778.         return InjectedScript._nodeForId(objectId);
  779.     } else if (typeof objectId === "string") {
  780.         return InjectedScript.unwrapObject(objectId);
  781.     } else if (typeof objectId === "object") {
  782.         var callFrame = InjectedScript._callFrameForId(objectId.callFrame);
  783.         if (objectId.thisObject)
  784.             return callFrame.thisObject;
  785.         else
  786.             return callFrame.scopeChain[objectId.chainIndex];
  787.     }
  788.     return objectId;
  789. }
  790.  
  791. InjectedScript.pushNodeToFrontend = function(objectProxy)
  792. {
  793.     var object = InjectedScript._resolveObject(objectProxy);
  794.     if (!object || InjectedScript._type(object) !== "node")
  795.         return false;
  796.     return InjectedScriptHost.pushNodePathToFrontend(object, false, false);
  797. }
  798.  
  799. InjectedScript.nodeByPath = function(path)
  800. {
  801.     // We make this call through the injected script only to get a nice
  802.     // callback for it.
  803.     return InjectedScriptHost.pushNodeByPathToFrontend(path.join(","));
  804. }
  805.  
  806. // Called from within InspectorController on the 'inspected page' side.
  807. InjectedScript.createProxyObject = function(object, objectId, abbreviate)
  808. {
  809.     var result = {};
  810.     result.injectedScriptId = injectedScriptId;
  811.     result.objectId = objectId;
  812.     result.type = InjectedScript._type(object);
  813.     if (result.type === "array")
  814.         result.propertyLength = object.length;
  815.  
  816.     var type = typeof object;
  817.     
  818.     result.hasChildren = (type === "object" && object !== null && (Object.getOwnPropertyNames(object).length || object.__proto__)) || type === "function";
  819.     try {
  820.         result.description = InjectedScript._describe(object, abbreviate);
  821.     } catch (e) {
  822.         result.errorText = e.toString();
  823.     }
  824.     return result;
  825. }
  826.  
  827. InjectedScript.evaluateOnSelf = function(funcBody, args)
  828. {
  829.     var func = window.eval("(" + funcBody + ")");
  830.     return func.apply(this, args || []);
  831. }
  832.  
  833. InjectedScript.CallFrameProxy = function(id, callFrame)
  834. {
  835.     this.id = id;
  836.     this.type = callFrame.type;
  837.     this.functionName = (this.type === "function" ? callFrame.functionName : "");
  838.     this.sourceID = callFrame.sourceID;
  839.     this.line = callFrame.line;
  840.     this.scopeChain = this._wrapScopeChain(callFrame);
  841. }
  842.  
  843. InjectedScript.CallFrameProxy.prototype = {
  844.     _wrapScopeChain: function(callFrame)
  845.     {
  846.         const GLOBAL_SCOPE = 0;
  847.         const LOCAL_SCOPE = 1;
  848.         const WITH_SCOPE = 2;
  849.         const CLOSURE_SCOPE = 3;
  850.         const CATCH_SCOPE = 4;
  851.     
  852.         var scopeChain = callFrame.scopeChain;
  853.         var scopeChainProxy = [];
  854.         var foundLocalScope = false;
  855.         for (var i = 0; i < scopeChain.length; i++) {
  856.             var scopeType = callFrame.scopeType(i);
  857.             var scopeObject = scopeChain[i];
  858.             var scopeObjectProxy = InjectedScript.createProxyObject(scopeObject, { callFrame: this.id, chainIndex: i }, true);
  859.  
  860.             switch(scopeType) {
  861.                 case LOCAL_SCOPE: {
  862.                     foundLocalScope = true;
  863.                     scopeObjectProxy.isLocal = true;
  864.                     scopeObjectProxy.thisObject = InjectedScript.createProxyObject(callFrame.thisObject, { callFrame: this.id, thisObject: true }, true);
  865.                     break;
  866.                 }
  867.                 case CLOSURE_SCOPE: {
  868.                     scopeObjectProxy.isClosure = true;
  869.                     break;
  870.                 }
  871.                 case WITH_SCOPE:
  872.                 case CATCH_SCOPE: {
  873.                     if (foundLocalScope && scopeObject instanceof inspectedWindow.Element)
  874.                         scopeObjectProxy.isElement = true;
  875.                     else if (foundLocalScope && scopeObject instanceof inspectedWindow.Document)
  876.                         scopeObjectProxy.isDocument = true;
  877.                     else
  878.                         scopeObjectProxy.isWithBlock = true;
  879.                     break;
  880.                 }
  881.             }
  882.             scopeChainProxy.push(scopeObjectProxy);
  883.         }
  884.         return scopeChainProxy;
  885.     }
  886. }
  887.  
  888. InjectedScript.executeSql = function(callId, databaseId, query)
  889. {
  890.     function successCallback(tx, result)
  891.     {
  892.         var rows = result.rows;
  893.         var result = [];
  894.         var length = rows.length;
  895.         for (var i = 0; i < length; ++i) {
  896.             var data = {};
  897.             result.push(data);
  898.             var row = rows.item(i);
  899.             for (var columnIdentifier in row) {
  900.                 // FIXME: (Bug 19439) We should specially format SQL NULL here
  901.                 // (which is represented by JavaScript null here, and turned
  902.                 // into the string "null" by the String() function).
  903.                 var text = row[columnIdentifier];
  904.                 data[columnIdentifier] = String(text);
  905.             }
  906.         }
  907.         InjectedScriptHost.reportDidDispatchOnInjectedScript(callId, result, false);
  908.     }
  909.  
  910.     function errorCallback(tx, error)
  911.     {
  912.         InjectedScriptHost.reportDidDispatchOnInjectedScript(callId, error, false);
  913.     }
  914.  
  915.     function queryTransaction(tx)
  916.     {
  917.         tx.executeSql(query, null, successCallback, errorCallback);
  918.     }
  919.  
  920.     var database = InjectedScriptHost.databaseForId(databaseId);
  921.     if (!database)
  922.         errorCallback(null, { code : 2 });  // Return as unexpected version.
  923.     database.transaction(queryTransaction, errorCallback);
  924.     return true;
  925. }
  926.  
  927. InjectedScript._isDefined = function(object)
  928. {
  929.     return object || InjectedScript._isHTMLAllCollection(object);
  930. }
  931.  
  932. InjectedScript._isHTMLAllCollection = function(object)
  933. {
  934.     // document.all is reported as undefined, but we still want to process it.
  935.     return (typeof object === "undefined") && inspectedWindow.HTMLAllCollection && object instanceof inspectedWindow.HTMLAllCollection;
  936. }
  937.  
  938. InjectedScript._type = function(obj)
  939. {
  940.     if (obj === null)
  941.         return "null";
  942.  
  943.     var type = typeof obj;
  944.     if (type !== "object" && type !== "function") {
  945.         // FIXME(33716): typeof document.all is always 'undefined'.
  946.         if (InjectedScript._isHTMLAllCollection(obj))
  947.             return "array";
  948.         return type;
  949.     }
  950.  
  951.     // If owning frame has navigated to somewhere else window properties will be undefined.
  952.     // In this case just return result of the typeof.
  953.     if (!inspectedWindow.document)
  954.         return type;
  955.  
  956.     if (obj instanceof inspectedWindow.Node)
  957.         return (obj.nodeType === undefined ? type : "node");
  958.     if (obj instanceof inspectedWindow.String)
  959.         return "string";
  960.     if (obj instanceof inspectedWindow.Array)
  961.         return "array";
  962.     if (obj instanceof inspectedWindow.Boolean)
  963.         return "boolean";
  964.     if (obj instanceof inspectedWindow.Number)
  965.         return "number";
  966.     if (obj instanceof inspectedWindow.Date)
  967.         return "date";
  968.     if (obj instanceof inspectedWindow.RegExp)
  969.         return "regexp";
  970.     if (obj instanceof inspectedWindow.NodeList)
  971.         return "array";
  972.     if (obj instanceof inspectedWindow.HTMLCollection)
  973.         return "array";
  974.     if (obj instanceof inspectedWindow.Error)
  975.         return "error";
  976.     return type;
  977. }
  978.  
  979. InjectedScript._describe = function(obj, abbreviated)
  980. {
  981.     var type1 = InjectedScript._type(obj);
  982.     var type2 = InjectedScript._className(obj);
  983.  
  984.     switch (type1) {
  985.     case "object":
  986.     case "node":
  987.     case "array":
  988.         return type2;
  989.     case "string":
  990.         if (!abbreviated)
  991.             return obj;
  992.         if (obj.length > 100)
  993.             return "\"" + obj.substring(0, 100) + "\u2026\"";
  994.         return "\"" + obj + "\"";
  995.     case "function":
  996.         var objectText = InjectedScript._toString(obj);
  997.         if (!/^function /.test(objectText))
  998.             objectText = (type2 == "object") ? type1 : type2;
  999.         else if (abbreviated)
  1000.             objectText = /.*/.exec(obj)[0].replace(/ +$/g, "");
  1001.         return objectText;
  1002.     default:
  1003.         return InjectedScript._toString(obj);
  1004.     }
  1005. }
  1006.  
  1007. InjectedScript._toString = function(obj)
  1008. {
  1009.     // We don't use String(obj) because inspectedWindow.String is undefined if owning frame navigated to another page.
  1010.     return "" + obj;
  1011. }
  1012.  
  1013. InjectedScript._className = function(obj)
  1014. {
  1015.     var str = inspectedWindow.Object ? inspectedWindow.Object.prototype.toString.call(obj) : InjectedScript._toString(obj);
  1016.     return str.replace(/^\[object (.*)\]$/i, "$1");
  1017. }
  1018.  
  1019. InjectedScript._escapeCharacters = function(str, chars)
  1020. {
  1021.     var foundChar = false;
  1022.     for (var i = 0; i < chars.length; ++i) {
  1023.         if (str.indexOf(chars.charAt(i)) !== -1) {
  1024.             foundChar = true;
  1025.             break;
  1026.         }
  1027.     }
  1028.  
  1029.     if (!foundChar)
  1030.         return str;
  1031.  
  1032.     var result = "";
  1033.     for (var i = 0; i < str.length; ++i) {
  1034.         if (chars.indexOf(str.charAt(i)) !== -1)
  1035.             result += "\\";
  1036.         result += str.charAt(i);
  1037.     }
  1038.  
  1039.     return result;
  1040. }
  1041.  
  1042. return InjectedScript;
  1043. });
  1044.