Merge the last green PGO changeset from mozilla-inbound into mozilla-central

This commit is contained in:
Ehsan Akhgari 2012-05-04 16:23:45 -04:00
commit 95c46760e3
43 changed files with 1800 additions and 277 deletions

175
.gdbinit Normal file
View File

@ -0,0 +1,175 @@
# .gdbinit file for debugging Mozilla
# Don't stop for the SIG32/33/etc signals that Flash produces
handle SIG32 noprint nostop pass
handle SIG33 noprint nostop pass
handle SIGPIPE noprint nostop pass
# Show the concrete types behind nsIFoo
set print object on
# run when using the auto-solib-add trick
def prun
tbreak main
run
set auto-solib-add 0
cont
end
# run -mail, when using the auto-solib-add trick
def pmail
tbreak main
run -mail
set auto-solib-add 0
cont
end
# Define a "pu" command to display PRUnichar * strings (100 chars max)
# Also allows an optional argument for how many chars to print as long as
# it's less than 100.
def pu
set $uni = $arg0
if $argc == 2
set $limit = $arg1
if $limit > 100
set $limit = 100
end
else
set $limit = 100
end
# scratch array with space for 100 chars plus null terminator. Make
# sure to not use ' ' as the char so this copy/pastes well.
set $scratch = "____________________________________________________________________________________________________"
set $i = 0
set $scratch_idx = 0
while (*$uni && $i++ < $limit)
if (*$uni < 0x80)
set $scratch[$scratch_idx++] = *(char*)$uni++
else
if ($scratch_idx > 0)
set $scratch[$scratch_idx] = '\0'
print $scratch
set $scratch_idx = 0
end
print /x *(short*)$uni++
end
end
if ($scratch_idx > 0)
set $scratch[$scratch_idx] = '\0'
print $scratch
end
end
# Define a "ps" command to display subclasses of nsAC?String. Note that
# this assumes strings as of Gecko 1.9 (well, and probably a few
# releases before that as well); going back far enough will get you
# to string classes that this function doesn't work for.
def ps
set $str = $arg0
if (sizeof(*$str.mData) == 1 && ($str.mFlags & 1) != 0)
print $str.mData
else
pu $str.mData $str.mLength
end
end
# Define a "pa" command to display the string value for an nsIAtom
def pa
set $atom = $arg0
if (sizeof(*((&*$atom)->mString)) == 2)
pu (&*$atom)->mString
end
end
# define a "pxul" command to display the type of a XUL element from
# an nsXULDocument* pointer.
def pxul
set $p = $arg0
print $p->mNodeInfo.mRawPtr->mInner.mName->mStaticAtom->mString
end
# define a "prefcnt" command to display the refcount of an XPCOM obj
def prefcnt
set $p = $arg0
print ((nsPurpleBufferEntry*)$p->mRefCnt.mTagged)->mRefCnt
end
# define a "ptag" command to display the tag name of a content node
def ptag
set $p = $arg0
pa $p->mNodeInfo.mRawPtr->mInner.mName
end
##
## nsTArray
##
define ptarray
if $argc == 0
help ptarray
else
set $size = $arg0.mHdr->mLength
set $capacity = $arg0.mHdr->mCapacity
set $size_max = $size - 1
set $elts = $arg0.Elements()
end
if $argc == 1
set $i = 0
while $i < $size
printf "elem[%u]: ", $i
p *($elts + $i)
set $i++
end
end
if $argc == 2
set $idx = $arg1
if $idx < 0 || $idx > $size_max
printf "idx1, idx2 are not in acceptable range: [0..%u].\n", $size_max
else
printf "elem[%u]: ", $idx
p *($elts + $idx)
end
end
if $argc == 3
set $start_idx = $arg1
set $stop_idx = $arg2
if $start_idx > $stop_idx
set $tmp_idx = $start_idx
set $start_idx = $stop_idx
set $stop_idx = $tmp_idx
end
if $start_idx < 0 || $stop_idx < 0 || $start_idx > $size_max || $stop_idx > $size_max
printf "idx1, idx2 are not in acceptable range: [0..%u].\n", $size_max
else
set $i = $start_idx
while $i <= $stop_idx
printf "elem[%u]: ", $i
p *($elts + $i)
set $i++
end
end
end
if $argc > 0
printf "nsTArray length = %u\n", $size
printf "nsTArray capacity = %u\n", $capacity
printf "Element "
whatis *$elts
end
end
document ptarray
Prints nsTArray information.
Syntax: ptarray
Note: idx, idx1 and idx2 must be in acceptable range [0...size()-1].
Examples:
ptarray a - Prints tarray content, size, capacity and T typedef
ptarray a 0 - Prints element[idx] from tarray
ptarray a 1 2 - Prints elements in range [idx1..idx2] from tarray
end
def js
call DumpJSStack()
end
def ft
call nsFrame::DumpFrameTree($arg0)
end

View File

@ -237,6 +237,10 @@ maybe_clobber_profiledbuild:
find $(DIST)/$(MOZ_APP_NAME) -name "*.pgc" -exec mv {} $(DIST)/bin \;
endif
# put in our default gdbinit so that the gdb debugging experience is happier.
libs:: .gdbinit
$(INSTALL) $< $(DIST)/bin
.PHONY: maybe_clobber_profiledbuild
# Look for R_386_PC32 relocations in shared libs, these

View File

@ -1058,9 +1058,14 @@ pref("devtools.layoutview.open", false);
// Enable the Debugger
pref("devtools.debugger.enabled", false);
pref("devtools.debugger.remote-enabled", false);
pref("devtools.debugger.remote-host", "localhost");
pref("devtools.debugger.remote-port", 6000);
// The default Debugger UI height
pref("devtools.debugger.ui.height", 250);
pref("devtools.debugger.ui.remote-win.width", 900);
pref("devtools.debugger.ui.remote-win.height", 400);
// Enable the style inspector
pref("devtools.styleinspector.enabled", true);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 438 B

After

Width:  |  Height:  |  Size: 961 B

View File

@ -194,6 +194,14 @@
label="&debuggerMenu.label;"
key="key_debugger"
command="Tools:Debugger"/>
<menuitem id="appmenu_remoteDebugger"
hidden="true"
label="&remoteDebuggerMenu.label;"
command="Tools:RemoteDebugger"/>
<menuitem id="appmenu_chromeDebugger"
hidden="true"
label="&chromeDebuggerMenu.label;"
command="Tools:ChromeDebugger"/>
<menuitem id="appmenu_scratchpad"
hidden="true"
label="&scratchpad.label;"

View File

