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 29var injectedScriptConstructor = (function (InjectedScriptHost, inspectedWindow, injectedScriptId) { 30 31var InjectedScript = {}; 32 33InjectedScript.lastBoundObjectId = 1; 34InjectedScript.idToWrappedObject = {}; 35InjectedScript.objectGroups = {}; 36InjectedScript.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 55InjectedScript.unwrapObject = function(objectId) { 56 return InjectedScript.idToWrappedObject[objectId]; 57}; 58 59InjectedScript.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. 69InjectedScript.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 79InjectedScript.reset(); 80 81InjectedScript.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 94InjectedScript.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 119InjectedScript.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 127InjectedScript.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 135InjectedScript.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 195InjectedScript.setStyleText = function(style, cssText) 196{ 197 style.cssText = cssText; 198 return true; 199} 200 201InjectedScript.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 239InjectedScript.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 268InjectedScript.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 298InjectedScript._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 311InjectedScript.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 321InjectedScript._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 350InjectedScript._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 389InjectedScript._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 406InjectedScript._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 422InjectedScript._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 451InjectedScript._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 466InjectedScript.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 483InjectedScript.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 517InjectedScript.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 551InjectedScript.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 560InjectedScript.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 574InjectedScript._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 592InjectedScript.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 626InjectedScript.evaluate = function(expression, objectGroup) 627{ 628 return InjectedScript._evaluateAndWrap(InjectedScript._window().eval, InjectedScript._window(), expression, objectGroup); 629} 630 631InjectedScript._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 649InjectedScript._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 664InjectedScript.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 678InjectedScript.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 893InjectedScript.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 913InjectedScript.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 921InjectedScript.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 936InjectedScript.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 944InjectedScript._callFrameForId = function(id) 945{ 946 var callFrame = InjectedScriptHost.currentCallFrame(); 947 while (--id >= 0 && callFrame) 948 callFrame = callFrame.caller; 949 return callFrame; 950} 951 952InjectedScript.clearConsoleMessages = function() 953{ 954 InjectedScriptHost.clearConsoleMessages(); 955 return true; 956} 957 958InjectedScript._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 978InjectedScript._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 988InjectedScript._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 1066InjectedScript._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 1083InjectedScript._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 1090InjectedScript._nodeForId = function(nodeId) 1091{ 1092 if (!nodeId) 1093 return null; 1094 return InjectedScriptHost.nodeForId(nodeId); 1095} 1096 1097InjectedScript._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 1117InjectedScript.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 1125InjectedScript.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. 1133InjectedScript.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 1155InjectedScript.evaluateOnSelf = function(funcBody) 1156{ 1157 return window.eval("(" + funcBody + ")();"); 1158} 1159 1160InjectedScript.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 1170InjectedScript.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 1199InjectedScript.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 1238InjectedScript._isDefined = function(object) 1239{ 1240 return object || object instanceof inspectedWindow.HTMLAllCollection; 1241} 1242 1243InjectedScript._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 1281InjectedScript._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 1309InjectedScript._className = function(obj) 1310{ 1311 return Object.prototype.toString.call(obj).replace(/^\[object (.*)\]$/i, "$1") 1312} 1313 1314InjectedScript._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 1337return InjectedScript; 1338}); 1339