| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2007 Apple Inc. All rights reserved. | 2 * Copyright (C) 2007 Apple Inc. All rights reserved. |
| 3 * | 3 * |
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
| 6 * are met: | 6 * are met: |
| 7 * | 7 * |
| 8 * 1. Redistributions of source code must retain the above copyright | 8 * 1. Redistributions of source code must retain the above copyright |
| 9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
| 10 * 2. Redistributions in binary form must reproduce the above copyright | 10 * 2. Redistributions in binary form must reproduce the above copyright |
| 11 * notice, this list of conditions and the following disclaimer in the | 11 * notice, this list of conditions and the following disclaimer in the |
| 12 * documentation and/or other materials provided with the distribution. | 12 * documentation and/or other materials provided with the distribution. |
| 13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of | 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 | 14 * its contributors may be used to endorse or promote products derived |
| 15 * from this software without specific prior written permission. | 15 * from this software without specific prior written permission. |
| 16 * | 16 * |
| 17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY | 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 | 18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | 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 | 20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
| 21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | 21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| 22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | 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 | 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 | 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 | 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. | 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 27 */ | 27 */ |
| 28 | 28 |
| 29 var injectedScriptConstructor = (function (InjectedScriptHost, inspectedWindow,
injectedScriptId) { |
| 30 |
| 29 var InjectedScript = {}; | 31 var InjectedScript = {}; |
| 30 | 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.HTML
AllCollection)) { // 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.wrapAndStringifyObject = function(object, objectGroupName) { |
| 56 var r = InjectedScript.wrapObject(object, objectGroupName); |
| 57 return InjectedScript.JSON.stringify(r); |
| 58 }; |
| 59 |
| 60 InjectedScript.unwrapObject = function(objectId) { |
| 61 return InjectedScript.idToWrappedObject[objectId]; |
| 62 }; |
| 63 |
| 64 InjectedScript.releaseWrapperObjectGroup = function(objectGroupName) { |
| 65 delete InjectedScript.objectGroups[objectGroupName]; |
| 66 }; |
| 67 |
| 31 // Called from within InspectorController on the 'inspected page' side. | 68 // Called from within InspectorController on the 'inspected page' side. |
| 32 InjectedScript.reset = function() | 69 InjectedScript.reset = function() |
| 33 { | 70 { |
| 34 InjectedScript._styles = {}; | 71 InjectedScript._styles = {}; |
| 35 InjectedScript._styleRules = {}; | 72 InjectedScript._styleRules = {}; |
| 36 InjectedScript._lastStyleId = 0; | 73 InjectedScript._lastStyleId = 0; |
| 37 InjectedScript._lastStyleRuleId = 0; | 74 InjectedScript._lastStyleRuleId = 0; |
| 38 InjectedScript._searchResults = []; | 75 InjectedScript._searchResults = []; |
| 39 InjectedScript._includedInSearchResultsPropertyName = "__includedInInspector
SearchResults"; | 76 InjectedScript._includedInSearchResultsPropertyName = "__includedInInspector
SearchResults"; |
| 40 } | 77 } |
| 41 | 78 |
| 42 InjectedScript.reset(); | 79 InjectedScript.reset(); |
| 43 | 80 |
| 44 InjectedScript.dispatch = function(methodName, args, callId) | 81 InjectedScript.dispatch = function(methodName, args, callId) |
| 45 { | 82 { |
| 46 var argsArray = JSON.parse(args); | 83 var argsArray = JSON.parse(args); |
| 47 if (callId) | 84 if (callId) |
| 48 argsArray.splice(0, 0, callId); // Methods that run asynchronously have
a call back id parameter. | 85 argsArray.splice(0, 0, callId); // Methods that run asynchronously have
a call back id parameter. |
| 49 var result = InjectedScript[methodName].apply(InjectedScript, argsArray); | 86 var result = InjectedScript[methodName].apply(InjectedScript, argsArray); |
| 50 if (typeof result === "undefined") { | 87 if (typeof result === "undefined") { |
| 51 InjectedScript._window().console.error("Web Inspector error: InjectedScr
ipt.%s returns undefined", methodName); | 88 InjectedScript._window().console.error("Web Inspector error: InjectedScr
ipt.%s returns undefined", methodName); |
| 52 result = null; | 89 result = null; |
| 53 } | 90 } |
| 54 return JSON.stringify(result); | 91 return InjectedScript.JSON.stringify(result); |
| 55 } | 92 } |
| 56 | 93 |
| 57 InjectedScript.getStyles = function(nodeId, authorOnly) | 94 InjectedScript.getStyles = function(nodeId, authorOnly) |
| 58 { | 95 { |
| 59 var node = InjectedScript._nodeForId(nodeId); | 96 var node = InjectedScript._nodeForId(nodeId); |
| 60 if (!node) | 97 if (!node) |
| 61 return false; | 98 return false; |
| 62 var defaultView = node.ownerDocument.defaultView; | 99 var defaultView = node.ownerDocument.defaultView; |
| 63 var matchedRules = defaultView.getMatchedCSSRules(node, "", authorOnly); | 100 var matchedRules = defaultView.getMatchedCSSRules(node, "", authorOnly); |
| 64 var matchedCSSRules = []; | 101 var matchedCSSRules = []; |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 162 } | 199 } |
| 163 | 200 |
| 164 InjectedScript.toggleStyleEnabled = function(styleId, propertyName, disabled) | 201 InjectedScript.toggleStyleEnabled = function(styleId, propertyName, disabled) |
| 165 { | 202 { |
| 166 var style = InjectedScript._styles[styleId]; | 203 var style = InjectedScript._styles[styleId]; |
| 167 if (!style) | 204 if (!style) |
| 168 return false; | 205 return false; |
| 169 | 206 |
| 170 if (disabled) { | 207 if (disabled) { |
| 171 if (!style.__disabledPropertyValues || !style.__disabledPropertyPrioriti
es) { | 208 if (!style.__disabledPropertyValues || !style.__disabledPropertyPrioriti
es) { |
| 172 var inspectedWindow = InjectedScript._window(); | 209 style.__disabledProperties = {}; |
| 173 style.__disabledProperties = new inspectedWindow.Object; | 210 style.__disabledPropertyValues = {}; |
| 174 style.__disabledPropertyValues = new inspectedWindow.Object; | 211 style.__disabledPropertyPriorities = {}; |
| 175 style.__disabledPropertyPriorities = new inspectedWindow.Object; | |
| 176 } | 212 } |
| 177 | 213 |
| 178 style.__disabledPropertyValues[propertyName] = style.getPropertyValue(pr
opertyName); | 214 style.__disabledPropertyValues[propertyName] = style.getPropertyValue(pr
opertyName); |
| 179 style.__disabledPropertyPriorities[propertyName] = style.getPropertyPrio
rity(propertyName); | 215 style.__disabledPropertyPriorities[propertyName] = style.getPropertyPrio
rity(propertyName); |
| 180 | 216 |
| 181 if (style.getPropertyShorthand(propertyName)) { | 217 if (style.getPropertyShorthand(propertyName)) { |
| 182 var longhandProperties = InjectedScript._getLonghandProperties(style
, propertyName); | 218 var longhandProperties = InjectedScript._getLonghandProperties(style
, propertyName); |
| 183 for (var i = 0; i < longhandProperties.length; ++i) { | 219 for (var i = 0; i < longhandProperties.length; ++i) { |
| 184 style.__disabledProperties[longhandProperties[i]] = true; | 220 style.__disabledProperties[longhandProperties[i]] = true; |
| 185 style.removeProperty(longhandProperties[i]); | 221 style.removeProperty(longhandProperties[i]); |
| (...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 299 // Bind editable scripts only. | 335 // Bind editable scripts only. |
| 300 var doBind = !ruleValue.isUserAgent && !ruleValue.isUser; | 336 var doBind = !ruleValue.isUserAgent && !ruleValue.isUser; |
| 301 ruleValue.style = InjectedScript._serializeStyle(rule.style, doBind); | 337 ruleValue.style = InjectedScript._serializeStyle(rule.style, doBind); |
| 302 | 338 |
| 303 if (doBind) { | 339 if (doBind) { |
| 304 if (!rule.id) { | 340 if (!rule.id) { |
| 305 rule.id = InjectedScript._lastStyleRuleId++; | 341 rule.id = InjectedScript._lastStyleRuleId++; |
| 306 InjectedScript._styleRules[rule.id] = rule; | 342 InjectedScript._styleRules[rule.id] = rule; |
| 307 } | 343 } |
| 308 ruleValue.id = rule.id; | 344 ruleValue.id = rule.id; |
| 345 ruleValue.injectedScriptId = injectedScriptId; |
| 309 } | 346 } |
| 310 return ruleValue; | 347 return ruleValue; |
| 311 } | 348 } |
| 312 | 349 |
| 313 InjectedScript._serializeStyle = function(style, doBind) | 350 InjectedScript._serializeStyle = function(style, doBind) |
| 314 { | 351 { |
| 315 var result = {}; | 352 var result = {}; |
| 316 result.width = style.width; | 353 result.width = style.width; |
| 317 result.height = style.height; | 354 result.height = style.height; |
| 318 result.__disabledProperties = style.__disabledProperties; | 355 result.__disabledProperties = style.__disabledProperties; |
| (...skipping 18 matching lines...) Expand all Loading... |
| 337 result.properties.push(property); | 374 result.properties.push(property); |
| 338 } | 375 } |
| 339 result.uniqueStyleProperties = InjectedScript._getUniqueStyleProperties(styl
e); | 376 result.uniqueStyleProperties = InjectedScript._getUniqueStyleProperties(styl
e); |
| 340 | 377 |
| 341 if (doBind) { | 378 if (doBind) { |
| 342 if (!style.id) { | 379 if (!style.id) { |
| 343 style.id = InjectedScript._lastStyleId++; | 380 style.id = InjectedScript._lastStyleId++; |
| 344 InjectedScript._styles[style.id] = style; | 381 InjectedScript._styles[style.id] = style; |
| 345 } | 382 } |
| 346 result.id = style.id; | 383 result.id = style.id; |
| 384 result.injectedScriptId = injectedScriptId; |
| 347 } | 385 } |
| 348 return result; | 386 return result; |
| 349 } | 387 } |
| 350 | 388 |
| 351 InjectedScript._getUniqueStyleProperties = function(style) | 389 InjectedScript._getUniqueStyleProperties = function(style) |
| 352 { | 390 { |
| 353 var properties = []; | 391 var properties = []; |
| 354 var foundProperties = {}; | 392 var foundProperties = {}; |
| 355 | 393 |
| 356 for (var i = 0; i < style.length; ++i) { | 394 for (var i = 0; i < style.length; ++i) { |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 438 title = title.replace(/Prototype$/, ""); | 476 title = title.replace(/Prototype$/, ""); |
| 439 } | 477 } |
| 440 result.push(title); | 478 result.push(title); |
| 441 } | 479 } |
| 442 return result; | 480 return result; |
| 443 } | 481 } |
| 444 | 482 |
| 445 InjectedScript.getProperties = function(objectProxy, ignoreHasOwnProperty, abbre
viate) | 483 InjectedScript.getProperties = function(objectProxy, ignoreHasOwnProperty, abbre
viate) |
| 446 { | 484 { |
| 447 var object = InjectedScript._resolveObject(objectProxy); | 485 var object = InjectedScript._resolveObject(objectProxy); |
| 448 if (!object) | 486 if (!InjectedScript._isDefined(object)) |
| 449 return false; | 487 return false; |
| 450 | 488 |
| 451 var properties = []; | 489 var properties = []; |
| 452 | 490 |
| 453 // Go over properties, prepare results. | 491 // Go over properties, prepare results. |
| 454 for (var propertyName in object) { | 492 for (var propertyName in object) { |
| 455 if (!ignoreHasOwnProperty && "hasOwnProperty" in object && !object.hasOw
nProperty(propertyName)) | 493 if (!ignoreHasOwnProperty && "hasOwnProperty" in object && !object.hasOw
nProperty(propertyName)) |
| 456 continue; | 494 continue; |
| 457 | 495 |
| 458 var property = {}; | 496 var property = {}; |
| (...skipping 13 matching lines...) Expand all Loading... |
| 472 property.isGetter = true; | 510 property.isGetter = true; |
| 473 } | 511 } |
| 474 properties.push(property); | 512 properties.push(property); |
| 475 } | 513 } |
| 476 return properties; | 514 return properties; |
| 477 } | 515 } |
| 478 | 516 |
| 479 InjectedScript.setPropertyValue = function(objectProxy, propertyName, expression
) | 517 InjectedScript.setPropertyValue = function(objectProxy, propertyName, expression
) |
| 480 { | 518 { |
| 481 var object = InjectedScript._resolveObject(objectProxy); | 519 var object = InjectedScript._resolveObject(objectProxy); |
| 482 if (!object) | 520 if (!InjectedScript._isDefined(object)) |
| 483 return false; | 521 return false; |
| 484 | 522 |
| 485 var expressionLength = expression.length; | 523 var expressionLength = expression.length; |
| 486 if (!expressionLength) { | 524 if (!expressionLength) { |
| 487 delete object[propertyName]; | 525 delete object[propertyName]; |
| 488 return !(propertyName in object); | 526 return !(propertyName in object); |
| 489 } | 527 } |
| 490 | 528 |
| 491 try { | 529 try { |
| 492 // Surround the expression in parenthesis so the result of the eval is t
he result | 530 // Surround the expression in parenthesis so the result of the eval is t
he result |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 576 | 614 |
| 577 InjectedScript.evaluate = function(expression, objectGroup) | 615 InjectedScript.evaluate = function(expression, objectGroup) |
| 578 { | 616 { |
| 579 return InjectedScript._evaluateAndWrap(InjectedScript._window().eval, Inject
edScript._window(), expression, objectGroup); | 617 return InjectedScript._evaluateAndWrap(InjectedScript._window().eval, Inject
edScript._window(), expression, objectGroup); |
| 580 } | 618 } |
| 581 | 619 |
| 582 InjectedScript._evaluateAndWrap = function(evalFunction, object, expression, obj
ectGroup) | 620 InjectedScript._evaluateAndWrap = function(evalFunction, object, expression, obj
ectGroup) |
| 583 { | 621 { |
| 584 var result = {}; | 622 var result = {}; |
| 585 try { | 623 try { |
| 586 result.value = InjectedScriptHost.wrapObject(InjectedScript._evaluateOn(
evalFunction, object, expression), objectGroup); | 624 result.value = InjectedScript.wrapObject(InjectedScript._evaluateOn(eval
Function, object, expression), objectGroup); |
| 625 |
| 587 // Handle error that might have happened while describing result. | 626 // Handle error that might have happened while describing result. |
| 588 if (result.value.errorText) { | 627 if (result.value.errorText) { |
| 589 result.value = result.value.errorText; | 628 result.value = result.value.errorText; |
| 590 result.isException = true; | 629 result.isException = true; |
| 591 } | 630 } |
| 592 } catch (e) { | 631 } catch (e) { |
| 593 result.value = e.toString(); | 632 result.value = e.toString(); |
| 594 result.isException = true; | 633 result.isException = true; |
| 595 } | 634 } |
| 596 return result; | 635 return result; |
| (...skipping 276 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 873 var callFrame = InjectedScriptHost.currentCallFrame(); | 912 var callFrame = InjectedScriptHost.currentCallFrame(); |
| 874 if (!callFrame) | 913 if (!callFrame) |
| 875 return false; | 914 return false; |
| 876 | 915 |
| 877 var result = []; | 916 var result = []; |
| 878 var depth = 0; | 917 var depth = 0; |
| 879 do { | 918 do { |
| 880 result.push(new InjectedScript.CallFrameProxy(depth++, callFrame)); | 919 result.push(new InjectedScript.CallFrameProxy(depth++, callFrame)); |
| 881 callFrame = callFrame.caller; | 920 callFrame = callFrame.caller; |
| 882 } while (callFrame); | 921 } while (callFrame); |
| 883 return result; | 922 return InjectedScript.JSON.stringify(result); |
| 884 } | 923 } |
| 885 | 924 |
| 886 InjectedScript.evaluateInCallFrame = function(callFrameId, code, objectGroup) | 925 InjectedScript.evaluateInCallFrame = function(callFrameId, code, objectGroup) |
| 887 { | 926 { |
| 888 var callFrame = InjectedScript._callFrameForId(callFrameId); | 927 var callFrame = InjectedScript._callFrameForId(callFrameId); |
| 889 if (!callFrame) | 928 if (!callFrame) |
| 890 return false; | 929 return false; |
| 891 return InjectedScript._evaluateAndWrap(callFrame.evaluate, callFrame, code,
objectGroup); | 930 return InjectedScript._evaluateAndWrap(callFrame.evaluate, callFrame, code,
objectGroup); |
| 892 } | 931 } |
| 893 | 932 |
| 894 InjectedScript._callFrameForId = function(id) | 933 InjectedScript._callFrameForId = function(id) |
| 895 { | 934 { |
| 896 var callFrame = InjectedScriptHost.currentCallFrame(); | 935 var callFrame = InjectedScriptHost.currentCallFrame(); |
| 897 while (--id >= 0 && callFrame) | 936 while (--id >= 0 && callFrame) |
| 898 callFrame = callFrame.caller; | 937 callFrame = callFrame.caller; |
| 899 return callFrame; | 938 return callFrame; |
| 900 } | 939 } |
| 901 | 940 |
| 902 InjectedScript.clearConsoleMessages = function() | 941 InjectedScript.clearConsoleMessages = function() |
| 903 { | 942 { |
| 904 InjectedScriptHost.clearConsoleMessages(); | 943 InjectedScriptHost.clearConsoleMessages(); |
| 905 return true; | 944 return true; |
| 906 } | 945 } |
| 907 | 946 |
| 908 InjectedScript._inspectObject = function(o) | 947 InjectedScript._inspectObject = function(o) |
| 909 { | 948 { |
| 910 if (arguments.length === 0) | 949 if (arguments.length === 0) |
| 911 return; | 950 return; |
| 912 | 951 |
| 913 var inspectedWindow = InjectedScript._window(); | |
| 914 inspectedWindow.console.log(o); | 952 inspectedWindow.console.log(o); |
| 915 if (Object.type(o) === "node") { | 953 if (Object.type(o) === "node") { |
| 916 InjectedScriptHost.pushNodePathToFrontend(o, false, true); | 954 InjectedScriptHost.pushNodePathToFrontend(o, false, true); |
| 917 } else { | 955 } else { |
| 918 switch (Object.describe(o)) { | 956 switch (Object.describe(o)) { |
| 919 case "Database": | 957 case "Database": |
| 920 InjectedScriptHost.selectDatabase(o); | 958 InjectedScriptHost.selectDatabase(o); |
| 921 break; | 959 break; |
| 922 case "Storage": | 960 case "Storage": |
| 923 InjectedScriptHost.selectDOMStorage(o); | 961 InjectedScriptHost.selectDOMStorage(o); |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1002 } \n\ | 1040 } \n\ |
| 1003 }, \n\ | 1041 }, \n\ |
| 1004 _inspectedNodes: [], \n\ | 1042 _inspectedNodes: [], \n\ |
| 1005 get $0() { return console._inspectorCommandLineAPI._inspectedNodes[0] },
\n\ | 1043 get $0() { return console._inspectorCommandLineAPI._inspectedNodes[0] },
\n\ |
| 1006 get $1() { return console._inspectorCommandLineAPI._inspectedNodes[1] },
\n\ | 1044 get $1() { return console._inspectorCommandLineAPI._inspectedNodes[1] },
\n\ |
| 1007 get $2() { return console._inspectorCommandLineAPI._inspectedNodes[2] },
\n\ | 1045 get $2() { return console._inspectorCommandLineAPI._inspectedNodes[2] },
\n\ |
| 1008 get $3() { return console._inspectorCommandLineAPI._inspectedNodes[3] },
\n\ | 1046 get $3() { return console._inspectorCommandLineAPI._inspectedNodes[3] },
\n\ |
| 1009 get $4() { return console._inspectorCommandLineAPI._inspectedNodes[4] },
\n\ | 1047 get $4() { return console._inspectorCommandLineAPI._inspectedNodes[4] },
\n\ |
| 1010 };"); | 1048 };"); |
| 1011 | 1049 |
| 1012 inspectorCommandLineAPI.clear = InjectedScriptHost.wrapCallback(InjectedScri
pt.clearConsoleMessages); | 1050 inspectorCommandLineAPI.clear = InjectedScript.clearConsoleMessages; |
| 1013 inspectorCommandLineAPI.inspect = InjectedScriptHost.wrapCallback(InjectedSc
ript._inspectObject); | 1051 inspectorCommandLineAPI.inspect = InjectedScript._inspectObject; |
| 1014 inspectorCommandLineAPI.copy = InjectedScriptHost.wrapCallback(InjectedScrip
t._copy); | 1052 inspectorCommandLineAPI.copy = InjectedScript._copy; |
| 1015 } | 1053 } |
| 1016 | 1054 |
| 1017 InjectedScript._resolveObject = function(objectProxy) | 1055 InjectedScript._resolveObject = function(objectProxy) |
| 1018 { | 1056 { |
| 1019 var object = InjectedScript._objectForId(objectProxy.objectId); | 1057 var object = InjectedScript._objectForId(objectProxy.objectId); |
| 1020 var path = objectProxy.path; | 1058 var path = objectProxy.path; |
| 1021 var protoDepth = objectProxy.protoDepth; | 1059 var protoDepth = objectProxy.protoDepth; |
| 1022 | 1060 |
| 1023 // Follow the property path. | 1061 // Follow the property path. |
| 1024 for (var i = 0; object && path && i < path.length; ++i) | 1062 for (var i = 0; InjectedScript._isDefined(object) && path && i < path.length
; ++i) |
| 1025 object = object[path[i]]; | 1063 object = object[path[i]]; |
| 1026 | 1064 |
| 1027 // Get to the necessary proto layer. | 1065 // Get to the necessary proto layer. |
| 1028 for (var i = 0; object && protoDepth && i < protoDepth; ++i) | 1066 for (var i = 0; InjectedScript._isDefined(object) && protoDepth && i < proto
Depth; ++i) |
| 1029 object = object.__proto__; | 1067 object = object.__proto__; |
| 1030 | 1068 |
| 1031 return object; | 1069 return object; |
| 1032 } | 1070 } |
| 1033 | 1071 |
| 1034 InjectedScript._window = function() | 1072 InjectedScript._window = function() |
| 1035 { | 1073 { |
| 1036 // TODO: replace with 'return window;' once this script is injected into | 1074 // TODO: replace with 'return window;' once this script is injected into |
| 1037 // the page's context. | 1075 // the page's context. |
| 1038 return InjectedScriptHost.inspectedWindow(); | 1076 return inspectedWindow; |
| 1039 } | 1077 } |
| 1040 | 1078 |
| 1041 InjectedScript._nodeForId = function(nodeId) | 1079 InjectedScript._nodeForId = function(nodeId) |
| 1042 { | 1080 { |
| 1043 if (!nodeId) | 1081 if (!nodeId) |
| 1044 return null; | 1082 return null; |
| 1045 return InjectedScriptHost.nodeForId(nodeId); | 1083 return InjectedScriptHost.nodeForId(nodeId); |
| 1046 } | 1084 } |
| 1047 | 1085 |
| 1048 InjectedScript._objectForId = function(objectId) | 1086 InjectedScript._objectForId = function(objectId) |
| 1049 { | 1087 { |
| 1050 // There are three types of object ids used: | 1088 // There are three types of object ids used: |
| 1051 // - numbers point to DOM Node via the InspectorDOMAgent mapping | 1089 // - numbers point to DOM Node via the InspectorDOMAgent mapping |
| 1052 // - strings point to console objects cached in InspectorController for lazy
evaluation upon them | 1090 // - strings point to console objects cached in InspectorController for lazy
evaluation upon them |
| 1053 // - objects contain complex ids and are currently used for scoped objects | 1091 // - objects contain complex ids and are currently used for scoped objects |
| 1054 if (typeof objectId === "number") { | 1092 if (typeof objectId === "number") { |
| 1055 return InjectedScript._nodeForId(objectId); | 1093 return InjectedScript._nodeForId(objectId); |
| 1056 } else if (typeof objectId === "string") { | 1094 } else if (typeof objectId === "string") { |
| 1057 return InjectedScriptHost.unwrapObject(objectId); | 1095 return InjectedScript.unwrapObject(objectId); |
| 1058 } else if (typeof objectId === "object") { | 1096 } else if (typeof objectId === "object") { |
| 1059 var callFrame = InjectedScript._callFrameForId(objectId.callFrame); | 1097 var callFrame = InjectedScript._callFrameForId(objectId.callFrame); |
| 1060 if (objectId.thisObject) | 1098 if (objectId.thisObject) |
| 1061 return callFrame.thisObject; | 1099 return callFrame.thisObject; |
| 1062 else | 1100 else |
| 1063 return callFrame.scopeChain[objectId.chainIndex]; | 1101 return callFrame.scopeChain[objectId.chainIndex]; |
| 1064 } | 1102 } |
| 1065 return objectId; | 1103 return objectId; |
| 1066 } | 1104 } |
| 1067 | 1105 |
| 1068 InjectedScript.pushNodeToFrontend = function(objectProxy) | 1106 InjectedScript.pushNodeToFrontend = function(objectProxy) |
| 1069 { | 1107 { |
| 1070 var object = InjectedScript._resolveObject(objectProxy); | 1108 var object = InjectedScript._resolveObject(objectProxy); |
| 1071 if (!object || Object.type(object) !== "node") | 1109 if (!object || Object.type(object) !== "node") |
| 1072 return false; | 1110 return false; |
| 1073 return InjectedScriptHost.pushNodePathToFrontend(object, false, false); | 1111 return InjectedScriptHost.pushNodePathToFrontend(object, false, false); |
| 1074 } | 1112 } |
| 1075 | 1113 |
| 1076 InjectedScript.nodeByPath = function(path) | 1114 InjectedScript.nodeByPath = function(path) |
| 1077 { | 1115 { |
| 1078 // We make this call through the injected script only to get a nice | 1116 // We make this call through the injected script only to get a nice |
| 1079 // callback for it. | 1117 // callback for it. |
| 1080 return InjectedScriptHost.pushNodeByPathToFrontend(path.join(",")); | 1118 return InjectedScriptHost.pushNodeByPathToFrontend(path.join(",")); |
| 1081 } | 1119 } |
| 1082 | 1120 |
| 1083 // Called from within InspectorController on the 'inspected page' side. | 1121 // Called from within InspectorController on the 'inspected page' side. |
| 1084 InjectedScript.createProxyObject = function(object, objectId, abbreviate) | 1122 InjectedScript.createProxyObject = function(object, objectId, abbreviate) |
| 1085 { | 1123 { |
| 1086 var result = {}; | 1124 var result = {}; |
| 1125 result.injectedScriptId = injectedScriptId; |
| 1087 result.objectId = objectId; | 1126 result.objectId = objectId; |
| 1088 result.type = Object.type(object); | 1127 result.type = Object.type(object); |
| 1089 | 1128 |
| 1090 var type = typeof object; | 1129 var type = typeof object; |
| 1091 if ((type === "object" && object !== null) || type === "function") { | 1130 if ((type === "object" && object !== null) || type === "function") { |
| 1092 for (var subPropertyName in object) { | 1131 for (var subPropertyName in object) { |
| 1093 result.hasChildren = true; | 1132 result.hasChildren = true; |
| 1094 break; | 1133 break; |
| 1095 } | 1134 } |
| 1096 } | 1135 } |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1158 result.push(data); | 1197 result.push(data); |
| 1159 var row = rows.item(i); | 1198 var row = rows.item(i); |
| 1160 for (var columnIdentifier in row) { | 1199 for (var columnIdentifier in row) { |
| 1161 // FIXME: (Bug 19439) We should specially format SQL NULL here | 1200 // FIXME: (Bug 19439) We should specially format SQL NULL here |
| 1162 // (which is represented by JavaScript null here, and turned | 1201 // (which is represented by JavaScript null here, and turned |
| 1163 // into the string "null" by the String() function). | 1202 // into the string "null" by the String() function). |
| 1164 var text = row[columnIdentifier]; | 1203 var text = row[columnIdentifier]; |
| 1165 data[columnIdentifier] = String(text); | 1204 data[columnIdentifier] = String(text); |
| 1166 } | 1205 } |
| 1167 } | 1206 } |
| 1168 InjectedScriptHost.reportDidDispatchOnInjectedScript(callId, JSON.string
ify(result), false); | 1207 InjectedScriptHost.reportDidDispatchOnInjectedScript(callId, InjectedScr
ipt.JSON.stringify(result), false); |
| 1169 } | 1208 } |
| 1170 | 1209 |
| 1171 function errorCallback(tx, error) | 1210 function errorCallback(tx, error) |
| 1172 { | 1211 { |
| 1173 InjectedScriptHost.reportDidDispatchOnInjectedScript(callId, JSON.string
ify(error), false); | 1212 InjectedScriptHost.reportDidDispatchOnInjectedScript(callId, InjectedScr
ipt.JSON.stringify(error), false); |
| 1174 } | 1213 } |
| 1175 | 1214 |
| 1176 function queryTransaction(tx) | 1215 function queryTransaction(tx) |
| 1177 { | 1216 { |
| 1178 tx.executeSql(query, null, InjectedScriptHost.wrapCallback(successCallba
ck), InjectedScriptHost.wrapCallback(errorCallback)); | 1217 tx.executeSql(query, null, successCallback, errorCallback); |
| 1179 } | 1218 } |
| 1180 | 1219 |
| 1181 var database = InjectedScriptHost.databaseForId(databaseId); | 1220 var database = InjectedScriptHost.databaseForId(databaseId); |
| 1182 if (!database) | 1221 if (!database) |
| 1183 errorCallback(null, { code : 2 }); // Return as unexpected version. | 1222 errorCallback(null, { code : 2 }); // Return as unexpected version. |
| 1184 database.transaction(InjectedScriptHost.wrapCallback(queryTransaction), Inje
ctedScriptHost.wrapCallback(errorCallback)); | 1223 database.transaction(queryTransaction, errorCallback); |
| 1185 return true; | 1224 return true; |
| 1186 } | 1225 } |
| 1187 | 1226 |
| 1227 InjectedScript._isDefined = function(object) |
| 1228 { |
| 1229 return object || object instanceof inspectedWindow.HTMLAllCollection; |
| 1230 } |
| 1231 |
| 1188 Object.type = function(obj) | 1232 Object.type = function(obj) |
| 1189 { | 1233 { |
| 1190 if (obj === null) | 1234 if (obj === null) |
| 1191 return "null"; | 1235 return "null"; |
| 1192 | 1236 |
| 1237 // FIXME(33716): typeof document.all is always 'undefined'. |
| 1238 if (obj instanceof inspectedWindow.HTMLAllCollection) |
| 1239 return "array"; |
| 1240 |
| 1193 var type = typeof obj; | 1241 var type = typeof obj; |
| 1194 if (type !== "object" && type !== "function") | 1242 if (type !== "object" && type !== "function") |
| 1195 return type; | 1243 return type; |
| 1196 | 1244 |
| 1197 var win = InjectedScript._window(); | 1245 var win = InjectedScript._window(); |
| 1198 | 1246 |
| 1199 if (obj instanceof win.Node) | 1247 if (obj instanceof win.Node) |
| 1200 return (obj.nodeType === undefined ? type : "node"); | 1248 return (obj.nodeType === undefined ? type : "node"); |
| 1201 if (obj instanceof win.String) | 1249 if (obj instanceof win.String) |
| 1202 return "string"; | 1250 return "string"; |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1286 | 1334 |
| 1287 var result = ""; | 1335 var result = ""; |
| 1288 for (var i = 0; i < this.length; ++i) { | 1336 for (var i = 0; i < this.length; ++i) { |
| 1289 if (chars.indexOf(this.charAt(i)) !== -1) | 1337 if (chars.indexOf(this.charAt(i)) !== -1) |
| 1290 result += "\\"; | 1338 result += "\\"; |
| 1291 result += this.charAt(i); | 1339 result += this.charAt(i); |
| 1292 } | 1340 } |
| 1293 | 1341 |
| 1294 return result; | 1342 return result; |
| 1295 } | 1343 } |
| 1344 |
| 1345 InjectedScript.JSON = {}; |
| 1346 |
| 1347 // The following code is a slightly modified version of http://www.json.org/json
2.js last modified on 2009-09-29. |
| 1348 // Compared to the original version it ignores toJSON method on objects it seria
lizes. |
| 1349 // It's done to avoid weird behaviour when inspected application provides it's o
wn implementation |
| 1350 // of toJSON methods to the Object and other intrinsic types. We use InjectedScr
ipt.JSON implementation |
| 1351 // instead of global JSON object since it can have been modified by the inspecte
d code. |
| 1352 (function() { |
| 1353 |
| 1354 function f(n) { |
| 1355 // Format integers to have at least two digits. |
| 1356 return n < 10 ? '0' + n : n; |
| 1357 } |
| 1358 |
| 1359 var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u
202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, |
| 1360 escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b
5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, |
| 1361 gap, |
| 1362 indent, |
| 1363 meta = { // table of character substitutions |
| 1364 '\b': '\\b', |
| 1365 '\t': '\\t', |
| 1366 '\n': '\\n', |
| 1367 '\f': '\\f', |
| 1368 '\r': '\\r', |
| 1369 '"' : '\\"', |
| 1370 '\\': '\\\\' |
| 1371 }, |
| 1372 rep; |
| 1373 |
| 1374 |
| 1375 function quote(string) { |
| 1376 |
| 1377 // If the string contains no control characters, no quote characters, and no |
| 1378 // backslash characters, then we can safely slap some quotes around it. |
| 1379 // Otherwise we must also replace the offending characters with safe escape |
| 1380 // sequences. |
| 1381 |
| 1382 escapable.lastIndex = 0; |
| 1383 return escapable.test(string) ? |
| 1384 '"' + string.replace(escapable, function (a) { |
| 1385 var c = meta[a]; |
| 1386 return typeof c === 'string' ? c : |
| 1387 '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); |
| 1388 }) + '"' : |
| 1389 '"' + string + '"'; |
| 1390 } |
| 1391 |
| 1392 |
| 1393 function str(key, holder) { |
| 1394 |
| 1395 // Produce a string from holder[key]. |
| 1396 |
| 1397 var i, // The loop counter. |
| 1398 k, // The member key. |
| 1399 v, // The member value. |
| 1400 length, |
| 1401 mind = gap, |
| 1402 partial, |
| 1403 value = holder[key]; |
| 1404 |
| 1405 // If we were called with a replacer function, then call the replacer to |
| 1406 // obtain a replacement value. |
| 1407 |
| 1408 if (typeof rep === 'function') { |
| 1409 value = rep.call(holder, key, value); |
| 1410 } |
| 1411 |
| 1412 // What happens next depends on the value's type. |
| 1413 |
| 1414 switch (typeof value) { |
| 1415 case 'string': |
| 1416 return quote(value); |
| 1417 |
| 1418 case 'number': |
| 1419 |
| 1420 // JSON numbers must be finite. Encode non-finite numbers as null. |
| 1421 |
| 1422 return isFinite(value) ? String(value) : 'null'; |
| 1423 |
| 1424 case 'boolean': |
| 1425 case 'null': |
| 1426 |
| 1427 // If the value is a boolean or null, convert it to a string. Note: |
| 1428 // typeof null does not produce 'null'. The case is included here in |
| 1429 // the remote chance that this gets fixed someday. |
| 1430 |
| 1431 return String(value); |
| 1432 |
| 1433 // If the type is 'object', we might be dealing with an object or an array or |
| 1434 // null. |
| 1435 |
| 1436 case 'object': |
| 1437 |
| 1438 // Due to a specification blunder in ECMAScript, typeof null is 'object', |
| 1439 // so watch out for that case. |
| 1440 |
| 1441 if (!value) { |
| 1442 return 'null'; |
| 1443 } |
| 1444 |
| 1445 // Make an array to hold the partial results of stringifying this object value. |
| 1446 |
| 1447 gap += indent; |
| 1448 partial = []; |
| 1449 |
| 1450 // Is the value an array? |
| 1451 |
| 1452 if (Object.prototype.toString.apply(value) === '[object Array]') { |
| 1453 |
| 1454 // The value is an array. Stringify every element. Use null as a placeholder |
| 1455 // for non-JSON values. |
| 1456 |
| 1457 length = value.length; |
| 1458 for (i = 0; i < length; i += 1) { |
| 1459 partial[i] = str(i, value) || 'null'; |
| 1460 } |
| 1461 |
| 1462 // Join all of the elements together, separated with commas, and wrap them in |
| 1463 // brackets. |
| 1464 |
| 1465 v = partial.length === 0 ? '[]' : |
| 1466 gap ? '[\n' + gap + |
| 1467 partial.join(',\n' + gap) + '\n' + |
| 1468 mind + ']' : |
| 1469 '[' + partial.join(',') + ']'; |
| 1470 gap = mind; |
| 1471 return v; |
| 1472 } |
| 1473 |
| 1474 // If the replacer is an array, use it to select the members to be stringified. |
| 1475 |
| 1476 if (rep && typeof rep === 'object') { |
| 1477 length = rep.length; |
| 1478 for (i = 0; i < length; i += 1) { |
| 1479 k = rep[i]; |
| 1480 if (typeof k === 'string') { |
| 1481 v = str(k, value); |
| 1482 if (v) { |
| 1483 partial.push(quote(k) + (gap ? ': ' : ':') + v); |
| 1484 } |
| 1485 } |
| 1486 } |
| 1487 } else { |
| 1488 |
| 1489 // Otherwise, iterate through all of the keys in the object. |
| 1490 |
| 1491 for (k in value) { |
| 1492 if (Object.hasOwnProperty.call(value, k)) { |
| 1493 v = str(k, value); |
| 1494 if (v) { |
| 1495 partial.push(quote(k) + (gap ? ': ' : ':') + v); |
| 1496 } |
| 1497 } |
| 1498 } |
| 1499 } |
| 1500 |
| 1501 // Join all of the member texts together, separated with commas, |
| 1502 // and wrap them in braces. |
| 1503 |
| 1504 v = partial.length === 0 ? '{}' : |
| 1505 gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + |
| 1506 mind + '}' : '{' + partial.join(',') + '}'; |
| 1507 gap = mind; |
| 1508 return v; |
| 1509 } |
| 1510 } |
| 1511 |
| 1512 InjectedScript.JSON.stringify = function (value, replacer, space) { |
| 1513 |
| 1514 // The stringify method takes a value and an optional replacer, and an optional |
| 1515 // space parameter, and returns a JSON text. The replacer can be a function |
| 1516 // that can replace values, or an array of strings that will select the keys. |
| 1517 // A default replacer method can be provided. Use of the space parameter can |
| 1518 // produce text that is more easily readable. |
| 1519 |
| 1520 var i; |
| 1521 gap = ''; |
| 1522 indent = ''; |
| 1523 |
| 1524 // If the space parameter is a number, make an indent string containing that |
| 1525 // many spaces. |
| 1526 |
| 1527 if (typeof space === 'number') { |
| 1528 for (i = 0; i < space; i += 1) { |
| 1529 indent += ' '; |
| 1530 } |
| 1531 |
| 1532 // If the space parameter is a string, it will be used as the indent string. |
| 1533 |
| 1534 } else if (typeof space === 'string') { |
| 1535 indent = space; |
| 1536 } |
| 1537 |
| 1538 // If there is a replacer, it must be a function or an array. |
| 1539 // Otherwise, throw an error. |
| 1540 |
| 1541 rep = replacer; |
| 1542 if (replacer && typeof replacer !== 'function' && |
| 1543 (typeof replacer !== 'object' || |
| 1544 typeof replacer.length !== 'number')) { |
| 1545 throw new Error('JSON.stringify'); |
| 1546 } |
| 1547 |
| 1548 // Make a fake root object containing our value under the key of ''. |
| 1549 // Return the result of stringifying the value. |
| 1550 |
| 1551 return str('', {'': value}); |
| 1552 }; |
| 1553 |
| 1554 |
| 1555 // If the JSON object does not yet have a parse method, give it one. |
| 1556 |
| 1557 InjectedScript.JSON.parse = function (text, reviver) { |
| 1558 |
| 1559 // The parse method takes a text and an optional reviver function, and returns |
| 1560 // a JavaScript value if the text is a valid JSON text. |
| 1561 |
| 1562 var j; |
| 1563 |
| 1564 function walk(holder, key) { |
| 1565 |
| 1566 // The walk method is used to recursively walk the resulting structure so |
| 1567 // that modifications can be made. |
| 1568 |
| 1569 var k, v, value = holder[key]; |
| 1570 if (value && typeof value === 'object') { |
| 1571 for (k in value) { |
| 1572 if (Object.hasOwnProperty.call(value, k)) { |
| 1573 v = walk(value, k); |
| 1574 if (v !== undefined) { |
| 1575 value[k] = v; |
| 1576 } else { |
| 1577 delete value[k]; |
| 1578 } |
| 1579 } |
| 1580 } |
| 1581 } |
| 1582 return reviver.call(holder, key, value); |
| 1583 } |
| 1584 |
| 1585 |
| 1586 // Parsing happens in four stages. In the first stage, we replace certain |
| 1587 // Unicode characters with escape sequences. JavaScript handles many characters |
| 1588 // incorrectly, either silently deleting them, or treating them as line endings. |
| 1589 |
| 1590 cx.lastIndex = 0; |
| 1591 if (cx.test(text)) { |
| 1592 text = text.replace(cx, function (a) { |
| 1593 return '\\u' + |
| 1594 ('0000' + a.charCodeAt(0).toString(16)).slice(-4); |
| 1595 }); |
| 1596 } |
| 1597 |
| 1598 // In the second stage, we run the text against regular expressions that look |
| 1599 // for non-JSON patterns. We are especially concerned with '()' and 'new' |
| 1600 // because they can cause invocation, and '=' because it can cause mutation. |
| 1601 // But just to be safe, we want to reject all unexpected forms. |
| 1602 |
| 1603 // We split the second stage into 4 regexp operations in order to work around |
| 1604 // crippling inefficiencies in IE's and Safari's regexp engines. First we |
| 1605 // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we |
| 1606 // replace all simple value tokens with ']' characters. Third, we delete all |
| 1607 // open brackets that follow a colon or comma or that begin the text. Finally, |
| 1608 // we look to see that the remaining characters are only whitespace or ']' or |
| 1609 // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. |
| 1610 |
| 1611 if (/^[\],:{}\s]*$/. |
| 1612 test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@'). |
| 1613 replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
. |
| 1614 replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { |
| 1615 |
| 1616 // In the third stage we use the eval function to compile the text into a |
| 1617 // JavaScript structure. The '{' operator is subject to a syntactic ambiguity |
| 1618 // in JavaScript: it can begin a block or an object literal. We wrap the text |
| 1619 // in parens to eliminate the ambiguity. |
| 1620 |
| 1621 j = eval('(' + text + ')'); |
| 1622 |
| 1623 // In the optional fourth stage, we recursively walk the new structure, passing |
| 1624 // each name/value pair to a reviver function for possible transformation. |
| 1625 |
| 1626 return typeof reviver === 'function' ? |
| 1627 walk({'': j}, '') : j; |
| 1628 } |
| 1629 |
| 1630 // If the text is not JSON parseable, then a SyntaxError is thrown. |
| 1631 |
| 1632 throw new SyntaxError('JSON.parse'); |
| 1633 }; |
| 1634 }()); |
| 1635 |
| 1636 return InjectedScript; |
| 1637 }); |
| OLD | NEW |