mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-27 08:29:26 +00:00
14d496a73d
- Bug 1222098 - Devicemotion event timestamp should return values from Android sensor API and not Gecko. r:smaug (0fb05341d5)
- Bug 1266701 - some header missing in dom/events, r=jwatt (59a8a30af3)
- namespace (c5796648b6)
- Bug 1243555: Remove unnecessary nsDocShell static_cast in EventListenerManager::HandleEventInternal(). r=smaug (21c559122c)
- Bug 1254629 - Let query events fail when content root is wrong; r=masayuki (64454249aa)
- Bug 1224233 - fix crashy usage of IMENotificationSender::Run using on-screen keyboard on Windows, r=smaug (ddcdf13ad0)
- Bug 1252058 IMEContentObserver::IMENotificationSender shouldn't post notifications when IMEContentObserver which is the owner of it stopped observing contents r=smaug (9f4a14f13a)
- Bug 1259671 part.1 Rename InternalScrollPortEvent::orientType to InternalScrollPortEvent::OrientType r=smaug (eaefa4472f)
- Bug 1259671 part.2 Rename InternalScrollPortEvent::vertical to InternalScrollPortEvent::eVertical r=smaug (91bcdcd1df)
- Bug 1259671 part.3 Rename InternalScrollPortEvent::horizontal to InternalScrollPortEvent::eHorizontal r=smaug (f3c206dc4c)
- Bug 1259671 part.4 Rename InternalScrollPortEvent::both to InternalScrollPortEvent::eBoth r=smaug (ac34dc95c1)
- Bug 1259671 part.5 Rename InternalScrollPortEvent::orient to InternalScrollPortEvent::mOrient r=smaug (6736862c75)
- Bug 1257873 - Split Addons and Workers into separate Target classes in about:debugging. r=ochameau (e7e41532ae)
- Bug 1232931 - Skip browser_service_workers_timeout.js for frequent failures across all platforms. (b02b21518d)
- Bug 1258902 - part1:add service worker unregister link in about:debugging;r=janx (dfdd0935dc)
- Bug 1258902 - part2: create service-worker-target component;r=janx (e2af5c51a0)
- Bug 1258902 - Fix a typo that caused the newly-added test to be skipped (and re-enabled a frequently-failing one). (1c7d4c32e1)
- Bug 1257874 - Use getProperties instead of getFrames in animation-detail. r=pbro (04741384f1)
- Bug 1260680 - Filter out unwanted data in getProperties to reduce protocol traffic; r=miker (583bdcf768)
- Bug 1262324 (part 1) - Remove "locale.all" prefix from Unix charsets. r=emk. (b337137c3d)
- Bug 1255655 - Const-ify dom encodings and similar arrays. r=baku. (93d79e84a7)
- Bug 1262324 (part 2) - Introduce nsUConvProp. r=emk. (bad497dee6)
- Bug 1206420 - Display animated pseudo-elements in the animation-inspector; r=tromey (bbc2795e22)
- Bug 1257877 - Remove UTF-16 support from TextEncoder. r=hsivonen (ad647b12a3)
- Bug 1120813 - Add support for the MS932 label of Shift_JIS. r=emk, r=Ms2ger (8a35fd93cf)
- Bug 1250930 - Use correct global when creating a key in GenerateAsymmetricKeyTask r=bz (ba65e0ae34)
- Bug 842818 - Allow calling WebCryptoTask::DispatchWithPromise() from workers r=keeler (e99301ba3e)
- Bug 1251390. Make timer queries available at the appropriate time. r=jgilbert (f1a0dcf7d3)
- Bug 1259696 - Check read buffer mode when doing CopyTexImage. r=jgilbert (fe51211b56)
- Bug 1241042 - Get correct frag varying from angle validator. r=jmuizelaar (29a4298a7e)
- Bug 1244611 - "Using named uniform buffer objects in the fragment shader fails". r=jgilbert (2e05e31d09)
- Bug 1263018 - Only update active prog info if linking active prog. - r=jrmuizel (1603ebdd8c)
- Bug 1257593 - Handle webgl FramebufferTexture2D() with an unbound texture. r=jgilbert (4d75981cb7)
- Bug 1257593 followup, fix bustage from believing a reviewer and then sending a different patch to try (7766b0cdb4)
- Bug 1258061 - Clarify FramebufferTexture2D handling for tex2D and cubeMap; r=jgilbert (0635d9412a)
- Bug 1240762 - Fix ServiceWorker request coming from Cache/Fetch. r=vporof (0251ffbb9f)
- remove PM hack (c6a751ac41)
- Bug 1230544 - JSON Viewer: support for .json URL extensions; r=jryans (32930bc41a)
- Bug 1235118 - JSON Viewer: remove support for .json URL extensions; r=jryans (626b490083)
- Bug 1255799 - make devtools/client/webconsole eslint-clean; r=odvarko (f4f85bdc4c)
- Bug 1240896 - Reorder imports to match style. r=gl (8f69df841c)
- Bug 1240896 - Port browser_responsive_cmd.js, add GCLI resize support. r=gl (33ce902874)
- Bug 1237986 - Avoid timeouts when the inspector opens in devtools mochitests; r=miker (2dd794f43a)
- Bug 1244755 - 1 - Enable browser_layout.js with e10s by using the testActor; r=miker (94504c2c96)
- Bug 1244755 - 2 - Remove addTest logic from layout-view tests for better consistency; r=miker (d3058ca6d5)
- Bug 1244755 - 3 - Remove CPOW usages and eslint warnings from devtools/client/inspector/layout; r=miker (5b7274dbc5)
- Bug 1239438 - fix most eslint warnings in styles.js; r=pbro (b03014105e)
- Bug 1139187 - Allow moving and resizing elements in content; r=pbro (b95284a4c4)
- Bug 1252099 - Do not use CPOWs in browser_markup_copy_image_data.js; r=jdescottes (5becee8341)
- Bug 1259449 - Require GLFeature::sync for WebGL's disjoint_timer_query. - r=jrmuizel (b6017134a3)
- Bug 1247804 - Enable seamless cubemaps where available. - r=jrmuizel (e7fd3ec5ed)
- Bug 1247977: More information when we hit the OpenGL error in FakeBlackTexture. r=jgilbert (f6f2c82e33)
- Bug 1261179 - Don't fallback GLContextProviders in WebGL. - r=jrmuizel (296223131f)
- Bug 1255655 - Const-ify sExtensionNames. r=mattwoodrow. (c0c60b34c7)
- Bug 1262757 - Use StaticRefPtr for the global context in GLContextProviderWGL, r=jrmuizel (0cc29a3385)
- Bug 1259811 - Require FBO support for GLContexts. - r=jrmuizel (ec3c1a6045)
- Bug 1234441 - Allow malformed ESSL version string. - r=jrmuizel (8fda1569ab)
- Bug 1199923 - Work around Intel Linux driver lying about max texture size - r=jgilbert (546b7dfe6a)
- Bug 1262265 - Cleanup GLContext symbol init. - r=jrmuizel (8da8ce647a)
- Bug 1232334 - [1.2] Only set context on successful attach. r=snorp (ca29c322bf)
- Bug 1261320 - Check DataSurface is vaild before using, r=milan (be9aebba47)
- Bug 1245868 - repalce pass by value with pass by pointer in IsHeadRequest. r=jst (f9d7f6185f)
- fix tests (3ccf9d1715)
- Bug 1232941 - Register Observer and listen to NS_XPCOM_SHUTDOWN_OBSERVER_ID for GATT, r=shuang (aa367807fa)
- Bug 1239979: Init and uninit all Bluetooth profile managers, r=shuang (78c1ebbd13)
- Bug 1239979: Uninitialized Bluetooth profile managers explictly to release refs, r=shuang (83b5389539)
- Bug 1262630. Replace workers::GetGlobalObjectForGlobal with xpc::NativeGlobal. r=khuey (9f639580be)
- Bug 1255817 part 6. Fix up some comments in CallbackObject that refer to things that no longer exist. r=bholley (575bf90c98)
- Bug 1259545. Remove the JS_SaveFrameChain bits in CallbackObject error reporting, since they are no longer needed. r=bholley (16181ddb38)
- Bug 1260511 part 1. Fix WebContentConverter to behave consistently in both e10s and non-e10s mode in terms of the argument sanity checking registerContentHandler does. r=gijs (1b24614bab)
- Bug 1260511 part 2. Change XPConnect's handling of exceptions thrown from JS components so that if an nsresult integer is thrown we convert it into an actual exception object before handing it out to content instead of propagating out the numeric value. r=khuey (dc02854e3f)
- Bug 1260511 part 3. In dom::Throw, ignore the pending xpconnect exception if we were given a non-default message string (because we don't want to lose that string). Also, make sure to always clear the pending xpconnect exception there. r=khuey (978a1e9132)
- Bug 1193093 - Tighten up these checks a little. r=Gijs (0ba7ebd060)
- Bug 1260511 part 4. Fix some of the places where registerProtocolHandler should be throwing a SECURITY_ERR to actually do so. r=gijs (11e6c5bfee)
- Bug 1130449 - fix reuse of accesskey string for protocol handler notification "Add" button, r=gijs (8c0c45aee5)
- Bug 1263045 - Avoid JavaScript strict warning in WebContentConverter.js during startup. r=Gijs (0948bbae40)
- Bug 1249702 - Fix e10s feed handling with auto content handler prefs. r=Gijs (7f0d9085f3)
- Bug 1250106 - Correctly set charging status and remaining time when battery level is rounded to 1.0 r=bz (662e18648c)
- Bug 1253641 - DOMException's CC participant should traverse mData. r=khuey. (0a48e3f8e7)
- Bug 1261115 - when Console is running in the main thread the existence of mWindow should always be ensured, r=smaug (caa1efd087)
- Bug 1257208 - Use the nsTextNode concrete type in several places in DirectionalityUtils.cpp instead of nsINode and nsIContent; r=peterv (69529fd8d4)
- Bug 1260982 - BlobFileImpl::GetType() should work also in workers, r=smaug (4b01d269bc)
- Bug 1262104 - Remove a non-used CTOR for BlobImplFile, r=ehsan (37ee0ec6d4)
- Bug 1259477 - Port test_document_register.html to mochitest-plain so that it can be turned on in e10s mode; r=mrbkap (411c220cda)
- Bug 1222128 - Turn test_bug1011748.html into a browser mochitest to make it run properly in e10s mode r=bzbarsky (45403d3d15)
- Bug 1259588 - new File("") throws TypeError exception, r=baku (05f6e7292d)
- Bug 1264710 - Catch IDB exceptions in IndexedDBHelper. r=fabrice (0d3c860a89)
- Bug 1263553 - Move MultipartBlobImpl into mozilla::dom namespace, r=smaug (e286c6cfe1)
- Bug 1263551 - Remove unused method in MultipartBlobImpl, r=smaug (54c2da9a12)
- Bug 1252687 - get rid of static nsStrings in PerformanceObserver.cpp r=bz (319f2697d4)
- Bug 1148535 - Check if the density descriptor in srcset consists of a valid floating-point number. r=jdm (79ac8d8dea)
- Bug 1257742 - Part 1: Follow the update-source-set rule to append default source into source set; r=jdm (853c69cc8c)
- Bug 1257742 - Part 2: Allow both width and static density candidates showing in same selector; r=jdm (4f1e00225d)
- Bug 1257742 - Part 3: Support using floating point in sizes descriptor; r=jdm (c1a7e36bb3)
- Bug 1257742 - Part 4: Update web-platform test expectation; r=jdm (e41044b88e)
- Bug 1262942 - Remove unnecessary warning message in ResponsiveImageSelector; r=jdm (ffb757204d)
- Bug 1158412 - Remove assertion for document prescontex and add crash test; r=jdm (947ccdfbfc)
- Bug 1237633 - Part 1: Percentages are not allowed in a <source-size-value>. r=jdm (614b560097)
- Bug 1237633 - Part 2: Avoid fatal assertion when a responsive image's size specifier is invalid. r=johns, r=jdm (4e90829d97)
- Bug 495546 - Add crashtest. (c2765ecbf4)
- fix some tests (d9b393b168)
- Bug 1256419. Null-check our nsDOMWindowList before trying to get its length. r=smaug (6c14430e5d)
- Bug 1162775: Make contentAreaDropListener use dataTransfer.files to get the files dropped. r=smaug (d2850f2008)
- Bug 1220679 - replace AutoSafeJSContext with AutoJSAPI. r=bz. (64538bdd44)
- Bug 1239047 - Make comment nodes draggable in the markup-view; r=miker (d829b881c0)
- reinstantiate assert, present in up to esr68 (221cec538a)
- Bug 1209329 - Improve comments about about: URIs in nsContentUtils::InternalStorageAllowedForPrincipal, r=bholley (71152e5639)
- Bug 1246250 - Deal with failure to create a blob actor. r=khuey (102686ac28)
- Bug 1265902 - part 1 - be more efficient when using nsContentUtils::GetSurfaceData(); r=mccr8 (ba2a52abd3)
- Bug 1258857 - Add empty items to an IPC transferable object for every flavor of the source object that did not have any data associated to it r=enndeakin (0a02b61566)
- Bug 1265902 - part 2 - don't construct unnecessary string temporaries in TransferablesToIPCTransferables; r=mccr8 (aec10c8fc6)
- remove unknown blob handling, not found in Tycho-dev repo, nor esr60 or TFF (eb6a24720a)
- Bug 1155486 - Convert nsDOMAttributeMap::mLocalName to void* to ensure that we can never dereference it; r=baku (34e2864340)
- Bug 1250926 - Remove unused SCRIPTABLE_FLAGS defines from nsDOMClassInfo; r=peterv (76917fb76b)
- No bug. Helper tool to partially autogenerate portions of the release notes. (3818e5534b)
- Bug 1203423 - Move call to AddClone outside nsMutationReceiver constructor; r=smaug (35c94ad785)
- Bug 1254096 - Update CaretPositionFromPoint() for type=number, r=ehsan (df31edca8f)
- Bug 1265771 P1 Only store active documents in the global observer list. r=bz (01502e91e5)
- Bug 1265771 P2 Expand navigate-window.https.html wpt test to cover uncontrolled windows. r=bz (3333906720)
- Bug 1265771 P3 Expand browser_force_refresh.js to verify Clients.matchAll() behavior on refresh. r=bz (01394ec8f1)
- stop hiding things for _LIBCPP_VERSION (5de86e8bbf)
1464 lines
41 KiB
JavaScript
1464 lines
41 KiB
JavaScript
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
|
/* vim: set ft= javascript ts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
"use strict";
|
|
|
|
const {Cc, Ci, Cu, components} = require("chrome");
|
|
const {isWindowIncluded} = require("devtools/shared/layout/utils");
|
|
const Services = require("Services");
|
|
|
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
|
|
// TODO: Bug 842672 - browser/ imports modules from toolkit/.
|
|
// Note that these are only used in WebConsoleCommands, see $0 and pprint().
|
|
loader.lazyImporter(this, "VariablesView", "resource://devtools/client/shared/widgets/VariablesView.jsm");
|
|
|
|
XPCOMUtils.defineLazyServiceGetter(this,
|
|
"swm",
|
|
"@mozilla.org/serviceworkers/manager;1",
|
|
"nsIServiceWorkerManager");
|
|
|
|
// Match the function name from the result of toString() or toSource().
|
|
//
|
|
// Examples:
|
|
// (function foobar(a, b) { ...
|
|
// function foobar2(a) { ...
|
|
// function() { ...
|
|
const REGEX_MATCH_FUNCTION_NAME = /^\(?function\s+([^(\s]+)\s*\(/;
|
|
|
|
// Number of terminal entries for the self-xss prevention to go away
|
|
const CONSOLE_ENTRY_THRESHOLD = 5;
|
|
|
|
const CONSOLE_WORKER_IDS = exports.CONSOLE_WORKER_IDS = [
|
|
"SharedWorker",
|
|
"ServiceWorker",
|
|
"Worker"
|
|
];
|
|
|
|
var WebConsoleUtils = {
|
|
|
|
/**
|
|
* Wrap a string in an nsISupportsString object.
|
|
*
|
|
* @param string string
|
|
* @return nsISupportsString
|
|
*/
|
|
supportsString: function(string) {
|
|
let str = Cc["@mozilla.org/supports-string;1"]
|
|
.createInstance(Ci.nsISupportsString);
|
|
str.data = string;
|
|
return str;
|
|
},
|
|
|
|
/**
|
|
* Given a message, return one of CONSOLE_WORKER_IDS if it matches
|
|
* one of those.
|
|
*
|
|
* @return string
|
|
*/
|
|
getWorkerType: function(message) {
|
|
let id = message ? message.innerID : null;
|
|
return CONSOLE_WORKER_IDS[CONSOLE_WORKER_IDS.indexOf(id)] || null;
|
|
},
|
|
|
|
/**
|
|
* Clone an object.
|
|
*
|
|
* @param object object
|
|
* The object you want cloned.
|
|
* @param boolean recursive
|
|
* Tells if you want to dig deeper into the object, to clone
|
|
* recursively.
|
|
* @param function [filter]
|
|
* Optional, filter function, called for every property. Three
|
|
* arguments are passed: key, value and object. Return true if the
|
|
* property should be added to the cloned object. Return false to skip
|
|
* the property.
|
|
* @return object
|
|
* The cloned object.
|
|
*/
|
|
cloneObject: function(object, recursive, filter) {
|
|
if (typeof object != "object") {
|
|
return object;
|
|
}
|
|
|
|
let temp;
|
|
|
|
if (Array.isArray(object)) {
|
|
temp = [];
|
|
Array.forEach(object, function(value, index) {
|
|
if (!filter || filter(index, value, object)) {
|
|
temp.push(recursive ? WebConsoleUtils.cloneObject(value) : value);
|
|
}
|
|
});
|
|
} else {
|
|
temp = {};
|
|
for (let key in object) {
|
|
let value = object[key];
|
|
if (object.hasOwnProperty(key) &&
|
|
(!filter || filter(key, value, object))) {
|
|
temp[key] = recursive ? WebConsoleUtils.cloneObject(value) : value;
|
|
}
|
|
}
|
|
}
|
|
|
|
return temp;
|
|
},
|
|
|
|
/**
|
|
* Copies certain style attributes from one element to another.
|
|
*
|
|
* @param nsIDOMNode from
|
|
* The target node.
|
|
* @param nsIDOMNode to
|
|
* The destination node.
|
|
*/
|
|
copyTextStyles: function(from, to) {
|
|
let win = from.ownerDocument.defaultView;
|
|
let style = win.getComputedStyle(from);
|
|
to.style.fontFamily = style.getPropertyCSSValue("font-family").cssText;
|
|
to.style.fontSize = style.getPropertyCSSValue("font-size").cssText;
|
|
to.style.fontWeight = style.getPropertyCSSValue("font-weight").cssText;
|
|
to.style.fontStyle = style.getPropertyCSSValue("font-style").cssText;
|
|
},
|
|
|
|
/**
|
|
* Gets the ID of the inner window of this DOM window.
|
|
*
|
|
* @param nsIDOMWindow window
|
|
* @return integer
|
|
* Inner ID for the given window.
|
|
*/
|
|
getInnerWindowId: function(window) {
|
|
return window.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
.getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
|
|
},
|
|
|
|
/**
|
|
* Recursively gather a list of inner window ids given a
|
|
* top level window.
|
|
*
|
|
* @param nsIDOMWindow window
|
|
* @return Array
|
|
* list of inner window ids.
|
|
*/
|
|
getInnerWindowIDsForFrames: function(window) {
|
|
let innerWindowID = this.getInnerWindowId(window);
|
|
let ids = [innerWindowID];
|
|
|
|
if (window.frames) {
|
|
for (let i = 0; i < window.frames.length; i++) {
|
|
let frame = window.frames[i];
|
|
ids = ids.concat(this.getInnerWindowIDsForFrames(frame));
|
|
}
|
|
}
|
|
|
|
return ids;
|
|
},
|
|
|
|
/**
|
|
* Gets the ID of the outer window of this DOM window.
|
|
*
|
|
* @param nsIDOMWindow window
|
|
* @return integer
|
|
* Outer ID for the given window.
|
|
*/
|
|
getOuterWindowId: function(window) {
|
|
return window.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
.getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
|
|
},
|
|
|
|
/**
|
|
* Tells if the given function is native or not.
|
|
*
|
|
* @param function func
|
|
* The function you want to check if it is native or not.
|
|
* @return boolean
|
|
* True if the given function is native, false otherwise.
|
|
*/
|
|
isNativeFunction: function(func) {
|
|
return typeof func == "function" && !("prototype" in func);
|
|
},
|
|
|
|
/**
|
|
* Tells if the given property of the provided object is a
|
|
* non-native getter or not.
|
|
*
|
|
* @param object object
|
|
* The object that contains the property.
|
|
* @param string prop
|
|
* The property you want to check if it is a getter or not.
|
|
* @return boolean
|
|
* True if the given property is a getter, false otherwise.
|
|
*/
|
|
isNonNativeGetter: function(object, prop) {
|
|
if (typeof object != "object") {
|
|
return false;
|
|
}
|
|
let desc = this.getPropertyDescriptor(object, prop);
|
|
return desc && desc.get && !this.isNativeFunction(desc.get);
|
|
},
|
|
|
|
/**
|
|
* Get the property descriptor for the given object.
|
|
*
|
|
* @param object object
|
|
* The object that contains the property.
|
|
* @param string prop
|
|
* The property you want to get the descriptor for.
|
|
* @return object
|
|
* Property descriptor.
|
|
*/
|
|
getPropertyDescriptor: function(object, prop) {
|
|
let desc = null;
|
|
while (object) {
|
|
try {
|
|
if ((desc = Object.getOwnPropertyDescriptor(object, prop))) {
|
|
break;
|
|
}
|
|
} catch (ex) {
|
|
// Native getters throw here. See bug 520882.
|
|
// null throws TypeError.
|
|
if (ex.name != "NS_ERROR_XPC_BAD_CONVERT_JS" &&
|
|
ex.name != "NS_ERROR_XPC_BAD_OP_ON_WN_PROTO" &&
|
|
ex.name != "TypeError") {
|
|
throw ex;
|
|
}
|
|
}
|
|
|
|
try {
|
|
object = Object.getPrototypeOf(object);
|
|
} catch (ex) {
|
|
if (ex.name == "TypeError") {
|
|
return desc;
|
|
}
|
|
throw ex;
|
|
}
|
|
}
|
|
return desc;
|
|
},
|
|
|
|
/**
|
|
* Sort function for object properties.
|
|
*
|
|
* @param object a
|
|
* Property descriptor.
|
|
* @param object b
|
|
* Property descriptor.
|
|
* @return integer
|
|
* -1 if a.name < b.name,
|
|
* 1 if a.name > b.name,
|
|
* 0 otherwise.
|
|
*/
|
|
propertiesSort: function(a, b) {
|
|
// Convert the pair.name to a number for later sorting.
|
|
let number = parseFloat(a.name);
|
|
let bNumber = parseFloat(b.name);
|
|
|
|
// Sort numbers, then string.
|
|
if (!isNaN(number) && isNaN(bNumber)) {
|
|
return -1;
|
|
} else if (isNaN(number) && !isNaN(bNumber)) {
|
|
return 1;
|
|
} else if (!isNaN(number) && !isNaN(bNumber)) {
|
|
return number - bNumber;
|
|
} else if (a.name < b.name) {
|
|
return -1;
|
|
} else if (a.name > b.name) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
},
|
|
|
|
/**
|
|
* Create a grip for the given value. If the value is an object,
|
|
* an object wrapper will be created.
|
|
*
|
|
* @param mixed value
|
|
* The value you want to create a grip for, before sending it to the
|
|
* client.
|
|
* @param function objectWrapper
|
|
* If the value is an object then the objectWrapper function is
|
|
* invoked to give us an object grip. See this.getObjectGrip().
|
|
* @return mixed
|
|
* The value grip.
|
|
*/
|
|
createValueGrip: function(value, objectWrapper) {
|
|
switch (typeof value) {
|
|
case "boolean":
|
|
return value;
|
|
case "string":
|
|
return objectWrapper(value);
|
|
case "number":
|
|
if (value === Infinity) {
|
|
return { type: "Infinity" };
|
|
} else if (value === -Infinity) {
|
|
return { type: "-Infinity" };
|
|
} else if (Number.isNaN(value)) {
|
|
return { type: "NaN" };
|
|
} else if (!value && 1 / value === -Infinity) {
|
|
return { type: "-0" };
|
|
}
|
|
return value;
|
|
case "undefined":
|
|
return { type: "undefined" };
|
|
case "object":
|
|
if (value === null) {
|
|
return { type: "null" };
|
|
}
|
|
// Fall through.
|
|
case "function":
|
|
return objectWrapper(value);
|
|
default:
|
|
Cu.reportError("Failed to provide a grip for value of " + typeof value
|
|
+ ": " + value);
|
|
return null;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Check if the given object is an iterator or a generator.
|
|
*
|
|
* @param object object
|
|
* The object you want to check.
|
|
* @return boolean
|
|
* True if the given object is an iterator or a generator, otherwise
|
|
* false is returned.
|
|
*/
|
|
isIteratorOrGenerator: function(object) {
|
|
if (object === null) {
|
|
return false;
|
|
}
|
|
|
|
if (typeof object == "object") {
|
|
if (typeof object.__iterator__ == "function" ||
|
|
object.constructor && object.constructor.name == "Iterator") {
|
|
return true;
|
|
}
|
|
|
|
try {
|
|
let str = object.toString();
|
|
if (typeof object.next == "function" &&
|
|
str.indexOf("[object Generator") == 0) {
|
|
return true;
|
|
}
|
|
} catch (ex) {
|
|
// window.history.next throws in the typeof check above.
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
},
|
|
|
|
/**
|
|
* Determine if the given request mixes HTTP with HTTPS content.
|
|
*
|
|
* @param string request
|
|
* Location of the requested content.
|
|
* @param string location
|
|
* Location of the current page.
|
|
* @return boolean
|
|
* True if the content is mixed, false if not.
|
|
*/
|
|
isMixedHTTPSRequest: function(request, location) {
|
|
try {
|
|
let requestURI = Services.io.newURI(request, null, null);
|
|
let contentURI = Services.io.newURI(location, null, null);
|
|
return (contentURI.scheme == "https" && requestURI.scheme != "https");
|
|
} catch (ex) {
|
|
return false;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Helper function to deduce the name of the provided function.
|
|
*
|
|
* @param funtion function
|
|
* The function whose name will be returned.
|
|
* @return string
|
|
* Function name.
|
|
*/
|
|
getFunctionName: function(func) {
|
|
let name = null;
|
|
if (func.name) {
|
|
name = func.name;
|
|
} else {
|
|
let desc;
|
|
try {
|
|
desc = func.getOwnPropertyDescriptor("displayName");
|
|
} catch (ex) {
|
|
// Ignore.
|
|
}
|
|
if (desc && typeof desc.value == "string") {
|
|
name = desc.value;
|
|
}
|
|
}
|
|
if (!name) {
|
|
try {
|
|
let str = (func.toString() || func.toSource()) + "";
|
|
name = (str.match(REGEX_MATCH_FUNCTION_NAME) || [])[1];
|
|
} catch (ex) {
|
|
// Ignore.
|
|
}
|
|
}
|
|
return name;
|
|
},
|
|
|
|
/**
|
|
* Get the object class name. For example, the |window| object has the Window
|
|
* class name (based on [object Window]).
|
|
*
|
|
* @param object object
|
|
* The object you want to get the class name for.
|
|
* @return string
|
|
* The object class name.
|
|
*/
|
|
getObjectClassName: function(object) {
|
|
if (object === null) {
|
|
return "null";
|
|
}
|
|
if (object === undefined) {
|
|
return "undefined";
|
|
}
|
|
|
|
let type = typeof object;
|
|
if (type != "object") {
|
|
// Grip class names should start with an uppercase letter.
|
|
return type.charAt(0).toUpperCase() + type.substr(1);
|
|
}
|
|
|
|
let className;
|
|
|
|
try {
|
|
className = ((object + "").match(/^\[object (\S+)\]$/) || [])[1];
|
|
if (!className) {
|
|
className = ((object.constructor + "")
|
|
.match(/^\[object (\S+)\]$/) || [])[1];
|
|
}
|
|
if (!className && typeof object.constructor == "function") {
|
|
className = this.getFunctionName(object.constructor);
|
|
}
|
|
} catch (ex) {
|
|
// Ignore.
|
|
}
|
|
|
|
return className;
|
|
},
|
|
|
|
/**
|
|
* Check if the given value is a grip with an actor.
|
|
*
|
|
* @param mixed grip
|
|
* Value you want to check if it is a grip with an actor.
|
|
* @return boolean
|
|
* True if the given value is a grip with an actor.
|
|
*/
|
|
isActorGrip: function(grip) {
|
|
return grip && typeof (grip) == "object" && grip.actor;
|
|
},
|
|
|
|
/**
|
|
* Value of devtools.selfxss.count preference
|
|
*
|
|
* @type number
|
|
* @private
|
|
*/
|
|
_usageCount: 0,
|
|
get usageCount() {
|
|
if (WebConsoleUtils._usageCount < CONSOLE_ENTRY_THRESHOLD) {
|
|
WebConsoleUtils._usageCount =
|
|
Services.prefs.getIntPref("devtools.selfxss.count");
|
|
if (Services.prefs.getBoolPref("devtools.chrome.enabled")) {
|
|
WebConsoleUtils.usageCount = CONSOLE_ENTRY_THRESHOLD;
|
|
}
|
|
}
|
|
return WebConsoleUtils._usageCount;
|
|
},
|
|
set usageCount(newUC) {
|
|
if (newUC <= CONSOLE_ENTRY_THRESHOLD) {
|
|
WebConsoleUtils._usageCount = newUC;
|
|
Services.prefs.setIntPref("devtools.selfxss.count", newUC);
|
|
}
|
|
},
|
|
/**
|
|
* The inputNode "paste" event handler generator. Helps prevent
|
|
* self-xss attacks
|
|
*
|
|
* @param nsIDOMElement inputField
|
|
* @param nsIDOMElement notificationBox
|
|
* @returns A function to be added as a handler to 'paste' and
|
|
*'drop' events on the input field
|
|
*/
|
|
pasteHandlerGen: function(inputField, notificationBox, msg, okstring) {
|
|
let handler = function(event) {
|
|
if (WebConsoleUtils.usageCount >= CONSOLE_ENTRY_THRESHOLD) {
|
|
inputField.removeEventListener("paste", handler);
|
|
inputField.removeEventListener("drop", handler);
|
|
return true;
|
|
}
|
|
if (notificationBox.getNotificationWithValue("selfxss-notification")) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
return false;
|
|
}
|
|
|
|
let notification = notificationBox.appendNotification(msg,
|
|
"selfxss-notification", null,
|
|
notificationBox.PRIORITY_WARNING_HIGH, null,
|
|
function(eventType) {
|
|
// Cleanup function if notification is dismissed
|
|
if (eventType == "removed") {
|
|
inputField.removeEventListener("keyup", pasteKeyUpHandler);
|
|
}
|
|
});
|
|
|
|
function pasteKeyUpHandler(event2) {
|
|
let value = inputField.value || inputField.textContent;
|
|
if (value.includes(okstring)) {
|
|
notificationBox.removeNotification(notification);
|
|
inputField.removeEventListener("keyup", pasteKeyUpHandler);
|
|
WebConsoleUtils.usageCount = CONSOLE_ENTRY_THRESHOLD;
|
|
}
|
|
}
|
|
inputField.addEventListener("keyup", pasteKeyUpHandler);
|
|
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
return false;
|
|
};
|
|
return handler;
|
|
},
|
|
};
|
|
|
|
exports.Utils = WebConsoleUtils;
|
|
|
|
// ////////////////////////////////////////////////////////////////////////
|
|
// Localization
|
|
// ////////////////////////////////////////////////////////////////////////
|
|
|
|
WebConsoleUtils.L10n = function(bundleURI) {
|
|
this._bundleUri = bundleURI;
|
|
};
|
|
|
|
WebConsoleUtils.L10n.prototype = {
|
|
_stringBundle: null,
|
|
|
|
get stringBundle() {
|
|
if (!this._stringBundle) {
|
|
this._stringBundle = Services.strings.createBundle(this._bundleUri);
|
|
}
|
|
return this._stringBundle;
|
|
},
|
|
|
|
/**
|
|
* Generates a formatted timestamp string for displaying in console messages.
|
|
*
|
|
* @param integer [milliseconds]
|
|
* Optional, allows you to specify the timestamp in milliseconds since
|
|
* the UNIX epoch.
|
|
* @return string
|
|
* The timestamp formatted for display.
|
|
*/
|
|
timestampString: function(milliseconds) {
|
|
let d = new Date(milliseconds ? milliseconds : null);
|
|
let hours = d.getHours(), minutes = d.getMinutes();
|
|
let seconds = d.getSeconds();
|
|
milliseconds = d.getMilliseconds();
|
|
let parameters = [hours, minutes, seconds, milliseconds];
|
|
return this.getFormatStr("timestampFormat", parameters);
|
|
},
|
|
|
|
/**
|
|
* Retrieve a localized string.
|
|
*
|
|
* @param string name
|
|
* The string name you want from the Web Console string bundle.
|
|
* @return string
|
|
* The localized string.
|
|
*/
|
|
getStr: function(name) {
|
|
let result;
|
|
try {
|
|
result = this.stringBundle.GetStringFromName(name);
|
|
} catch (ex) {
|
|
Cu.reportError("Failed to get string: " + name);
|
|
throw ex;
|
|
}
|
|
return result;
|
|
},
|
|
|
|
/**
|
|
* Retrieve a localized string formatted with values coming from the given
|
|
* array.
|
|
*
|
|
* @param string name
|
|
* The string name you want from the Web Console string bundle.
|
|
* @param array array
|
|
* The array of values you want in the formatted string.
|
|
* @return string
|
|
* The formatted local string.
|
|
*/
|
|
getFormatStr: function(name, array) {
|
|
let result;
|
|
try {
|
|
result = this.stringBundle.formatStringFromName(name, array,
|
|
array.length);
|
|
} catch (ex) {
|
|
Cu.reportError("Failed to format string: " + name);
|
|
throw ex;
|
|
}
|
|
return result;
|
|
},
|
|
};
|
|
|
|
// /////////////////////////////////////////////////////////////////////////////
|
|
// The page errors listener
|
|
// /////////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* The nsIConsoleService listener. This is used to send all of the console
|
|
* messages (JavaScript, CSS and more) to the remote Web Console instance.
|
|
*
|
|
* @constructor
|
|
* @param nsIDOMWindow [window]
|
|
* Optional - the window object for which we are created. This is used
|
|
* for filtering out messages that belong to other windows.
|
|
* @param object listener
|
|
* The listener object must have one method:
|
|
* - onConsoleServiceMessage(). This method is invoked with one argument,
|
|
* the nsIConsoleMessage, whenever a relevant message is received.
|
|
*/
|
|
function ConsoleServiceListener(window, listener) {
|
|
this.window = window;
|
|
this.listener = listener;
|
|
}
|
|
exports.ConsoleServiceListener = ConsoleServiceListener;
|
|
|
|
ConsoleServiceListener.prototype =
|
|
{
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsIConsoleListener]),
|
|
|
|
/**
|
|
* The content window for which we listen to page errors.
|
|
* @type nsIDOMWindow
|
|
*/
|
|
window: null,
|
|
|
|
/**
|
|
* The listener object which is notified of messages from the console service.
|
|
* @type object
|
|
*/
|
|
listener: null,
|
|
|
|
/**
|
|
* Initialize the nsIConsoleService listener.
|
|
*/
|
|
init: function() {
|
|
Services.console.registerListener(this);
|
|
},
|
|
|
|
/**
|
|
* The nsIConsoleService observer. This method takes all the script error
|
|
* messages belonging to the current window and sends them to the remote Web
|
|
* Console instance.
|
|
*
|
|
* @param nsIConsoleMessage message
|
|
* The message object coming from the nsIConsoleService.
|
|
*/
|
|
observe: function(message) {
|
|
if (!this.listener) {
|
|
return;
|
|
}
|
|
|
|
if (this.window) {
|
|
if (!(message instanceof Ci.nsIScriptError) ||
|
|
!message.outerWindowID ||
|
|
!this.isCategoryAllowed(message.category)) {
|
|
return;
|
|
}
|
|
|
|
let errorWindow = Services.wm.getOuterWindowWithId(message.outerWindowID);
|
|
if (!errorWindow || !isWindowIncluded(this.window, errorWindow)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
this.listener.onConsoleServiceMessage(message);
|
|
},
|
|
|
|
/**
|
|
* Check if the given message category is allowed to be tracked or not.
|
|
* We ignore chrome-originating errors as we only care about content.
|
|
*
|
|
* @param string category
|
|
* The message category you want to check.
|
|
* @return boolean
|
|
* True if the category is allowed to be logged, false otherwise.
|
|
*/
|
|
isCategoryAllowed: function(category) {
|
|
if (!category) {
|
|
return false;
|
|
}
|
|
|
|
switch (category) {
|
|
case "XPConnect JavaScript":
|
|
case "component javascript":
|
|
case "chrome javascript":
|
|
case "chrome registration":
|
|
case "XBL":
|
|
case "XBL Prototype Handler":
|
|
case "XBL Content Sink":
|
|
case "xbl javascript":
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
},
|
|
|
|
/**
|
|
* Get the cached page errors for the current inner window and its (i)frames.
|
|
*
|
|
* @param boolean [includePrivate=false]
|
|
* Tells if you want to also retrieve messages coming from private
|
|
* windows. Defaults to false.
|
|
* @return array
|
|
* The array of cached messages. Each element is an nsIScriptError or
|
|
* an nsIConsoleMessage
|
|
*/
|
|
getCachedMessages: function(includePrivate = false) {
|
|
let errors = Services.console.getMessageArray() || [];
|
|
|
|
// if !this.window, we're in a browser console. Still need to filter
|
|
// private messages.
|
|
if (!this.window) {
|
|
return errors.filter((error) => {
|
|
if (error instanceof Ci.nsIScriptError) {
|
|
if (!includePrivate && error.isFromPrivateWindow) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
});
|
|
}
|
|
|
|
let ids = WebConsoleUtils.getInnerWindowIDsForFrames(this.window);
|
|
|
|
return errors.filter((error) => {
|
|
if (error instanceof Ci.nsIScriptError) {
|
|
if (!includePrivate && error.isFromPrivateWindow) {
|
|
return false;
|
|
}
|
|
if (ids &&
|
|
(ids.indexOf(error.innerWindowID) == -1 ||
|
|
!this.isCategoryAllowed(error.category))) {
|
|
return false;
|
|
}
|
|
} else if (ids && ids[0]) {
|
|
// If this is not an nsIScriptError and we need to do window-based
|
|
// filtering we skip this message.
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Remove the nsIConsoleService listener.
|
|
*/
|
|
destroy: function() {
|
|
Services.console.unregisterListener(this);
|
|
this.listener = this.window = null;
|
|
},
|
|
};
|
|
|
|
// /////////////////////////////////////////////////////////////////////////////
|
|
// The window.console API observer
|
|
// /////////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* The window.console API observer. This allows the window.console API messages
|
|
* to be sent to the remote Web Console instance.
|
|
*
|
|
* @constructor
|
|
* @param nsIDOMWindow window
|
|
* Optional - the window object for which we are created. This is used
|
|
* for filtering out messages that belong to other windows.
|
|
* @param object owner
|
|
* The owner object must have the following methods:
|
|
* - onConsoleAPICall(). This method is invoked with one argument, the
|
|
* Console API message that comes from the observer service, whenever
|
|
* a relevant console API call is received.
|
|
* @param string consoleID
|
|
* Options - The consoleID that this listener should listen to
|
|
*/
|
|
function ConsoleAPIListener(window, owner, consoleID) {
|
|
this.window = window;
|
|
this.owner = owner;
|
|
this.consoleID = consoleID;
|
|
}
|
|
exports.ConsoleAPIListener = ConsoleAPIListener;
|
|
|
|
ConsoleAPIListener.prototype =
|
|
{
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
|
|
|
|
/**
|
|
* The content window for which we listen to window.console API calls.
|
|
* @type nsIDOMWindow
|
|
*/
|
|
window: null,
|
|
|
|
/**
|
|
* The owner object which is notified of window.console API calls. It must
|
|
* have a onConsoleAPICall method which is invoked with one argument: the
|
|
* console API call object that comes from the observer service.
|
|
*
|
|
* @type object
|
|
* @see WebConsoleActor
|
|
*/
|
|
owner: null,
|
|
|
|
/**
|
|
* The consoleID that we listen for. If not null then only messages from this
|
|
* console will be returned.
|
|
*/
|
|
consoleID: null,
|
|
|
|
/**
|
|
* Initialize the window.console API observer.
|
|
*/
|
|
init: function() {
|
|
// Note that the observer is process-wide. We will filter the messages as
|
|
// needed, see CAL_observe().
|
|
Services.obs.addObserver(this, "console-api-log-event", false);
|
|
},
|
|
|
|
/**
|
|
* The console API message observer. When messages are received from the
|
|
* observer service we forward them to the remote Web Console instance.
|
|
*
|
|
* @param object message
|
|
* The message object receives from the observer service.
|
|
* @param string topic
|
|
* The message topic received from the observer service.
|
|
*/
|
|
observe: function(message, topic) {
|
|
if (!this.owner) {
|
|
return;
|
|
}
|
|
|
|
// Here, wrappedJSObject is not a security wrapper but a property defined
|
|
// by the XPCOM component which allows us to unwrap the XPCOM interface and
|
|
// access the underlying JSObject.
|
|
let apiMessage = message.wrappedJSObject;
|
|
|
|
if (!this.isMessageRelevant(apiMessage)) {
|
|
return;
|
|
}
|
|
|
|
this.owner.onConsoleAPICall(apiMessage);
|
|
},
|
|
|
|
/**
|
|
* Given a message, return true if this window should show it and false
|
|
* if it should be ignored.
|
|
*
|
|
* @param message
|
|
* The message from the Storage Service
|
|
* @return bool
|
|
* Do we care about this message?
|
|
*/
|
|
isMessageRelevant: function(message) {
|
|
let workerType = WebConsoleUtils.getWorkerType(message);
|
|
|
|
if (this.window && workerType === "ServiceWorker") {
|
|
// For messages from Service Workers, message.ID is the
|
|
// scope, which can be used to determine whether it's controlling
|
|
// a window.
|
|
let scope = message.ID;
|
|
|
|
if (!swm.shouldReportToWindow(this.window, scope)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (this.window && !workerType) {
|
|
let msgWindow = Services.wm.getCurrentInnerWindowWithId(message.innerID);
|
|
if (!msgWindow || !isWindowIncluded(this.window, msgWindow)) {
|
|
// Not the same window!
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (this.consoleID && message.consoleID !== this.consoleID) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
},
|
|
|
|
/**
|
|
* Get the cached messages for the current inner window and its (i)frames.
|
|
*
|
|
* @param boolean [includePrivate=false]
|
|
* Tells if you want to also retrieve messages coming from private
|
|
* windows. Defaults to false.
|
|
* @return array
|
|
* The array of cached messages.
|
|
*/
|
|
getCachedMessages: function(includePrivate = false) {
|
|
let messages = [];
|
|
let ConsoleAPIStorage = Cc["@mozilla.org/consoleAPI-storage;1"]
|
|
.getService(Ci.nsIConsoleAPIStorage);
|
|
|
|
// if !this.window, we're in a browser console. Retrieve all events
|
|
// for filtering based on privacy.
|
|
if (!this.window) {
|
|
messages = ConsoleAPIStorage.getEvents();
|
|
} else {
|
|
let ids = WebConsoleUtils.getInnerWindowIDsForFrames(this.window);
|
|
ids.forEach((id) => {
|
|
messages = messages.concat(ConsoleAPIStorage.getEvents(id));
|
|
});
|
|
}
|
|
|
|
CONSOLE_WORKER_IDS.forEach((id) => {
|
|
messages = messages.concat(ConsoleAPIStorage.getEvents(id));
|
|
});
|
|
|
|
messages = messages.filter(msg => {
|
|
return this.isMessageRelevant(msg);
|
|
});
|
|
|
|
if (includePrivate) {
|
|
return messages;
|
|
}
|
|
|
|
return messages.filter((m) => !m.private);
|
|
},
|
|
|
|
/**
|
|
* Destroy the console API listener.
|
|
*/
|
|
destroy: function() {
|
|
Services.obs.removeObserver(this, "console-api-log-event");
|
|
this.window = this.owner = null;
|
|
},
|
|
};
|
|
|
|
/**
|
|
* WebConsole commands manager.
|
|
*
|
|
* Defines a set of functions /variables ("commands") that are available from
|
|
* the Web Console but not from the web page.
|
|
*
|
|
*/
|
|
var WebConsoleCommands = {
|
|
_registeredCommands: new Map(),
|
|
_originalCommands: new Map(),
|
|
|
|
/**
|
|
* @private
|
|
* Reserved for built-in commands. To register a command from the code of an
|
|
* add-on, see WebConsoleCommands.register instead.
|
|
*
|
|
* @see WebConsoleCommands.register
|
|
*/
|
|
_registerOriginal: function(name, command) {
|
|
this.register(name, command);
|
|
this._originalCommands.set(name, this.getCommand(name));
|
|
},
|
|
|
|
/**
|
|
* Register a new command.
|
|
* @param {string} name The command name (exemple: "$")
|
|
* @param {(function|object)} command The command to register.
|
|
* It can be a function so the command is a function (like "$()"),
|
|
* or it can also be a property descriptor to describe a getter / value (like
|
|
* "$0").
|
|
*
|
|
* The command function or the command getter are passed a owner object as
|
|
* their first parameter (see the example below).
|
|
*
|
|
* Note that setters don't work currently and "enumerable" and "configurable"
|
|
* are forced to true.
|
|
*
|
|
* @example
|
|
*
|
|
* WebConsoleCommands.register("$", function JSTH_$(owner, selector)
|
|
* {
|
|
* return owner.window.document.querySelector(selector);
|
|
* });
|
|
*
|
|
* WebConsoleCommands.register("$0", {
|
|
* get: function(owner) {
|
|
* return owner.makeDebuggeeValue(owner.selectedNode);
|
|
* }
|
|
* });
|
|
*/
|
|
register: function(name, command) {
|
|
this._registeredCommands.set(name, command);
|
|
},
|
|
|
|
/**
|
|
* Unregister a command.
|
|
*
|
|
* If the command being unregister overrode a built-in command,
|
|
* the latter is restored.
|
|
*
|
|
* @param {string} name The name of the command
|
|
*/
|
|
unregister: function(name) {
|
|
this._registeredCommands.delete(name);
|
|
if (this._originalCommands.has(name)) {
|
|
this.register(name, this._originalCommands.get(name));
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Returns a command by its name.
|
|
*
|
|
* @param {string} name The name of the command.
|
|
*
|
|
* @return {(function|object)} The command.
|
|
*/
|
|
getCommand: function(name) {
|
|
return this._registeredCommands.get(name);
|
|
},
|
|
|
|
/**
|
|
* Returns true if a command is registered with the given name.
|
|
*
|
|
* @param {string} name The name of the command.
|
|
*
|
|
* @return {boolean} True if the command is registered.
|
|
*/
|
|
hasCommand: function(name) {
|
|
return this._registeredCommands.has(name);
|
|
},
|
|
};
|
|
|
|
exports.WebConsoleCommands = WebConsoleCommands;
|
|
|
|
/*
|
|
* Built-in commands.
|
|
*
|
|
* A list of helper functions used by Firebug can be found here:
|
|
* http://getfirebug.com/wiki/index.php/Command_Line_API
|
|
*/
|
|
|
|
/**
|
|
* Find a node by ID.
|
|
*
|
|
* @param string id
|
|
* The ID of the element you want.
|
|
* @return nsIDOMNode or null
|
|
* The result of calling document.querySelector(selector).
|
|
*/
|
|
WebConsoleCommands._registerOriginal("$", function(owner, selector) {
|
|
return owner.window.document.querySelector(selector);
|
|
});
|
|
|
|
/**
|
|
* Find the nodes matching a CSS selector.
|
|
*
|
|
* @param string selector
|
|
* A string that is passed to window.document.querySelectorAll.
|
|
* @return nsIDOMNodeList
|
|
* Returns the result of document.querySelectorAll(selector).
|
|
*/
|
|
WebConsoleCommands._registerOriginal("$$", function(owner, selector) {
|
|
let nodes = owner.window.document.querySelectorAll(selector);
|
|
|
|
// Calling owner.window.Array.from() doesn't work without accessing the
|
|
// wrappedJSObject, so just loop through the results instead.
|
|
let result = new owner.window.Array();
|
|
for (let i = 0; i < nodes.length; i++) {
|
|
result.push(nodes[i]);
|
|
}
|
|
return result;
|
|
});
|
|
|
|
/**
|
|
* Returns the result of the last console input evaluation
|
|
*
|
|
* @return object|undefined
|
|
* Returns last console evaluation or undefined
|
|
*/
|
|
WebConsoleCommands._registerOriginal("$_", {
|
|
get: function(owner) {
|
|
return owner.consoleActor.getLastConsoleInputEvaluation();
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Runs an xPath query and returns all matched nodes.
|
|
*
|
|
* @param string xPath
|
|
* xPath search query to execute.
|
|
* @param [optional] nsIDOMNode context
|
|
* Context to run the xPath query on. Uses window.document if not set.
|
|
* @return array of nsIDOMNode
|
|
*/
|
|
WebConsoleCommands._registerOriginal("$x", function(owner, xPath, context) {
|
|
let nodes = new owner.window.Array();
|
|
|
|
// Not waiving Xrays, since we want the original Document.evaluate function,
|
|
// instead of anything that's been redefined.
|
|
let doc = owner.window.document;
|
|
context = context || doc;
|
|
|
|
let results = doc.evaluate(xPath, context, null,
|
|
Ci.nsIDOMXPathResult.ANY_TYPE, null);
|
|
let node;
|
|
while ((node = results.iterateNext())) {
|
|
nodes.push(node);
|
|
}
|
|
|
|
return nodes;
|
|
});
|
|
|
|
/**
|
|
* Returns the currently selected object in the highlighter.
|
|
*
|
|
* @return Object representing the current selection in the
|
|
* Inspector, or null if no selection exists.
|
|
*/
|
|
WebConsoleCommands._registerOriginal("$0", {
|
|
get: function(owner) {
|
|
return owner.makeDebuggeeValue(owner.selectedNode);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Clears the output of the WebConsole.
|
|
*/
|
|
WebConsoleCommands._registerOriginal("clear", function(owner) {
|
|
owner.helperResult = {
|
|
type: "clearOutput",
|
|
};
|
|
});
|
|
|
|
/**
|
|
* Clears the input history of the WebConsole.
|
|
*/
|
|
WebConsoleCommands._registerOriginal("clearHistory", function(owner) {
|
|
owner.helperResult = {
|
|
type: "clearHistory",
|
|
};
|
|
});
|
|
|
|
/**
|
|
* Returns the result of Object.keys(object).
|
|
*
|
|
* @param object object
|
|
* Object to return the property names from.
|
|
* @return array of strings
|
|
*/
|
|
WebConsoleCommands._registerOriginal("keys", function(owner, object) {
|
|
// Need to waive Xrays so we can iterate functions and accessor properties
|
|
return Cu.cloneInto(Object.keys(Cu.waiveXrays(object)), owner.window);
|
|
});
|
|
|
|
/**
|
|
* Returns the values of all properties on object.
|
|
*
|
|
* @param object object
|
|
* Object to display the values from.
|
|
* @return array of string
|
|
*/
|
|
WebConsoleCommands._registerOriginal("values", function(owner, object) {
|
|
let values = [];
|
|
// Need to waive Xrays so we can iterate functions and accessor properties
|
|
let waived = Cu.waiveXrays(object);
|
|
let names = Object.getOwnPropertyNames(waived);
|
|
|
|
for (let name of names) {
|
|
values.push(waived[name]);
|
|
}
|
|
|
|
return Cu.cloneInto(values, owner.window);
|
|
});
|
|
|
|
/**
|
|
* Opens a help window in MDN.
|
|
*/
|
|
WebConsoleCommands._registerOriginal("help", function(owner) {
|
|
owner.helperResult = { type: "help" };
|
|
});
|
|
|
|
/**
|
|
* Change the JS evaluation scope.
|
|
*
|
|
* @param DOMElement|string|window window
|
|
* The window object to use for eval scope. This can be a string that
|
|
* is used to perform document.querySelector(), to find the iframe that
|
|
* you want to cd() to. A DOMElement can be given as well, the
|
|
* .contentWindow property is used. Lastly, you can directly pass
|
|
* a window object. If you call cd() with no arguments, the current
|
|
* eval scope is cleared back to its default (the top window).
|
|
*/
|
|
WebConsoleCommands._registerOriginal("cd", function(owner, window) {
|
|
if (!window) {
|
|
owner.consoleActor.evalWindow = null;
|
|
owner.helperResult = { type: "cd" };
|
|
return;
|
|
}
|
|
|
|
if (typeof window == "string") {
|
|
window = owner.window.document.querySelector(window);
|
|
}
|
|
if (window instanceof Ci.nsIDOMElement && window.contentWindow) {
|
|
window = window.contentWindow;
|
|
}
|
|
if (!(window instanceof Ci.nsIDOMWindow)) {
|
|
owner.helperResult = {
|
|
type: "error",
|
|
message: "cdFunctionInvalidArgument"
|
|
};
|
|
return;
|
|
}
|
|
|
|
owner.consoleActor.evalWindow = window;
|
|
owner.helperResult = { type: "cd" };
|
|
});
|
|
|
|
/**
|
|
* Inspects the passed object. This is done by opening the PropertyPanel.
|
|
*
|
|
* @param object object
|
|
* Object to inspect.
|
|
*/
|
|
WebConsoleCommands._registerOriginal("inspect", function(owner, object) {
|
|
let dbgObj = owner.makeDebuggeeValue(object);
|
|
let grip = owner.createValueGrip(dbgObj);
|
|
owner.helperResult = {
|
|
type: "inspectObject",
|
|
input: owner.evalInput,
|
|
object: grip,
|
|
};
|
|
});
|
|
|
|
/**
|
|
* Prints object to the output.
|
|
*
|
|
* @param object object
|
|
* Object to print to the output.
|
|
* @return string
|
|
*/
|
|
WebConsoleCommands._registerOriginal("pprint", function(owner, object) {
|
|
if (object === null || object === undefined || object === true ||
|
|
object === false) {
|
|
owner.helperResult = {
|
|
type: "error",
|
|
message: "helperFuncUnsupportedTypeError",
|
|
};
|
|
return null;
|
|
}
|
|
|
|
owner.helperResult = { rawOutput: true };
|
|
|
|
if (typeof object == "function") {
|
|
return object + "\n";
|
|
}
|
|
|
|
let output = [];
|
|
|
|
let obj = object;
|
|
for (let name in obj) {
|
|
let desc = WebConsoleUtils.getPropertyDescriptor(obj, name) || {};
|
|
if (desc.get || desc.set) {
|
|
// TODO: Bug 842672 - toolkit/ imports modules from browser/.
|
|
let getGrip = VariablesView.getGrip(desc.get);
|
|
let setGrip = VariablesView.getGrip(desc.set);
|
|
let getString = VariablesView.getString(getGrip);
|
|
let setString = VariablesView.getString(setGrip);
|
|
output.push(name + ":", " get: " + getString, " set: " + setString);
|
|
} else {
|
|
let valueGrip = VariablesView.getGrip(obj[name]);
|
|
let valueString = VariablesView.getString(valueGrip);
|
|
output.push(name + ": " + valueString);
|
|
}
|
|
}
|
|
|
|
return " " + output.join("\n ");
|
|
});
|
|
|
|
/**
|
|
* Print the String representation of a value to the output, as-is.
|
|
*
|
|
* @param any value
|
|
* A value you want to output as a string.
|
|
* @return void
|
|
*/
|
|
WebConsoleCommands._registerOriginal("print", function(owner, value) {
|
|
owner.helperResult = { rawOutput: true };
|
|
if (typeof value === "symbol") {
|
|
return Symbol.prototype.toString.call(value);
|
|
}
|
|
// Waiving Xrays here allows us to see a closer representation of the
|
|
// underlying object. This may execute arbitrary content code, but that
|
|
// code will run with content privileges, and the result will be rendered
|
|
// inert by coercing it to a String.
|
|
return String(Cu.waiveXrays(value));
|
|
});
|
|
|
|
/**
|
|
* Copy the String representation of a value to the clipboard.
|
|
*
|
|
* @param any value
|
|
* A value you want to copy as a string.
|
|
* @return void
|
|
*/
|
|
WebConsoleCommands._registerOriginal("copy", function(owner, value) {
|
|
let payload;
|
|
try {
|
|
if (value instanceof Ci.nsIDOMElement) {
|
|
payload = value.outerHTML;
|
|
} else if (typeof value == "string") {
|
|
payload = value;
|
|
} else {
|
|
payload = JSON.stringify(value, null, " ");
|
|
}
|
|
} catch (ex) {
|
|
payload = "/* " + ex + " */";
|
|
}
|
|
owner.helperResult = {
|
|
type: "copyValueToClipboard",
|
|
value: payload,
|
|
};
|
|
});
|
|
|
|
/**
|
|
* (Internal only) Add the bindings to |owner.sandbox|.
|
|
* This is intended to be used by the WebConsole actor only.
|
|
*
|
|
* @param object owner
|
|
* The owning object.
|
|
*/
|
|
function addWebConsoleCommands(owner) {
|
|
if (!owner) {
|
|
throw new Error("The owner is required");
|
|
}
|
|
for (let [name, command] of WebConsoleCommands._registeredCommands) {
|
|
if (typeof command === "function") {
|
|
owner.sandbox[name] = command.bind(undefined, owner);
|
|
} else if (typeof command === "object") {
|
|
let clone = Object.assign({}, command, {
|
|
// We force the enumerability and the configurability (so the
|
|
// WebConsoleActor can reconfigure the property).
|
|
enumerable: true,
|
|
configurable: true
|
|
});
|
|
|
|
if (typeof command.get === "function") {
|
|
clone.get = command.get.bind(undefined, owner);
|
|
}
|
|
if (typeof command.set === "function") {
|
|
clone.set = command.set.bind(undefined, owner);
|
|
}
|
|
|
|
Object.defineProperty(owner.sandbox, name, clone);
|
|
}
|
|
}
|
|
}
|
|
|
|
exports.addWebConsoleCommands = addWebConsoleCommands;
|
|
|
|
/**
|
|
* A ReflowObserver that listens for reflow events from the page.
|
|
* Implements nsIReflowObserver.
|
|
*
|
|
* @constructor
|
|
* @param object window
|
|
* The window for which we need to track reflow.
|
|
* @param object owner
|
|
* The listener owner which needs to implement:
|
|
* - onReflowActivity(reflowInfo)
|
|
*/
|
|
|
|
function ConsoleReflowListener(window, listener) {
|
|
this.docshell = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
.getInterface(Ci.nsIWebNavigation)
|
|
.QueryInterface(Ci.nsIDocShell);
|
|
this.listener = listener;
|
|
this.docshell.addWeakReflowObserver(this);
|
|
}
|
|
|
|
exports.ConsoleReflowListener = ConsoleReflowListener;
|
|
|
|
ConsoleReflowListener.prototype =
|
|
{
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsIReflowObserver,
|
|
Ci.nsISupportsWeakReference]),
|
|
docshell: null,
|
|
listener: null,
|
|
|
|
/**
|
|
* Forward reflow event to listener.
|
|
*
|
|
* @param DOMHighResTimeStamp start
|
|
* @param DOMHighResTimeStamp end
|
|
* @param boolean interruptible
|
|
*/
|
|
sendReflow: function(start, end, interruptible) {
|
|
let frame = components.stack.caller.caller;
|
|
|
|
let filename = frame ? frame.filename : null;
|
|
|
|
if (filename) {
|
|
// Because filename could be of the form "xxx.js -> xxx.js -> xxx.js",
|
|
// we only take the last part.
|
|
filename = filename.split(" ").pop();
|
|
}
|
|
|
|
this.listener.onReflowActivity({
|
|
interruptible: interruptible,
|
|
start: start,
|
|
end: end,
|
|
sourceURL: filename,
|
|
sourceLine: frame ? frame.lineNumber : null,
|
|
functionName: frame ? frame.name : null
|
|
});
|
|
},
|
|
|
|
/**
|
|
* On uninterruptible reflow
|
|
*
|
|
* @param DOMHighResTimeStamp start
|
|
* @param DOMHighResTimeStamp end
|
|
*/
|
|
reflow: function(start, end) {
|
|
this.sendReflow(start, end, false);
|
|
},
|
|
|
|
/**
|
|
* On interruptible reflow
|
|
*
|
|
* @param DOMHighResTimeStamp start
|
|
* @param DOMHighResTimeStamp end
|
|
*/
|
|
reflowInterruptible: function(start, end) {
|
|
this.sendReflow(start, end, true);
|
|
},
|
|
|
|
/**
|
|
* Unregister listener.
|
|
*/
|
|
destroy: function() {
|
|
this.docshell.removeWeakReflowObserver(this);
|
|
this.listener = this.docshell = null;
|
|
},
|
|
};
|
|
|
|
function gSequenceId() {
|
|
return gSequenceId.n++;
|
|
}
|
|
gSequenceId.n = 0;
|