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