Bug 809561 - Integrate xpcshell test harness with chrome remote debugging. r=past/chmanchester
This commit is contained in:
51
testing/xpcshell/dbg-actors.js
Normal file
51
testing/xpcshell/dbg-actors.js
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
/* 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 { Promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
|
||||||
|
let { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
|
||||||
|
const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
|
||||||
|
const { RootActor } = devtools.require("devtools/server/actors/root");
|
||||||
|
const { BrowserTabList } = devtools.require("devtools/server/actors/webbrowser");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xpcshell-test (XPCST) specific actors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a root actor appropriate for use in a server running xpcshell
|
||||||
|
* tests. <snip boilerplate> :)
|
||||||
|
*/
|
||||||
|
function createRootActor(connection)
|
||||||
|
{
|
||||||
|
let parameters = {
|
||||||
|
tabList: new XPCSTTabList(connection),
|
||||||
|
globalActorFactories: DebuggerServer.globalActorFactories,
|
||||||
|
onShutdown() {
|
||||||
|
// If the user never switches to the "debugger" tab we might get a
|
||||||
|
// shutdown before we've attached.
|
||||||
|
Services.obs.notifyObservers(null, "xpcshell-test-devtools-shutdown", null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return new RootActor(connection, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A "stub" TabList implementation that provides no tabs.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function XPCSTTabList(connection)
|
||||||
|
{
|
||||||
|
BrowserTabList.call(this, connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
XPCSTTabList.prototype = Object.create(BrowserTabList.prototype);
|
||||||
|
|
||||||
|
XPCSTTabList.prototype.constructor = XPCSTTabList;
|
||||||
|
|
||||||
|
XPCSTTabList.prototype.getList = function() {
|
||||||
|
return Promise.resolve([]);
|
||||||
|
};
|
||||||
@@ -336,7 +336,101 @@ function _register_modules_protocol_handler() {
|
|||||||
protocolHandler.setSubstitution("testing-common", modulesURI);
|
protocolHandler.setSubstitution("testing-common", modulesURI);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _initDebugging(port) {
|
||||||
|
let prefs = Components.classes["@mozilla.org/preferences-service;1"]
|
||||||
|
.getService(Components.interfaces.nsIPrefBranch);
|
||||||
|
|
||||||
|
// Always allow remote debugging.
|
||||||
|
prefs.setBoolPref("devtools.debugger.remote-enabled", true);
|
||||||
|
|
||||||
|
// for debugging-the-debugging, let an env var cause log spew.
|
||||||
|
let env = Components.classes["@mozilla.org/process/environment;1"]
|
||||||
|
.getService(Components.interfaces.nsIEnvironment);
|
||||||
|
if (env.get("DEVTOOLS_DEBUGGER_LOG")) {
|
||||||
|
prefs.setBoolPref("devtools.debugger.log", true);
|
||||||
|
}
|
||||||
|
if (env.get("DEVTOOLS_DEBUGGER_LOG_VERBOSE")) {
|
||||||
|
prefs.setBoolPref("devtools.debugger.log.verbose", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
let {DebuggerServer} = Components.utils.import('resource://gre/modules/devtools/dbg-server.jsm', {});
|
||||||
|
DebuggerServer.init(() => true);
|
||||||
|
DebuggerServer.addBrowserActors();
|
||||||
|
DebuggerServer.addActors("resource://testing-common/dbg-actors.js");
|
||||||
|
|
||||||
|
// An observer notification that tells us when we can "resume" script
|
||||||
|
// execution.
|
||||||
|
let obsSvc = Components.classes["@mozilla.org/observer-service;1"].
|
||||||
|
getService(Components.interfaces.nsIObserverService);
|
||||||
|
let initialized = false;
|
||||||
|
|
||||||
|
const TOPICS = ["devtools-thread-resumed", "xpcshell-test-devtools-shutdown"];
|
||||||
|
let observe = function(subject, topic, data) {
|
||||||
|
switch (topic) {
|
||||||
|
case "devtools-thread-resumed":
|
||||||
|
// Exceptions in here aren't reported and block the debugger from
|
||||||
|
// resuming, so...
|
||||||
|
try {
|
||||||
|
// Add a breakpoint for the first line in our test files.
|
||||||
|
let threadActor = subject.wrappedJSObject;
|
||||||
|
let location = { line: 1 };
|
||||||
|
for (let file of _TEST_FILE) {
|
||||||
|
let sourceActor = threadActor.sources.source({originalUrl: file});
|
||||||
|
sourceActor.createAndStoreBreakpoint(location);
|
||||||
|
}
|
||||||
|
} catch (ex) {
|
||||||
|
do_print("Failed to initialize breakpoints: " + ex + "\n" + ex.stack);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "xpcshell-test-devtools-shutdown":
|
||||||
|
// the debugger has shutdown before we got a resume event - nothing
|
||||||
|
// special to do here.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
initialized = true;
|
||||||
|
for (let topicToRemove of TOPICS) {
|
||||||
|
obsSvc.removeObserver(observe, topicToRemove);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (let topic of TOPICS) {
|
||||||
|
obsSvc.addObserver(observe, topic, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
do_print("");
|
||||||
|
do_print("*******************************************************************");
|
||||||
|
do_print("Waiting for the debugger to connect on port " + port)
|
||||||
|
do_print("")
|
||||||
|
do_print("To connect the debugger, open a Firefox instance, select 'Connect'");
|
||||||
|
do_print("from the Developer menu and specify the port as " + port);
|
||||||
|
do_print("*******************************************************************");
|
||||||
|
do_print("")
|
||||||
|
|
||||||
|
DebuggerServer.openListener(port);
|
||||||
|
|
||||||
|
// spin an event loop until the debugger connects.
|
||||||
|
let thr = Components.classes["@mozilla.org/thread-manager;1"]
|
||||||
|
.getService().currentThread;
|
||||||
|
while (!initialized) {
|
||||||
|
do_print("Still waiting for debugger to connect...");
|
||||||
|
thr.processNextEvent(true);
|
||||||
|
}
|
||||||
|
// NOTE: if you want to debug the harness itself, you can now add a 'debugger'
|
||||||
|
// statement anywhere and it will stop - but we've already added a breakpoint
|
||||||
|
// for the first line of the test scripts, so we just continue...
|
||||||
|
do_print("Debugger connected, starting test execution");
|
||||||
|
}
|
||||||
|
|
||||||
function _execute_test() {
|
function _execute_test() {
|
||||||
|
// _JSDEBUGGER_PORT is dynamically defined by <runxpcshelltests.py>.
|
||||||
|
if (_JSDEBUGGER_PORT) {
|
||||||
|
try {
|
||||||
|
_initDebugging(_JSDEBUGGER_PORT);
|
||||||
|
} catch (ex) {
|
||||||
|
do_print("Failed to initialize debugging: " + ex + "\n" + ex.stack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_register_protocol_handlers();
|
_register_protocol_handlers();
|
||||||
|
|
||||||
// Override idle service by default.
|
// Override idle service by default.
|
||||||
@@ -1072,6 +1166,8 @@ function do_load_child_test_harness()
|
|||||||
+ "const _HEAD_FILES=" + uneval(_HEAD_FILES) + "; "
|
+ "const _HEAD_FILES=" + uneval(_HEAD_FILES) + "; "
|
||||||
+ "const _TAIL_FILES=" + uneval(_TAIL_FILES) + "; "
|
+ "const _TAIL_FILES=" + uneval(_TAIL_FILES) + "; "
|
||||||
+ "const _TEST_NAME=" + uneval(_TEST_NAME) + "; "
|
+ "const _TEST_NAME=" + uneval(_TEST_NAME) + "; "
|
||||||
|
// We'll need more magic to get the debugger working in the child
|
||||||
|
+ "const _JSDEBUGGER_PORT=0; "
|
||||||
+ "const _XPCSHELL_PROCESS='child';";
|
+ "const _XPCSHELL_PROCESS='child';";
|
||||||
|
|
||||||
if (this._TESTING_MODULES_DIR) {
|
if (this._TESTING_MODULES_DIR) {
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ class XPCShellRunner(MozbuildObject):
|
|||||||
def run_test(self, test_paths, interactive=False,
|
def run_test(self, test_paths, interactive=False,
|
||||||
keep_going=False, sequential=False, shuffle=False,
|
keep_going=False, sequential=False, shuffle=False,
|
||||||
debugger=None, debuggerArgs=None, debuggerInteractive=None,
|
debugger=None, debuggerArgs=None, debuggerInteractive=None,
|
||||||
|
jsDebugger=False, jsDebuggerPort=None,
|
||||||
rerun_failures=False, test_objects=None, verbose=False,
|
rerun_failures=False, test_objects=None, verbose=False,
|
||||||
log=None,
|
log=None,
|
||||||
# ignore parameters from other platforms' options
|
# ignore parameters from other platforms' options
|
||||||
@@ -83,6 +84,7 @@ class XPCShellRunner(MozbuildObject):
|
|||||||
keep_going=keep_going, shuffle=shuffle, sequential=sequential,
|
keep_going=keep_going, shuffle=shuffle, sequential=sequential,
|
||||||
debugger=debugger, debuggerArgs=debuggerArgs,
|
debugger=debugger, debuggerArgs=debuggerArgs,
|
||||||
debuggerInteractive=debuggerInteractive,
|
debuggerInteractive=debuggerInteractive,
|
||||||
|
jsDebugger=jsDebugger, jsDebuggerPort=jsDebuggerPort,
|
||||||
rerun_failures=rerun_failures,
|
rerun_failures=rerun_failures,
|
||||||
verbose=verbose, log=log)
|
verbose=verbose, log=log)
|
||||||
return
|
return
|
||||||
@@ -113,6 +115,8 @@ class XPCShellRunner(MozbuildObject):
|
|||||||
'debugger': debugger,
|
'debugger': debugger,
|
||||||
'debuggerArgs': debuggerArgs,
|
'debuggerArgs': debuggerArgs,
|
||||||
'debuggerInteractive': debuggerInteractive,
|
'debuggerInteractive': debuggerInteractive,
|
||||||
|
'jsDebugger': jsDebugger,
|
||||||
|
'jsDebuggerPort': jsDebuggerPort,
|
||||||
'rerun_failures': rerun_failures,
|
'rerun_failures': rerun_failures,
|
||||||
'manifest': manifest,
|
'manifest': manifest,
|
||||||
'verbose': verbose,
|
'verbose': verbose,
|
||||||
@@ -125,6 +129,7 @@ class XPCShellRunner(MozbuildObject):
|
|||||||
test_path=None, shuffle=False, interactive=False,
|
test_path=None, shuffle=False, interactive=False,
|
||||||
keep_going=False, sequential=False,
|
keep_going=False, sequential=False,
|
||||||
debugger=None, debuggerArgs=None, debuggerInteractive=None,
|
debugger=None, debuggerArgs=None, debuggerInteractive=None,
|
||||||
|
jsDebugger=False, jsDebuggerPort=None,
|
||||||
rerun_failures=False, verbose=False, log=None):
|
rerun_failures=False, verbose=False, log=None):
|
||||||
|
|
||||||
# Obtain a reference to the xpcshell test runner.
|
# Obtain a reference to the xpcshell test runner.
|
||||||
@@ -161,6 +166,8 @@ class XPCShellRunner(MozbuildObject):
|
|||||||
'debugger': debugger,
|
'debugger': debugger,
|
||||||
'debuggerArgs': debuggerArgs,
|
'debuggerArgs': debuggerArgs,
|
||||||
'debuggerInteractive': debuggerInteractive,
|
'debuggerInteractive': debuggerInteractive,
|
||||||
|
'jsDebugger': jsDebugger,
|
||||||
|
'jsDebuggerPort': jsDebuggerPort,
|
||||||
}
|
}
|
||||||
|
|
||||||
if test_path is not None:
|
if test_path is not None:
|
||||||
@@ -417,6 +424,13 @@ class MachCommands(MachCommandBase):
|
|||||||
dest = "debuggerInteractive",
|
dest = "debuggerInteractive",
|
||||||
help = "prevents the test harness from redirecting "
|
help = "prevents the test harness from redirecting "
|
||||||
"stdout and stderr for interactive debuggers")
|
"stdout and stderr for interactive debuggers")
|
||||||
|
@CommandArgument("--jsdebugger", dest="jsDebugger", action="store_true",
|
||||||
|
help="Waits for a devtools JS debugger to connect before "
|
||||||
|
"starting the test.")
|
||||||
|
@CommandArgument("--jsdebugger-port", dest="jsDebuggerPort",
|
||||||
|
type=int, default=6000,
|
||||||
|
help="The port to listen on for a debugger connection if "
|
||||||
|
"--jsdebugger is specified (default=6000).")
|
||||||
@CommandArgument('--interactive', '-i', action='store_true',
|
@CommandArgument('--interactive', '-i', action='store_true',
|
||||||
help='Open an xpcshell prompt before running tests.')
|
help='Open an xpcshell prompt before running tests.')
|
||||||
@CommandArgument('--keep-going', '-k', action='store_true',
|
@CommandArgument('--keep-going', '-k', action='store_true',
|
||||||
|
|||||||
@@ -9,3 +9,7 @@ TEST_DIRS += ['example']
|
|||||||
PYTHON_UNIT_TESTS += [
|
PYTHON_UNIT_TESTS += [
|
||||||
'selftest.py',
|
'selftest.py',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
TESTING_JS_MODULES += [
|
||||||
|
'dbg-actors.js',
|
||||||
|
]
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import sys
|
|||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
from collections import deque
|
from collections import deque, namedtuple
|
||||||
from distutils import dir_util
|
from distutils import dir_util
|
||||||
from multiprocessing import cpu_count
|
from multiprocessing import cpu_count
|
||||||
from optparse import OptionParser
|
from optparse import OptionParser
|
||||||
@@ -111,6 +111,7 @@ class XPCShellTestThread(Thread):
|
|||||||
self.xrePath = kwargs.get('xrePath')
|
self.xrePath = kwargs.get('xrePath')
|
||||||
self.testingModulesDir = kwargs.get('testingModulesDir')
|
self.testingModulesDir = kwargs.get('testingModulesDir')
|
||||||
self.debuggerInfo = kwargs.get('debuggerInfo')
|
self.debuggerInfo = kwargs.get('debuggerInfo')
|
||||||
|
self.jsDebuggerInfo = kwargs.get('jsDebuggerInfo')
|
||||||
self.pluginsPath = kwargs.get('pluginsPath')
|
self.pluginsPath = kwargs.get('pluginsPath')
|
||||||
self.httpdManifest = kwargs.get('httpdManifest')
|
self.httpdManifest = kwargs.get('httpdManifest')
|
||||||
self.httpdJSPath = kwargs.get('httpdJSPath')
|
self.httpdJSPath = kwargs.get('httpdJSPath')
|
||||||
@@ -366,10 +367,15 @@ class XPCShellTestThread(Thread):
|
|||||||
for f in headfiles])
|
for f in headfiles])
|
||||||
cmdT = ", ".join(['"' + f.replace('\\', '/') + '"'
|
cmdT = ", ".join(['"' + f.replace('\\', '/') + '"'
|
||||||
for f in tailfiles])
|
for f in tailfiles])
|
||||||
|
|
||||||
|
dbgport = 0 if self.jsDebuggerInfo is None else self.jsDebuggerInfo.port
|
||||||
|
|
||||||
return xpcscmd + \
|
return xpcscmd + \
|
||||||
['-e', 'const _SERVER_ADDR = "localhost"',
|
['-e', 'const _SERVER_ADDR = "localhost"',
|
||||||
'-e', 'const _HEAD_FILES = [%s];' % cmdH,
|
'-e', 'const _HEAD_FILES = [%s];' % cmdH,
|
||||||
'-e', 'const _TAIL_FILES = [%s];' % cmdT]
|
'-e', 'const _TAIL_FILES = [%s];' % cmdT,
|
||||||
|
'-e', 'const _JSDEBUGGER_PORT = %d;' % dbgport,
|
||||||
|
]
|
||||||
|
|
||||||
def getHeadAndTailFiles(self, test_object):
|
def getHeadAndTailFiles(self, test_object):
|
||||||
"""Obtain the list of head and tail files.
|
"""Obtain the list of head and tail files.
|
||||||
@@ -632,7 +638,7 @@ class XPCShellTestThread(Thread):
|
|||||||
testTimeoutInterval *= int(self.test_object['requesttimeoutfactor'])
|
testTimeoutInterval *= int(self.test_object['requesttimeoutfactor'])
|
||||||
|
|
||||||
testTimer = None
|
testTimer = None
|
||||||
if not self.interactive and not self.debuggerInfo:
|
if not self.interactive and not self.debuggerInfo and not self.jsDebuggerInfo:
|
||||||
testTimer = Timer(testTimeoutInterval, lambda: self.testTimeout(proc))
|
testTimer = Timer(testTimeoutInterval, lambda: self.testTimeout(proc))
|
||||||
testTimer.start()
|
testTimer.start()
|
||||||
|
|
||||||
@@ -1004,7 +1010,8 @@ class XPCShellTests(object):
|
|||||||
profileName=None, mozInfo=None, sequential=False, shuffle=False,
|
profileName=None, mozInfo=None, sequential=False, shuffle=False,
|
||||||
testsRootDir=None, testingModulesDir=None, pluginsPath=None,
|
testsRootDir=None, testingModulesDir=None, pluginsPath=None,
|
||||||
testClass=XPCShellTestThread, failureManifest=None,
|
testClass=XPCShellTestThread, failureManifest=None,
|
||||||
log=None, stream=None, **otherOptions):
|
log=None, stream=None, jsDebugger=False, jsDebuggerPort=0,
|
||||||
|
**otherOptions):
|
||||||
"""Run xpcshell tests.
|
"""Run xpcshell tests.
|
||||||
|
|
||||||
|xpcshell|, is the xpcshell executable to use to run the tests.
|
|xpcshell|, is the xpcshell executable to use to run the tests.
|
||||||
@@ -1075,6 +1082,12 @@ class XPCShellTests(object):
|
|||||||
if debugger:
|
if debugger:
|
||||||
self.debuggerInfo = mozdebug.get_debugger_info(debugger, debuggerArgs, debuggerInteractive)
|
self.debuggerInfo = mozdebug.get_debugger_info(debugger, debuggerArgs, debuggerInteractive)
|
||||||
|
|
||||||
|
self.jsDebuggerInfo = None
|
||||||
|
if jsDebugger:
|
||||||
|
# A namedtuple let's us keep .port instead of ['port']
|
||||||
|
JSDebuggerInfo = namedtuple('JSDebuggerInfo', ['port'])
|
||||||
|
self.jsDebuggerInfo = JSDebuggerInfo(port=jsDebuggerPort)
|
||||||
|
|
||||||
self.xpcshell = xpcshell
|
self.xpcshell = xpcshell
|
||||||
self.xrePath = xrePath
|
self.xrePath = xrePath
|
||||||
self.appPath = appPath
|
self.appPath = appPath
|
||||||
@@ -1161,6 +1174,7 @@ class XPCShellTests(object):
|
|||||||
'xrePath': self.xrePath,
|
'xrePath': self.xrePath,
|
||||||
'testingModulesDir': self.testingModulesDir,
|
'testingModulesDir': self.testingModulesDir,
|
||||||
'debuggerInfo': self.debuggerInfo,
|
'debuggerInfo': self.debuggerInfo,
|
||||||
|
'jsDebuggerInfo': self.jsDebuggerInfo,
|
||||||
'pluginsPath': self.pluginsPath,
|
'pluginsPath': self.pluginsPath,
|
||||||
'httpdManifest': self.httpdManifest,
|
'httpdManifest': self.httpdManifest,
|
||||||
'httpdJSPath': self.httpdJSPath,
|
'httpdJSPath': self.httpdJSPath,
|
||||||
@@ -1190,6 +1204,13 @@ class XPCShellTests(object):
|
|||||||
if self.debuggerInfo.interactive:
|
if self.debuggerInfo.interactive:
|
||||||
signal.signal(signal.SIGINT, lambda signum, frame: None)
|
signal.signal(signal.SIGINT, lambda signum, frame: None)
|
||||||
|
|
||||||
|
if self.jsDebuggerInfo:
|
||||||
|
# The js debugger magic needs more work to do the right thing
|
||||||
|
# if debugging multiple files.
|
||||||
|
if len(self.alltests) != 1:
|
||||||
|
self.log.error("Error: --jsdebugger can only be used with a single test!")
|
||||||
|
return False
|
||||||
|
|
||||||
# create a queue of all tests that will run
|
# create a queue of all tests that will run
|
||||||
tests_queue = deque()
|
tests_queue = deque()
|
||||||
# also a list for the tests that need to be run sequentially
|
# also a list for the tests that need to be run sequentially
|
||||||
@@ -1434,6 +1455,13 @@ class XPCShellOptions(OptionParser):
|
|||||||
action = "store_true", dest = "debuggerInteractive",
|
action = "store_true", dest = "debuggerInteractive",
|
||||||
help = "prevents the test harness from redirecting "
|
help = "prevents the test harness from redirecting "
|
||||||
"stdout and stderr for interactive debuggers")
|
"stdout and stderr for interactive debuggers")
|
||||||
|
self.add_option("--jsdebugger", dest="jsDebugger", action="store_true",
|
||||||
|
help="Waits for a devtools JS debugger to connect before "
|
||||||
|
"starting the test.")
|
||||||
|
self.add_option("--jsdebugger-port", type="int", dest="jsDebuggerPort",
|
||||||
|
default=6000,
|
||||||
|
help="The port to listen on for a debugger connection if "
|
||||||
|
"--jsdebugger is specified.")
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = XPCShellOptions()
|
parser = XPCShellOptions()
|
||||||
|
|||||||
@@ -602,6 +602,9 @@ function ThreadActor(aParent, aGlobal)
|
|||||||
this.uncaughtExceptionHook = this.uncaughtExceptionHook.bind(this);
|
this.uncaughtExceptionHook = this.uncaughtExceptionHook.bind(this);
|
||||||
this.onDebuggerStatement = this.onDebuggerStatement.bind(this);
|
this.onDebuggerStatement = this.onDebuggerStatement.bind(this);
|
||||||
this.onNewScript = this.onNewScript.bind(this);
|
this.onNewScript = this.onNewScript.bind(this);
|
||||||
|
// Set a wrappedJSObject property so |this| can be sent via the observer svc
|
||||||
|
// for the xpcshell harness.
|
||||||
|
this.wrappedJSObject = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
ThreadActor.prototype = {
|
ThreadActor.prototype = {
|
||||||
@@ -1178,6 +1181,11 @@ ThreadActor.prototype = {
|
|||||||
|
|
||||||
let packet = this._resumed();
|
let packet = this._resumed();
|
||||||
this._popThreadPause();
|
this._popThreadPause();
|
||||||
|
// Tell anyone who cares of the resume (as of now, that's the xpcshell
|
||||||
|
// harness)
|
||||||
|
if (Services.obs) {
|
||||||
|
Services.obs.notifyObservers(this, "devtools-thread-resumed", null);
|
||||||
|
}
|
||||||
return packet;
|
return packet;
|
||||||
}, error => {
|
}, error => {
|
||||||
return error instanceof Error
|
return error instanceof Error
|
||||||
@@ -1322,7 +1330,7 @@ ThreadActor.prototype = {
|
|||||||
for (let line = 0, n = offsets.length; line < n; line++) {
|
for (let line = 0, n = offsets.length; line < n; line++) {
|
||||||
if (offsets[line]) {
|
if (offsets[line]) {
|
||||||
let location = { line: line };
|
let location = { line: line };
|
||||||
let resp = sourceActor._createAndStoreBreakpoint(location);
|
let resp = sourceActor.createAndStoreBreakpoint(location);
|
||||||
dbg_assert(!resp.actualLocation, "No actualLocation should be returned");
|
dbg_assert(!resp.actualLocation, "No actualLocation should be returned");
|
||||||
if (resp.error) {
|
if (resp.error) {
|
||||||
reportError(new Error("Unable to set breakpoint on event listener"));
|
reportError(new Error("Unable to set breakpoint on event listener"));
|
||||||
@@ -2516,11 +2524,10 @@ SourceActor.prototype = {
|
|||||||
let sourceFetched = fetch(this.url, { loadFromCache: !this.source });
|
let sourceFetched = fetch(this.url, { loadFromCache: !this.source });
|
||||||
|
|
||||||
// Record the contentType we just learned during fetching
|
// Record the contentType we just learned during fetching
|
||||||
sourceFetched.then(({ contentType }) => {
|
return sourceFetched.then(result => {
|
||||||
this._contentType = contentType;
|
this._contentType = result.contentType;
|
||||||
|
return result;
|
||||||
});
|
});
|
||||||
|
|
||||||
return sourceFetched;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -2848,7 +2855,7 @@ SourceActor.prototype = {
|
|||||||
|
|
||||||
_createBreakpoint: function(loc, originalLoc, condition) {
|
_createBreakpoint: function(loc, originalLoc, condition) {
|
||||||
return resolve(null).then(() => {
|
return resolve(null).then(() => {
|
||||||
return this._createAndStoreBreakpoint({
|
return this.createAndStoreBreakpoint({
|
||||||
line: loc.line,
|
line: loc.line,
|
||||||
column: loc.column,
|
column: loc.column,
|
||||||
condition: condition
|
condition: condition
|
||||||
@@ -2915,12 +2922,14 @@ SourceActor.prototype = {
|
|||||||
* Create a breakpoint at the specified location and store it in the
|
* Create a breakpoint at the specified location and store it in the
|
||||||
* cache. Takes ownership of `aRequest`. This is the
|
* cache. Takes ownership of `aRequest`. This is the
|
||||||
* generated location if this source is sourcemapped.
|
* generated location if this source is sourcemapped.
|
||||||
|
* Used by the XPCShell test harness to set breakpoints in a script before
|
||||||
|
* it has loaded.
|
||||||
*
|
*
|
||||||
* @param Object aRequest
|
* @param Object aRequest
|
||||||
* An object of the form { line[, column, condition] }. The
|
* An object of the form { line[, column, condition] }. The
|
||||||
* location is in the generated source, if sourcemapped.
|
* location is in the generated source, if sourcemapped.
|
||||||
*/
|
*/
|
||||||
_createAndStoreBreakpoint: function (aRequest) {
|
createAndStoreBreakpoint: function (aRequest) {
|
||||||
let bp = update({}, aRequest, { source: this.form() });
|
let bp = update({}, aRequest, { source: this.form() });
|
||||||
this.breakpointStore.addBreakpoint(bp);
|
this.breakpointStore.addBreakpoint(bp);
|
||||||
return this._setBreakpoint(aRequest);
|
return this._setBreakpoint(aRequest);
|
||||||
|
|||||||
@@ -13,10 +13,19 @@ const { Cc, Ci } = require("chrome");
|
|||||||
Object.defineProperty(this, "addonManager", {
|
Object.defineProperty(this, "addonManager", {
|
||||||
get: (function () {
|
get: (function () {
|
||||||
let cached;
|
let cached;
|
||||||
return () => cached
|
return () => {
|
||||||
? cached
|
if (cached === undefined) {
|
||||||
: (cached = Cc["@mozilla.org/addons/integration;1"]
|
// catch errors as the addonManager might not exist in this environment
|
||||||
.getService(Ci.amIAddonManager))
|
// (eg, xpcshell)
|
||||||
|
try {
|
||||||
|
cached = Cc["@mozilla.org/addons/integration;1"]
|
||||||
|
.getService(Ci.amIAddonManager);
|
||||||
|
} catch (ex) {
|
||||||
|
cached = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
}())
|
}())
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user