@ -225,7 +225,7 @@ var FullZoom = {
return;
// Avoid the cps roundtrip and apply the default/global pref.
if (isBlankPageURL(aURI.spec)) {
if (aURI.spec == "about:blank") {
this._applyPrefToSetting(undefined, aBrowser);
return;
}

View File

@ -551,6 +551,14 @@
label="&debuggerMenu.label;"
key="key_debugger"
command="Tools:Debugger"/>
<menuitem id="menu_remoteDebugger"
hidden="true"
label="&remoteDebuggerMenu.label;"
command="Tools:RemoteDebugger"/>
<menuitem id="menu_chromeDebugger"
hidden="true"
label="&chromeDebuggerMenu.label;"
command="Tools:ChromeDebugger"/>
<menuitem id="menu_scratchpad"
hidden="true"
label="&scratchpad.label;"

View File

@ -129,6 +129,8 @@
<command id="Tools:WebConsole" oncommand="HUDConsoleUI.toggleHUD();"/>
<command id="Tools:Inspect" oncommand="InspectorUI.toggleInspectorUI();" disabled="true"/>
<command id="Tools:Debugger" oncommand="DebuggerUI.toggleDebugger();" disabled="true"/>
<command id="Tools:RemoteDebugger" oncommand="DebuggerUI.toggleRemoteDebugger();" disabled="true"/>
<command id="Tools:ChromeDebugger" oncommand="DebuggerUI.toggleChromeDebugger();" disabled="true"/>
<command id="Tools:Scratchpad" oncommand="Scratchpad.openScratchpad();" disabled="true"/>
<command id="Tools:StyleEditor" oncommand="StyleEditor.openChrome();" disabled="true"/>
<command id="Tools:Addons" oncommand="BrowserOpenAddonsMgr();"/>

View File

@ -1716,6 +1716,26 @@ function delayedStartup(isLoadingBlank, mustLoadSidebar) {
#endif
}
// Enable Remote Debugger?
let enabled = gPrefService.getBoolPref("devtools.debugger.remote-enabled");
if (enabled) {
document.getElementById("menu_remoteDebugger").hidden = false;
document.getElementById("Tools:RemoteDebugger").removeAttribute("disabled");
#ifdef MENUBAR_CAN_AUTOHIDE
document.getElementById("appmenu_remoteDebugger").hidden = false;
#endif
}
// Enable Chrome Debugger?
let enabled = gPrefService.getBoolPref("devtools.chrome.enabled");
if (enabled) {
document.getElementById("menu_chromeDebugger").hidden = false;
document.getElementById("Tools:ChromeDebugger").removeAttribute("disabled");
#ifdef MENUBAR_CAN_AUTOHIDE
document.getElementById("appmenu_chromeDebugger").hidden = false;
#endif
}
// Enable Error Console?
// XXX Temporarily always-enabled, see bug 601201
let consoleEnabled = true || gPrefService.getBoolPref("devtools.errorconsole.enabled");

View File

@ -45,7 +45,13 @@ const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
const DBG_XUL = "chrome://browser/content/debugger.xul";
const REMOTE_PROFILE_NAME = "_remote-debug";
Cu.import("resource://gre/modules/devtools/dbg-server.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/FileUtils.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
let EXPORTED_SYMBOLS = ["DebuggerUI"];
@ -75,6 +81,36 @@ DebuggerUI.prototype = {
return new DebuggerPane(tab);
},
/**
* Starts a remote debugger in a new process, or stops it if already started.
* @see DebuggerProcess.constructor
* @return DebuggerProcess if the debugger is started, null if it's stopped.
*/
toggleRemoteDebugger: function DUI_toggleRemoteDebugger(aOnClose, aOnRun) {
let win = this.chromeWindow;
if (win._remoteDebugger) {
win._remoteDebugger.close();
return null;
}
return new DebuggerProcess(win, aOnClose, aOnRun);
},
/**
* Starts a chrome debugger in a new process, or stops it if already started.
* @see DebuggerProcess.constructor
* @return DebuggerProcess if the debugger is started, null if it's stopped.
*/
toggleChromeDebugger: function DUI_toggleChromeDebugger(aOnClose, aOnRun) {
let win = this.chromeWindow;
if (win._chromeDebugger) {
win._chromeDebugger.close();
return null;
}
return new DebuggerProcess(win, aOnClose, aOnRun, true);
},
/**
* Get the debugger for a specified tab.
* @return DebuggerPane if a debugger exists for the tab, null otherwise
@ -88,7 +124,7 @@ DebuggerUI.prototype = {
* @return object
*/
get preferences() {
return DebuggerUIPreferences;
return DebuggerPreferences;
}
};
@ -100,11 +136,23 @@ DebuggerUI.prototype = {
*/
function DebuggerPane(aTab) {
this._tab = aTab;
this._initServer();
this._create();
}
DebuggerPane.prototype = {
/**
* Initializes the debugger server.
*/
_initServer: function DP__initServer() {
if (!DebuggerServer.initialized) {
DebuggerServer.init();
DebuggerServer.addBrowserActors();
}
},
/**
* Creates and initializes the widgets containing the debugger UI.
*/
@ -118,7 +166,7 @@ DebuggerPane.prototype = {
this._splitter.setAttribute("class", "hud-splitter");
this._frame = ownerDocument.createElement("iframe");
this._frame.height = DebuggerUIPreferences.height;
this._frame.height = DebuggerPreferences.height;
this._nbox = gBrowser.getNotificationBox(this._tab.linkedBrowser);
this._nbox.appendChild(this._splitter);
@ -139,7 +187,7 @@ DebuggerPane.prototype = {
self.getBreakpoint = bkp.getBreakpoint;
}, true);
this._frame.setAttribute("src", "chrome://browser/content/debugger.xul");
this._frame.setAttribute("src", DBG_XUL);
},
/**
@ -149,10 +197,10 @@ DebuggerPane.prototype = {
if (!this._tab) {
return;
}
this._tab._scriptDebugger = null;
delete this._tab._scriptDebugger;
this._tab = null;
DebuggerUIPreferences.height = this._frame.height;
DebuggerPreferences.height = this._frame.height;
this._frame.removeEventListener("Debugger:Close", this.close, true);
this._frame.removeEventListener("unload", this.close, true);
@ -186,9 +234,117 @@ DebuggerPane.prototype = {
};
/**
* Various debugger UI preferences (currently just the pane height).
* Creates a process that will hold the remote debugger.
*
* @param function aOnClose
* Optional, a function called when the process exits.
* @param function aOnRun
* Optional, a function called when the process starts running.
* @param boolean aInitServerFlag
* True to initialize the server. This should happen only in the chrome
* debugging case. This should also be true by default after bug #747429.
* @param nsIDOMWindow aWindow
* The chrome window for which the remote debugger instance is created.
*/
let DebuggerUIPreferences = {
function DebuggerProcess(aWindow, aOnClose, aOnRun, aInitServerFlag) {
this._win = aWindow;
this._closeCallback = aOnClose;
this._runCallback = aOnRun;
aInitServerFlag && this._initServer();
this._initProfile();
this._create();
}
DebuggerProcess.prototype = {
/**
* Initializes the debugger server.
*/
_initServer: function RDP__initServer() {
if (!DebuggerServer.initialized) {
DebuggerServer.init();
DebuggerServer.addBrowserActors();
}
DebuggerServer.closeListener();
DebuggerServer.openListener(DebuggerPreferences.remotePort, false);
},
/**
* Initializes a profile for the remote debugger process.
*/
_initProfile: function RDP__initProfile() {
let profileService = Cc["@mozilla.org/toolkit/profile-service;1"]
.createInstance(Ci.nsIToolkitProfileService);
let dbgProfileName;
try {
dbgProfileName = profileService.selectedProfile.name + REMOTE_PROFILE_NAME;
} catch(e) {
dbgProfileName = REMOTE_PROFILE_NAME;
Cu.reportError(e);
}
this._dbgProfile = profileService.createProfile(null, null, dbgProfileName);
profileService.flush();
},
/**
* Creates and initializes the profile & process for the remote debugger.
*/
_create: function RDP__create() {
this._win._remoteDebugger = this;
let file = FileUtils.getFile("CurProcD",
[Services.appinfo.OS == "WINNT" ? "firefox.exe"
: "firefox-bin"]);
let process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
process.init(file);
let args = [
"-no-remote", "-P", this._dbgProfile.name,
"-chrome", DBG_XUL,
"-width", DebuggerPreferences.remoteWinWidth,
"-height", DebuggerPreferences.remoteWinHeight];
process.runwAsync(args, args.length, { observe: this.close.bind(this) });
this._dbgProcess = process;
if (typeof this._runCallback === "function") {
this._runCallback.call({}, this);
}
},
/**
* Closes the remote debugger, removing the profile and killing the process.
*/
close: function RDP_close() {
if (!this._win) {
return;
}
delete this._win._remoteDebugger;
this._win = null;
if (this._dbgProcess.isRunning) {
this._dbgProcess.kill();
}
if (this._dbgProfile) {
this._dbgProfile.remove(false);
}
if (typeof this._closeCallback === "function") {
this._closeCallback.call({}, this);
}
this._dbgProcess = null;
this._dbgProfile = null;
}
};
/**
* Various debugger preferences.
*/
let DebuggerPreferences = {
/**
* Gets the preferred height of the debugger pane.
@ -210,3 +366,35 @@ let DebuggerUIPreferences = {
this._height = value;
}
};
/**
* Gets the preferred width of the remote debugger window.
* @return number
*/
XPCOMUtils.defineLazyGetter(DebuggerPreferences, "remoteWinWidth", function() {
return Services.prefs.getIntPref("devtools.debugger.ui.remote-win.width");
});
/**
* Gets the preferred height of the remote debugger window.
* @return number
*/
XPCOMUtils.defineLazyGetter(DebuggerPreferences, "remoteWinHeight", function() {
return Services.prefs.getIntPref("devtools.debugger.ui.remote-win.height");
});
/**
* Gets the preferred default remote debugging host.
* @return string
*/
XPCOMUtils.defineLazyGetter(DebuggerPreferences, "remoteHost", function() {
return Services.prefs.getCharPref("devtools.debugger.remote-host");
});
/**
* Gets the preferred default remote debugging port.
* @return number
*/
XPCOMUtils.defineLazyGetter(DebuggerPreferences, "remotePort", function() {
return Services.prefs.getIntPref("devtools.debugger.remote-port");
});

View File

@ -114,6 +114,7 @@ let DebuggerController = {
this.dispatchEvent("Debugger:Unloaded");
this._disconnect();
this._isRemote && this._quitApp();
},
/**
@ -121,12 +122,10 @@ let DebuggerController = {
* wiring event handlers as necessary.
*/
_connect: function DC__connect() {
if (!DebuggerServer.initialized) {
DebuggerServer.init();
DebuggerServer.addBrowserActors();
}
let transport =
this._isRemote ? debuggerSocketConnect(Prefs.remoteHost, Prefs.remotePort)
: DebuggerServer.connectPipe();
let transport = DebuggerServer.connectPipe();
let client = this.client = new DebuggerClient(transport);
client.addListener("tabNavigated", this._onTabNavigated);
@ -220,6 +219,31 @@ let DebuggerController = {
}.bind(this));
},
/**
* Returns true if this is a remote debugger instance.
* @return boolean
*/
get _isRemote() {
return !window.parent.content;
},
/**
* Attempts to quit the current process if allowed.
*/
_quitApp: function DC__quitApp() {
let canceled = Cc["@mozilla.org/supports-PRBool;1"]
.createInstance(Ci.nsISupportsPRBool);
Services.obs.notifyObservers(canceled, "quit-application-requested", null);
// Somebody canceled our quit request.
if (canceled.data) {
return;
}
Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit);
},
/**
* Convenience method, dispatching a custom event.
*
@ -470,6 +494,38 @@ StackFrames.prototype = {
this._addExpander(thisVar, frame.this);
}
if (frame.environment) {
// Add nodes for every argument.
let variables = frame.environment.bindings.arguments;
for each (let variable in variables) {
let name = Object.getOwnPropertyNames(variable)[0];
let paramVar = localScope.addVar(name);
let paramVal = variable[name].value;
paramVar.setGrip(paramVal);
this._addExpander(paramVar, paramVal);
}
// Add nodes for every other variable in scope.
variables = frame.environment.bindings.variables;
for (let variable in variables) {
let paramVar = localScope.addVar(variable);
let paramVal = variables[variable].value;
paramVar.setGrip(paramVal);
this._addExpander(paramVar, paramVal);
}
// If we already found 'arguments', we are done here.
if ("arguments" in frame.environment.bindings.variables) {
// Signal that variables have been fetched.
DebuggerController.dispatchEvent("Debugger:FetchedVariables");
return;
}
}
// Sometimes in call frames with arguments we don't get 'arguments' in the
// environment (bug 746601) and we have to construct it manually. Note, that
// in this case arguments.callee will be absent, even in the cases where it
// shouldn't be.
if (frame.arguments && frame.arguments.length > 0) {
// Add "arguments".
let argsVar = localScope.addVar("arguments");
@ -479,33 +535,20 @@ StackFrames.prototype = {
});
this._addExpander(argsVar, frame.arguments);
// Add variables for every argument.
let objClient = this.activeThread.pauseGrip(frame.callee);
objClient.getSignature(function SF_getSignature(aResponse) {
for (let i = 0, l = aResponse.parameters.length; i < l; i++) {
let param = aResponse.parameters[i];
let paramVar = localScope.addVar(param);
let paramVal = frame.arguments[i];
paramVar.setGrip(paramVal);
this._addExpander(paramVar, paramVal);
}
// Signal that call parameters have been fetched.
DebuggerController.dispatchEvent("Debugger:FetchedParameters");
}.bind(this));
// Signal that variables have been fetched.
DebuggerController.dispatchEvent("Debugger:FetchedVariables");
}
},
/**
* Adds a onexpand callback for a variable, lazily handling the addition of
* Adds an 'onexpand' callback for a variable, lazily handling the addition of
* new properties.
*/
_addExpander: function SF__addExpander(aVar, aObject) {
// No need for expansion for null and undefined values, but we do need them
// for frame.arguments which is a regular array.
if (!aObject || typeof aObject !== "object" ||
if (!aVar || !aObject || typeof aObject !== "object" ||
(aObject.type !== "object" && !Array.isArray(aObject))) {
return;
}
@ -686,7 +729,7 @@ SourceScripts.prototype = {
* Handler for the debugger client's unsolicited newScript notification.
*/
_onNewScript: function SS__onNewScript(aNotification, aPacket) {
this._addScript({ url: aPacket.url, startLine: aPacket.startLine });
this._addScript({ url: aPacket.url, startLine: aPacket.startLine }, true);
},
/**
@ -694,8 +737,9 @@ SourceScripts.prototype = {
*/
_onScriptsAdded: function SS__onScriptsAdded() {
for each (let script in this.activeThread.cachedScripts) {
this._addScript(script);
this._addScript(script, false);
}
DebuggerView.Scripts.commitScripts();
},
/**
@ -747,6 +791,22 @@ SourceScripts.prototype = {
return aUrl;
},
/**
* Gets the prePath for a script URL.
*
* @param string aUrl
* The script url.
* @return string
* The script prePath if the url is valid, null otherwise.
*/
_getScriptPrePath: function SS__getScriptDomain(aUrl) {
try {
return Services.io.newURI(aUrl, null, null).prePath + "/";
} catch (e) {
}
return null;
},
/**
* Gets a unique, simplified label from a script url.
* ex: a). ici://some.address.com/random/subrandom/
@ -763,7 +823,7 @@ SourceScripts.prototype = {
* The script url.
* @param string aHref
* The content location href to be used. If unspecified, it will
* defalult to debugged panrent window location.
* default to the script url prepath.
* @return string
* The simplified label.
*/
@ -774,15 +834,18 @@ SourceScripts.prototype = {
return this._labelsCache[url];
}
let href = aHref || window.parent.content.location.href;
let content = window.parent.content;
let domain = content ? content.location.href : this._getScriptPrePath(aUrl);
let href = aHref || domain;
let pathElements = url.split("/");
let label = pathElements.pop() || (pathElements.pop() + "/");
// if the label as a leaf name is alreay present in the scripts list
// If the label as a leaf name is already present in the scripts list.
if (DebuggerView.Scripts.containsLabel(label)) {
label = url.replace(href.substring(0, href.lastIndexOf("/") + 1), "");
// if the path/to/script is exactly the same, we're in different domains
// If the path/to/script is exactly the same, we're in different domains.
if (DebuggerView.Scripts.containsLabel(label)) {
label = url;
}
@ -800,15 +863,16 @@ SourceScripts.prototype = {
},
/**
* Add the specified script to the list and display it in the editor if the
* editor is empty.
* Add the specified script to the list.
*
* @param object aScript
* The script object coming from the active thread.
* @param boolean aForceFlag
* True to force the script to be immediately added.
*/
_addScript: function SS__addScript(aScript) {
DebuggerView.Scripts.addScript(this._getScriptLabel(aScript.url), aScript);
if (DebuggerView.editor.getCharCount() == 0) {
this.showScript(aScript);
}
_addScript: function SS__addScript(aScript, aForceFlag) {
DebuggerView.Scripts.addScript(
this._getScriptLabel(aScript.url), aScript, aForceFlag);
},
/**
@ -876,7 +940,7 @@ SourceScripts.prototype = {
* Handles notifications to load a source script from the cache or from a
* local file.
*
* XXX: Tt may be better to use nsITraceableChannel to get to the sources
* XXX: It may be better to use nsITraceableChannel to get to the sources
* without relying on caching when we can (not for eval, etc.):
* http://www.softwareishard.com/blog/firebug/nsitraceablechannel-intercept-http-traffic/
*/
@ -967,7 +1031,7 @@ SourceScripts.prototype = {
* The failure status code.
*/
_logError: function SS__logError(aUrl, aStatus) {
Components.utils.reportError(L10N.getFormatStr("loadingError", [aUrl, aStatus]));
Cu.reportError(L10N.getFormatStr("loadingError", [aUrl, aStatus]));
},
};
@ -1258,6 +1322,27 @@ XPCOMUtils.defineLazyGetter(L10N, "stringBundle", function() {
return Services.strings.createBundle(DBG_STRINGS_URI);
});
/**
* Shortcuts for accessing various debugger preferences.
*/
let Prefs = {};
/**
* Gets the preferred default remote debugging host.
* @return string
*/
XPCOMUtils.defineLazyGetter(Prefs, "remoteHost", function() {
return Services.prefs.getCharPref("devtools.debugger.remote-host");
});
/**
* Gets the preferred default remote debugging port.
* @return number
*/
XPCOMUtils.defineLazyGetter(Prefs, "remotePort", function() {
return Services.prefs.getIntPref("devtools.debugger.remote-port");
});
/**
* Preliminary setup for the DebuggerController object.
*/

View File

@ -90,6 +90,7 @@ let DebuggerView = {
*/
function ScriptsView() {
this._onScriptsChange = this._onScriptsChange.bind(this);
this._onScriptsSearch = this._onScriptsSearch.bind(this);
}
ScriptsView.prototype = {
@ -103,6 +104,14 @@ ScriptsView.prototype = {
}
},
/**
* Removes the input in the searchbox and unhides all the scripts.
*/
clearSearch: function DVS_clearSearch() {
this._searchbox.value = "";
this._onScriptsSearch({});
},
/**
* Checks whether the script with the specified URL is among the scripts
* known to the debugger and shown in the list.
@ -112,6 +121,11 @@ ScriptsView.prototype = {
* @return boolean
*/
contains: function DVS_contains(aUrl) {
if (this._tmpScripts.some(function(element) {
return element.script.url == aUrl;
})) {
return true;
}
if (this._scripts.getElementsByAttribute("value", aUrl).length > 0) {
return true;
}
@ -127,6 +141,11 @@ ScriptsView.prototype = {
* @return boolean
*/
containsLabel: function DVS_containsLabel(aLabel) {
if (this._tmpScripts.some(function(element) {
return element.label == aLabel;
})) {
return true;
}
if (this._scripts.getElementsByAttribute("label", aLabel).length > 0) {
return true;
}
@ -171,6 +190,18 @@ ScriptsView.prototype = {
this._scripts.selectedItem.value : null;
},
/**
* Returns the list of labels in the scripts container.
* @return array
*/
get scriptLabels() {
let labels = [];
for (let i = 0, l = this._scripts.itemCount; i < l; i++) {
labels.push(this._scripts.getItemAtIndex(i).label);
}
return labels;
},
/**
* Returns the list of URIs for scripts in the page.
* @return array
@ -184,50 +215,212 @@ ScriptsView.prototype = {
},
/**
* Adds a script to the scripts container.
* If the script already exists (was previously added), null is returned.
* Otherwise, the newly created element is returned.
* Gets the number of visible (hidden=false) scripts in the container.
* @return number
*/
get visibleItemsCount() {
let count = 0;
for (let i = 0, l = this._scripts.itemCount; i < l; i++) {
count += this._scripts.getItemAtIndex(i).hidden ? 0 : 1;
}
return count;
},
/**
* Prepares a script to be added to the scripts container. This allows
* for a large number of scripts to be batched up before being
* alphabetically sorted and added in the container.
* @see ScriptsView.commitScripts
*
* If aForceFlag is true, the script will be immediately inserted at the
* necessary position in the container so that all the scripts remain sorted.
* This can be much slower than batching up multiple scripts.
*
* @param string aLabel
* The simplified script location to be shown.
* @param string aScript
* The source script.
* @return object
* The newly created html node representing the added script.
* @param boolean aForceFlag
* True to force the script to be immediately added.
*/
addScript: function DVS_addScript(aLabel, aScript) {
// Make sure we don't duplicate anything.
if (this.containsLabel(aLabel)) {
return null;
addScript: function DVS_addScript(aLabel, aScript, aForceFlag) {
// Batch the script to be added later.
if (!aForceFlag) {
this._tmpScripts.push({ label: aLabel, script: aScript });
return;
}
let script = this._scripts.appendItem(aLabel, aScript.url);
script.setAttribute("tooltiptext", aScript.url);
script.setUserData("sourceScript", aScript, null);
this._scripts.selectedItem = script;
return script;
// Find the target position in the menulist and insert the script there.
for (let i = 0, l = this._scripts.itemCount; i < l; i++) {
if (this._scripts.getItemAtIndex(i).label > aLabel) {
this._createScriptElement(aLabel, aScript, i);
return;
}
}
// The script is alphabetically the last one.
this._createScriptElement(aLabel, aScript, -1, true);
},
/**
* The cached click listener for the scripts container.
* Adds all the prepared scripts to the scripts container.
* If a script already exists (was previously added), nothing happens.
*/
commitScripts: function DVS_commitScripts() {
let newScripts = this._tmpScripts;
this._tmpScripts = [];
if (!newScripts || !newScripts.length) {
return;
}
newScripts.sort(function(a, b) {
return a.label.toLowerCase() > b.label.toLowerCase();
});
for (let i = 0, l = newScripts.length; i < l; i++) {
let item = newScripts[i];
this._createScriptElement(item.label, item.script, -1, true);
}
},
/**
* Creates a custom script element and adds it to the scripts container.
* If the script with the specified label already exists, nothing happens.
*
* @param string aLabel
* The simplified script location to be shown.
* @param string aScript
* The source script.
* @param number aIndex
* The index where to insert to new script in the container.
* Pass -1 to append the script at the end.
* @param boolean aSelectIfEmptyFlag
* True to set the newly created script as the currently selected item
* if there are no other existing scripts in the container.
*/
_createScriptElement: function DVS__createScriptElement(
aLabel, aScript, aIndex, aSelectIfEmptyFlag)
{
// Make sure we don't duplicate anything.
if (aLabel == "null" || this.containsLabel(aLabel)) {
return;
}
let scriptItem =
aIndex == -1 ? this._scripts.appendItem(aLabel, aScript.url)
: this._scripts.insertItemAt(aIndex, aLabel, aScript.url);
scriptItem.setAttribute("tooltiptext", aScript.url);
scriptItem.setUserData("sourceScript", aScript, null);
if (this._scripts.itemCount == 1 && aSelectIfEmptyFlag) {
this._scripts.selectedItem = scriptItem;
}
},
/**
* The click listener for the scripts container.
*/
_onScriptsChange: function DVS__onScriptsChange() {
let script = this._scripts.selectedItem.getUserData("sourceScript");
this._preferredScript = script;
DebuggerController.SourceScripts.showScript(script);
},
/**
* The cached scripts container.
* The search listener for the scripts search box.
*/
_onScriptsSearch: function DVS__onScriptsSearch(e) {
let editor = DebuggerView.editor;
let scripts = this._scripts;
let rawValue = this._searchbox.value.toLowerCase();
let rawLength = rawValue.length;
let lastColon = rawValue.lastIndexOf(":");
let lastAt = rawValue.lastIndexOf("@");
let fileEnd = lastColon != -1 ? lastColon : lastAt != -1 ? lastAt : rawLength;
let lineEnd = lastAt != -1 ? lastAt : rawLength;
let file = rawValue.slice(0, fileEnd);
let line = window.parseInt(rawValue.slice(fileEnd + 1, lineEnd)) || -1;
let token = rawValue.slice(lineEnd + 1);
// Presume we won't find anything.
scripts.selectedItem = this._preferredScript;
// If we're not searching for a file anymore, unhide all the scripts.
if (!file) {
for (let i = 0, l = scripts.itemCount; i < l; i++) {
scripts.getItemAtIndex(i).hidden = false;
}
} else {
for (let i = 0, l = scripts.itemCount, found = false; i < l; i++) {
let item = scripts.getItemAtIndex(i);
let target = item.value.toLowerCase();
// Search is not case sensitive, and is tied to the url not the label.
if (target.match(file)) {
item.hidden = false;
if (!found) {
found = true;
scripts.selectedItem = item;
}
}
// Hide what doesn't match our search.
else {
item.hidden = true;
}
}
}
if (line > -1) {
editor.setCaretPosition(line - 1);
}
if (token) {
let offset = editor.find(token, { ignoreCase: true });
if (offset > -1) {
editor.setCaretPosition(0);
editor.setCaretOffset(offset);
}
}
},
/**
* The keyup listener for the scripts search box.
*/
_onScriptsKeyUp: function DVS__onScriptsKeyUp(e) {
if (e.keyCode === e.DOM_VK_ESCAPE) {
DebuggerView.editor.focus();
return;
}
if (e.keyCode === e.DOM_VK_RETURN || e.keyCode === e.DOM_VK_ENTER) {
let editor = DebuggerView.editor;
let offset = editor.findNext(true);
if (offset > -1) {
editor.setCaretPosition(0);
editor.setCaretOffset(offset);
}
}
},
/**
* The cached scripts container and search box.
*/
_scripts: null,
_searchbox: null,
/**
* Initialization function, called when the debugger is initialized.
*/
initialize: function DVS_initialize() {
this._scripts = document.getElementById("scripts");
this._searchbox = document.getElementById("scripts-search");
this._scripts.addEventListener("select", this._onScriptsChange, false);
this._searchbox.addEventListener("select", this._onScriptsSearch, false);
this._searchbox.addEventListener("input", this._onScriptsSearch, false);
this._searchbox.addEventListener("keyup", this._onScriptsKeyUp, false);
this.commitScripts();
},
/**
@ -235,7 +428,11 @@ ScriptsView.prototype = {
*/
destroy: function DVS_destroy() {
this._scripts.removeEventListener("select", this._onScriptsChange, false);
this._searchbox.removeEventListener("select", this._onScriptsSearch, false);
this._searchbox.removeEventListener("input", this._onScriptsSearch, false);
this._searchbox.removeEventListener("keyup", this._onScriptsKeyUp, false);
this._scripts = null;
this._searchbox = null;
}
};
@ -277,6 +474,8 @@ StackFramesView.prototype = {
else {
status.textContent = "";
}
DebuggerView.Scripts.clearSearch();
},
/**
@ -300,7 +499,7 @@ StackFramesView.prototype = {
// The empty node should look grayed out to avoid confusion.
item.className = "empty list-item";
item.appendChild(document.createTextNode(L10N.getStr("emptyText")));
item.appendChild(document.createTextNode(L10N.getStr("emptyStackText")));
this._frames.appendChild(item);
},
@ -381,7 +580,7 @@ StackFramesView.prototype = {
* The frame depth specified by the debugger.
*/
unhighlightFrame: function DVF_unhighlightFrame(aDepth) {
this.highlightFrame(aDepth, true)
this.highlightFrame(aDepth, true);
},
/**

View File

@ -81,6 +81,8 @@
<xul:button id="step-in">&debuggerUI.stepInButton;</xul:button>
<xul:button id="step-out">&debuggerUI.stepOutButton;</xul:button>
<xul:menulist id="scripts"/>
<xul:textbox id="scripts-search" type="search"
emptytext="&debuggerUI.emptyFilterText;"/>
</xul:toolbar>
<div id="dbg-content" class="hbox flex">
<div id="stack" class="vbox">

View File

@ -46,6 +46,7 @@ include $(DEPTH)/config/autoconf.mk
include $(topsrcdir)/config/rules.mk
_BROWSER_TEST_FILES = \
browser_dbg_createRemote.js \
browser_dbg_debuggerstatement.js \
browser_dbg_listtabs.js \
browser_dbg_tabactor-01.js \
@ -70,6 +71,9 @@ _BROWSER_TEST_FILES = \
browser_dbg_stack-05.js \
browser_dbg_location-changes.js \
browser_dbg_script-switching.js \
browser_dbg_scripts-sorting.js \
browser_dbg_scripts-searching-01.js \
browser_dbg_scripts-searching-02.js \
browser_dbg_pause-resume.js \
browser_dbg_update-editor-mode.js \
$(warning browser_dbg_select-line.js temporarily disabled due to oranges, see bug 726609) \

View File

@ -23,37 +23,44 @@ function test()
let SourceEditor = tempScope.SourceEditor;
let scriptShown = false;
let framesAdded = false;
let resumed = false;
let testStarted = false;
debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
gTab = aTab;
gDebuggee = aDebuggee;
gPane = aPane;
gDebugger = gPane.debuggerWindow;
resumed = true;
gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
framesAdded = true;
runTest();
executeSoon(startTest);
});
gDebuggee.firstCall();
executeSoon(function() {
gDebuggee.firstCall();
});
});
window.addEventListener("Debugger:ScriptShown", function _onEvent(aEvent) {
let url = aEvent.detail.url;
if (url.indexOf("-02.js") != -1) {
scriptShown = true;
window.removeEventListener(aEvent.type, _onEvent);
runTest();
}
});
function runTest()
function onScriptShown(aEvent)
{
if (scriptShown && framesAdded) {
Services.tm.currentThread.dispatch({ run: onScriptShown }, 0);
scriptShown = aEvent.detail.url.indexOf("-02.js") != -1;
executeSoon(startTest);
}
window.addEventListener("Debugger:ScriptShown", onScriptShown);
function startTest()
{
if (scriptShown && framesAdded && resumed && !testStarted) {
window.removeEventListener("Debugger:ScriptShown", onScriptShown);
testStarted = true;
Services.tm.currentThread.dispatch({ run: performTest }, 0);
}
}
function onScriptShown()
function performTest()
{
gScripts = gDebugger.DebuggerView.Scripts;

View File

@ -21,37 +21,43 @@ function test()
let contextMenu = null;
let scriptShown = false;
let framesAdded = false;
let resumed = false;
let testStarted = false;
debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
gTab = aTab;
gDebuggee = aDebuggee;
gPane = aPane;
gDebugger = gPane.debuggerWindow;
resumed = true;
gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
framesAdded = true;
runTest();
executeSoon(startTest);
});
executeSoon(function() {
gDebuggee.firstCall();
});
gDebuggee.firstCall();
});
window.addEventListener("Debugger:ScriptShown", function _onEvent(aEvent) {
let url = aEvent.detail.url;
if (url.indexOf("-02.js") != -1) {
scriptShown = true;
window.removeEventListener(aEvent.type, _onEvent);
runTest();
}
});
function onScriptShown(aEvent) {
scriptShown = aEvent.detail.url.indexOf("-02.js") != -1;
executeSoon(startTest);
}
function runTest()
window.addEventListener("Debugger:ScriptShown", onScriptShown);
function startTest()
{
if (scriptShown && framesAdded) {
Services.tm.currentThread.dispatch({ run: onScriptShown }, 0);
if (scriptShown && framesAdded && resumed && !testStarted) {
testStarted = true;
window.removeEventListener("Debugger:ScriptShown", onScriptShown);
Services.tm.currentThread.dispatch({ run: performTest }, 0);
}
}
function onScriptShown()
function performTest()
{
let scripts = gDebugger.DebuggerView.Scripts._scripts;

View File

@ -0,0 +1,86 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
var gProcess = null;
var gTab = null;
var gDebuggee = null;
function test() {
remote_debug_tab_pane(STACK_URL, aOnClosing, function(aTab, aDebuggee, aProcess) {
gTab = aTab;
gDebuggee = aDebuggee;
gProcess = aProcess;
testSimpleCall();
});
}
function testSimpleCall() {
Services.tm.currentThread.dispatch({ run: function() {
ok(gProcess._dbgProcess,
"The remote debugger process wasn't created properly!");
ok(gProcess._dbgProcess.isRunning,
"The remote debugger process isn't running!");
is(typeof gProcess._dbgProcess.pid, "number",
"The remote debugger process doesn't have a pid (?!)");
info("process location: " + gProcess._dbgProcess.location);
info("process pid: " + gProcess._dbgProcess.pid);
info("process name: " + gProcess._dbgProcess.processName);
info("process sig: " + gProcess._dbgProcess.processSignature);
ok(gProcess._dbgProfile,
"The remote debugger profile wasn't created properly!");
ok(gProcess._dbgProfile.localDir,
"The remote debugger profile doesn't have a localDir...");
ok(gProcess._dbgProfile.rootDir,
"The remote debugger profile doesn't have a rootDir...");
ok(gProcess._dbgProfile.name,
"The remote debugger profile doesn't have a name...");
info("profile localDir: " + gProcess._dbgProfile.localDir);
info("profile rootDir: " + gProcess._dbgProfile.rootDir);
info("profile name: " + gProcess._dbgProfile.name);
let profileService = Cc["@mozilla.org/toolkit/profile-service;1"]
.createInstance(Ci.nsIToolkitProfileService);
let profile = profileService.getProfileByName(gProcess._dbgProfile.name);
ok(profile,
"The remote debugger profile wasn't *actually* created properly!");
is(profile.localDir.path, gProcess._dbgProfile.localDir.path,
"The remote debugger profile doesn't have the correct localDir!");
is(profile.rootDir.path, gProcess._dbgProfile.rootDir.path,
"The remote debugger profile doesn't have the correct rootDir!");
DebuggerUI.toggleRemoteDebugger();
}}, 0);
}
function aOnClosing() {
ok(!gProcess._dbgProcess.isRunning,
"The remote debugger process isn't closed as it should be!");
is(gProcess._dbgProcess.exitValue, (Services.appinfo.OS == "WINNT" ? 0 : 256),
"The remote debugger process didn't die cleanly.");
info("process exit value: " + gProcess._dbgProcess.exitValue);
info("profile localDir: " + gProcess._dbgProfile.localDir.path);
info("profile rootDir: " + gProcess._dbgProfile.rootDir.path);
info("profile name: " + gProcess._dbgProfile.name);
executeSoon(function() {
finish();
});
}
registerCleanupFunction(function() {
removeTab(gTab);
gProcess = null;
gTab = null;
gDebuggee = null;
});

View File

@ -85,7 +85,8 @@ function resumeAndFinish() {
gDebugger.DebuggerController.activeThread.resume(function() {
let vs = gDebugger.DebuggerView.Scripts;
let ss = gDebugger.DebuggerController.SourceScripts;
ss._onScriptsCleared();
vs.empty();
vs._scripts.removeEventListener("select", vs._onScriptsChange, false);
is(ss._trimUrlQuery("a/b/c.d?test=1&random=4"), "a/b/c.d",
"Trimming the url query isn't done properly.");
@ -101,12 +102,11 @@ function resumeAndFinish() {
{ href: "si://interesting.address.moc/random/x/y/", leaf: "script.js?a=1&b=2&c=3" }
];
vs._scripts.removeEventListener("select", vs._onScriptsChange, false);
urls.forEach(function(url) {
executeSoon(function() {
let loc = url.href + url.leaf;
vs.addScript(ss._getScriptLabel(loc, url.href), { url: loc });
vs.commitScripts();
});
});

View File

@ -27,10 +27,10 @@ function testFrameParameters()
{
dump("Started testFrameParameters!\n");
gDebugger.addEventListener("Debugger:FetchedParameters", function test() {
dump("Entered Debugger:FetchedParameters!\n");
gDebugger.addEventListener("Debugger:FetchedVariables", function test() {
dump("Entered Debugger:FetchedVariables!\n");
gDebugger.removeEventListener("Debugger:FetchedParameters", test, false);
gDebugger.removeEventListener("Debugger:FetchedVariables", test, false);
Services.tm.currentThread.dispatch({ run: function() {
dump("After currentThread.dispatch!\n");
@ -52,33 +52,42 @@ function testFrameParameters()
is(frames.querySelectorAll(".dbg-stackframe").length, 3,
"Should have three frames.");
is(localNodes.length, 8,
is(localNodes.length, 11,
"The localScope should contain all the created variable elements.");
is(localNodes[0].querySelector(".info").textContent, "[object Proxy]",
"Should have the right property value for 'this'.");
is(localNodes[1].querySelector(".info").textContent, "[object Arguments]",
"Should have the right property value for 'arguments'.");
is(localNodes[2].querySelector(".info").textContent, "[object Object]",
is(localNodes[1].querySelector(".info").textContent, "[object Object]",
"Should have the right property value for 'aArg'.");
is(localNodes[3].querySelector(".info").textContent, '"beta"',
is(localNodes[2].querySelector(".info").textContent, '"beta"',
"Should have the right property value for 'bArg'.");
is(localNodes[4].querySelector(".info").textContent, "3",
is(localNodes[3].querySelector(".info").textContent, "3",
"Should have the right property value for 'cArg'.");
is(localNodes[5].querySelector(".info").textContent, "false",
is(localNodes[4].querySelector(".info").textContent, "false",
"Should have the right property value for 'dArg'.");
is(localNodes[6].querySelector(".info").textContent, "null",
is(localNodes[5].querySelector(".info").textContent, "null",
"Should have the right property value for 'eArg'.");
is(localNodes[7].querySelector(".info").textContent, "undefined",
is(localNodes[6].querySelector(".info").textContent, "undefined",
"Should have the right property value for 'fArg'.");
is(localNodes[7].querySelector(".info").textContent, "1",
"Should have the right property value for 'a'.");
is(localNodes[8].querySelector(".info").textContent, "[object Object]",
"Should have the right property value for 'b'.");
is(localNodes[9].querySelector(".info").textContent, "[object Object]",
"Should have the right property value for 'c'.");
is(localNodes[10].querySelector(".info").textContent, "[object Arguments]",
"Should have the right property value for 'arguments'.");
resumeAndFinish();
}}, 0);
}, false);

View File

@ -27,10 +27,10 @@ function testFrameParameters()
{
dump("Started testFrameParameters!\n");
gDebugger.addEventListener("Debugger:FetchedParameters", function test() {
dump("Entered Debugger:FetchedParameters!\n");
gDebugger.addEventListener("Debugger:FetchedVariables", function test() {
dump("Entered Debugger:FetchedVariables!\n");
gDebugger.removeEventListener("Debugger:FetchedParameters", test, false);
gDebugger.removeEventListener("Debugger:FetchedVariables", test, false);
Services.tm.currentThread.dispatch({ run: function() {
dump("After currentThread.dispatch!\n");
@ -50,16 +50,17 @@ function testFrameParameters()
is(frames.querySelectorAll(".dbg-stackframe").length, 3,
"Should have three frames.");
is(localNodes.length, 8,
is(localNodes.length, 11,
"The localScope should contain all the created variable elements.");
is(localNodes[0].querySelector(".info").textContent, "[object Proxy]",
"Should have the right property value for 'this'.");
// Expand the __proto__ and arguments tree nodes. This causes their
// properties to be retrieved and displayed.
// Expand the '__proto__', 'arguments' and 'a' tree nodes. This causes
// their properties to be retrieved and displayed.
localNodes[0].expand();
localNodes[1].expand();
localNodes[9].expand();
localNodes[10].expand();
// Poll every few milliseconds until the properties are retrieved.
// It's important to set the timer in the chrome window, because the
@ -70,7 +71,9 @@ function testFrameParameters()
ok(false, "Timed out while polling for the properties.");
resumeAndFinish();
}
if (!localNodes[0].fetched || !localNodes[1].fetched) {
if (!localNodes[0].fetched ||
!localNodes[9].fetched ||
!localNodes[10].fetched) {
return;
}
window.clearInterval(intervalID);
@ -82,14 +85,26 @@ function testFrameParameters()
.textContent.search(/object/) != -1,
"__proto__ should be an object.");
is(localNodes[1].querySelector(".info").textContent, "[object Arguments]",
is(localNodes[9].querySelector(".info").textContent, "[object Object]",
"Should have the right property value for 'c'.");
is(localNodes[9].querySelectorAll(".property > .title > .key")[1]
.textContent, "a",
"Should have the right property name for 'a'.");
is(localNodes[9].querySelectorAll(".property > .title > .value")[1]
.textContent, 1,
"Should have the right value for 'c.a'.");
is(localNodes[10].querySelector(".info").textContent,
"[object Arguments]",
"Should have the right property value for 'arguments'.");
is(localNodes[1].querySelector(".property > .title > .key")
is(localNodes[10].querySelector(".property > .title > .key")
.textContent, "length",
"Should have the right property name for length.");
"Should have the right property name for 'length'.");
is(localNodes[1].querySelector(".property > .title > .value")
is(localNodes[10].querySelector(".property > .title > .value")
.textContent, 5,
"Should have the right argument length.");
@ -104,7 +119,8 @@ function testFrameParameters()
}
function resumeAndFinish() {
gDebugger.DebuggerController.activeThread.addOneTimeListener("framescleared", function() {
let thread = gDebugger.DebuggerController.activeThread;
thread.addOneTimeListener("framescleared", function() {
Services.tm.currentThread.dispatch({ run: function() {
var frames = gDebugger.DebuggerView.StackFrames._frames;
@ -115,7 +131,7 @@ function resumeAndFinish() {
}}, 0);
});
gDebugger.DebuggerController.activeThread.resume();
thread.resume();
}
registerCleanupFunction(function() {

View File

@ -8,10 +8,6 @@
const TAB_URL = EXAMPLE_URL + "browser_dbg_script-switching.html";
let tempScope = {};
Cu.import("resource:///modules/source-editor.jsm", tempScope);
let SourceEditor = tempScope.SourceEditor;
var gPane = null;
var gTab = null;
var gDebuggee = null;
@ -22,33 +18,39 @@ function test()
{
let scriptShown = false;
let framesAdded = false;
let resumed = false;
let testStarted = false;
debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
gTab = aTab;
gDebuggee = aDebuggee;
gPane = aPane;
gDebugger = gPane.debuggerWindow;
resumed = true;
gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
framesAdded = true;
runTest();
executeSoon(startTest);
});
gDebuggee.firstCall();
executeSoon(function() {
gDebuggee.firstCall();
});
});
window.addEventListener("Debugger:ScriptShown", function _onEvent(aEvent) {
let url = aEvent.detail.url;
if (url.indexOf("-02.js") != -1) {
scriptShown = true;
window.removeEventListener(aEvent.type, _onEvent);
runTest();
}
});
function runTest()
function onScriptShown(aEvent)
{
if (scriptShown && framesAdded) {
scriptShown = aEvent.detail.url.indexOf("-02.js") != -1;
executeSoon(startTest);
}
window.addEventListener("Debugger:ScriptShown", onScriptShown);
function startTest()
{
if (scriptShown && framesAdded && resumed && !testStarted) {
window.removeEventListener("Debugger:ScriptShown", onScriptShown);
testStarted = true;
Services.tm.currentThread.dispatch({ run: testScriptsDisplay }, 0);
}
}
@ -79,8 +81,6 @@ function testScriptsDisplay() {
ok(gDebugger.DebuggerView.Scripts.containsLabel(
label2), "Second script label is incorrect.");
dump("Debugger editor text:\n" + gDebugger.editor.getText() + "\n");
ok(gDebugger.editor.getText().search(/debugger/) != -1,
"The correct script was loaded initially.");
@ -100,8 +100,6 @@ function testScriptsDisplay() {
function testSwitchPaused()
{
dump("Debugger editor text:\n" + gDebugger.editor.getText() + "\n");
ok(gDebugger.editor.getText().search(/debugger/) == -1,
"The second script is no longer displayed.");
@ -127,6 +125,8 @@ function testSwitchPaused()
function testSwitchRunning()
{
dump("Debugger editor text:\n" + gDebugger.editor.getText() + "\n");
ok(gDebugger.editor.getText().search(/debugger/) != -1,
"The second script is displayed again.");

View File

@ -0,0 +1,188 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
var gPane = null;
var gTab = null;
var gDebuggee = null;
var gDebugger = null;
var gEditor = null;
var gScripts = null;
var gSearchBox = null;
var gMenulist = null;
function test()
{
let scriptShown = false;
let framesAdded = false;
debug_tab_pane(STACK_URL, function(aTab, aDebuggee, aPane) {
gTab = aTab;
gDebuggee = aDebuggee;
gPane = aPane;
gDebugger = gPane.debuggerWindow;
gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
framesAdded = true;
runTest();
});
gDebuggee.simpleCall();
});
window.addEventListener("Debugger:ScriptShown", function _onEvent(aEvent) {
window.removeEventListener(aEvent.type, _onEvent);
scriptShown = true;
runTest();
});
function runTest()
{
if (scriptShown && framesAdded) {
Services.tm.currentThread.dispatch({ run: testScriptSearching }, 0);
}
}
}
function testScriptSearching() {
gDebugger.DebuggerController.activeThread.resume(function() {
gEditor = gDebugger.DebuggerView.editor;
gScripts = gDebugger.DebuggerView.Scripts;
gSearchBox = gScripts._searchbox;
gMenulist = gScripts._scripts;
write(":12");
ok(gEditor.getCaretPosition().line == 11 &&
gEditor.getCaretPosition().col == 0,
"The editor didn't jump to the correct line.");
write("@debugger");
ok(gEditor.getCaretPosition().line == 2 &&
gEditor.getCaretPosition().col == 44,
"The editor didn't jump to the correct token. (1)");
EventUtils.sendKey("RETURN");
ok(gEditor.getCaretPosition().line == 8 &&
gEditor.getCaretPosition().col == 2,
"The editor didn't jump to the correct token. (2)");
EventUtils.sendKey("ENTER");
ok(gEditor.getCaretPosition().line == 12 &&
gEditor.getCaretPosition().col == 8,
"The editor didn't jump to the correct token. (3)");
EventUtils.sendKey("ENTER");
ok(gEditor.getCaretPosition().line == 19 &&
gEditor.getCaretPosition().col == 4,
"The editor didn't jump to the correct token. (4)");
EventUtils.sendKey("RETURN");
ok(gEditor.getCaretPosition().line == 2 &&
gEditor.getCaretPosition().col == 44,
"The editor didn't jump to the correct token. (5)");
write(":bogus@debugger;");
ok(gEditor.getCaretPosition().line == 8 &&
gEditor.getCaretPosition().col == 2,
"The editor didn't jump to the correct token. (7)");
write(":13@debugger;");
ok(gEditor.getCaretPosition().line == 8 &&
gEditor.getCaretPosition().col == 2,
"The editor didn't jump to the correct token. (7)");
write(":@debugger;");
ok(gEditor.getCaretPosition().line == 8 &&
gEditor.getCaretPosition().col == 2,
"The editor didn't jump to the correct token. (8)");
write("::@debugger;");
ok(gEditor.getCaretPosition().line == 8 &&
gEditor.getCaretPosition().col == 2,
"The editor didn't jump to the correct token. (9)");
write(":::@debugger;");
ok(gEditor.getCaretPosition().line == 8 &&
gEditor.getCaretPosition().col == 2,
"The editor didn't jump to the correct token. (10)");
write(":i am not a number");
ok(gEditor.getCaretPosition().line == 8 &&
gEditor.getCaretPosition().col == 2,
"The editor didn't remain at the correct token. (11)");
write("@__i do not exist__");
ok(gEditor.getCaretPosition().line == 8 &&
gEditor.getCaretPosition().col == 2,
"The editor didn't remain at the correct token. (12)");
write(":1:2:3:a:b:c:::12");
ok(gEditor.getCaretPosition().line == 11 &&
gEditor.getCaretPosition().col == 0,
"The editor didn't jump to the correct line. (13)");
write("@don't@find@me@instead@find@debugger");
ok(gEditor.getCaretPosition().line == 2 &&
gEditor.getCaretPosition().col == 44,
"The editor didn't jump to the correct token. (14)");
EventUtils.sendKey("RETURN");
ok(gEditor.getCaretPosition().line == 8 &&
gEditor.getCaretPosition().col == 2,
"The editor didn't jump to the correct token. (15)");
EventUtils.sendKey("ENTER");
ok(gEditor.getCaretPosition().line == 12 &&
gEditor.getCaretPosition().col == 8,
"The editor didn't jump to the correct token. (16)");
EventUtils.sendKey("RETURN");
ok(gEditor.getCaretPosition().line == 19 &&
gEditor.getCaretPosition().col == 4,
"The editor didn't jump to the correct token. (17)");
EventUtils.sendKey("ENTER");
ok(gEditor.getCaretPosition().line == 2 &&
gEditor.getCaretPosition().col == 44,
"The editor didn't jump to the correct token. (18)");
clear();
ok(gEditor.getCaretPosition().line == 2 &&
gEditor.getCaretPosition().col == 44,
"The editor didn't remain at the correct token. (19)");
is(gScripts.visibleItemsCount, 1,
"Not all the scripts are shown after the search. (20)");
closeDebuggerAndFinish(gTab);
});
}
function clear() {
gSearchBox.focus();
gSearchBox.value = "";
}
function write(text) {
clear();
for (let i = 0; i < text.length; i++) {
EventUtils.sendChar(text[i]);
}
dump("editor caret position: " + gEditor.getCaretPosition().toSource() + "\n");
}
registerCleanupFunction(function() {
removeTab(gTab);
gPane = null;
gTab = null;
gDebuggee = null;
gDebugger = null;
gEditor = null;
gScripts = null;
gSearchBox = null;
gMenulist = null;
});

View File

@ -0,0 +1,146 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const TAB_URL = EXAMPLE_URL + "browser_dbg_script-switching.html";
var gPane = null;
var gTab = null;
var gDebuggee = null;
var gDebugger = null;
var gEditor = null;
var gScripts = null;
var gSearchBox = null;
var gMenulist = null;
function test()
{
let scriptShown = false;
let framesAdded = false;
debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
gTab = aTab;
gDebuggee = aDebuggee;
gPane = aPane;
gDebugger = gPane.debuggerWindow;
gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
framesAdded = true;
runTest();
});
gDebuggee.firstCall();
});
window.addEventListener("Debugger:ScriptShown", function _onEvent(aEvent) {
let url = aEvent.detail.url;
if (url.indexOf("-02.js") != -1) {
scriptShown = true;
window.removeEventListener(aEvent.type, _onEvent);
runTest();
}
});
function runTest()
{
if (scriptShown && framesAdded) {
Services.tm.currentThread.dispatch({ run: testScriptSearching }, 0);
}
}
}
function testScriptSearching() {
gDebugger.DebuggerController.activeThread.resume(function() {
gEditor = gDebugger.DebuggerView.editor;
gScripts = gDebugger.DebuggerView.Scripts;
gSearchBox = gScripts._searchbox;
gMenulist = gScripts._scripts;
firstSearch();
});
}
function firstSearch() {
window.addEventListener("Debugger:ScriptShown", function _onEvent(aEvent) {
dump("Current script url:\n" + aEvent.detail.url + "\n");
dump("Debugger editor text:\n" + gEditor.getText() + "\n");
let url = aEvent.detail.url;
if (url.indexOf("-01.js") != -1) {
window.removeEventListener(aEvent.type, _onEvent);
executeSoon(function() {
dump("Editor caret position: " + gEditor.getCaretPosition().toSource() + "\n");
ok(gEditor.getCaretPosition().line == 4 &&
gEditor.getCaretPosition().col == 0,
"The editor didn't jump to the correct line. (1)");
is(gScripts.visibleItemsCount, 1,
"Not all the correct scripts are shown after the search. (1)");
secondSearch();
});
}
});
write(".*-01\.js:5");
}
function secondSearch() {
window.addEventListener("Debugger:ScriptShown", function _onEvent(aEvent) {
dump("Current script url:\n" + aEvent.detail.url + "\n");
dump("Debugger editor text:\n" + gEditor.getText() + "\n");
let url = aEvent.detail.url;
if (url.indexOf("-02.js") != -1) {
window.removeEventListener(aEvent.type, _onEvent);
executeSoon(function() {
dump("Editor caret position: " + gEditor.getCaretPosition().toSource() + "\n");
ok(gEditor.getCaretPosition().line == 5 &&
gEditor.getCaretPosition().col == 8,
"The editor didn't jump to the correct line. (2)");
is(gScripts.visibleItemsCount, 1,
"Not all the correct scripts are shown after the search. (2)");
finalCheck();
});
}
});
write(".*-02\.js@debugger;");
}
function finalCheck() {
clear();
ok(gEditor.getCaretPosition().line == 5 &&
gEditor.getCaretPosition().col == 8,
"The editor didn't remain at the correct token. (3)");
is(gScripts.visibleItemsCount, 2,
"Not all the scripts are shown after the search. (3)");
closeDebuggerAndFinish(gTab);
}
function clear() {
gSearchBox.focus();
gSearchBox.value = "";
}
function write(text) {
clear();
for (let i = 0; i < text.length; i++) {
EventUtils.sendChar(text[i]);
}
dump("editor caret position: " + gEditor.getCaretPosition().toSource() + "\n");
}
registerCleanupFunction(function() {
removeTab(gTab);
gPane = null;
gTab = null;
gDebuggee = null;
gDebugger = null;
gEditor = null;
gScripts = null;
gSearchBox = null;
gMenulist = null;
});

View File

@ -0,0 +1,124 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
var gPane = null;
var gTab = null;
var gDebuggee = null;
var gDebugger = null;
function test() {
debug_tab_pane(STACK_URL, function(aTab, aDebuggee, aPane) {
gTab = aTab;
gDebuggee = aDebuggee;
gPane = aPane;
gDebugger = gPane.debuggerWindow;
testSimpleCall();
});
}
function testSimpleCall() {
gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
Services.tm.currentThread.dispatch({ run: function() {
resumeAndFinish();
}}, 0);
});
gDebuggee.simpleCall();
}
function resumeAndFinish() {
gDebugger.DebuggerController.activeThread.resume(function() {
checkScriptsOrder();
addScriptsAndCheckOrder(1, function() {
addScriptsAndCheckOrder(2, function() {
addScriptsAndCheckOrder(3, function() {
closeDebuggerAndFinish(gTab);
});
});
});
});
}
function addScriptsAndCheckOrder(method, callback) {
let vs = gDebugger.DebuggerView.Scripts;
let ss = gDebugger.DebuggerController.SourceScripts;
vs.empty();
vs._scripts.removeEventListener("select", vs._onScriptsChange, false);
let urls = [
{ href: "ici://some.address.com/random/", leaf: "subrandom/" },
{ href: "ni://another.address.org/random/subrandom/", leaf: "page.html" },
{ href: "san://interesting.address.gro/random/", leaf: "script.js" },
{ href: "si://interesting.address.moc/random/", leaf: "script.js" },
{ href: "si://interesting.address.moc/random/", leaf: "x/script.js" },
{ href: "si://interesting.address.moc/random/", leaf: "x/y/script.js?a=1" },
{ href: "si://interesting.address.moc/random/x/", leaf: "y/script.js?a=1&b=2" },
{ href: "si://interesting.address.moc/random/x/y/", leaf: "script.js?a=1&b=2&c=3" }
];
urls.sort(function(a, b) {
return Math.random() - 0.5;
});
switch (method) {
case 1:
urls.forEach(function(url) {
let loc = url.href + url.leaf;
vs.addScript(ss._getScriptLabel(loc, url.href), { url: loc });
});
vs.commitScripts();
break;
case 2:
urls.forEach(function(url) {
let loc = url.href + url.leaf;
vs.addScript(ss._getScriptLabel(loc, url.href), { url: loc }, true);
});
break;
case 3:
let i = 0
for (; i < urls.length / 2; i++) {
let url = urls[i];
let loc = url.href + url.leaf;
vs.addScript(ss._getScriptLabel(loc, url.href), { url: loc });
}
vs.commitScripts();
for (; i < urls.length; i++) {
let url = urls[i];
let loc = url.href + url.leaf;
vs.addScript(ss._getScriptLabel(loc, url.href), { url: loc }, true);
}
break;
}
executeSoon(function() {
checkScriptsOrder(method);
callback();
});
}
function checkScriptsOrder(method) {
let labels = gDebugger.DebuggerView.Scripts.scriptLabels;
let sorted = labels.reduce(function(prev, curr, index, array) {
return array[index - 1] < array[index];
});
ok(sorted,
"Using method " + method + ", " +
"the scripts weren't in the correct order: " + labels.toSource());
return sorted;
}
registerCleanupFunction(function() {
removeTab(gTab);
gPane = null;
gTab = null;
gDebuggee = null;
gDebugger = null;
});

View File

@ -22,32 +22,38 @@ function test()
{
let scriptShown = false;
let framesAdded = false;
let testStarted = false;
let resumed = false;
debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
gTab = aTab;
gDebuggee = aDebuggee;
gPane = aPane;
gDebugger = gPane.debuggerWindow;
resumed = true;
gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
framesAdded = true;
runTest();
executeSoon(startTest);
});
executeSoon(function() {
gDebuggee.firstCall();
});
gDebuggee.firstCall();
});
window.addEventListener("Debugger:ScriptShown", function _onEvent(aEvent) {
let url = aEvent.detail.url;
if (url.indexOf("editor-mode") != -1) {
scriptShown = true;
window.removeEventListener(aEvent.type, _onEvent);
runTest();
}
});
function onScriptShown(aEvent) {
scriptShown = aEvent.detail.url.indexOf("test-editor-mode") != -1;
executeSoon(startTest);
}
function runTest()
window.addEventListener("Debugger:ScriptShown", onScriptShown);
function startTest()
{
if (scriptShown && framesAdded) {
if (scriptShown && framesAdded && resumed && !testStarted) {
window.removeEventListener("Debugger:ScriptShown", onScriptShown);
testStarted = true;
Services.tm.currentThread.dispatch({ run: testScriptsDisplay }, 0);
}
}

View File

@ -92,7 +92,6 @@ function debug_tab_pane(aURL, aOnDebugging)
{
let tab = addTab(aURL, function() {
gBrowser.selectedTab = gTab;
let debuggee = tab.linkedBrowser.contentWindow.wrappedJSObject;
let pane = DebuggerUI.toggleDebugger();
@ -106,3 +105,17 @@ function debug_tab_pane(aURL, aOnDebugging)
}, true);
});
}
function remote_debug_tab_pane(aURL, aOnClosing, aOnDebugging)
{
let tab = addTab(aURL, function() {
gBrowser.selectedTab = gTab;
let debuggee = tab.linkedBrowser.contentWindow.wrappedJSObject;
DebuggerUI.toggleRemoteDebugger(aOnClosing, function dbgRan(process) {
// Wait for the remote debugging process to start...
aOnDebugging(tab, debuggee, process);
});
});
}

View File

@ -293,37 +293,43 @@ InspectorUI.prototype = {
buildButtonsTooltip: function IUI_buildButtonsTooltip()
{
let keysbundle = Services.strings.createBundle("chrome://global-platform/locale/platformKeys.properties");
let separator = keysbundle.GetStringFromName("MODIFIER_SEPARATOR");
let button, tooltip;
// Inspect Button - the shortcut string is built from the <key> element
let key = this.chromeDoc.getElementById("key_inspect");
let modifiersAttr = key.getAttribute("modifiers");
if (key) {
let modifiersAttr = key.getAttribute("modifiers");
let combo = [];
let combo = [];
if (modifiersAttr.match("accel"))
if (modifiersAttr.match("accel"))
#ifdef XP_MACOSX
combo.push(keysbundle.GetStringFromName("VK_META"));
combo.push(keysbundle.GetStringFromName("VK_META"));
#else
combo.push(keysbundle.GetStringFromName("VK_CONTROL"));
combo.push(keysbundle.GetStringFromName("VK_CONTROL"));
#endif
if (modifiersAttr.match("shift"))
combo.push(keysbundle.GetStringFromName("VK_SHIFT"));
if (modifiersAttr.match("alt"))
combo.push(keysbundle.GetStringFromName("VK_ALT"));
if (modifiersAttr.match("ctrl"))
combo.push(keysbundle.GetStringFromName("VK_CONTROL"));
if (modifiersAttr.match("meta"))
combo.push(keysbundle.GetStringFromName("VK_META"));
if (modifiersAttr.match("shift"))
combo.push(keysbundle.GetStringFromName("VK_SHIFT"));
if (modifiersAttr.match("alt"))
combo.push(keysbundle.GetStringFromName("VK_ALT"));
if (modifiersAttr.match("ctrl"))
combo.push(keysbundle.GetStringFromName("VK_CONTROL"));
if (modifiersAttr.match("meta"))
combo.push(keysbundle.GetStringFromName("VK_META"));
combo.push(key.getAttribute("key"));
combo.push(key.getAttribute("key"));
let separator = keysbundle.GetStringFromName("MODIFIER_SEPARATOR");
tooltip = this.strings.formatStringFromName("inspectButtonWithShortcutKey.tooltip",
[combo.join(separator)], 1);
} else {
tooltip = this.strings.GetStringFromName("inspectButton.tooltip");
}
let tooltip = this.strings.formatStringFromName("inspectButton.tooltiptext",
[combo.join(separator)], 1);
let button = this.chromeDoc.getElementById("inspector-inspect-toolbutton");
button = this.chromeDoc.getElementById("inspector-inspect-toolbutton");
button.setAttribute("tooltiptext", tooltip);
// Markup Button - the shortcut string is built from the accesskey attribute
@ -335,7 +341,6 @@ InspectorUI.prototype = {
#else
let altString = keysbundle.GetStringFromName("VK_ALT");
let accesskey = button.getAttribute("accesskey");
let separator = keysbundle.GetStringFromName("MODIFIER_SEPARATOR");
let shortcut = altString + separator + accesskey;
tooltip = this.strings.formatStringFromName("markupButton.tooltipWithAccesskey",
[shortcut], 1);

View File

@ -42,6 +42,11 @@ var EXPORTED_SYMBOLS = [ "Templater", "template" ];
Components.utils.import("resource://gre/modules/Services.jsm");
const Node = Components.interfaces.nsIDOMNode;
/**
* For full documentation, see:
* https://github.com/mozilla/domtemplate/blob/master/README.md
*/
// WARNING: do not 'use_strict' without reading the notes in _envEval();
/**
@ -50,8 +55,16 @@ const Node = Components.interfaces.nsIDOMNode;
* @param data Data to use in filling out the template
* @param options Options to customize the template processing. One of:
* - allowEval: boolean (default false) Basic template interpolations are
* either property paths (e.g. ${a.b.c.d}), however if allowEval=true then we
* allow arbitrary JavaScript
* either property paths (e.g. ${a.b.c.d}), or if allowEval=true then we
* allow arbitrary JavaScript
* - stack: string or array of strings (default empty array) The template
* engine maintains a stack of tasks to help debug where it is. This allows
* this stack to be prefixed with a template name
* - blankNullUndefined: By default DOMTemplate exports null and undefined
* values using the strings 'null' and 'undefined', which can be helpful for
* debugging, but can introduce unnecessary extra logic in a template to
* convert null/undefined to ''. By setting blankNullUndefined:true, this
* conversion is handled by DOMTemplate
*/
function template(node, data, options) {
var template = new Templater(options || {});
@ -68,7 +81,15 @@ function Templater(options) {
options = { allowEval: true };
}
this.options = options;
this.stack = [];
if (options.stack && Array.isArray(options.stack)) {
this.stack = options.stack;
}
else if (typeof options.stack === 'string') {
this.stack = [ options.stack ];
}
else {
this.stack = [];
}
}
/**
@ -90,7 +111,7 @@ Templater.prototype._splitSpecial = /\uF001|\uF002/;
* Cached regex used to detect if a script is capable of being interpreted
* using Template._property() or if we need to use Template._envEval()
*/
Templater.prototype._isPropertyScript = /^[a-zA-Z0-9.]*$/;
Templater.prototype._isPropertyScript = /^[_a-zA-Z0-9.]*$/;
/**
* Recursive function to walk the tree processing the attributes as it goes.
@ -153,7 +174,11 @@ Templater.prototype.processNode = function(node, data) {
} else {
// Replace references in all other attributes
var newValue = value.replace(this._templateRegion, function(path) {
return this._envEval(path.slice(2, -1), data, value);
var insert = this._envEval(path.slice(2, -1), data, value);
if (this.options.blankNullUndefined && insert == null) {
insert = '';
}
return insert;
}.bind(this));
// Remove '_' prefix of attribute names so the DOM won't try
// to use them before we've processed the template
@ -177,7 +202,7 @@ Templater.prototype.processNode = function(node, data) {
this.processNode(childNodes[j], data);
}
if (node.nodeType === Node.TEXT_NODE) {
if (node.nodeType === 3 /*Node.TEXT_NODE*/) {
this._processTextNode(node, data);
}
} finally {
@ -347,8 +372,27 @@ Templater.prototype._processTextNode = function(node, data) {
part = this._envEval(part.slice(1), data, node.data);
}
this._handleAsync(part, node, function(reply, siblingNode) {
reply = this._toNode(reply, siblingNode.ownerDocument);
siblingNode.parentNode.insertBefore(reply, siblingNode);
var doc = siblingNode.ownerDocument;
if (reply == null) {
reply = this.options.blankNullUndefined ? '' : '' + reply;
}
if (typeof reply.cloneNode === 'function') {
// i.e. if (reply instanceof Element) { ...
reply = this._maybeImportNode(reply, doc);
siblingNode.parentNode.insertBefore(reply, siblingNode);
} else if (typeof reply.item === 'function' && reply.length) {
// if thing is a NodeList, then import the children
for (var i = 0; i < reply.length; i++) {
var child = this._maybeImportNode(reply.item(i), doc);
siblingNode.parentNode.insertBefore(child, siblingNode);
}
}
else {
// if thing isn't a DOM element then wrap its string value in one
reply = doc.createTextNode(reply.toString());
siblingNode.parentNode.insertBefore(reply, siblingNode);
}
}.bind(this));
}, this);
node.parentNode.removeChild(node);
@ -356,21 +400,13 @@ Templater.prototype._processTextNode = function(node, data) {
};
/**
* Helper to convert a 'thing' to a DOM Node.
* This is (obviously) a no-op for DOM Elements (which are detected using
* 'typeof thing.cloneNode !== "function"' (is there a better way that will
* work in all environments, including a .jsm?)
* Non DOM elements are converted to a string and wrapped in a TextNode.
* Return node or a import of node, if it's not in the given document
* @param node The node that we want to be properly owned
* @param doc The document that the given node should belong to
* @return A node that belongs to the given document
*/
Templater.prototype._toNode = function(thing, document) {
if (thing == null) {
thing = '' + thing;
}
// if thing isn't a DOM element then wrap its string value in one
if (typeof thing.cloneNode !== 'function') {
thing = document.createTextNode(thing.toString());
}
return thing;
Templater.prototype._maybeImportNode = function(node, doc) {
return node.ownerDocument === doc ? node : doc.importNode(node, true);
};
/**
@ -429,7 +465,6 @@ Templater.prototype._stripBraces = function(str) {
* <tt>newValue</tt> is applied.
*/
Templater.prototype._property = function(path, data, newValue) {
this.stack.push(path);
try {
if (typeof path === 'string') {
path = path.split('.');
@ -445,12 +480,13 @@ Templater.prototype._property = function(path, data, newValue) {
return value;
}
if (!value) {
this._handleError('Can\'t find path=' + path);
this._handleError('"' + path[0] + '" is undefined');
return null;
}
return this._property(path.slice(1), value, newValue);
} finally {
this.stack.pop();
} catch (ex) {
this._handleError('Path error with \'' + path + '\'', ex);
return '${' + path + '}';
}
};
@ -469,7 +505,7 @@ Templater.prototype._property = function(path, data, newValue) {
*/
Templater.prototype._envEval = function(script, data, frame) {
try {
this.stack.push(frame);
this.stack.push(frame.replace(/\s+/g, ' '));
if (this._isPropertyScript.test(script)) {
return this._property(script, data);
} else {
@ -483,8 +519,7 @@ Templater.prototype._envEval = function(script, data, frame) {
}
}
} catch (ex) {
this._handleError('Template error evaluating \'' + script + '\'' +
' environment=' + Object.keys(data).join(', '), ex);
this._handleError('Template error evaluating \'' + script + '\'', ex);
return '${' + script + '}';
} finally {
this.stack.pop();
@ -498,8 +533,7 @@ Templater.prototype._envEval = function(script, data, frame) {
* @param ex optional associated exception.
*/
Templater.prototype._handleError = function(message, ex) {
this._logError(message);
this._logError('In: ' + this.stack.join(' > '));
this._logError(message + ' (In: ' + this.stack.join(' > ') + ')');
if (ex) {
this._logError(ex);
}

View File

@ -3,11 +3,15 @@
// Tests that the DOM Template engine works properly
let tempScope = {};
Cu.import("resource:///modules/devtools/Templater.jsm", tempScope);
Cu.import("resource:///modules/devtools/Promise.jsm", tempScope);
let template = tempScope.template;
let Promise = tempScope.Promise;
/*
* These tests run both in Mozilla/Mochitest and plain browsers (as does
* domtemplate)
* We should endevour to keep the source in sync.
*/
var imports = {};
Cu.import("resource:///modules/devtools/Templater.jsm", imports);
Cu.import("resource:///modules/devtools/Promise.jsm", imports);
function test() {
addTab("http://example.com/browser/browser/devtools/shared/test/browser_templater_basic.html", function() {
@ -25,7 +29,7 @@ function runTest(index) {
holder.innerHTML = options.template;
info('Running ' + options.name);
template(holder, options.data, options.options);
imports.template(holder, options.data, options.options);
if (typeof options.result == 'string') {
is(holder.innerHTML, options.result, options.name);
@ -238,11 +242,43 @@ var tests = [
name: 'propertyFail',
template: '<p>${Math.max(1, 2)}</p>',
result: '<p>${Math.max(1, 2)}</p>'
};},
// Bug 723431: DOMTemplate should allow customisation of display of
// null/undefined values
function() { return {
name: 'propertyUndefAttrFull',
template: '<p>${nullvar}|${undefinedvar1}|${undefinedvar2}</p>',
data: { nullvar: null, undefinedvar1: undefined },
result: '<p>null|undefined|undefined</p>'
};},
function() { return {
name: 'propertyUndefAttrBlank',
template: '<p>${nullvar}|${undefinedvar1}|${undefinedvar2}</p>',
data: { nullvar: null, undefinedvar1: undefined },
options: { blankNullUndefined: true },
result: '<p>||</p>'
};},
function() { return {
name: 'propertyUndefAttrFull',
template: '<div><p value="${nullvar}"></p><p value="${undefinedvar1}"></p><p value="${undefinedvar2}"></p></div>',
data: { nullvar: null, undefinedvar1: undefined },
result: '<div><p value="null"></p><p value="undefined"></p><p value="undefined"></p></div>'
};},
function() { return {
name: 'propertyUndefAttrBlank',
template: '<div><p value="${nullvar}"></p><p value="${undefinedvar1}"></p><p value="${undefinedvar2}"></p></div>',
data: { nullvar: null, undefinedvar1: undefined },
options: { blankNullUndefined: true },
result: '<div><p value=""></p><p value=""></p><p value=""></p></div>'
};}
];
function delayReply(data) {
var p = new Promise();
var p = new imports.Promise();
executeSoon(function() {
p.resolve(data);
});

View File

@ -11,6 +11,14 @@
- application menu item that opens the debugger UI. -->
<!ENTITY debuggerMenu.label "Script Debugger">
<!-- LOCALIZATION NOTE (remoteDebuggerMenu.label): This is the label for the
- application menu item that opens the remote debugger UI. -->
<!ENTITY remoteDebuggerMenu.label "Remote Debugger">
<!-- LOCALIZATION NOTE (chromeDebuggerMenu.label): This is the label for the
- application menu item that opens the browser debugger UI. -->
<!ENTITY chromeDebuggerMenu.label "Browser Debugger">
<!-- LOCALIZATION NOTE (debuggerMenu.commandkey): This is the command key that
- launches the debugger UI. Do not translate this one! -->
<!ENTITY debuggerMenu.commandkey "S">
@ -44,3 +52,7 @@
- widget that displays the variables in the various available scopes in the
- debugger. -->
<!ENTITY debuggerUI.propertiesTitle "Scope variables">
<!-- LOCALIZATION NOTE (debuggerUI.emptyFilterText): This is the text that
- appears in the filter text box when it is empty. -->
<!ENTITY debuggerUI.emptyFilterText "Filter scripts">

View File

@ -38,9 +38,9 @@ withScope=With block
# pane as a header on container for identifiers in a closure scope.
closureScope=Closure
# LOCALIZATION NOTE (emptyText): The text that is displayed in the stack frames
# list when there are no frames to display.
emptyText=Empty
# LOCALIZATION NOTE (emptyStackText): The text that is displayed in the stack
# frames list when there are no frames to display.
emptyStackText=No stacks to display.
# LOCALIZATION NOTE (loadingText): The text that is displayed in the script
# editor when the laoding process has started but there is no file to display

View File

@ -20,11 +20,16 @@ breadcrumbs.siblings=Siblings
# LOCALIZATION NOTE (htmlPanel): Used in the Inspector tool's openInspectorUI
# method when registering the HTML panel.
# LOCALIZATION NOTE (inspectButton.tooltiptext):
# LOCALIZATION NOTE (inspectButtonWithShortcutKey.tooltip):
# This button appears in the Inspector Toolbar. inspectButton is stateful,
# if it's pressed users can select an element with the mouse.
# %S is the keyboard shortcut.
inspectButton.tooltiptext=Select element with mouse (%S)
inspectButtonWithShortcutKey.tooltip=Select element with mouse (%S)
# LOCALIZATION NOTE (inspectButton.tooltip):
# Same as inspectButtonWithShortcutKey.tooltip but used when an add-on
# overrides the shortcut key.
inspectButton.tooltip=Select element with mouse
# LOCALIZATION NOTE (markupButton.*):
# This button is the button located at the beginning of the breadcrumbs

View File

@ -2783,8 +2783,13 @@ nsDocument::GetActiveElement(nsIDOMElement **aElement)
getter_AddRefs(focusedWindow));
// be safe and make sure the element is from this document
if (focusedContent && focusedContent->OwnerDoc() == this) {
CallQueryInterface(focusedContent, aElement);
return NS_OK;
if (focusedContent->IsInNativeAnonymousSubtree()) {
focusedContent = focusedContent->FindFirstNonNativeAnonymous();
}
if (focusedContent) {
CallQueryInterface(focusedContent, aElement);
return NS_OK;
}
}
}

View File

@ -563,7 +563,8 @@ nsEventListenerManager::AddScriptEventListener(nsIAtom *aName,
// Try to get context from doc
// XXX sXBL/XBL2 issue -- do we really want the owner here? What
// if that's the XBL document?
global = node->OwnerDoc()->GetScriptGlobalObject();
doc = node->OwnerDoc();
global = doc->GetScriptGlobalObject();
} else {
nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(mTarget));
if (win) {

View File

@ -61,6 +61,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=238987
expected + "], got [" + e.target.id + "]");
} else {
var expected = forwardFocusArray.shift();
is(e.target, document.activeElement, "Wrong activeElement!");
ok(expected == e.target.id,
"(focus) Forward tabbing, expected [" +
expected + "], got [" + e.target.id + "]");

View File

@ -216,13 +216,8 @@ var nonFocusableElements = [
"<input type=\"checkbox\" tabindex=\"0\" disabled>",
"<input type=\"checkbox\" disabled>",
"<input type=\"file\">",
"<input type=\"file\" tabindex=\"-1\">",
"<input type=\"file\" tabindex=\"0\">",
"<input type=\"file\" tabindex=\"0\" disabled>",
"<input type=\"file\" tabindex=\"1\">",
"<input type=\"file\" disabled>",
"<input type=\"file\" contenteditable=\"true\">",
"<input type=\"hidden\">",
"<input type=\"hidden\" tabindex=\"-1\">",
@ -326,6 +321,12 @@ var focusableInContentEditable = [
"<input type=\"button\" tabindex=\"1\">",
"<input type=\"button\" contenteditable=\"true\">",
"<input type=\"file\">",
"<input type=\"file\" tabindex=\"-1\">",
"<input type=\"file\" tabindex=\"0\">",
"<input type=\"file\" tabindex=\"1\">",
"<input type=\"file\" contenteditable=\"true\">",
"<input type=\"checkbox\">",
"<input type=\"checkbox\" tabindex=\"-1\">",
"<input type=\"checkbox\" tabindex=\"0\">",
@ -456,6 +457,13 @@ function testElements(parent, tags, shouldBeFocusable)
var errorPrefix = serializer.serializeToString(element) + " in " +
serializer.serializeToString(parent);
try {
// Make sure activeElement doesn't point to a
// native anonymous element.
parent.ownerDocument.activeElement.localName;
} catch (ex) {
ok(false, ex + errorPrefix + errorSuffix);
}
if (focusable ? focusable.indexOf(tag) > -1 : shouldBeFocusable) {
is(parent.ownerDocument.activeElement, element,
errorPrefix + " should be focusable" + errorSuffix);

0
media/libopus/update.sh Executable file → Normal file
View File

View File

@ -714,29 +714,29 @@ ThreadActor.prototype = {
},
/**
* Create and return an environment actor that corresponds to the
* Debugger.Environment for the provided object.
* @param Debugger.Object aObject
* The object whose lexical environment we want to extract.
* Create and return an environment actor that corresponds to the provided
* Debugger.Environment.
* @param Debugger.Environment aEnvironment
* The lexical environment we want to extract.
* @param object aPool
* The pool where the newly-created actor will be placed.
* @return The EnvironmentActor for aObject or undefined for host functions or
* functions scoped to a non-debuggee global.
* @return The EnvironmentActor for aEnvironment or undefined for host
* functions or functions scoped to a non-debuggee global.
*/
createEnvironmentActor: function TA_createEnvironmentActor(aObject, aPool) {
let environment = aObject.environment;
if (!environment) {
createEnvironmentActor:
function TA_createEnvironmentActor(aEnvironment, aPool) {
if (!aEnvironment) {
return undefined;
}
if (environment.actor) {
return environment.actor;
if (aEnvironment.actor) {
return aEnvironment.actor;
}
let actor = new EnvironmentActor(aObject, this);
let actor = new EnvironmentActor(aEnvironment, this);
this._environmentActors.push(actor);
aPool.addActor(actor);
environment.actor = actor;
aEnvironment.actor = actor;
return actor;
},
@ -1054,7 +1054,7 @@ ObjectActor.prototype = {
let descriptor = {};
descriptor.configurable = aObject.configurable;
descriptor.enumerable = aObject.enumerable;
if (aObject.value) {
if (aObject.value !== undefined) {
descriptor.writable = aObject.writable;
descriptor.value = this.threadActor.createValueGrip(aObject.value);
} else {
@ -1102,7 +1102,7 @@ ObjectActor.prototype = {
" 'Function' class." };
}
let envActor = this.threadActor.createEnvironmentActor(this.obj,
let envActor = this.threadActor.createEnvironmentActor(this.obj.environment,
this.registeredPool);
if (!envActor) {
return { error: "notDebuggee",
@ -1110,7 +1110,7 @@ ObjectActor.prototype = {
}
return { name: this.obj.name || null,
scope: envActor.form() };
scope: envActor.form(this.obj) };
},
/**
@ -1239,9 +1239,9 @@ FrameActor.prototype = {
}
let envActor = this.threadActor
.createEnvironmentActor(this.frame,
.createEnvironmentActor(this.frame.environment,
this.frameLifetimePool);
form.environment = envActor ? envActor.form() : envActor;
form.environment = envActor ? envActor.form(this.frame) : envActor;
form.this = this.threadActor.createValueGrip(this.frame.this);
form.arguments = this._args();
if (this.frame.script) {
@ -1350,14 +1350,14 @@ BreakpointActor.prototype.requestTypes = {
* the bindings introduced by a lexical environment and assigning new values to
* those identifier bindings.
*
* @param Debugger.Object aObject
* The object whose lexical environment will be used to create the actor.
* @param Debugger.Environment aEnvironment
* The lexical environment that will be used to create the actor.
* @param ThreadActor aThreadActor
* The parent thread actor that contains this environment.
*/
function EnvironmentActor(aObject, aThreadActor)
function EnvironmentActor(aEnvironment, aThreadActor)
{
this.obj = aObject;
this.obj = aEnvironment;
this.threadActor = aThreadActor;
}
@ -1365,41 +1365,47 @@ EnvironmentActor.prototype = {
actorPrefix: "environment",
/**
* Returns an environment form for use in a protocol message.
* Returns an environment form for use in a protocol message. Note that the
* requirement of passing the frame or function as a parameter is only
* temporary, since when bug 747514 lands, the environment will have a callee
* property that will contain it.
*
* @param object aObject
* The stack frame or function object whose environment bindings are
* being generated.
*/
form: function EA_form() {
form: function EA_form(aObject) {
// Debugger.Frame might be dead by the time we get here, which will cause
// accessing its properties to throw.
if (!this.obj.live) {
if (!aObject.live) {
return undefined;
}
let parent;
if (this.obj.environment.parent) {
parent = this.threadActor
.createEnvironmentActor(this.obj.environment.parent,
this.registeredPool);
if (this.obj.parent) {
let thread = this.threadActor;
parent = thread.createEnvironmentActor(this.obj.parent.environment,
this.registeredPool);
}
let form = { actor: this.actorID,
parent: parent ? parent.form() : parent };
parent: parent ? parent.form(this.obj.parent) : parent };
if (this.obj.environment.type == "object") {
if (this.obj.environment.parent) {
if (aObject.type == "object") {
if (this.obj.parent) {
form.type = "with";
} else {
form.type = "object";
}
form.object = this.threadActor.createValueGrip(this.obj.environment.object);
form.object = this.threadActor.createValueGrip(aObject.object);
} else {
if (this.obj.class == "Function") {
if (aObject.class == "Function") {
form.type = "function";
form.function = this.threadActor.createValueGrip(this.obj);
form.functionName = this.obj.name;
form.function = this.threadActor.createValueGrip(aObject);
form.functionName = aObject.name;
} else {
form.type = "block";
}
form.bindings = this._bindings();
form.bindings = this._bindings(aObject);
}
return form;
@ -1407,19 +1413,41 @@ EnvironmentActor.prototype = {
/**
* Return the identifier bindings object as required by the remote protocol
* specification.
* specification. Note that the requirement of passing the frame or function
* as a parameter is only temporary, since when bug 747514 lands, the
* environment will have a callee property that will contain it.
*
* @param object aObject [optional]
* The stack frame or function object whose environment bindings are
* being generated. When left unspecified, the bindings do not contain
* an 'arguments' property.
*/
_bindings: function EA_bindings() {
_bindings: function EA_bindings(aObject) {
let bindings = { arguments: [], variables: {} };
// TODO: this will be redundant after bug 692984 is fixed.
if (typeof this.obj.environment.getVariableDescriptor != "function") {
// TODO: this part should be removed in favor of the commented-out part
// below when getVariableDescriptor lands (bug 725815).
if (typeof this.obj.getVariable != "function") {
//if (typeof this.obj.getVariableDescriptor != "function") {
return bindings;
}
for (let name in this.obj.parameterNames) {
let parameterNames;
if (aObject && aObject.callee) {
parameterNames = aObject.callee.parameterNames;
}
for each (let name in parameterNames) {
let arg = {};
let desc = this.obj.environment.getVariableDescriptor(name);
// TODO: this part should be removed in favor of the commented-out part
// below when getVariableDescriptor lands (bug 725815).
let desc = {
value: this.obj.getVariable(name),
configurable: false,
writable: true,
enumerable: true
};
// let desc = this.obj.getVariableDescriptor(name);
let descForm = {
enumerable: true,
configurable: desc.configurable
@ -1435,14 +1463,22 @@ EnvironmentActor.prototype = {
bindings.arguments.push(arg);
}
for (let name in this.obj.environment.names()) {
for each (let name in this.obj.names()) {
if (bindings.arguments.some(function exists(element) {
return !!element[name];
})) {
continue;
}
let desc = this.obj.environment.getVariableDescriptor(name);
// TODO: this part should be removed in favor of the commented-out part
// below when getVariableDescriptor lands.
let desc = {
value: this.obj.getVariable(name),
configurable: false,
writable: true,
enumerable: true
};
//let desc = this.obj.getVariableDescriptor(name);
let descForm = {
enumerable: true,
configurable: desc.configurable
@ -1468,7 +1504,7 @@ EnvironmentActor.prototype = {
* The protocol request object.
*/
onAssign: function EA_onAssign(aRequest) {
let desc = this.obj.environment.getVariableDescriptor(aRequest.name);
let desc = this.obj.getVariableDescriptor(aRequest.name);
if (!desc.writable) {
return { error: "immutableBinding",
@ -1477,7 +1513,7 @@ EnvironmentActor.prototype = {
}
try {
this.obj.environment.setVariable(aRequest.name, aRequest.value);
this.obj.setVariable(aRequest.name, aRequest.value);
} catch (e) {
if (e instanceof Debugger.DebuggeeWouldRun) {
return { error: "threadWouldRun",

View File

@ -0,0 +1,63 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Check a frame actor's bindings property.
*/
var gDebuggee;
var gClient;
var gThreadClient;
function run_test()
{
initTestDebuggerServer();
gDebuggee = addTestGlobal("test-stack");
gClient = new DebuggerClient(DebuggerServer.connectPipe());
gClient.connect(function() {
attachTestGlobalClientAndResume(gClient, "test-stack", function(aResponse, aThreadClient) {
gThreadClient = aThreadClient;
test_pause_frame();
});
});
do_test_pending();
}
function test_pause_frame()
{
gThreadClient.addOneTimeListener("paused", function(aEvent, aPacket) {
let bindings = aPacket.frame.environment.bindings;
let args = bindings.arguments;
let vars = bindings.variables;
do_check_eq(args.length, 6);
do_check_eq(args[0].aNumber.value, 42);
do_check_eq(args[1].aBool.value, true);
do_check_eq(args[2].aString.value, "nasu");
do_check_eq(args[3].aNull.value.type, "null");
do_check_eq(args[4].aUndefined.value.type, "undefined");
do_check_eq(args[5].aObject.value.type, "object");
do_check_eq(args[5].aObject.value.class, "Object");
do_check_true(!!args[5].aObject.value.actor);
do_check_eq(vars.a.value, 1);
do_check_eq(vars.b.value, true);
do_check_eq(vars.c.value.type, "object");
do_check_eq(vars.c.value.class, "Object");
do_check_true(!!vars.c.value.actor);
gThreadClient.resume(function() {
finishClient(gClient);
});
});
gDebuggee.eval("(" + function() {
function stopMe(aNumber, aBool, aString, aNull, aUndefined, aObject) {
var a = 1;
var b = true;
var c = { a: "a" };
debugger;
};
stopMe(42, true, "nasu", null, undefined, { foo: "bar" });
} + ")()");
}

View File

@ -53,3 +53,4 @@ tail =
[test_stepping-02.js]
[test_stepping-03.js]
[test_stepping-04.js]
[test_framebindings-01.js]

View File

@ -80,6 +80,13 @@ const IS_ANDROID = true;
const IS_ANDROID = false;
#endif
#ifdef MOZ_VERIFY_MAR_SIGNATURE
const IS_MAR_CHECKS_ENABLED = true;
#else
const IS_MAR_CHECKS_ENABLED = false;
#endif
const URL_HOST = "http://localhost:4444/";
const URL_PATH = "data";

View File

@ -47,6 +47,10 @@ const TEST_FILES = [];
const VERSION_DOWNGRADE_ERROR = "23";
function run_test() {
if (!IS_MAR_CHECKS_ENABLED) {
return;
}
// Setup an old version MAR file
do_register_cleanup(cleanupUpdaterTest);
setupUpdaterTest(MAR_OLD_VERSION_FILE);

View File

@ -47,6 +47,10 @@ const TEST_FILES = [];
const MAR_CHANNEL_MISMATCH_ERROR = "22";
function run_test() {
if (!IS_MAR_CHECKS_ENABLED) {
return;
}
// Setup a wrong channel MAR file
do_register_cleanup(cleanupUpdaterTest);
setupUpdaterTest(MAR_WRONG_CHANNEL_FILE);