Home | History | Annotate | Download | only in front-end
      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) {
     30 
     31 var InjectedScript = {};
     32 
     33 InjectedScript.lastBoundObjectId = 1;
     34 InjectedScript.idToWrappedObject = {};
     35 InjectedScript.objectGroups = {};
     36 InjectedScript.wrapObject = function(object, objectGroupName)
     37 {
     38     var objectId;
     39     if (typeof object === "object" || typeof object === "function" ||
     40         (typeof object === "undefined" && object instanceof inspectedWindow.HTMLAllCollection)) { // FIXME(33716)
     41         var id = InjectedScript.lastBoundObjectId++;
     42         objectId = "object#" + id;
     43         InjectedScript.idToWrappedObject[objectId] = object;
     44 
     45         var group = InjectedScript.objectGroups[objectGroupName];
     46         if (!group) {
     47             group = [];
     48             InjectedScript.objectGroups[objectGroupName] = group;
     49         }
     50         group.push(objectId);
     51     }
     52     return InjectedScript.createProxyObject(object, objectId);
     53 };
     54 
     55 InjectedScript.unwrapObject = function(objectId) {
     56     return InjectedScript.idToWrappedObject[objectId];
     57 };
     58 
     59 InjectedScript.releaseWrapperObjectGroup = function(objectGroupName) {
     60     var group = InjectedScript.objectGroups[objectGroupName];
     61     if (!group)
     62         return;
     63     for (var i = 0; i < group.length; i++)
     64         delete InjectedScript.idToWrappedObject[group[i]];
     65     delete InjectedScript.objectGroups[objectGroupName];
     66 };
     67 
     68 // Called from within InspectorController on the 'inspected page' side.
     69 InjectedScript.reset = function()
     70 {
     71     InjectedScript._styles = {};
     72     InjectedScript._styleRules = {};
     73     InjectedScript._lastStyleId = 0;
     74     InjectedScript._lastStyleRuleId = 0;
     75     InjectedScript._searchResults = [];
     76     InjectedScript._includedInSearchResultsPropertyName = "__includedInInspectorSearchResults";
     77 }
     78 
     79 InjectedScript.reset();
     80 
     81 InjectedScript.dispatch = function(methodName, args, callId)
     82 {
     83     var argsArray = eval("(" + args + ")");
     84     if (callId)
     85         argsArray.splice(0, 0, callId);  // Methods that run asynchronously have a call back id parameter.
     86     var result = InjectedScript[methodName].apply(InjectedScript, argsArray);
     87     if (typeof result === "undefined") {
     88         InjectedScript._window().console.error("Web Inspector error: InjectedScript.%s returns undefined", methodName);
     89         result = null;
     90     }
     91     return result;
     92 }
     93 
     94 InjectedScript.getStyles = function(nodeId, authorOnly)
     95 {
     96     var node = InjectedScript._nodeForId(nodeId);
     97     if (!node)
     98         return false;
     99     var defaultView = node.ownerDocument.defaultView;
    100     var matchedRules = defaultView.getMatchedCSSRules(node, "", authorOnly);
    101     var matchedCSSRules = [];
    102     for (var i = 0; matchedRules && i < matchedRules.length; ++i)
    103         matchedCSSRules.push(InjectedScript._serializeRule(matchedRules[i]));
    104 
    105     var styleAttributes = {};
    106     var attributes = node.attributes;
    107     for (var i = 0; attributes && i < attributes.length; ++i) {
    108         if (attributes[i].style)
    109             styleAttributes[attributes[i].name] = InjectedScript._serializeStyle(attributes[i].style, true);
    110     }
    111     var result = {};
    112     result.inlineStyle = InjectedScript._serializeStyle(node.style, true);
    113     result.computedStyle = InjectedScript._serializeStyle(defaultView.getComputedStyle(node));
    114     result.matchedCSSRules = matchedCSSRules;
    115     result.styleAttributes = styleAttributes;
    116     return result;
    117 }
    118 
    119 InjectedScript.getComputedStyle = function(nodeId)
    120 {
    121     var node = InjectedScript._nodeForId(nodeId);
    122     if (!node)
    123         return false;
    124     return InjectedScript._serializeStyle(node.ownerDocument.defaultView.getComputedStyle(node));
    125 }
    126 
    127 InjectedScript.getInlineStyle = function(nodeId)
    128 {
    129     var node = InjectedScript._nodeForId(nodeId);
    130     if (!node)
    131         return false;
    132     return InjectedScript._serializeStyle(node.style, true);
    133 }
    134 
    135 InjectedScript.applyStyleText = function(styleId, styleText, propertyName)
    136 {
    137     var style = InjectedScript._styles[styleId];
    138     if (!style)
    139         return false;
    140 
    141     var styleTextLength = styleText.length;
    142 
    143     // Create a new element to parse the user input CSS.
    144     var parseElement = document.createElement("span");
    145     parseElement.setAttribute("style", styleText);
    146 
    147     var tempStyle = parseElement.style;
    148     if (tempStyle.length || !styleTextLength) {
    149         // The input was parsable or the user deleted everything, so remove the
    150         // original property from the real style declaration. If this represents
    151         // a shorthand remove all the longhand properties.
    152         if (style.getPropertyShorthand(propertyName)) {
    153             var longhandProperties = InjectedScript._getLonghandProperties(style, propertyName);
    154             for (var i = 0; i < longhandProperties.length; ++i)
    155                 style.removeProperty(longhandProperties[i]);
    156         } else
    157             style.removeProperty(propertyName);
    158     }
    159 
    160     // Notify caller that the property was successfully deleted.
    161     if (!styleTextLength)
    162         return [null, [propertyName]];
    163 
    164     if (!tempStyle.length)
    165         return false;
    166 
    167     // Iterate of the properties on the test element's style declaration and
    168     // add them to the real style declaration. We take care to move shorthands.
    169     var foundShorthands = {};
    170     var changedProperties = [];
    171     var uniqueProperties = InjectedScript._getUniqueStyleProperties(tempStyle);
    172     for (var i = 0; i < uniqueProperties.length; ++i) {
    173         var name = uniqueProperties[i];
    174         var shorthand = tempStyle.getPropertyShorthand(name);
    175 
    176         if (shorthand && shorthand in foundShorthands)
    177             continue;
    178 
    179         if (shorthand) {
    180             var value = InjectedScript._getShorthandValue(tempStyle, shorthand);
    181             var priority = InjectedScript._getShorthandPriority(tempStyle, shorthand);
    182             foundShorthands[shorthand] = true;
    183         } else {
    184             var value = tempStyle.getPropertyValue(name);
    185             var priority = tempStyle.getPropertyPriority(name);
    186         }
    187 
    188         // Set the property on the real style declaration.
    189         style.setProperty((shorthand || name), value, priority);
    190         changedProperties.push(shorthand || name);
    191     }
    192     return [InjectedScript._serializeStyle(style, true), changedProperties];
    193 }
    194 
    195 InjectedScript.setStyleText = function(style, cssText)
    196 {
    197     style.cssText = cssText;
    198     return true;
    199 }
    200 
    201 InjectedScript.toggleStyleEnabled = function(styleId, propertyName, disabled)
    202 {
    203     var style = InjectedScript._styles[styleId];
    204     if (!style)
    205         return false;
    206 
    207     if (disabled) {
    208         if (!style.__disabledPropertyValues || !style.__disabledPropertyPriorities) {
    209             style.__disabledProperties = {};
    210             style.__disabledPropertyValues = {};
    211             style.__disabledPropertyPriorities = {};
    212         }
    213 
    214         style.__disabledPropertyValues[propertyName] = style.getPropertyValue(propertyName);
    215         style.__disabledPropertyPriorities[propertyName] = style.getPropertyPriority(propertyName);
    216 
    217         if (style.getPropertyShorthand(propertyName)) {
    218             var longhandProperties = InjectedScript._getLonghandProperties(style, propertyName);
    219             for (var i = 0; i < longhandProperties.length; ++i) {
    220                 style.__disabledProperties[longhandProperties[i]] = true;
    221                 style.removeProperty(longhandProperties[i]);
    222             }
    223         } else {
    224             style.__disabledProperties[propertyName] = true;
    225             style.removeProperty(propertyName);
    226         }
    227     } else if (style.__disabledProperties && style.__disabledProperties[propertyName]) {
    228         var value = style.__disabledPropertyValues[propertyName];
    229         var priority = style.__disabledPropertyPriorities[propertyName];
    230 
    231         style.setProperty(propertyName, value, priority);
    232         delete style.__disabledProperties[propertyName];
    233         delete style.__disabledPropertyValues[propertyName];
    234         delete style.__disabledPropertyPriorities[propertyName];
    235     }
    236     return InjectedScript._serializeStyle(style, true);
    237 }
    238 
    239 InjectedScript.applyStyleRuleText = function(ruleId, newContent, selectedNodeId)
    240 {
    241     var rule = InjectedScript._styleRules[ruleId];
    242     if (!rule)
    243         return false;
    244 
    245     var selectedNode = InjectedScript._nodeForId(selectedNodeId);
    246 
    247     try {
    248         var stylesheet = rule.parentStyleSheet;
    249         stylesheet.addRule(newContent);
    250         var newRule = stylesheet.cssRules[stylesheet.cssRules.length - 1];
    251         newRule.style.cssText = rule.style.cssText;
    252 
    253         var parentRules = stylesheet.cssRules;
    254         for (var i = 0; i < parentRules.length; ++i) {
    255             if (parentRules[i] === rule) {
    256                 rule.parentStyleSheet.removeRule(i);
    257                 break;
    258             }
    259         }
    260 
    261         return [InjectedScript._serializeRule(newRule), InjectedScript._doesSelectorAffectNode(newContent, selectedNode)];
    262     } catch(e) {
    263         // Report invalid syntax.
    264         return false;
    265     }
    266 }
    267 
    268 InjectedScript.addStyleSelector = function(newContent, selectedNodeId)
    269 {
    270     var selectedNode = InjectedScript._nodeForId(selectedNodeId);
    271     if (!selectedNode)
    272         return false;
    273     var ownerDocument = selectedNode.ownerDocument;
    274 
    275     var stylesheet = ownerDocument.__stylesheet;
    276     if (!stylesheet) {
    277         var head = ownerDocument.head;
    278         var styleElement = ownerDocument.createElement("style");
    279         styleElement.type = "text/css";
    280         head.appendChild(styleElement);
    281         stylesheet = ownerDocument.styleSheets[ownerDocument.styleSheets.length - 1];
    282         ownerDocument.__stylesheet = stylesheet;
    283     }
    284 
    285     try {
    286         stylesheet.addRule(newContent);
    287     } catch (e) {
    288         // Invalid Syntax for a Selector
    289         return false;
    290     }
    291 
    292     var rule = stylesheet.cssRules[stylesheet.cssRules.length - 1];
    293     rule.__isViaInspector = true;
    294 
    295     return [ InjectedScript._serializeRule(rule), InjectedScript._doesSelectorAffectNode(newContent, selectedNode) ];
    296 }
    297 
    298 InjectedScript._doesSelectorAffectNode = function(selectorText, node)
    299 {
    300     if (!node)
    301         return false;
    302     var nodes = node.ownerDocument.querySelectorAll(selectorText);
    303     for (var i = 0; i < nodes.length; ++i) {
    304         if (nodes[i] === node) {
    305             return true;
    306         }
    307     }
    308     return false;
    309 }
    310 
    311 InjectedScript.setStyleProperty = function(styleId, name, value)
    312 {
    313     var style = InjectedScript._styles[styleId];
    314     if (!style)
    315         return false;
    316 
    317     style.setProperty(name, value, "");
    318     return true;
    319 }
    320 
    321 InjectedScript._serializeRule = function(rule)
    322 {
    323     var parentStyleSheet = rule.parentStyleSheet;
    324 
    325     var ruleValue = {};
    326     ruleValue.selectorText = rule.selectorText;
    327     if (parentStyleSheet) {
    328         ruleValue.parentStyleSheet = {};
    329         ruleValue.parentStyleSheet.href = parentStyleSheet.href;
    330     }
    331     ruleValue.isUserAgent = parentStyleSheet && !parentStyleSheet.ownerNode && !parentStyleSheet.href;
    332     ruleValue.isUser = parentStyleSheet && parentStyleSheet.ownerNode && parentStyleSheet.ownerNode.nodeName == "#document";
    333     ruleValue.isViaInspector = !!rule.__isViaInspector;
    334 
    335     // Bind editable scripts only.
    336     var doBind = !ruleValue.isUserAgent && !ruleValue.isUser;
    337     ruleValue.style = InjectedScript._serializeStyle(rule.style, doBind);
    338 
    339     if (doBind) {
    340         if (!rule.id) {
    341             rule.id = InjectedScript._lastStyleRuleId++;
    342             InjectedScript._styleRules[rule.id] = rule;
    343         }
    344         ruleValue.id = rule.id;
    345         ruleValue.injectedScriptId = injectedScriptId;
    346     }
    347     return ruleValue;
    348 }
    349 
    350 InjectedScript._serializeStyle = function(style, doBind)
    351 {
    352     var result = {};
    353     result.width = style.width;
    354     result.height = style.height;
    355     result.__disabledProperties = style.__disabledProperties;
    356     result.__disabledPropertyValues = style.__disabledPropertyValues;
    357     result.__disabledPropertyPriorities = style.__disabledPropertyPriorities;
    358     result.properties = [];
    359     result.shorthandValues = {};
    360     var foundShorthands = {};
    361     for (var i = 0; i < style.length; ++i) {
    362         var property = {};
    363         var name = style[i];
    364         property.name = name;
    365         property.priority = style.getPropertyPriority(name);
    366         property.implicit = style.isPropertyImplicit(name);
    367         var shorthand =  style.getPropertyShorthand(name);
    368         property.shorthand = shorthand;
    369         if (shorthand && !(shorthand in foundShorthands)) {
    370             foundShorthands[shorthand] = true;
    371             result.shorthandValues[shorthand] = InjectedScript._getShorthandValue(style, shorthand);
    372         }
    373         property.value = style.getPropertyValue(name);
    374         result.properties.push(property);
    375     }
    376     result.uniqueStyleProperties = InjectedScript._getUniqueStyleProperties(style);
    377 
    378     if (doBind) {
    379         if (!style.id) {
    380             style.id = InjectedScript._lastStyleId++;
    381             InjectedScript._styles[style.id] = style;
    382         }
    383         result.id = style.id;
    384         result.injectedScriptId = injectedScriptId;
    385     }
    386     return result;
    387 }
    388 
    389 InjectedScript._getUniqueStyleProperties = function(style)
    390 {
    391     var properties = [];
    392     var foundProperties = {};
    393 
    394     for (var i = 0; i < style.length; ++i) {
    395         var property = style[i];
    396         if (property in foundProperties)
    397             continue;
    398         foundProperties[property] = true;
    399         properties.push(property);
    400     }
    401 
    402     return properties;
    403 }
    404 
    405 
    406 InjectedScript._getLonghandProperties = function(style, shorthandProperty)
    407 {
    408     var properties = [];
    409     var foundProperties = {};
    410 
    411     for (var i = 0; i < style.length; ++i) {
    412         var individualProperty = style[i];
    413         if (individualProperty in foundProperties || style.getPropertyShorthand(individualProperty) !== shorthandProperty)
    414             continue;
    415         foundProperties[individualProperty] = true;
    416         properties.push(individualProperty);
    417     }
    418 
    419     return properties;
    420 }
    421 
    422 InjectedScript._getShorthandValue = function(style, shorthandProperty)
    423 {
    424     var value = style.getPropertyValue(shorthandProperty);
    425     if (!value) {
    426         // Some shorthands (like border) return a null value, so compute a shorthand value.
    427         // FIXME: remove this when http://bugs.webkit.org/show_bug.cgi?id=15823 is fixed.
    428 
    429         var foundProperties = {};
    430         for (var i = 0; i < style.length; ++i) {
    431             var individualProperty = style[i];
    432             if (individualProperty in foundProperties || style.getPropertyShorthand(individualProperty) !== shorthandProperty)
    433                 continue;
    434 
    435             var individualValue = style.getPropertyValue(individualProperty);
    436             if (style.isPropertyImplicit(individualProperty) || individualValue === "initial")
    437                 continue;
    438 
    439             foundProperties[individualProperty] = true;
    440 
    441             if (!value)
    442                 value = "";
    443             else if (value.length)
    444                 value += " ";
    445             value += individualValue;
    446         }
    447     }
    448     return value;
    449 }
    450 
    451 InjectedScript._getShorthandPriority = function(style, shorthandProperty)
    452 {
    453     var priority = style.getPropertyPriority(shorthandProperty);
    454     if (!priority) {
    455         for (var i = 0; i < style.length; ++i) {
    456             var individualProperty = style[i];
    457             if (style.getPropertyShorthand(individualProperty) !== shorthandProperty)
    458                 continue;
    459             priority = style.getPropertyPriority(individualProperty);
    460             break;
    461         }
    462     }
    463     return priority;
    464 }
    465 
    466 InjectedScript.getPrototypes = function(nodeId)
    467 {
    468     var node = InjectedScript._nodeForId(nodeId);
    469     if (!node)
    470         return false;
    471 
    472     var result = [];
    473     for (var prototype = node; prototype; prototype = prototype.__proto__) {
    474         var title = InjectedScript._describe(prototype, true);
    475         if (title.match(/Prototype$/)) {
    476             title = title.replace(/Prototype$/, "");
    477         }
    478         result.push(title);
    479     }
    480     return result;
    481 }
    482 
    483 InjectedScript.getProperties = function(objectProxy, ignoreHasOwnProperty, abbreviate)
    484 {
    485     var object = InjectedScript._resolveObject(objectProxy);
    486     if (!InjectedScript._isDefined(object))
    487         return false;
    488 
    489     var properties = [];
    490 
    491     // Go over properties, prepare results.
    492     for (var propertyName in object) {
    493         if (!ignoreHasOwnProperty && "hasOwnProperty" in object && !object.hasOwnProperty(propertyName))
    494             continue;
    495 
    496         var property = {};
    497         property.name = propertyName;
    498         property.parentObjectProxy = objectProxy;
    499         var isGetter = object["__lookupGetter__"] && object.__lookupGetter__(propertyName);
    500         if (!property.isGetter) {
    501             var childObject = object[propertyName];
    502             var childObjectProxy = new InjectedScript.createProxyObject(childObject, objectProxy.objectId, abbreviate);
    503             childObjectProxy.path = objectProxy.path ? objectProxy.path.slice() : [];
    504             childObjectProxy.path.push(propertyName);
    505             childObjectProxy.protoDepth = objectProxy.protoDepth || 0;
    506             property.value = childObjectProxy;
    507         } else {
    508             // FIXME: this should show something like "getter" (bug 16734).
    509             property.value = { description: "\u2014" }; // em dash
    510             property.isGetter = true;
    511         }
    512         properties.push(property);
    513     }
    514     return properties;
    515 }
    516 
    517 InjectedScript.setPropertyValue = function(objectProxy, propertyName, expression)
    518 {
    519     var object = InjectedScript._resolveObject(objectProxy);
    520     if (!InjectedScript._isDefined(object))
    521         return false;
    522 
    523     var expressionLength = expression.length;
    524     if (!expressionLength) {
    525         delete object[propertyName];
    526         return !(propertyName in object);
    527     }
    528 
    529     try {
    530         // Surround the expression in parenthesis so the result of the eval is the result
    531         // of the whole expression not the last potential sub-expression.
    532 
    533         // There is a regression introduced here: eval is now happening against global object,
    534         // not call frame while on a breakpoint.
    535         // TODO: bring evaluation against call frame back.
    536         var result = InjectedScript._window().eval("(" + expression + ")");
    537         // Store the result in the property.
    538         object[propertyName] = result;
    539         return true;
    540     } catch(e) {
    541         try {
    542             var result = InjectedScript._window().eval("\"" + InjectedScript._escapeCharacters(expression, "\"") + "\"");
    543             object[propertyName] = result;
    544             return true;
    545         } catch(e) {
    546             return false;
    547         }
    548     }
    549 }
    550 
    551 InjectedScript.getNodePropertyValue = function(nodeId, propertyName)
    552 {
    553     var node = InjectedScript._nodeForId(nodeId);
    554     if (!node)
    555         return false;
    556     var result = node[propertyName];
    557     return result !== undefined ? result : false;
    558 }
    559 
    560 InjectedScript.setOuterHTML = function(nodeId, value, expanded)
    561 {
    562     var node = InjectedScript._nodeForId(nodeId);
    563     if (!node)
    564         return false;
    565 
    566     var parent = node.parentNode;
    567     var prevSibling = node.previousSibling;
    568     node.outerHTML = value;
    569     var newNode = prevSibling ? prevSibling.nextSibling : parent.firstChild;
    570 
    571     return InjectedScriptHost.pushNodePathToFrontend(newNode, expanded, false);
    572 }
    573 
    574 InjectedScript._getPropertyNames = function(object, resultSet)
    575 {
    576     if (Object.getOwnPropertyNames) {
    577         for (var o = object; o; o = o.__proto__) {
    578             try {
    579                 var names = Object.getOwnPropertyNames(o);
    580                 for (var i = 0; i < names.length; ++i)
    581                     resultSet[names[i]] = true;
    582             } catch (e) {
    583             }
    584         }
    585     } else {
    586         // Chromium doesn't support getOwnPropertyNames yet.
    587         for (var name in object)
    588             resultSet[name] = true;
    589     }
    590 }
    591 
    592 InjectedScript.getCompletions = function(expression, includeInspectorCommandLineAPI, callFrameId)
    593 {
    594     var props = {};
    595     try {
    596         var expressionResult;
    597         // Evaluate on call frame if call frame id is available.
    598         if (typeof callFrameId === "number") {
    599             var callFrame = InjectedScript._callFrameForId(callFrameId);
    600             if (!callFrame)
    601                 return props;
    602             if (expression)
    603                 expressionResult = InjectedScript._evaluateOn(callFrame.evaluate, callFrame, expression);
    604             else {
    605                 // Evaluate into properties in scope of the selected call frame.
    606                 var scopeChain = callFrame.scopeChain;
    607                 for (var i = 0; i < scopeChain.length; ++i)
    608                     InjectedScript._getPropertyNames(scopeChain[i], props);
    609             }
    610         } else {
    611             if (!expression)
    612                 expression = "this";
    613             expressionResult = InjectedScript._evaluateOn(InjectedScript._window().eval, InjectedScript._window(), expression);
    614         }
    615         if (typeof expressionResult == "object")
    616             InjectedScript._getPropertyNames(expressionResult, props);
    617         if (includeInspectorCommandLineAPI)
    618             for (var prop in InjectedScript._window().console._inspectorCommandLineAPI)
    619                 if (prop.charAt(0) !== '_')
    620                     props[prop] = true;
    621     } catch(e) {
    622     }
    623     return props;
    624 }
    625 
    626 InjectedScript.evaluate = function(expression, objectGroup)
    627 {
    628     return InjectedScript._evaluateAndWrap(InjectedScript._window().eval, InjectedScript._window(), expression, objectGroup);
    629 }
    630 
    631 InjectedScript._evaluateAndWrap = function(evalFunction, object, expression, objectGroup)
    632 {
    633     var result = {};
    634     try {
    635         result.value = InjectedScript.wrapObject(InjectedScript._evaluateOn(evalFunction, object, expression), objectGroup);
    636 
    637         // Handle error that might have happened while describing result.
    638         if (result.value.errorText) {
    639             result.value = result.value.errorText;
    640             result.isException = true;
    641         }
    642     } catch (e) {
    643         result.value = e.toString();
    644         result.isException = true;
    645     }
    646     return result;
    647 }
    648 
    649 InjectedScript._evaluateOn = function(evalFunction, object, expression)
    650 {
    651     InjectedScript._ensureCommandLineAPIInstalled(evalFunction, object);
    652     // Surround the expression in with statements to inject our command line API so that
    653     // the window object properties still take more precedent than our API functions.
    654     expression = "with (window.console._inspectorCommandLineAPI) { with (window) {\n" + expression + "\n} }";
    655     var value = evalFunction.call(object, expression);
    656 
    657     // When evaluating on call frame error is not thrown, but returned as a value.
    658     if (InjectedScript._type(value) === "error")
    659         throw value.toString();
    660 
    661     return value;
    662 }
    663 
    664 InjectedScript.addInspectedNode = function(nodeId)
    665 {
    666     var node = InjectedScript._nodeForId(nodeId);
    667     if (!node)
    668         return false;
    669 
    670     InjectedScript._ensureCommandLineAPIInstalled(InjectedScript._window().eval, InjectedScript._window());
    671     var inspectedNodes = InjectedScript._window().console._inspectorCommandLineAPI._inspectedNodes;
    672     inspectedNodes.unshift(node);
    673     if (inspectedNodes.length >= 5)
    674         inspectedNodes.pop();
    675     return true;
    676 }
    677 
    678 InjectedScript.performSearch = function(whitespaceTrimmedQuery)
    679 {
    680     var tagNameQuery = whitespaceTrimmedQuery;
    681     var attributeNameQuery = whitespaceTrimmedQuery;
    682     var startTagFound = (tagNameQuery.indexOf("<") === 0);
    683     var endTagFound = (tagNameQuery.lastIndexOf(">") === (tagNameQuery.length - 1));
    684 
    685     if (startTagFound || endTagFound) {
    686         var tagNameQueryLength = tagNameQuery.length;
    687         tagNameQuery = tagNameQuery.substring((startTagFound ? 1 : 0), (endTagFound ? (tagNameQueryLength - 1) : tagNameQueryLength));
    688     }
    689 
    690     // Check the tagNameQuery is it is a possibly valid tag name.
    691     if (!/^[a-zA-Z0-9\-_:]+$/.test(tagNameQuery))
    692         tagNameQuery = null;
    693 
    694     // Check the attributeNameQuery is it is a possibly valid tag name.
    695     if (!/^[a-zA-Z0-9\-_:]+$/.test(attributeNameQuery))
    696         attributeNameQuery = null;
    697 
    698     const escapedQuery = InjectedScript._escapeCharacters(whitespaceTrimmedQuery, "'");
    699     const escapedTagNameQuery = (tagNameQuery ?  InjectedScript._escapeCharacters(tagNameQuery, "'") : null);
    700     const escapedWhitespaceTrimmedQuery = InjectedScript._escapeCharacters(whitespaceTrimmedQuery, "'");
    701     const searchResultsProperty = InjectedScript._includedInSearchResultsPropertyName;
    702 
    703     function addNodesToResults(nodes, length, getItem)
    704     {
    705         if (!length)
    706             return;
    707 
    708         var nodeIds = [];
    709         for (var i = 0; i < length; ++i) {
    710             var node = getItem.call(nodes, i);
    711             // Skip this node if it already has the property.
    712             if (searchResultsProperty in node)
    713                 continue;
    714 
    715             if (!InjectedScript._searchResults.length) {
    716                 InjectedScript._currentSearchResultIndex = 0;
    717             }
    718 
    719             node[searchResultsProperty] = true;
    720             InjectedScript._searchResults.push(node);
    721             var nodeId = InjectedScriptHost.pushNodePathToFrontend(node, false, false);
    722             nodeIds.push(nodeId);
    723         }
    724         InjectedScriptHost.addNodesToSearchResult(nodeIds.join(","));
    725     }
    726 
    727     function matchExactItems(doc)
    728     {
    729         matchExactId.call(this, doc);
    730         matchExactClassNames.call(this, doc);
    731         matchExactTagNames.call(this, doc);
    732         matchExactAttributeNames.call(this, doc);
    733     }
    734 
    735     function matchExactId(doc)
    736     {
    737         const result = doc.__proto__.getElementById.call(doc, whitespaceTrimmedQuery);
    738         addNodesToResults.call(this, result, (result ? 1 : 0), function() { return this });
    739     }
    740 
    741     function matchExactClassNames(doc)
    742     {
    743         const result = doc.__proto__.getElementsByClassName.call(doc, whitespaceTrimmedQuery);
    744         addNodesToResults.call(this, result, result.length, result.item);
    745     }
    746 
    747     function matchExactTagNames(doc)
    748     {
    749         if (!tagNameQuery)
    750             return;
    751         const result = doc.__proto__.getElementsByTagName.call(doc, tagNameQuery);
    752         addNodesToResults.call(this, result, result.length, result.item);
    753     }
    754 
    755     function matchExactAttributeNames(doc)
    756     {
    757         if (!attributeNameQuery)
    758             return;
    759         const result = doc.__proto__.querySelectorAll.call(doc, "[" + attributeNameQuery + "]");
    760         addNodesToResults.call(this, result, result.length, result.item);
    761     }
    762 
    763     function matchPartialTagNames(doc)
    764     {
    765         if (!tagNameQuery)
    766             return;
    767         const result = doc.__proto__.evaluate.call(doc, "//*[contains(name(), '" + escapedTagNameQuery + "')]", doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE);
    768         addNodesToResults.call(this, result, result.snapshotLength, result.snapshotItem);
    769     }
    770 
    771     function matchStartOfTagNames(doc)
    772     {
    773         if (!tagNameQuery)
    774             return;
    775         const result = doc.__proto__.evaluate.call(doc, "//*[starts-with(name(), '" + escapedTagNameQuery + "')]", doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE);
    776         addNodesToResults.call(this, result, result.snapshotLength, result.snapshotItem);
    777     }
    778 
    779     function matchPartialTagNamesAndAttributeValues(doc)
    780     {
    781         if (!tagNameQuery) {
    782             matchPartialAttributeValues.call(this, doc);
    783             return;
    784         }
    785 
    786         const result = doc.__proto__.evaluate.call(doc, "//*[contains(name(), '" + escapedTagNameQuery + "') or contains(@*, '" + escapedQuery + "')]", doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE);
    787         addNodesToResults.call(this, result, result.snapshotLength, result.snapshotItem);
    788     }
    789 
    790     function matchPartialAttributeValues(doc)
    791     {
    792         const result = doc.__proto__.evaluate.call(doc, "//*[contains(@*, '" + escapedQuery + "')]", doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE);
    793         addNodesToResults.call(this, result, result.snapshotLength, result.snapshotItem);
    794     }
    795 
    796     function matchStyleSelector(doc)
    797     {
    798         const result = doc.__proto__.querySelectorAll.call(doc, whitespaceTrimmedQuery);
    799         addNodesToResults.call(this, result, result.length, result.item);
    800     }
    801 
    802     function matchPlainText(doc)
    803     {
    804         const result = doc.__proto__.evaluate.call(doc, "//text()[contains(., '" + escapedQuery + "')] | //comment()[contains(., '" + escapedQuery + "')]", doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE);
    805         addNodesToResults.call(this, result, result.snapshotLength, result.snapshotItem);
    806     }
    807 
    808     function matchXPathQuery(doc)
    809     {
    810         const result = doc.__proto__.evaluate.call(doc, whitespaceTrimmedQuery, doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE);
    811         addNodesToResults.call(this, result, result.snapshotLength, result.snapshotItem);
    812     }
    813 
    814     function finishedSearching()
    815     {
    816         // Remove the searchResultsProperty now that the search is finished.
    817         for (var i = 0; i < InjectedScript._searchResults.length; ++i)
    818             delete InjectedScript._searchResults[i][searchResultsProperty];
    819     }
    820 
    821     const mainFrameDocument = InjectedScript._window().document;
    822     const searchDocuments = [mainFrameDocument];
    823     var searchFunctions;
    824     if (tagNameQuery && startTagFound && endTagFound)
    825         searchFunctions = [matchExactTagNames, matchPlainText];
    826     else if (tagNameQuery && startTagFound)
    827         searchFunctions = [matchStartOfTagNames, matchPlainText];
    828     else if (tagNameQuery && endTagFound) {
    829         // FIXME: we should have a matchEndOfTagNames search function if endTagFound is true but not startTagFound.
    830         // This requires ends-with() support in XPath, WebKit only supports starts-with() and contains().
    831         searchFunctions = [matchPartialTagNames, matchPlainText];
    832     } else if (whitespaceTrimmedQuery === "//*" || whitespaceTrimmedQuery === "*") {
    833         // These queries will match every node. Matching everything isn't useful and can be slow for large pages,
    834         // so limit the search functions list to plain text and attribute matching.
    835         searchFunctions = [matchPartialAttributeValues, matchPlainText];
    836     } else
    837         searchFunctions = [matchExactItems, matchStyleSelector, matchPartialTagNamesAndAttributeValues, matchPlainText, matchXPathQuery];
    838 
    839     // Find all frames, iframes and object elements to search their documents.
    840     const querySelectorAllFunction = InjectedScript._window().Document.prototype.querySelectorAll;
    841     const subdocumentResult = querySelectorAllFunction.call(mainFrameDocument, "iframe, frame, object");
    842 
    843     for (var i = 0; i < subdocumentResult.length; ++i) {
    844         var element = subdocumentResult.item(i);
    845         if (element.contentDocument)
    846             searchDocuments.push(element.contentDocument);
    847     }
    848 
    849     const panel = InjectedScript;
    850     var documentIndex = 0;
    851     var searchFunctionIndex = 0;
    852     var chunkIntervalIdentifier = null;
    853 
    854     // Split up the work into chunks so we don't block the UI thread while processing.
    855 
    856     function processChunk()
    857     {
    858         var searchDocument = searchDocuments[documentIndex];
    859         var searchFunction = searchFunctions[searchFunctionIndex];
    860 
    861         if (++searchFunctionIndex > searchFunctions.length) {
    862             searchFunction = searchFunctions[0];
    863             searchFunctionIndex = 0;
    864 
    865             if (++documentIndex > searchDocuments.length) {
    866                 if (panel._currentSearchChunkIntervalIdentifier === chunkIntervalIdentifier)
    867                     delete panel._currentSearchChunkIntervalIdentifier;
    868                 clearInterval(chunkIntervalIdentifier);
    869                 finishedSearching.call(panel);
    870                 return;
    871             }
    872 
    873             searchDocument = searchDocuments[documentIndex];
    874         }
    875 
    876         if (!searchDocument || !searchFunction)
    877             return;
    878 
    879         try {
    880             searchFunction.call(panel, searchDocument);
    881         } catch(err) {
    882             // ignore any exceptions. the query might be malformed, but we allow that.
    883         }
    884     }
    885 
    886     processChunk();
    887 
    888     chunkIntervalIdentifier = setInterval(processChunk, 25);
    889     InjectedScript._currentSearchChunkIntervalIdentifier = chunkIntervalIdentifier;
    890     return true;
    891 }
    892 
    893 InjectedScript.searchCanceled = function()
    894 {
    895     if (InjectedScript._searchResults) {
    896         const searchResultsProperty = InjectedScript._includedInSearchResultsPropertyName;
    897         for (var i = 0; i < this._searchResults.length; ++i) {
    898             var node = this._searchResults[i];
    899 
    900             // Remove the searchResultsProperty since there might be an unfinished search.
    901             delete node[searchResultsProperty];
    902         }
    903     }
    904 
    905     if (InjectedScript._currentSearchChunkIntervalIdentifier) {
    906         clearInterval(InjectedScript._currentSearchChunkIntervalIdentifier);
    907         delete InjectedScript._currentSearchChunkIntervalIdentifier;
    908     }
    909     InjectedScript._searchResults = [];
    910     return true;
    911 }
    912 
    913 InjectedScript.openInInspectedWindow = function(url)
    914 {
    915     // Don't call window.open on wrapper - popup blocker mutes it.
    916     // URIs should have no double quotes.
    917     InjectedScript._window().eval("window.open(\"" + url + "\")");
    918     return true;
    919 }
    920 
    921 InjectedScript.callFrames = function()
    922 {
    923     var callFrame = InjectedScriptHost.currentCallFrame();
    924     if (!callFrame)
    925         return false;
    926 
    927     var result = [];
    928     var depth = 0;
    929     do {
    930         result.push(new InjectedScript.CallFrameProxy(depth++, callFrame));
    931         callFrame = callFrame.caller;
    932     } while (callFrame);
    933     return result;
    934 }
    935 
    936 InjectedScript.evaluateInCallFrame = function(callFrameId, code, objectGroup)
    937 {
    938     var callFrame = InjectedScript._callFrameForId(callFrameId);
    939     if (!callFrame)
    940         return false;
    941     return InjectedScript._evaluateAndWrap(callFrame.evaluate, callFrame, code, objectGroup);
    942 }
    943 
    944 InjectedScript._callFrameForId = function(id)
    945 {
    946     var callFrame = InjectedScriptHost.currentCallFrame();
    947     while (--id >= 0 && callFrame)
    948         callFrame = callFrame.caller;
    949     return callFrame;
    950 }
    951 
    952 InjectedScript.clearConsoleMessages = function()
    953 {
    954     InjectedScriptHost.clearConsoleMessages();
    955     return true;
    956 }
    957 
    958 InjectedScript._inspectObject = function(o)
    959 {
    960     if (arguments.length === 0)
    961         return;
    962 
    963     inspectedWindow.console.log(o);
    964     if (InjectedScript._type(o) === "node") {
    965         InjectedScriptHost.pushNodePathToFrontend(o, false, true);
    966     } else {
    967         switch (InjectedScript._describe(o)) {
    968             case "Database":
    969                 InjectedScriptHost.selectDatabase(o);
    970                 break;
    971             case "Storage":
    972                 InjectedScriptHost.selectDOMStorage(o);
    973                 break;
    974         }
    975     }
    976 }
    977 
    978 InjectedScript._copy = function(o)
    979 {
    980     if (InjectedScript._type(o) === "node") {
    981         var nodeId = InjectedScriptHost.pushNodePathToFrontend(o, false, false);
    982         InjectedScriptHost.copyNode(nodeId);
    983     } else {
    984         InjectedScriptHost.copyText(o);
    985     }
    986 }
    987 
    988 InjectedScript._ensureCommandLineAPIInstalled = function(evalFunction, evalObject)
    989 {
    990     if (evalFunction.call(evalObject, "window.console._inspectorCommandLineAPI"))
    991         return;
    992     var inspectorCommandLineAPI = evalFunction.call(evalObject, "window.console._inspectorCommandLineAPI = { \n\
    993         $: function() { return document.getElementById.apply(document, arguments) }, \n\
    994         $$: function() { return document.querySelectorAll.apply(document, arguments) }, \n\
    995         $x: function(xpath, context) \n\
    996         { \n\
    997             var nodes = []; \n\
    998             try { \n\
    999                 var doc = context || document; \n\
   1000                 var results = doc.evaluate(xpath, doc, null, XPathResult.ANY_TYPE, null); \n\
   1001                 var node; \n\
   1002                 while (node = results.iterateNext()) nodes.push(node); \n\
   1003             } catch (e) {} \n\
   1004             return nodes; \n\
   1005         }, \n\
   1006         dir: function() { return console.dir.apply(console, arguments) }, \n\
   1007         dirxml: function() { return console.dirxml.apply(console, arguments) }, \n\
   1008         keys: function(o) { var a = []; for (var k in o) a.push(k); return a; }, \n\
   1009         values: function(o) { var a = []; for (var k in o) a.push(o[k]); return a; }, \n\
   1010         profile: function() { return console.profile.apply(console, arguments) }, \n\
   1011         profileEnd: function() { return console.profileEnd.apply(console, arguments) }, \n\
   1012         _logEvent: function _inspectorCommandLineAPI_logEvent(e) { console.log(e.type, e); }, \n\
   1013         _allEventTypes: [\"mouse\", \"key\", \"load\", \"unload\", \"abort\", \"error\", \n\
   1014             \"select\", \"change\", \"submit\", \"reset\", \"focus\", \"blur\", \n\
   1015             \"resize\", \"scroll\"], \n\
   1016         _normalizeEventTypes: function(t) \n\
   1017         { \n\
   1018             if (typeof t === \"undefined\") \n\
   1019                 t = console._inspectorCommandLineAPI._allEventTypes; \n\
   1020             else if (typeof t === \"string\") \n\
   1021                 t = [t]; \n\
   1022             var i, te = []; \n\
   1023             for (i = 0; i < t.length; i++) { \n\
   1024                 if (t[i] === \"mouse\") \n\
   1025                     te.splice(0, 0, \"mousedown\", \"mouseup\", \"click\", \"dblclick\", \n\
   1026                         \"mousemove\", \"mouseover\", \"mouseout\"); \n\
   1027                 else if (t[i] === \"key\") \n\
   1028                     te.splice(0, 0, \"keydown\", \"keyup\", \"keypress\"); \n\
   1029                 else \n\
   1030                     te.push(t[i]); \n\
   1031             } \n\
   1032             return te; \n\
   1033         }, \n\
   1034         monitorEvents: function(o, t) \n\
   1035         { \n\
   1036             if (!o || !o.addEventListener || !o.removeEventListener) \n\
   1037                 return; \n\
   1038             t = console._inspectorCommandLineAPI._normalizeEventTypes(t); \n\
   1039             for (i = 0; i < t.length; i++) { \n\
   1040                 o.removeEventListener(t[i], console._inspectorCommandLineAPI._logEvent, false); \n\
   1041                 o.addEventListener(t[i], console._inspectorCommandLineAPI._logEvent, false); \n\
   1042             } \n\
   1043         }, \n\
   1044         unmonitorEvents: function(o, t) \n\
   1045         { \n\
   1046             if (!o || !o.removeEventListener) \n\
   1047                 return; \n\
   1048             t = console._inspectorCommandLineAPI._normalizeEventTypes(t); \n\
   1049             for (i = 0; i < t.length; i++) { \n\
   1050                 o.removeEventListener(t[i], console._inspectorCommandLineAPI._logEvent, false); \n\
   1051             } \n\
   1052         }, \n\
   1053         _inspectedNodes: [], \n\
   1054         get $0() { return console._inspectorCommandLineAPI._inspectedNodes[0] }, \n\
   1055         get $1() { return console._inspectorCommandLineAPI._inspectedNodes[1] }, \n\
   1056         get $2() { return console._inspectorCommandLineAPI._inspectedNodes[2] }, \n\
   1057         get $3() { return console._inspectorCommandLineAPI._inspectedNodes[3] }, \n\
   1058         get $4() { return console._inspectorCommandLineAPI._inspectedNodes[4] }, \n\
   1059     };");
   1060 
   1061     inspectorCommandLineAPI.clear = InjectedScript.clearConsoleMessages;
   1062     inspectorCommandLineAPI.inspect = InjectedScript._inspectObject;
   1063     inspectorCommandLineAPI.copy = InjectedScript._copy;
   1064 }
   1065 
   1066 InjectedScript._resolveObject = function(objectProxy)
   1067 {
   1068     var object = InjectedScript._objectForId(objectProxy.objectId);
   1069     var path = objectProxy.path;
   1070     var protoDepth = objectProxy.protoDepth;
   1071 
   1072     // Follow the property path.
   1073     for (var i = 0; InjectedScript._isDefined(object) && path && i < path.length; ++i)
   1074         object = object[path[i]];
   1075 
   1076     // Get to the necessary proto layer.
   1077     for (var i = 0; InjectedScript._isDefined(object) && protoDepth && i < protoDepth; ++i)
   1078         object = object.__proto__;
   1079 
   1080     return object;
   1081 }
   1082 
   1083 InjectedScript._window = function()
   1084 {
   1085     // TODO: replace with 'return window;' once this script is injected into
   1086     // the page's context.
   1087     return inspectedWindow;
   1088 }
   1089 
   1090 InjectedScript._nodeForId = function(nodeId)
   1091 {
   1092     if (!nodeId)
   1093         return null;
   1094     return InjectedScriptHost.nodeForId(nodeId);
   1095 }
   1096 
   1097 InjectedScript._objectForId = function(objectId)
   1098 {
   1099     // There are three types of object ids used:
   1100     // - numbers point to DOM Node via the InspectorDOMAgent mapping
   1101     // - strings point to console objects cached in InspectorController for lazy evaluation upon them
   1102     // - objects contain complex ids and are currently used for scoped objects
   1103     if (typeof objectId === "number") {
   1104         return InjectedScript._nodeForId(objectId);
   1105     } else if (typeof objectId === "string") {
   1106         return InjectedScript.unwrapObject(objectId);
   1107     } else if (typeof objectId === "object") {
   1108         var callFrame = InjectedScript._callFrameForId(objectId.callFrame);
   1109         if (objectId.thisObject)
   1110             return callFrame.thisObject;
   1111         else
   1112             return callFrame.scopeChain[objectId.chainIndex];
   1113     }
   1114     return objectId;
   1115 }
   1116 
   1117 InjectedScript.pushNodeToFrontend = function(objectProxy)
   1118 {
   1119     var object = InjectedScript._resolveObject(objectProxy);
   1120     if (!object || InjectedScript._type(object) !== "node")
   1121         return false;
   1122     return InjectedScriptHost.pushNodePathToFrontend(object, false, false);
   1123 }
   1124 
   1125 InjectedScript.nodeByPath = function(path)
   1126 {
   1127     // We make this call through the injected script only to get a nice
   1128     // callback for it.
   1129     return InjectedScriptHost.pushNodeByPathToFrontend(path.join(","));
   1130 }
   1131 
   1132 // Called from within InspectorController on the 'inspected page' side.
   1133 InjectedScript.createProxyObject = function(object, objectId, abbreviate)
   1134 {
   1135     var result = {};
   1136     result.injectedScriptId = injectedScriptId;
   1137     result.objectId = objectId;
   1138     result.type = InjectedScript._type(object);
   1139 
   1140     var type = typeof object;
   1141     if ((type === "object" && object !== null) || type === "function") {
   1142         for (var subPropertyName in object) {
   1143             result.hasChildren = true;
   1144             break;
   1145         }
   1146     }
   1147     try {
   1148         result.description = InjectedScript._describe(object, abbreviate);
   1149     } catch (e) {
   1150         result.errorText = e.toString();
   1151     }
   1152     return result;
   1153 }
   1154 
   1155 InjectedScript.evaluateOnSelf = function(funcBody)
   1156 {
   1157     return window.eval("(" + funcBody + ")();");
   1158 }
   1159 
   1160 InjectedScript.CallFrameProxy = function(id, callFrame)
   1161 {
   1162     this.id = id;
   1163     this.type = callFrame.type;
   1164     this.functionName = (this.type === "function" ? callFrame.functionName : "");
   1165     this.sourceID = callFrame.sourceID;
   1166     this.line = callFrame.line;
   1167     this.scopeChain = this._wrapScopeChain(callFrame);
   1168 }
   1169 
   1170 InjectedScript.CallFrameProxy.prototype = {
   1171     _wrapScopeChain: function(callFrame)
   1172     {
   1173         var foundLocalScope = false;
   1174         var scopeChain = callFrame.scopeChain;
   1175         var scopeChainProxy = [];
   1176         for (var i = 0; i < scopeChain.length; ++i) {
   1177             var scopeObject = scopeChain[i];
   1178             var scopeObjectProxy = InjectedScript.createProxyObject(scopeObject, { callFrame: this.id, chainIndex: i }, true);
   1179 
   1180             if (InjectedScriptHost.isActivation(scopeObject)) {
   1181                 if (!foundLocalScope)
   1182                     scopeObjectProxy.thisObject = InjectedScript.createProxyObject(callFrame.thisObject, { callFrame: this.id, thisObject: true }, true);
   1183                 else
   1184                     scopeObjectProxy.isClosure = true;
   1185                 foundLocalScope = true;
   1186                 scopeObjectProxy.isLocal = true;
   1187             } else if (foundLocalScope && scopeObject instanceof InjectedScript._window().Element)
   1188                 scopeObjectProxy.isElement = true;
   1189             else if (foundLocalScope && scopeObject instanceof InjectedScript._window().Document)
   1190                 scopeObjectProxy.isDocument = true;
   1191             else if (!foundLocalScope)
   1192                 scopeObjectProxy.isWithBlock = true;
   1193             scopeChainProxy.push(scopeObjectProxy);
   1194         }
   1195         return scopeChainProxy;
   1196     }
   1197 }
   1198 
   1199 InjectedScript.executeSql = function(callId, databaseId, query)
   1200 {
   1201     function successCallback(tx, result)
   1202     {
   1203         var rows = result.rows;
   1204         var result = [];
   1205         var length = rows.length;
   1206         for (var i = 0; i < length; ++i) {
   1207             var data = {};
   1208             result.push(data);
   1209             var row = rows.item(i);
   1210             for (var columnIdentifier in row) {
   1211                 // FIXME: (Bug 19439) We should specially format SQL NULL here
   1212                 // (which is represented by JavaScript null here, and turned
   1213                 // into the string "null" by the String() function).
   1214                 var text = row[columnIdentifier];
   1215                 data[columnIdentifier] = String(text);
   1216             }
   1217         }
   1218         InjectedScriptHost.reportDidDispatchOnInjectedScript(callId, result, false);
   1219     }
   1220 
   1221     function errorCallback(tx, error)
   1222     {
   1223         InjectedScriptHost.reportDidDispatchOnInjectedScript(callId, error, false);
   1224     }
   1225 
   1226     function queryTransaction(tx)
   1227     {
   1228         tx.executeSql(query, null, successCallback, errorCallback);
   1229     }
   1230 
   1231     var database = InjectedScriptHost.databaseForId(databaseId);
   1232     if (!database)
   1233         errorCallback(null, { code : 2 });  // Return as unexpected version.
   1234     database.transaction(queryTransaction, errorCallback);
   1235     return true;
   1236 }
   1237 
   1238 InjectedScript._isDefined = function(object)
   1239 {
   1240     return object || object instanceof inspectedWindow.HTMLAllCollection;
   1241 }
   1242 
   1243 InjectedScript._type = function(obj)
   1244 {
   1245     if (obj === null)
   1246         return "null";
   1247 
   1248     // FIXME(33716): typeof document.all is always 'undefined'.
   1249     if (obj instanceof inspectedWindow.HTMLAllCollection)
   1250         return "array";
   1251 
   1252     var type = typeof obj;
   1253     if (type !== "object" && type !== "function")
   1254         return type;
   1255 
   1256     var win = InjectedScript._window();
   1257 
   1258     if (obj instanceof win.Node)
   1259         return (obj.nodeType === undefined ? type : "node");
   1260     if (obj instanceof win.String)
   1261         return "string";
   1262     if (obj instanceof win.Array)
   1263         return "array";
   1264     if (obj instanceof win.Boolean)
   1265         return "boolean";
   1266     if (obj instanceof win.Number)
   1267         return "number";
   1268     if (obj instanceof win.Date)
   1269         return "date";
   1270     if (obj instanceof win.RegExp)
   1271         return "regexp";
   1272     if (obj instanceof win.NodeList)
   1273         return "array";
   1274     if (obj instanceof win.HTMLCollection || obj instanceof win.HTMLAllCollection)
   1275         return "array";
   1276     if (obj instanceof win.Error)
   1277         return "error";
   1278     return type;
   1279 }
   1280 
   1281 InjectedScript._describe = function(obj, abbreviated)
   1282 {
   1283     var type1 = InjectedScript._type(obj);
   1284     var type2 = InjectedScript._className(obj);
   1285 
   1286     switch (type1) {
   1287     case "object":
   1288     case "node":
   1289     case "array":
   1290         return type2;
   1291     case "string":
   1292         if (!abbreviated)
   1293             return obj;
   1294         if (obj.length > 100)
   1295             return "\"" + obj.substring(0, 100) + "\u2026\"";
   1296         return "\"" + obj + "\"";
   1297     case "function":
   1298         var objectText = String(obj);
   1299         if (!/^function /.test(objectText))
   1300             objectText = (type2 == "object") ? type1 : type2;
   1301         else if (abbreviated)
   1302             objectText = /.*/.exec(obj)[0].replace(/ +$/g, "");
   1303         return objectText;
   1304     default:
   1305         return String(obj);
   1306     }
   1307 }
   1308 
   1309 InjectedScript._className = function(obj)
   1310 {
   1311     return Object.prototype.toString.call(obj).replace(/^\[object (.*)\]$/i, "$1")
   1312 }
   1313 
   1314 InjectedScript._escapeCharacters = function(str, chars)
   1315 {
   1316     var foundChar = false;
   1317     for (var i = 0; i < chars.length; ++i) {
   1318         if (str.indexOf(chars.charAt(i)) !== -1) {
   1319             foundChar = true;
   1320             break;
   1321         }
   1322     }
   1323 
   1324     if (!foundChar)
   1325         return str;
   1326 
   1327     var result = "";
   1328     for (var i = 0; i < str.length; ++i) {
   1329         if (chars.indexOf(str.charAt(i)) !== -1)
   1330             result += "\\";
   1331         result += str.charAt(i);
   1332     }
   1333 
   1334     return result;
   1335 }
   1336 
   1337 return InjectedScript;
   1338 });
   1339