Merge from mozilla-central.
Merge from mozilla-central.
--- a/.hgtags
+++ b/.hgtags
@@ -76,8 +76,13 @@ 462c726144bc1fb45b61e774f64ac5d61b4e047c
bbc7014db2de49e2301680d2a86be8a53108a88a AURORA_BASE_20120131
bbc7014db2de49e2301680d2a86be8a53108a88a AURORA_BASE_20120131
0000000000000000000000000000000000000000 AURORA_BASE_20120131
0000000000000000000000000000000000000000 AURORA_BASE_20120131
bbc7014db2de49e2301680d2a86be8a53108a88a AURORA_BASE_20120131
b6627f28b7ec17e1b46a594df0f780d3a40847e4 FIREFOX_AURORA_13_BASE
357da346ceb705d196a46574804c7c4ec44ac186 FIREFOX_AURORA_14_BASE
26dcd1b1a20893ad99341c61c6b1239ff1523858 FIREFOX_AURORA_15_BASE
+0accd12a8e7e217836ea3f1ee7c411913fc75d8e FIREFOX_AURORA_16_BASE
+0000000000000000000000000000000000000000 FIREFOX_AURORA_16_BASE
+9697eadafa13b4e9233b39aaeecfeac79503cb54 FIREFOX_AURORA_16_BASE
+9697eadafa13b4e9233b39aaeecfeac79503cb54 FIREFOX_AURORA_16_BASE
+6fdf9985acfe6f939da584b2559464ab22264fe7 FIREFOX_AURORA_16_BASE
--- a/accessible/src/base/RoleMap.h
+++ b/accessible/src/base/RoleMap.h
@@ -826,17 +826,17 @@ ROLE(COMBOBOX_OPTION,
ATK_ROLE_MENU_ITEM,
NSAccessibilityMenuItemRole,
ROLE_SYSTEM_LISTITEM,
ROLE_SYSTEM_LISTITEM)
ROLE(IMAGE_MAP,
"image map",
ATK_ROLE_IMAGE,
- NSAccessibilityImageRole,
+ NSAccessibilityUnknownRole,
ROLE_SYSTEM_GRAPHIC,
ROLE_SYSTEM_GRAPHIC)
ROLE(OPTION,
"listbox option",
ATK_ROLE_LIST_ITEM,
NSAccessibilityRowRole,
ROLE_SYSTEM_LISTITEM,
--- a/accessible/src/base/nsAccDocManager.h
+++ b/accessible/src/base/nsAccDocManager.h
@@ -19,17 +19,17 @@ class DocAccessible;
/**
* Manage the document accessible life cycle.
*/
class nsAccDocManager : public nsIWebProgressListener,
public nsIDOMEventListener,
public nsSupportsWeakReference
{
public:
- virtual ~nsAccDocManager() { };
+ virtual ~nsAccDocManager() { }
NS_DECL_ISUPPORTS
NS_DECL_NSIWEBPROGRESSLISTENER
NS_DECL_NSIDOMEVENTLISTENER
/**
* Return document accessible for the given DOM node.
*/
@@ -65,17 +65,17 @@ public:
mDocAccessibleCache.Remove(aDocument);
}
#ifdef DEBUG
bool IsProcessingRefreshDriverNotification() const;
#endif
protected:
- nsAccDocManager() { };
+ nsAccDocManager() { }
/**
* Initialize the manager.
*/
bool Init();
/**
* Shutdown the manager.
--- a/accessible/src/mac/mozAccessible.mm
+++ b/accessible/src/mac/mozAccessible.mm
@@ -407,17 +407,17 @@ GetClosestInterestingAccessible(id anObj
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
}
- (NSString*)role
{
if (!mGeckoAccessible)
return nil;
-#ifdef DEBUG
+#ifdef DEBUG_A11Y
NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(mGeckoAccessible),
"Does not support nsIAccessibleText when it should");
#endif
#define ROLE(geckoRole, stringRole, atkRole, macRole, msaaRole, ia2Role) \
case roles::geckoRole: \
return macRole;
--- a/accessible/src/mac/mozHTMLAccessible.mm
+++ b/accessible/src/mac/mozHTMLAccessible.mm
@@ -87,16 +87,26 @@
return;
if ([action isEqualToString:NSAccessibilityPressAction])
mGeckoAccessible->DoAction(0);
else
[super accessibilityPerformAction:action];
}
+- (NSString*)customDescription
+{
+ return @"";
+}
+
+- (NSString*)value
+{
+ return @"";
+}
+
- (NSURL*)url
{
if (!mGeckoAccessible || mGeckoAccessible->IsDefunct())
return nil;
nsAutoString value;
mGeckoAccessible->Value(value);
--- a/accessible/tests/mochitest/events/test_focus_autocomplete.xul
+++ b/accessible/tests/mochitest/events/test_focus_autocomplete.xul
@@ -225,17 +225,17 @@
var itemX = {}, itemY = {}, treeX = {}, treeY = {};
accessible.getBounds(itemX, itemY, {}, {});
tree.getBounds(treeX, treeY, {}, {});
x = itemX.value - treeX.value;
y = itemY.value - treeY.value;
}
}
- synthesizeMouse(node, x + 1, y + 1, {}, targetWindow);
+ synthesizeMouseAtCenter(node, {}, targetWindow);
}
this.getID = function selectByClick_getID()
{
return "select by click " + prettyName(aIDFunc.call(null, aIDFuncArg));
}
}
--- a/accessible/tests/mochitest/tree/test_map.html
+++ b/accessible/tests/mochitest/tree/test_map.html
@@ -17,19 +17,19 @@
function doTest()
{
// map used as imagemap, not accessible
var accTree =
{ SECTION: [ ] };
testAccessibleTree("imagemapcontainer", accTree);
- // map group
+ // map group. Imagemaps are inlines by default, so TEXT_CONTAINER.
accTree =
- { PARAGRAPH: [
+ { TEXT_CONTAINER: [
{ PARAGRAPH: [
{ TEXT_LEAF: [ ] },
{ LINK: [
{ TEXT_LEAF: [ ] }
] },
{ TEXT_LEAF: [ ] },
{ LINK: [
{ TEXT_LEAF: [ ] }
--- a/accessible/tests/mochitest/treeupdate/test_imagemap.html
+++ b/accessible/tests/mochitest/treeupdate/test_imagemap.html
@@ -422,13 +422,16 @@
<map name="atoz_map" id="map">
<area href="http://www.bbc.co.uk/radio4/atoz/index.shtml#b"
coords="17,0,30,14" alt="b" shape="rect">
</map>
<div id="container">
<img id="imgmap" width="447" height="15"
usemap="#atoz_map"
- src="../letters.gif">
- </div>
+ src="../letters.gif"><!--
+ Important: no whitespace between the <img> and the </div>, so we
+ don't end up with textframes there, because those would be reflected
+ in our accessible tree in some cases.
+ --></div>
</body>
</html>
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -240,17 +240,18 @@ pref("editor.singleLine.pasteNewlines",
// threshold where a tap becomes a drag, in 1/240" reference pixels
// The names of the preferences are to be in sync with nsEventStateManager.cpp
pref("ui.dragThresholdX", 25);
pref("ui.dragThresholdY", 25);
// Layers Acceleration
pref("layers.acceleration.disabled", false);
-pref("layers.offmainthreadcomposition.enabled", false);
+pref("layers.offmainthreadcomposition.enabled", true);
+pref("layers.async-video.enabled", true);
// Web Notifications
pref("notification.feature.enabled", true);
// IndexedDB
pref("indexedDB.feature.enabled", true);
pref("dom.indexedDB.warningQuota", 5);
@@ -404,16 +405,26 @@ pref("b2g.remote-js.port", 9999);
// Handle hardware buttons in the b2g chrome package
pref("b2g.keys.menu.enabled", true);
// Screen timeout in seconds
pref("power.screen.timeout", 60);
pref("full-screen-api.enabled", true);
+#ifndef MOZ_WIDGET_GONK
+// If we're not actually on physical hardware, don't make the top level widget
+// fullscreen when transitioning to fullscreen. This means in emulated
+// environments (like the b2g desktop client) we won't make the client window
+// fill the whole screen, we'll just make the content fill the client window,
+// i.e. it won't give the impression to content that the number of device
+// screen pixels changes!
+pref("full-screen-api.ignore-widgets", true);
+#endif
+
pref("media.volume.steps", 10);
//Enable/disable marionette server, set listening port
pref("marionette.defaultPrefs.enabled", true);
pref("marionette.defaultPrefs.port", 2828);
#ifdef MOZ_UPDATER
pref("app.update.enabled", true);
@@ -455,8 +466,14 @@ pref("media.plugins.enabled", true);
// Disable printing (particularly, window.print())
pref("dom.disable_window_print", true);
// Disable window.showModalDialog
pref("dom.disable_window_showModalDialog", true);
// Turns on gralloc-based direct texturing for Gonk
pref("gfx.gralloc.enabled", false);
+
+// XXXX REMOVE FOR PRODUCTION. Turns on GC and CC logging
+pref("javascript.options.mem.log", true);
+
+// Increase mark slice time from 10ms to 30ms
+pref("javascript.options.mem.gc_incremental_slice_ms", 30);
--- a/b2g/chrome/content/settings.js
+++ b/b2g/chrome/content/settings.js
@@ -42,16 +42,25 @@ var SettingsListener = {
}));
this._callbacks[name] = callback;
}
};
SettingsListener.init();
+// =================== Audio ====================
+SettingsListener.observe('audio.volume.master', 0.5, function(value) {
+ let audioManager = Services.audioManager;
+ if (!audioManager)
+ return;
+
+ audioManager.masterVolume = Math.max(0.0, Math.min(value, 1.0));
+});
+
// =================== Languages ====================
SettingsListener.observe('language.current', 'en-US', function(value) {
Services.prefs.setCharPref('intl.accept_languages', value);
});
// =================== RIL ====================
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -171,69 +171,32 @@ var shell = {
window.removeEventListener('sizemodechange', this);
this.contentBrowser.removeEventListener('mozbrowserloadstart', this, true);
#ifndef MOZ_WIDGET_GONK
delete Services.audioManager;
#endif
},
- changeVolume: function shell_changeVolume(delta) {
- let steps = 10;
- try {
- steps = Services.prefs.getIntPref("media.volume.steps");
- if (steps <= 0)
- steps = 1;
- } catch(e) {}
-
- let audioManager = Services.audioManager;
- if (!audioManager)
- return;
-
- let currentVolume = audioManager.masterVolume;
- let newStep = Math.round(steps * Math.sqrt(currentVolume)) + delta;
- let volume = (newStep / steps) * (newStep / steps);
-
- if (volume > 1)
- volume = 1;
- if (volume < 0)
- volume = 0;
- audioManager.masterVolume = volume;
- },
-
forwardKeyToContent: function shell_forwardKeyToContent(evt) {
let content = shell.contentBrowser.contentWindow;
let generatedEvent = content.document.createEvent('KeyboardEvent');
generatedEvent.initKeyEvent(evt.type, true, true, evt.view, evt.ctrlKey,
evt.altKey, evt.shiftKey, evt.metaKey,
evt.keyCode, evt.charCode);
content.document.documentElement.dispatchEvent(generatedEvent);
},
handleEvent: function shell_handleEvent(evt) {
let content = this.contentBrowser.contentWindow;
switch (evt.type) {
case 'keydown':
case 'keyup':
case 'keypress':
- // For debug purposes and because some of the APIs are not yet exposed
- // to the content, let's react on some of the keyup events.
- if (evt.type == 'keyup' && evt.eventPhase == evt.BUBBLING_PHASE) {
- switch (evt.keyCode) {
- case evt.DOM_VK_PAGE_DOWN:
- this.changeVolume(-1);
- break;
-
- case evt.DOM_VK_PAGE_UP:
- this.changeVolume(1);
- break;
- }
- }
-
// Redirect the HOME key to System app and stop the applications from
// handling it.
let rootContentEvt = (evt.target.ownerDocument.defaultView == content);
if (!rootContentEvt && evt.eventPhase == evt.CAPTURING_PHASE &&
evt.keyCode == evt.DOM_VK_HOME) {
this.forwardKeyToContent(evt);
evt.preventDefault();
evt.stopImmediatePropagation();
--- a/b2g/confvars.sh
+++ b/b2g/confvars.sh
@@ -1,16 +1,16 @@
# 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/.
MOZ_APP_BASENAME=B2G
MOZ_APP_VENDOR=Mozilla
-MOZ_APP_VERSION=16.0a1
+MOZ_APP_VERSION=17.0a1
MOZ_APP_UA_NAME=Firefox
MOZ_BRANDING_DIRECTORY=b2g/branding/unofficial
MOZ_OFFICIAL_BRANDING_DIRECTORY=b2g/branding/official
# MOZ_APP_DISPLAYNAME is set by branding/configure.sh
MOZ_SAFE_BROWSING=
MOZ_SERVICES_SYNC=1
--- a/browser/app/blocklist.xml
+++ b/browser/app/blocklist.xml
@@ -1,10 +1,10 @@
<?xml version="1.0"?>
-<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1341001222000">
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1341959296000">
<emItems>
<emItem blockID="i58" id="webmaster@buzzzzvideos.info">
<versionRange minVersion="0" maxVersion="*">
</versionRange>
</emItem>
<emItem blockID="i86" id="{45147e67-4020-47e2-8f7a-55464fb535aa}">
<versionRange minVersion="0" maxVersion="*">
</versionRange>
@@ -23,17 +23,17 @@
</emItem>
<emItem blockID="i43" id="supportaccessplugin@gmail.com">
</emItem>
<emItem blockID="i82" id="{8f42fb8b-b6f6-45de-81c0-d6d39f54f971}">
<versionRange minVersion="0" maxVersion="*">
</versionRange>
</emItem>
<emItem blockID="i107" os="WINNT" id="{ABDE892B-13A8-4d1b-88E6-365A6E755758}">
- <versionRange minVersion="0" maxVersion="15.0.4" severity="1">
+ <versionRange minVersion="0" maxVersion="15.0.5" severity="1">
</versionRange>
</emItem>
<emItem blockID="i88" id="anttoolbar@ant.com">
<versionRange minVersion="2.4.6.4" maxVersion="2.4.6.4" severity="1">
</versionRange>
</emItem>
<emItem blockID="i17" id="{3252b9ae-c69a-4eaf-9502-dc9c1f6c009e}">
<versionRange minVersion="2.2" maxVersion="2.2">
@@ -210,16 +210,20 @@
<emItem blockID="i109" id="{392e123b-b691-4a5e-b52f-c4c1027e749c}">
<versionRange minVersion="0" maxVersion="*">
</versionRange>
</emItem>
<emItem blockID="i60" id="youtb3@youtb3.com">
<versionRange minVersion="0" maxVersion="*">
</versionRange>
</emItem>
+ <emItem blockID="i111" os="WINNT" id="{C3949AC2-4B17-43ee-B4F1-D26B9D42404D}">
+ <versionRange minVersion="0" maxVersion="15.0.5" severity="1">
+ </versionRange>
+ </emItem>
<emItem blockID="i38" id="{B7082FAA-CB62-4872-9106-E42DD88EDE45}">
<versionRange minVersion="0.1" maxVersion="3.3.0.*">
<targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
<versionRange minVersion="3.7a1" maxVersion="*" />
</targetApplication>
</versionRange>
<versionRange minVersion="3.3.1" maxVersion="*">
<targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
@@ -323,17 +327,17 @@
<versionRange minVersion="0" maxVersion="*">
</versionRange>
</emItem>
<emItem blockID="i77" id="{fa277cfc-1d75-4949-a1f9-4ac8e41b2dfd}">
<versionRange minVersion="0" maxVersion="*">
</versionRange>
</emItem>
<emItem blockID="i106" os="WINNT" id="{97E22097-9A2F-45b1-8DAF-36AD648C7EF4}">
- <versionRange minVersion="0" maxVersion="15.0.4" severity="1">
+ <versionRange minVersion="0" maxVersion="15.0.5" severity="1">
</versionRange>
</emItem>
<emItem blockID="i11" id="yslow@yahoo-inc.com">
<versionRange minVersion="2.0.5" maxVersion="2.0.5">
<targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
<versionRange minVersion="3.5.7" maxVersion="*" />
</targetApplication>
</versionRange>
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1179,9 +1179,9 @@ pref("pdfjs.previousHandler.preferredAct
pref("pdfjs.previousHandler.alwaysAskBeforeHandling", false);
// The maximum amount of decoded image data we'll willingly keep around (we
// might keep around more than this, but we'll try to get down to this value).
// (This is intentionally on the high side; see bug 746055.)
pref("image.mem.max_decoded_image_kb", 256000);
// Example social provider
-pref("social.manifest.motown", "{\"origin\":\"https://motown-dev.mozillalabs.com\",\"name\":\"MoTown\",\"workerURL\":\"https://motown-dev.mozillalabs.com/social/worker.js\"}");
+pref("social.manifest.motown", "{\"origin\":\"https://motown-dev.mozillalabs.com\",\"name\":\"MoTown\",\"workerURL\":\"https://motown-dev.mozillalabs.com/social/worker.js\",\"iconURL\":\"https://motown-dev.mozillalabs.com/images/motown-icon.png\"}");
--- a/browser/base/content/aboutRobots.xhtml
+++ b/browser/base/content/aboutRobots.xhtml
@@ -44,17 +44,17 @@
<style type="text/css"><![CDATA[
#errorPageContainer:before {
content: url('chrome://browser/content/aboutRobots-icon.png');
position: absolute;
}
body[dir=rtl] #icon,
body[dir=rtl] #errorPageContainer:before {
- -moz-transform: scaleX(-1);
+ transform: scaleX(-1);
}
]]></style>
</head>
<body dir="&locale.dir;">
<!-- PAGE CONTAINER (for styling purposes only) -->
<div id="errorPageContainer">
@@ -92,13 +92,13 @@
<!-- Button -->
<button id="errorTryAgain"
label2="&robots.dontpress;"
onclick="robotButton();">&retry.label;</button>
<img src="chrome://browser/content/aboutRobots-widget-left.png"
style="position: absolute; bottom: -12px; left: -10px;"/>
<img src="chrome://browser/content/aboutRobots-widget-left.png"
- style="position: absolute; bottom: -12px; right: -10px; -moz-transform: scaleX(-1);"/>
+ style="position: absolute; bottom: -12px; right: -10px; transform: scaleX(-1);"/>
</div>
</body>
</html>
--- a/browser/base/content/abouthome/aboutHome.css
+++ b/browser/base/content/abouthome/aboutHome.css
@@ -38,16 +38,17 @@ a {
}
#topSection {
text-align: center;
}
#brandLogo {
height: 154px;
+ width: 154px;
margin: 22px 0 31px;
}
#searchForm,
#snippets {
width: 470px;
}
@@ -59,16 +60,18 @@ a {
display: -moz-box;
-moz-box-align: center;
padding-top: 2px;
-moz-padding-end: 8px;
}
#searchEngineLogo {
display: inline-block;
+ height: 28px;
+ width: 70px;
}
#searchText {
-moz-box-flex: 1;
padding: 6px 8px;
background: hsla(0,0%,100%,.9) padding-box;
border: 1px solid;
border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.17) hsla(210,54%,20%,.2);
@@ -77,17 +80,18 @@ a {
0 1px 0 hsla(0,0%,100%,.2);
border-radius: 2.5px 0 0 2.5px;
}
body[dir=rtl] #searchText {
border-radius: 0 2.5px 2.5px 0;
}
-#searchText:focus {
+#searchText:focus,
+#searchText[autofocus] {
border-color: hsla(206,100%,60%,.6) hsla(206,76%,52%,.6) hsla(204,100%,40%,.6);
}
#searchSubmit {
-moz-margin-start: -1px;
background: -moz-linear-gradient(hsla(0,0%,100%,.8), hsla(0,0%,100%,.1)) padding-box;
padding: 0 9px;
border: 1px solid;
@@ -101,22 +105,24 @@ body[dir=rtl] #searchText {
transition-duration: 150ms;
}
body[dir=rtl] #searchSubmit {
border-radius: 2.5px 0 0 2.5px;
}
#searchText:focus + #searchSubmit,
-#searchText + #searchSubmit:hover {
+#searchText + #searchSubmit:hover,
+#searchText[autofocus] + #searchSubmit {
border-color: #59b5fc #45a3e7 #3294d5;
color: white;
}
-#searchText:focus + #searchSubmit {
+#searchText:focus + #searchSubmit,
+#searchText[autofocus] + #searchSubmit {
background-image: -moz-linear-gradient(#4cb1ff, #1793e5);
box-shadow: 0 1px 0 hsla(0,0%,100%,.2) inset,
0 0 0 1px hsla(0,0%,100%,.1) inset,
0 1px 0 hsla(210,54%,20%,.03);
}
#searchText + #searchSubmit:hover {
background-image: -moz-linear-gradient(#66bdff, #0d9eff);
@@ -155,16 +161,22 @@ body[dir=rtl] #defaultSnippet2 {
}
#snippets {
display: inline-block;
text-align: start;
margin: 12px 0;
color: #3c3c3c;
font-size: 75%;
+ /* 12px is the computed font size, 15px the computed line height of the snippets
+ with Segoe UI on a default Windows 7 setup. The 15/12 multiplier approximately
+ converts em from units of font-size to units of line-height. The goal is to
+ preset the height of a three-line snippet to avoid visual moving/flickering as
+ the snippets load. */
+ min-height: -moz-calc(15/12 * 3em);
}
#launcher {
display: -moz-box;
-moz-box-align: center;
-moz-box-pack: center;
width: 100%;
background-color: hsla(0,0%,0%,.03);
@@ -251,18 +263,19 @@ body[narrow] #restorePreviousSessionSepa
}
body[narrow] #restorePreviousSession {
font-size: 80%;
}
.launchButton::before {
display: block;
+ width: 32px;
height: 32px;
- margin-bottom: 6px;
+ margin: 0 auto 6px;
line-height: 0; /* remove extra vertical space due to non-zero font-size */
}
#downloads::before {
content: url("chrome://browser/content/abouthome/downloads.png");
}
#bookmarks::before {
@@ -295,17 +308,17 @@ body[narrow] #restorePreviousSession {
width: 48px;
display: inline-block; /* display on same line as text label */
vertical-align: middle;
margin-bottom: 0;
-moz-margin-end: 8px;
}
body[dir=rtl] #restorePreviousSession::before {
- -moz-transform: scaleX(-1);
+ transform: scaleX(-1);
}
body[narrow] #restorePreviousSession::before {
content: url("chrome://browser/content/abouthome/restore.png");
height: 32px;
width: 32px;
}
--- a/browser/base/content/abouthome/aboutHome.js
+++ b/browser/base/content/abouthome/aboutHome.js
@@ -160,16 +160,25 @@ function setupSearchEngine()
// Add search engine logo.
if (gSearchEngine.image) {
let logoElt = document.getElementById("searchEngineLogo");
logoElt.src = gSearchEngine.image;
logoElt.alt = gSearchEngine.name;
}
+ // The "autofocus" attribute doesn't focus the form element
+ // immediately when the element is first drawn, so the
+ // attribute is also used for styling when the page first loads.
+ let searchText = document.getElementById("searchText");
+ searchText.addEventListener("blur", function searchText_onBlur() {
+ searchText.removeEventListener("blur", searchText_onBlur);
+ searchText.removeAttribute("autofocus");
+ });
+
}
function loadSnippets()
{
// Check last snippets update.
let lastUpdate = localStorage["snippets-last-update"];
let updateURL = localStorage["snippets-update-url"];
if (updateURL && (!lastUpdate ||
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -100,16 +100,18 @@
<command id="Tools:ResponsiveUI" oncommand="ResponsiveUI.toggle();" disabled="true"/>
<command id="Tools:Addons" oncommand="BrowserOpenAddonsMgr();"/>
<command id="Tools:Sanitize"
oncommand="Cc['@mozilla.org/browser/browserglue;1'].getService(Ci.nsIBrowserGlue).sanitize(window);"/>
<command id="Tools:PrivateBrowsing" oncommand="gPrivateBrowsingUI.toggleMode();"/>
<command id="History:UndoCloseTab" oncommand="undoCloseTab();"/>
<command id="History:UndoCloseWindow" oncommand="undoCloseWindow();"/>
<command id="Browser:ToggleAddonBar" oncommand="toggleAddonBar();"/>
+ <command id="Social:SharePage" oncommand="SocialShareButton.sharePage();"/>
+ <command id="Social:UnsharePage" oncommand="SocialShareButton.unsharePage();"/>
</commandset>
<commandset id="placesCommands">
<command id="Browser:ShowAllBookmarks"
oncommand="PlacesCommandHook.showPlacesOrganizer('AllBookmarks');"/>
<command id="Browser:ShowAllHistory"
oncommand="PlacesCommandHook.showPlacesOrganizer('History');"/>
</commandset>
@@ -332,16 +334,17 @@
<key id="manBookmarkKb" key="&bookmarksGtkCmd.commandkey;" command="Browser:ShowAllBookmarks" modifiers="accel,shift"/>
#endif
<key id="viewBookmarksSidebarKb" key="&bookmarksCmd.commandkey;" command="viewBookmarksSidebar" modifiers="accel"/>
#ifdef XP_WIN
# Cmd+I is conventially mapped to Info on MacOS X, thus it should not be
# overridden for other purposes there.
<key id="viewBookmarksSidebarWinKb" key="&bookmarksWinCmd.commandkey;" command="viewBookmarksSidebar" modifiers="accel"/>
#endif
+ <key id="sharePage" key="&sharePageCmd.commandkey;" command="Social:SharePage" modifiers="accel,shift"/>
# don't use |command="Browser:Stop"|, ESC is being used to freeze animated gifs,
# even if the stop button and menuitem are disabled (see Bug 284140)
<key id="key_stop" keycode="VK_ESCAPE" oncommand="BrowserStop();"/>
#ifdef XP_MACOSX
<key id="key_stop_mac" modifiers="accel" key="&stopCmd.macCommandKey;" oncommand="BrowserStop();"/>
#endif
new file mode 100644
--- /dev/null
+++ b/browser/base/content/browser-social.js
@@ -0,0 +1,256 @@
+// 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/.
+
+let SocialUI = {
+ // Called on delayed startup to initialize UI
+ init: function SocialUI_init() {
+ Services.obs.addObserver(this, "social:pref-changed", false);
+ Services.obs.addObserver(this, "social:ambient-notification-changed", false);
+ Services.obs.addObserver(this, "social:profile-changed", false);
+
+ Social.init(this._providerReady.bind(this));
+ },
+
+ // Called on window unload
+ uninit: function SocialUI_uninit() {
+ Services.obs.removeObserver(this, "social:pref-changed");
+ Services.obs.removeObserver(this, "social:ambient-notification-changed");
+ Services.obs.removeObserver(this, "social:profile-changed");
+ },
+
+ showProfile: function SocialUI_showProfile() {
+ if (Social.provider)
+ openUILink(Social.provider.profile.profileURL);
+ },
+
+ observe: function SocialUI_observe(subject, topic, data) {
+ switch (topic) {
+ case "social:pref-changed":
+ SocialShareButton.updateButtonHiddenState();
+ SocialToolbar.updateButtonHiddenState();
+ break;
+ case "social:ambient-notification-changed":
+ SocialToolbar.updateButton();
+ break;
+ case "social:profile-changed":
+ SocialToolbar.updateProfile();
+ break;
+ }
+ },
+
+ // Called once Social.jsm's provider has been set
+ _providerReady: function SocialUI_providerReady() {
+ SocialToolbar.init();
+ SocialShareButton.init();
+ }
+}
+
+let SocialShareButton = {
+ init: function SSB_init() {
+ this.sharePopup.hidden = false;
+ this.updateButtonHiddenState();
+
+ let profileRow = document.getElementById("editSharePopupHeader");
+ let profile = Social.provider.profile;
+ if (profile && profile.portrait && profile.displayName) {
+ profileRow.hidden = false;
+ let portrait = document.getElementById("socialUserPortrait");
+ portrait.style.listStyleImage = profile.portrait;
+ let displayName = document.getElementById("socialUserDisplayName");
+ displayName.setAttribute("label", profile.displayName);
+ } else {
+ profileRow.hidden = true;
+ }
+ },
+
+ get shareButton() {
+ return document.getElementById("share-button");
+ },
+ get sharePopup() {
+ return document.getElementById("editSharePopup");
+ },
+
+ dismissSharePopup: function SSB_dismissSharePopup() {
+ this.sharePopup.hidePopup();
+ },
+
+ updateButtonHiddenState: function SSB_updateButtonHiddenState() {
+ let shareButton = this.shareButton;
+ if (shareButton)
+ shareButton.hidden = !Social.uiVisible;
+ },
+
+ onClick: function SSB_onClick(aEvent) {
+ if (aEvent.button != 0)
+ return;
+
+ // Don't bubble to the textbox, to avoid unwanted selection of the address.
+ aEvent.stopPropagation();
+
+ this.sharePage();
+ },
+
+ panelShown: function SSB_panelShown(aEvent) {
+ let sharePopupOkButton = document.getElementById("editSharePopupOkButton");
+ if (sharePopupOkButton)
+ sharePopupOkButton.focus();
+ },
+
+ sharePage: function SSB_sharePage() {
+ let uri = gBrowser.currentURI;
+ if (!Social.isPageShared(uri)) {
+ Social.sharePage(uri);
+ this.updateShareState();
+ } else {
+ this.sharePopup.openPopup(this.shareButton, "bottomcenter topright");
+ }
+ },
+
+ unsharePage: function SSB_unsharePage() {
+ Social.unsharePage(gBrowser.currentURI);
+ this.updateShareState();
+ this.dismissSharePopup();
+ },
+
+ updateShareState: function SSB_updateShareState() {
+ let currentPageShared = Social.isPageShared(gBrowser.currentURI);
+
+ // Provide a11y-friendly notification of share.
+ let status = document.getElementById("share-button-status");
+ if (status) {
+ let statusString = currentPageShared ?
+ gNavigatorBundle.getString("social.pageShared.label") : "";
+ status.setAttribute("value", statusString);
+ }
+
+ // Update the share button, if present
+ let shareButton = this.shareButton;
+ if (!shareButton)
+ return;
+
+ if (currentPageShared) {
+ shareButton.setAttribute("shared", "true");
+ shareButton.setAttribute("tooltiptext", gNavigatorBundle.getString("social.shareButton.sharedtooltip"));
+ } else {
+ shareButton.removeAttribute("shared");
+ shareButton.setAttribute("tooltiptext", gNavigatorBundle.getString("social.shareButton.tooltip"));
+ }
+ }
+};
+
+var SocialToolbar = {
+ // Called once, after window load, when the Social.provider object is initialized
+ init: function SocialToolbar_init() {
+ document.getElementById("social-provider-image").setAttribute("image", Social.provider.iconURL);
+
+ // handle button state
+ document.getElementById("social-statusarea-popup").addEventListener("popupshowing", function(e) {
+ document.getElementById("social-toolbar-button").setAttribute("open", "true");
+ }, false);
+ document.getElementById("social-statusarea-popup").addEventListener("popuphiding", function(e) {
+ document.getElementById("social-toolbar-button").removeAttribute("open");
+ }, false);
+
+ this.updateButton();
+ this.updateProfile();
+ },
+
+ updateButtonHiddenState: function SocialToolbar_updateButtonHiddenState() {
+ let toolbarbutton = document.getElementById("social-toolbar-button");
+ toolbarbutton.hidden = !Social.uiVisible;
+ },
+
+ updateProfile: function SocialToolbar_updateProfile() {
+ // Profile may not have been initialized yet, since it depends on a worker
+ // response. In that case we'll be called again when it's available, via
+ // social:profile-changed
+ let profile = Social.provider.profile || {};
+ let userPortrait = profile.portrait || "chrome://browser/skin/social/social.png";
+ document.getElementById("social-statusarea-user-portrait").setAttribute("src", userPortrait);
+
+ let notLoggedInLabel = document.getElementById("social-statusarea-notloggedin");
+ let userNameBtn = document.getElementById("social-statusarea-username");
+ if (profile.userName) {
+ notLoggedInLabel.hidden = true;
+ userNameBtn.hidden = false;
+ userNameBtn.label = profile.userName;
+ } else {
+ notLoggedInLabel.hidden = false;
+ userNameBtn.hidden = true;
+ }
+ },
+
+ updateButton: function SocialToolbar_updateButton() {
+ this.updateButtonHiddenState();
+
+ let provider = Social.provider;
+ // if there are no ambient icons, we collapse them in the following loop
+ let iconNames = Object.keys(provider.ambientNotificationIcons);
+ let iconBox = document.getElementById("social-status-iconbox");
+ for (var i = 0; i < iconBox.childNodes.length; i++) {
+ let iconContainer = iconBox.childNodes[i];
+ if (i > iconNames.length - 1) {
+ iconContainer.collapsed = true;
+ continue;
+ }
+
+ iconContainer.collapsed = false;
+ let icon = provider.ambientNotificationIcons[iconNames[i]];
+ let iconImage = iconContainer.firstChild;
+ let iconCounter = iconImage.nextSibling;
+
+ iconImage.setAttribute("contentPanel", icon.contentPanel);
+ iconImage.setAttribute("src", icon.iconURL);
+
+ if (iconCounter.firstChild)
+ iconCounter.removeChild(iconCounter.firstChild);
+
+ if (icon.counter) {
+ iconCounter.appendChild(document.createTextNode(icon.counter));
+ iconCounter.collapsed = false;
+ } else {
+ iconCounter.collapsed = true;
+ }
+ }
+ },
+
+ showAmbientPopup: function SocialToolbar_showAmbientPopup(iconContainer) {
+ let iconImage = iconContainer.firstChild;
+ let panel = document.getElementById("social-notification-panel");
+ let notifBrowser = document.getElementById("social-notification-browser");
+
+ panel.hidden = false;
+
+ function sizePanelToContent() {
+ // XXX Maybe we can use nsIDOMWindowUtils.getRootBounds() here?
+ // XXX need to handle dynamic sizing
+ let doc = notifBrowser.contentDocument;
+ // XXX "notif" is an implementation detail that we should get rid of
+ // eventually
+ let body = doc.getElementById("notif") || doc.body.firstChild;
+ if (!body)
+ return;
+ let h = body.scrollHeight > 0 ? body.scrollHeight : 300;
+ notifBrowser.style.width = body.scrollWidth + "px";
+ notifBrowser.style.height = h + "px";
+ }
+
+ notifBrowser.addEventListener("DOMContentLoaded", function onload() {
+ notifBrowser.removeEventListener("DOMContentLoaded", onload);
+ sizePanelToContent();
+ });
+
+ panel.addEventListener("popuphiding", function onpopuphiding() {
+ panel.removeEventListener("popuphiding", onpopuphiding);
+ // unload the panel
+ document.getElementById("social-toolbar-button").removeAttribute("open");
+ notifBrowser.setAttribute("src", "about:blank");
+ });
+
+ notifBrowser.service = Social.provider;
+ notifBrowser.setAttribute("src", iconImage.getAttribute("contentPanel"));
+ document.getElementById("social-toolbar-button").setAttribute("open", "true");
+ panel.openPopup(iconImage, "bottomcenter topleft", 0, 0, false, false);
+ }
+}
--- a/browser/base/content/browser-syncui.js
+++ b/browser/base/content/browser-syncui.js
@@ -136,17 +136,17 @@ let gSyncUI = {
onLoginFinish: function SUI_onLoginFinish() {
// Clear out any login failure notifications
let title = this._stringBundle.GetStringFromName("error.login.title");
this.clearError(title);
},
onSetupComplete: function SUI_onSetupComplete() {
- onLoginFinish();
+ this.onLoginFinish();
},
onLoginError: function SUI_onLoginError() {
// if login fails, any other notifications are essentially moot
Weave.Notifications.removeAll();
// if we haven't set up the client, don't show errors
if (this._needsSetup()) {
--- a/browser/base/content/browser-tabPreviews.js
+++ b/browser/base/content/browser-tabPreviews.js
@@ -92,17 +92,16 @@ var tabPreviews = {
var tabPreviewPanelHelper = {
opening: function (host) {
host.panel.hidden = false;
var handler = this._generateHandler(host);
host.panel.addEventListener("popupshown", handler, false);
host.panel.addEventListener("popuphiding", handler, false);
- host.panel.addEventListener("popuphidden", handler, false);
host._prevFocus = document.commandDispatcher.focusedElement;
},
_generateHandler: function (host) {
var self = this;
return function (event) {
if (event.target == host.panel) {
host.panel.removeEventListener(event.type, arguments.callee, false);
@@ -125,21 +124,16 @@ var tabPreviewPanelHelper = {
host._prevFocus = null;
} else
gBrowser.selectedBrowser.focus();
if (host.tabToSelect) {
gBrowser.selectedTab = host.tabToSelect;
host.tabToSelect = null;
}
- },
- _popuphidden: function (host) {
- // Destroy the widget in order to prevent outdated content
- // when re-opening the panel.
- host.panel.hidden = true;
}
};
/**
* Ctrl-Tab panel
*/
var ctrlTab = {
get panel () {
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -129,30 +129,37 @@ XPCOMUtils.defineLazyGetter(this, "Debug
});
XPCOMUtils.defineLazyGetter(this, "Tilt", function() {
let tmp = {};
Cu.import("resource:///modules/devtools/Tilt.jsm", tmp);
return new tmp.Tilt(window);
});
+XPCOMUtils.defineLazyGetter(this, "Social", function() {
+ let tmp = {};
+ Cu.import("resource:///modules/Social.jsm", tmp);
+ return tmp.Social;
+});
+
let gInitialPages = [
"about:blank",
"about:newtab",
"about:home",
"about:privatebrowsing",
"about:sessionrestore"
];
#include browser-addons.js
#include browser-feeds.js
#include browser-fullScreen.js
#include browser-fullZoom.js
#include browser-places.js
#include browser-plugins.js
+#include browser-social.js
#include browser-tabPreviews.js
#include browser-tabview.js
#include browser-thumbnails.js
#ifdef MOZ_SERVICES_SYNC
#include browser-syncui.js
#endif
@@ -1241,16 +1248,17 @@ var gBrowserInit = {
Services.obs.addObserver(gXPInstallObserver, "addon-install-failed", false);
Services.obs.addObserver(gXPInstallObserver, "addon-install-complete", false);
Services.obs.addObserver(gFormSubmitObserver, "invalidformsubmit", false);
BrowserOffline.init();
OfflineApps.init();
IndexedDBPromptHelper.init();
gFormSubmitObserver.init();
+ SocialUI.init();
AddonManager.addAddonListener(AddonsMgrListener);
gBrowser.addEventListener("pageshow", function(evt) { setTimeout(pageShowEventHandlers, 0, evt); }, true);
// Ensure login manager is up and running.
Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
if (mustLoadSidebar) {
@@ -1612,16 +1620,17 @@ var gBrowserInit = {
} catch (ex) {
Cu.reportError(ex);
}
BrowserOffline.uninit();
OfflineApps.uninit();
IndexedDBPromptHelper.uninit();
AddonManager.removeAddonListener(AddonsMgrListener);
+ SocialUI.uninit();
}
// Final window teardown, do this last.
window.XULBrowserWindow.destroy();
window.XULBrowserWindow = null;
window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShellTreeItem).treeOwner
@@ -3557,16 +3566,17 @@ function BrowserToolboxCustomizeDone(aTo
UpdateUrlbarSearchSplitterState();
setUrlAndSearchBarWidthForConditionalForwardButton();
// Update the urlbar
if (gURLBar) {
URLBarSetURI();
XULBrowserWindow.asyncUpdateUI();
PlacesStarButton.updateState();
+ SocialShareButton.updateShareState();
}
TabsInTitlebar.allowedBy("customizing-toolbars", true);
// Re-enable parts of the UI we disabled during the dialog
var menubar = document.getElementById("main-menubar");
for (var i = 0; i < menubar.childNodes.length; ++i)
menubar.childNodes[i].setAttribute("disabled", false);
@@ -4035,16 +4045,17 @@ var XULBrowserWindow = {
let uri = aLocationURI;
try {
uri = this._uriFixup.createExposableURI(uri);
} catch (e) {}
URLBarSetURI(uri);
// Update starring UI
PlacesStarButton.updateState();
+ SocialShareButton.updateShareState();
}
// Show or hide browser chrome based on the whitelist
if (this.hideChromeForLocation(location)) {
document.documentElement.setAttribute("disablechrome", "true");
} else {
let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
if (ss.getTabValue(gBrowser.selectedTab, "appOrigin"))
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -177,16 +177,72 @@
class="editBookmarkPanelBottomButton"
label="&editBookmark.done.label;"
default="true"
oncommand="StarUI.panel.hidePopup();"/>
#endif
</hbox>
</panel>
+ <panel id="editSharePopup"
+ type="arrow"
+ orient="vertical"
+ ignorekeys="true"
+ hidden="true"
+ onpopupshown="SocialShareButton.panelShown(event);"
+ consumeoutsideclicks="true"
+ level="top">
+ <row id="editSharePopupHeader" align="center">
+ <vbox align="center">
+ <image id="socialUserPortrait" onclick="SocialUI.showProfile();"
+ aria-label="&social.sharePopup.portrait.arialabel;"/>
+ </vbox>
+ <vbox id="editSharePopupText">
+ <button id="socialUserDisplayName"
+ oncommand="SocialUI.showProfile();"/>
+ <spacer flex="1"/>
+ <label id="socialUserRecommendedText"
+ value="&social.sharePopup.shared.label;"/>
+ </vbox>
+ </row>
+ <hbox id="editSharePopupBottomButtons" pack="end">
+#ifdef XP_UNIX
+ <button id="editSharePopupUndoButton"
+ class="editSharePopupBottomButton"
+ label="&social.sharePopup.undo.label;"
+ accesskey="&social.sharePopup.undo.accesskey;"
+ command="Social:UnsharePage"/>
+ <button id="editSharePopupOkButton"
+ class="editSharePopupBottomButton"
+ default="true"
+ autofocus="autofocus"
+ label="&social.sharePopup.ok.label;"
+ accesskey="&social.sharePopup.ok.accesskey;"
+ oncommand="SocialShareButton.dismissSharePopup();"/>
+#else
+ <button id="editSharePopupOkButton"
+ class="editSharePopupBottomButton"
+ default="true"
+ autofocus="autofocus"
+ label="&social.sharePopup.ok.label;"
+ accesskey="&social.sharePopup.ok.accesskey;"
+ oncommand="SocialShareButton.dismissSharePopup();"/>
+ <button id="editSharePopupUndoButton"
+ class="editSharePopupBottomButton"
+ label="&social.sharePopup.undo.label;"
+ accesskey="&social.sharePopup.undo.accesskey;"
+ command="Social:UnsharePage"/>
+#endif
+ </hbox>
+ </panel>
+
+ <panel id="social-notification-panel" type="arrow" hidden="true" noautofocus="true">
+ <browser id="social-notification-browser" type="content" flex="1"/>
+ </panel>
+
<menupopup id="inspector-node-popup">
<menuitem id="inspectorHTMLCopyInner"
label="&inspectorHTMLCopyInner.label;"
accesskey="&inspectorHTMLCopyInner.accesskey;"
command="Inspector:CopyInner"/>
<menuitem id="inspectorHTMLCopyOuter"
label="&inspectorHTMLCopyOuter.label;"
accesskey="&inspectorHTMLCopyOuter.accesskey;"
@@ -459,16 +515,17 @@
autocompletesearch="urlinline history"
autocompletesearchparam="enable-actions"
autocompletepopup="PopupAutoCompleteRichResult"
completeselectedindex="true"
tabscrolling="true"
showcommentcolumn="true"
showimagecolumn="true"
enablehistory="true"
+ maxrows="6"
newlines="stripsurroundingwhitespace"
oninput="gBrowser.userTypedValue = this.value;"
ontextentered="this.handleCommand(param);"
ontextreverted="return this.handleRevert();"
pageproxystate="invalid"
onfocus="document.getElementById('identity-box').style.MozUserFocus= 'normal'"
onblur="setTimeout(function() document.getElementById('identity-box').style.MozUserFocus = '', 0);">
<box id="notification-popup-box" hidden="true" align="center">
@@ -502,16 +559,22 @@
</box>
<hbox id="urlbar-icons">
<image id="page-report-button"
class="urlbar-icon"
hidden="true"
tooltiptext="&pageReportIcon.tooltip;"
onclick="gPopupBlockerObserver.onReportButtonClick(event);"/>
+ <label id="share-button-status" collapsed="true" role="status"/>
+ <image id="share-button"
+ class="urlbar-icon"
+ hidden="true"
+ onclick="SocialShareButton.onClick(event);"/>
+
<image id="star-button"
class="urlbar-icon"
onclick="PlacesStarButton.onClick(event);"/>
<image id="go-button"
class="urlbar-icon"
tooltiptext="&goEndCap.tooltip;"
onclick="gURLBar.handleCommand(event);"/>
</hbox>
@@ -553,16 +616,55 @@
label="&homeButton.label;"
ondragover="homeButtonObserver.onDragOver(event)"
ondragenter="homeButtonObserver.onDragOver(event)"
ondrop="homeButtonObserver.onDrop(event)"
ondragexit="homeButtonObserver.onDragExit(event)"
onclick="BrowserGoHome(event);"
aboutHomeOverrideTooltip="&abouthome.pageTitle;"/>
+ <toolbaritem id="social-toolbar-button"
+ class="toolbarbutton-1 chromeclass-toolbar-additional"
+ removable="false"
+ title="&socialToolbar.title;"
+ hidden="true">
+ <hbox id="social-toolbar-button-box" class="social-statusarea-container">
+ <button id="social-provider-image" type="menu">
+ <menupopup id="social-statusarea-popup">
+ <hbox id="social-statusarea-user" pack="left" align="center">
+ <image id="social-statusarea-user-portrait"/>
+ <vbox>
+ <label id="social-statusarea-notloggedin"
+ value="&social.notLoggedIn.label;"/>
+ <button id="social-statusarea-username"
+ oncommand="SocialUI.showProfile(); document.getElementById('social-statusarea-popup').hidePopup();"/>
+ </vbox>
+ </hbox>
+ </menupopup>
+ </button>
+ <hbox id="social-status-iconbox" flex="1">
+ <box class="social-notification-icon-container" collapsed="true"
+ onclick="SocialToolbar.showAmbientPopup(this);">
+ <image class="social-notification-icon-image"/>
+ <box class="social-notification-icon-counter" collapsed="true"/>
+ </box>
+ <box class="social-notification-icon-container" collapsed="true"
+ onclick="SocialToolbar.showAmbientPopup(this);">
+ <image class="social-notification-icon-image"/>
+ <box class="social-notification-icon-counter" collapsed="true"/>
+ </box>
+ <box class="social-notification-icon-container" collapsed="true"
+ onclick="SocialToolbar.showAmbientPopup(this);">
+ <image class="social-notification-icon-image"/>
+ <box class="social-notification-icon-counter" collapsed="true"/>
+ </box>
+ </hbox>
+ </hbox>
+ </toolbaritem>
+
<toolbaritem id="bookmarks-menu-button-container"
class="chromeclass-toolbar-additional"
removable="true"
title="&bookmarksMenuButton.label;">
<toolbarbutton id="bookmarks-menu-button"
type="menu"
class="toolbarbutton-1"
label="&bookmarksMenuButton.label;"
--- a/browser/base/content/newtab/drag.js
+++ b/browser/base/content/newtab/drag.js
@@ -102,22 +102,27 @@ let gDrag = {
},
/**
* Checks whether we're responsible for a given drag event.
* @param aEvent The drag event to check.
* @return Whether we should handle this drag and drop operation.
*/
isValid: function Drag_isValid(aEvent) {
- let dt = aEvent.dataTransfer;
- let mimeType = "text/x-moz-url";
+ let link = gDragDataHelper.getLinkFromDragEvent(aEvent);
// Check that the drag data is non-empty.
// Can happen when dragging places folders.
- return dt && dt.types.contains(mimeType) && dt.getData(mimeType);
+ if (!link || !link.url) {
+ return false;
+ }
+
+ // Check that we're not accepting URLs which would inherit the caller's
+ // principal (such as javascript: or data:).
+ return gLinkChecker.checkLoadURI(link.url);
},
/**
* Initializes the drag data for the current drag operation.
* @param aSite The site that's being dragged.
* @param aEvent The 'dragstart' event.
*/
_setDragData: function Drag_setDragData(aSite, aEvent) {
new file mode 100644
--- /dev/null
+++ b/browser/base/content/newtab/dragDataHelper.js
@@ -0,0 +1,22 @@
+#ifdef 0
+/* 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/. */
+#endif
+
+let gDragDataHelper = {
+ get mimeType() {
+ return "text/x-moz-url";
+ },
+
+ getLinkFromDragEvent: function DragDataHelper_getLinkFromDragEvent(aEvent) {
+ let dt = aEvent.dataTransfer;
+ if (!dt || !dt.types.contains(this.mimeType)) {
+ return null;
+ }
+
+ let data = dt.getData(this.mimeType) || "";
+ let [url, title] = data.split(/[\r\n]+/);
+ return {url: url, title: title};
+ }
+};
--- a/browser/base/content/newtab/drop.js
+++ b/browser/base/content/newtab/drop.js
@@ -85,24 +85,24 @@ let gDrop = {
let index = aCell.index;
let draggedSite = gDrag.draggedSite;
if (draggedSite) {
// Pin the dragged site at its new place.
if (aCell != draggedSite.cell)
draggedSite.pin(index);
} else {
- // A new link was dragged onto the grid. Create it by pinning its URL.
- let dt = aEvent.dataTransfer;
- let [url, title] = dt.getData("text/x-moz-url").split(/[\r\n]+/);
- let link = {url: url, title: title};
- gPinnedLinks.pin(link, index);
+ let link = gDragDataHelper.getLinkFromDragEvent(aEvent);
+ if (link) {
+ // A new link was dragged onto the grid. Create it by pinning its URL.
+ gPinnedLinks.pin(link, index);
- // Make sure the newly added link is not blocked.
- gBlockedLinks.unblock(link);
+ // Make sure the newly added link is not blocked.
+ gBlockedLinks.unblock(link);
+ }
}
},
/**
* Time a rearrange with a little delay.
* @param aCell The drop target cell.
*/
_delayedRearrange: function Drop_delayedRearrange(aCell) {
--- a/browser/base/content/newtab/newTab.js
+++ b/browser/base/content/newtab/newTab.js
@@ -13,16 +13,17 @@ Cu.import("resource:///modules/PageThumb
Cu.import("resource:///modules/NewTabUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Rect",
"resource://gre/modules/Geometry.jsm");
let {
links: gLinks,
allPages: gAllPages,
+ linkChecker: gLinkChecker,
pinnedLinks: gPinnedLinks,
blockedLinks: gBlockedLinks
} = NewTabUtils;
XPCOMUtils.defineLazyGetter(this, "gStringBundle", function() {
return Services.strings.
createBundle("chrome://browser/locale/newTab.properties");
});
@@ -48,15 +49,16 @@ const HTML_NAMESPACE = "http://www.w3.or
#include batch.js
#include transformations.js
#include page.js
#include grid.js
#include cells.js
#include sites.js
#include drag.js
+#include dragDataHelper.js
#include drop.js
#include dropTargetShim.js
#include dropPreview.js
#include updater.js
// Everything is loaded. Initialize the New Tab Page.
gPage.init();
--- a/browser/base/content/sync/utils.js
+++ b/browser/base/content/sync/utils.js
@@ -192,18 +192,16 @@ let gSyncUtils = {
if (!el2)
valid = val1.length >= Weave.MIN_PASS_LENGTH;
else if (val1 && val1 == Weave.Identity.username)
error = "change.password.pwSameAsUsername";
else if (val1 && val1 == Weave.Identity.account)
error = "change.password.pwSameAsEmail";
else if (val1 && val1 == Weave.Identity.basicPassword)
error = "change.password.pwSameAsPassword";
- else if (val1 && val1 == Weave.Identity.syncKey)
- error = "change.password.pwSameAsRecoveryKey";
else if (val1 && val2) {
if (val1 == val2 && val1.length >= Weave.MIN_PASS_LENGTH)
valid = true;
else if (val1.length < Weave.MIN_PASS_LENGTH)
error = "change.password.tooShort";
else if (val1 != val2)
error = "change.password.mismatch";
}
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -3492,17 +3492,17 @@
}
ind.collapsed = false;
newMargin += ind.clientWidth / 2;
if (!ltr)
newMargin *= -1;
- ind.style.MozTransform = "translate(" + Math.round(newMargin) + "px)";
+ ind.style.transform = "translate(" + Math.round(newMargin) + "px)";
ind.style.MozMarginStart = (-ind.clientWidth) + "px";
]]></handler>
<handler event="drop"><![CDATA[
var dt = event.dataTransfer;
var dropEffect = dt.dropEffect;
var draggedTab;
if (dropEffect != "link") { // copy or move
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -57,16 +57,18 @@ endif
# browser_sanitizeDialog_treeView.js is disabled until the tree view is added
# back to the clear recent history dialog (sanitize.xul), if it ever is (bug
# 480169)
# browser_drag.js is disabled, as it needs to be updated for the new behavior from bug 320638.
# browser_bug321000.js is disabled because newline handling is shaky (bug 592528)
+# browser_pageInfo.js + feed_tab.html is disabled for leaking (bug 767896)
+
_BROWSER_FILES = \
head.js \
browser_typeAheadFind.js \
browser_keywordSearch.js \
browser_allTabsPanel.js \
browser_alltabslistener.js \
browser_bug304198.js \
title_test.svg \
@@ -149,16 +151,17 @@ endif
browser_bug710878.js \
browser_bug719271.js \
browser_bug724239.js \
browser_bug735471.js \
browser_bug743421.js \
browser_bug749738.js \
browser_bug763468.js \
browser_bug767836.js \
+ browser_shareButton.js \
browser_canonizeURL.js \
browser_customize.js \
browser_findbarClose.js \
browser_homeDrop.js \
browser_keywordBookmarklets.js \
browser_contextSearchTabPosition.js \
browser_ctrlTab.js \
browser_customize_popupNotification.js \
@@ -166,17 +169,16 @@ endif
browser_discovery.js \
browser_duplicateIDs.js \
browser_gestureSupport.js \
browser_getshortcutoruri.js \
browser_hide_removing.js \
browser_overflowScroll.js \
browser_locationBarCommand.js \
browser_locationBarExternalLoad.js \
- browser_pageInfo.js \
browser_page_style_menu.js \
browser_pinnedTabs.js \
browser_plainTextLinks.js \
browser_pluginnotification.js \
browser_relatedTabs.js \
browser_sanitize-passwordDisabledHosts.js \
browser_sanitize-sitepermissions.js \
browser_sanitize-timespans.js \
@@ -214,17 +216,16 @@ endif
disablechrome.html \
discovery.html \
domplate_test.js \
moz.png \
video.ogg \
test_bug435035.html \
test_bug462673.html \
page_style_sample.html \
- feed_tab.html \
plugin_unknown.html \
plugin_test.html \
plugin_test2.html \
plugin_test3.html \
plugin_alternate_content.html \
plugin_both.html \
plugin_both2.html \
plugin_bug743421.html \
@@ -250,16 +251,17 @@ endif
authenticate.sjs \
browser_minimize.js \
browser_aboutSyncProgress.js \
browser_middleMouse_inherit.js \
redirect_bug623155.sjs \
browser_tabDrop.js \
browser_lastAccessedTab.js \
browser_bug734076.js \
+ browser_social_toolbar.js \
$(NULL)
ifneq (cocoa,$(MOZ_WIDGET_TOOLKIT))
_BROWSER_FILES += \
browser_bug462289.js \
$(NULL)
else
_BROWSER_FILES += \
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_shareButton.js
@@ -0,0 +1,160 @@
+/* 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/. */
+
+let prefName = "social.enabled",
+ shareButton,
+ sharePopup,
+ okButton,
+ undoButton;
+
+function test() {
+ waitForExplicitFinish();
+
+ // Need to load a non-empty page for the social share button to appear
+ let tab = gBrowser.selectedTab = gBrowser.addTab("about:", {skipAnimation: true});
+ tab.linkedBrowser.addEventListener("load", function tabLoad(event) {
+ tab.linkedBrowser.removeEventListener("load", tabLoad, true);
+ executeSoon(tabLoaded);
+ }, true);
+
+ // Enable the service to start
+ Services.prefs.setBoolPref(prefName, true);
+
+ registerCleanupFunction(function() {
+ Services.prefs.clearUserPref(prefName);
+ gBrowser.removeTab(tab);
+ });
+}
+
+function tabLoaded() {
+ ok(Social, "Social module loaded");
+
+ // If the UI is already active, run the test immediately, otherwise wait
+ // for initialization.
+ if (Social.provider) {
+ executeSoon(testInitial);
+ } else {
+ Services.obs.addObserver(function obs() {
+ Services.obs.removeObserver(obs, "test-social-ui-ready");
+ executeSoon(testInitial);
+ }, "test-social-ui-ready", false);
+ }
+}
+
+function testInitial() {
+ ok(Social.provider, "Social provider is active");
+ ok(Social.provider.enabled, "Social provider is enabled");
+ ok(Social.provider.port, "Social provider has a port to its FrameWorker");
+
+ shareButton = SocialShareButton.shareButton;
+ sharePopup = SocialShareButton.sharePopup;
+ ok(shareButton, "share button exists");
+ ok(sharePopup, "share popup exists");
+ ok(!sharePopup.hidden, "share popup is not hidden");
+
+ okButton = document.getElementById("editSharePopupOkButton");
+ undoButton = document.getElementById("editSharePopupUndoButton");
+
+ is(shareButton.hidden, false, "share button should be visible");
+
+ // Test clicking the share button
+ shareButton.addEventListener("click", function listener() {
+ shareButton.removeEventListener("click", listener);
+ is(shareButton.hasAttribute("shared"), true, "Share button should have 'shared' attribute after share button is clicked");
+ executeSoon(testSecondClick.bind(window, testPopupOKButton));
+ });
+ EventUtils.synthesizeMouseAtCenter(shareButton, {});
+}
+
+function testSecondClick(nextTest) {
+ sharePopup.addEventListener("popupshown", function listener() {
+ sharePopup.removeEventListener("popupshown", listener);
+ ok(true, "popup was shown after second click");
+ executeSoon(nextTest);
+ });
+ EventUtils.synthesizeMouseAtCenter(shareButton, {});
+}
+
+function testPopupOKButton() {
+ sharePopup.addEventListener("popuphidden", function listener() {
+ sharePopup.removeEventListener("popuphidden", listener);
+ is(shareButton.hasAttribute("shared"), true, "Share button should still have 'shared' attribute after OK button is clicked");
+ executeSoon(testSecondClick.bind(window, testPopupUndoButton));
+ });
+ EventUtils.synthesizeMouseAtCenter(okButton, {});
+}
+
+function testPopupUndoButton() {
+ sharePopup.addEventListener("popuphidden", function listener() {
+ sharePopup.removeEventListener("popuphidden", listener);
+ is(shareButton.hasAttribute("shared"), false, "Share button should not have 'shared' attribute after Undo button is clicked");
+ executeSoon(testShortcut);
+ });
+ EventUtils.synthesizeMouseAtCenter(undoButton, {});
+}
+
+function testShortcut() {
+ let keyTarget = window;
+ keyTarget.addEventListener("keyup", function listener() {
+ keyTarget.removeEventListener("keyup", listener);
+ executeSoon(checkShortcutWorked.bind(window, keyTarget));
+ });
+ EventUtils.synthesizeKey("l", {accelKey: true, shiftKey: true}, keyTarget);
+}
+
+function checkShortcutWorked(keyTarget) {
+ is(shareButton.hasAttribute("shared"), true, "Share button should be in the 'shared' state after keyboard shortcut is used");
+
+ // Test a second invocation of the shortcut
+ sharePopup.addEventListener("popupshown", function listener() {
+ sharePopup.removeEventListener("popupshown", listener);
+ ok(true, "popup was shown after second use of keyboard shortcut");
+ executeSoon(checkOKButton);
+ });
+ EventUtils.synthesizeKey("l", {accelKey: true, shiftKey: true}, keyTarget);
+}
+
+function checkOKButton() {
+ is(document.activeElement, okButton, "ok button should be focused by default");
+ checkNextInTabOrder(undoButton, function () {
+ checkNextInTabOrder(okButton, testCloseBySpace);
+ });
+}
+
+function checkNextInTabOrder(element, next) {
+ // This particular test doesn't really apply on Mac, since buttons aren't
+ // focusable by default.
+ if (navigator.platform.indexOf("Mac") != -1) {
+ executeSoon(next);
+ return;
+ }
+
+ function listener() {
+ element.removeEventListener("focus", listener);
+ is(document.activeElement, element, element.id + " should be next in tab order");
+ executeSoon(next);
+ }
+ element.addEventListener("focus", listener);
+ // Register a cleanup function to remove the listener in case this test fails
+ registerCleanupFunction(function () {
+ element.removeEventListener("focus", listener);
+ });
+ EventUtils.synthesizeKey("VK_TAB", {});
+}
+
+function testCloseBySpace() {
+ is(document.activeElement.id, okButton.id, "testCloseBySpace, the ok button should be focused");
+ sharePopup.addEventListener("popuphidden", function listener() {
+ sharePopup.removeEventListener("popuphidden", listener);
+ ok(true, "space closed the share popup");
+ executeSoon(testDisable);
+ });
+ EventUtils.synthesizeKey("VK_SPACE", {});
+}
+
+function testDisable() {
+ Services.prefs.setBoolPref(prefName, false);
+ is(shareButton.hidden, true, "Share button should be hidden when pref is disabled");
+ finish();
+}
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_social_toolbar.js
@@ -0,0 +1,140 @@
+/* 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/. */
+
+let SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService;
+let gProvider;
+
+function test() {
+ waitForExplicitFinish();
+
+ Services.prefs.setBoolPref("social.enabled", true);
+ registerCleanupFunction(function () {
+ Services.prefs.clearUserPref("social.enabled");
+ });
+
+ let oldProvider;
+ function saveOldProviderAndStartTestWith(provider) {
+ oldProvider = Social.provider;
+ registerCleanupFunction(function () {
+ Social.provider = oldProvider;
+ });
+ Social.provider = gProvider = provider;
+ runTests(tests, undefined, undefined, function () {
+ SocialService.removeProvider(provider.origin, finish);
+ });
+ }
+
+ let manifest = { // normal provider
+ name: "provider 1",
+ origin: "https://example1.com",
+ workerURL: "https://example1.com/worker.js",
+ iconURL: "chrome://branding/content/icon48.png"
+ };
+ SocialService.addProvider(manifest, function(provider) {
+ // If the UI is already active, run the test immediately, otherwise wait
+ // for initialization.
+ if (Social.provider) {
+ saveOldProviderAndStartTestWith(provider);
+ } else {
+ Services.obs.addObserver(function obs() {
+ Services.obs.removeObserver(obs, "test-social-ui-ready");
+ saveOldProviderAndStartTestWith(provider);
+ }, "test-social-ui-ready", false);
+ }
+ });
+}
+
+var tests = {
+ testProfileSet: function(next) {
+ let profile = {
+ portrait: "chrome://branding/content/icon48.png",
+ userName: "trickster",
+ displayName: "Kuma Lisa",
+ profileURL: "http://en.wikipedia.org/wiki/Kuma_Lisa"
+ }
+ gProvider.updateUserProfile(profile);
+ // check dom values
+ let portrait = document.getElementById("social-statusarea-user-portrait").getAttribute("src");
+ is(portrait, profile.portrait, "portrait is set");
+ let userButton = document.getElementById("social-statusarea-username");
+ ok(!userButton.hidden, "username is visible");
+ is(userButton.label, profile.userName, "username is set");
+ next();
+ },
+ testAmbientNotifications: function(next) {
+ let ambience = {
+ name: "testIcon",
+ iconURL: "chrome://branding/content/icon48.png",
+ contentPanel: "about:blank",
+ counter: 42
+ };
+ gProvider.setAmbientNotification(ambience);
+
+ let statusIcons = document.getElementById("social-status-iconbox");
+ ok(!statusIcons.firstChild.collapsed, "status icon is visible");
+ ok(!statusIcons.firstChild.lastChild.collapsed, "status value is visible");
+ is(statusIcons.firstChild.lastChild.textContent, "42", "status value is correct");
+
+ ambience.counter = 0;
+ gProvider.setAmbientNotification(ambience);
+ ok(statusIcons.firstChild.lastChild.collapsed, "status value is not visible");
+ is(statusIcons.firstChild.lastChild.textContent, "", "status value is correct");
+ next();
+ },
+ testProfileUnset: function(next) {
+ gProvider.updateUserProfile({});
+ // check dom values
+ let portrait = document.getElementById("social-statusarea-user-portrait").getAttribute("src");
+ is(portrait, "chrome://browser/skin/social/social.png", "portrait is generic");
+ let userButton = document.getElementById("social-statusarea-username");
+ ok(userButton.hidden, "username is not visible");
+ let ambience = document.getElementById("social-status-iconbox").firstChild;
+ while (ambience) {
+ ok(ambience.collapsed, "ambient icon is collapsed");
+ ambience = ambience.nextSibling;
+ }
+
+ next();
+ }
+}
+
+function runTests(tests, cbPreTest, cbPostTest, cbFinish) {
+ let testIter = Iterator(tests);
+
+ if (cbPreTest === undefined) {
+ cbPreTest = function(cb) {cb()};
+ }
+ if (cbPostTest === undefined) {
+ cbPostTest = function(cb) {cb()};
+ }
+
+ function runNextTest() {
+ let name, func;
+ try {
+ [name, func] = testIter.next();
+ } catch (err if err instanceof StopIteration) {
+ // out of items:
+ (cbFinish || finish)();
+ return;
+ }
+ // We run on a timeout as the frameworker also makes use of timeouts, so
+ // this helps keep the debug messages sane.
+ executeSoon(function() {
+ function cleanupAndRunNextTest() {
+ info("sub-test " + name + " complete");
+ cbPostTest(runNextTest);
+ }
+ cbPreTest(function() {
+ info("sub-test " + name + " starting");
+ try {
+ func.call(tests, cleanupAndRunNextTest);
+ } catch (ex) {
+ ok(false, "sub-test " + name + " failed: " + ex.toString() +"\n"+ex.stack);
+ cleanupAndRunNextTest();
+ }
+ })
+ });
+ }
+ runNextTest();
+}
--- a/browser/base/content/test/newtab/Makefile.in
+++ b/browser/base/content/test/newtab/Makefile.in
@@ -23,13 +23,14 @@ include $(topsrcdir)/config/rules.mk
browser_newtab_unpin.js \
browser_newtab_bug721442.js \
browser_newtab_bug722273.js \
browser_newtab_bug723102.js \
browser_newtab_bug723121.js \
browser_newtab_bug725996.js \
browser_newtab_bug734043.js \
browser_newtab_bug735987.js \
+ browser_newtab_bug765628.js \
head.js \
$(NULL)
libs:: $(_BROWSER_FILES)
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/newtab/browser_newtab_bug765628.js
@@ -0,0 +1,27 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const BAD_DRAG_DATA = "javascript:alert('h4ck0rz');\nbad stuff";
+const GOOD_DRAG_DATA = "http://example.com/#99\nsite 99";
+
+function runTests() {
+ yield setLinks("0,1,2,3,4,5,6,7,8");
+ setPinnedLinks("");
+
+ yield addNewTabPageTab();
+ checkGrid("0,1,2,3,4,5,6,7,8");
+
+ sendDropEvent(0, BAD_DRAG_DATA);
+ sendDropEvent(1, GOOD_DRAG_DATA);
+
+ yield whenPagesUpdated();
+ checkGrid("0,99p,1,2,3,4,5,6,7");
+}
+
+function sendDropEvent(aCellIndex, aDragData) {
+ let ifaceReq = getContentWindow().QueryInterface(Ci.nsIInterfaceRequestor);
+ let windowUtils = ifaceReq.getInterface(Ci.nsIDOMWindowUtils);
+
+ let event = createDragEvent("drop", aDragData);
+ windowUtils.dispatchDOMEventViaPresShell(getCell(aCellIndex).node, event, true);
+}
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -620,18 +620,16 @@
case "underflow":
this._contentIsCropped = false;
this._hideURLTooltip();
break;
}
]]></body>
</method>
- <property name="maxRows" onget="return this.popup.maxResults;"/>
-
<property name="textValue"
onget="return this.value;">
<setter>
<![CDATA[
try {
val = losslessDecodeURI(makeURI(val));
} catch (ex) { }
--- a/browser/branding/nightly/pref/firefox-branding.js
+++ b/browser/branding/nightly/pref/firefox-branding.js
@@ -1,21 +1,21 @@
/* 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/. */
pref("startup.homepage_override_url","http://www.mozilla.org/projects/%APP%/%VERSION%/whatsnew/?oldversion=%OLD_VERSION%");
pref("startup.homepage_welcome_url","http://www.mozilla.org/projects/%APP%/%VERSION%/firstrun/");
// The time interval between checks for a new version (in seconds)
-pref("app.update.interval", 3600); // 1 hour
+pref("app.update.interval", 7200); // 2 hours
// The time interval between the downloading of mar file chunks in the
// background (in seconds)
pref("app.update.download.backgroundInterval", 60);
-// Give the user x seconds to react before showing the big UI. default=1 hour
-pref("app.update.promptWaitTime", 3600);
+// Give the user x seconds to react before showing the big UI. default=12 hours
+pref("app.update.promptWaitTime", 43200);
// URL user can browse to manually if for some reason all update installation
// attempts fail.
pref("app.update.url.manual", "http://nightly.mozilla.org/");
// A default value for the "More information about this update" link
// supplied in the "An update is available" page of the update wizard.
pref("app.update.url.details", "http://www.mozilla.org/projects/%APP%/");
// Release notes and vendor URLs
--- a/browser/components/about/AboutRedirector.cpp
+++ b/browser/components/about/AboutRedirector.cpp
@@ -116,25 +116,20 @@ AboutRedirector::NewChannel(nsIURI *aURI
rv = ioService->NewChannel(nsDependentCString(kRedirMap[i].url),
nsnull, nsnull, getter_AddRefs(tempChannel));
NS_ENSURE_SUCCESS(rv, rv);
tempChannel->SetOriginalURI(aURI);
// Keep the page from getting unnecessary privileges unless it needs them
if (kRedirMap[i].flags & nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT) {
- nsCOMPtr<nsIScriptSecurityManager> securityManager =
- do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
- NS_ENSURE_SUCCESS(rv, rv);
-
- nsCOMPtr<nsIPrincipal> principal;
- rv = securityManager->GetCodebasePrincipal(aURI, getter_AddRefs(principal));
- NS_ENSURE_SUCCESS(rv, rv);
-
- rv = tempChannel->SetOwner(principal);
+ // Setting the owner to null means that we'll go through the normal
+ // path in GetChannelPrincipal and create a codebase principal based
+ // on the channel's originalURI
+ rv = tempChannel->SetOwner(nsnull);
NS_ENSURE_SUCCESS(rv, rv);
}
NS_ADDREF(*result = tempChannel);
return rv;
}
}
--- a/browser/components/downloads/content/downloadsOverlay.xul
+++ b/browser/components/downloads/content/downloadsOverlay.xul
@@ -93,16 +93,17 @@
<menuseparator/>
<menuitem command="downloadsCmd_clearList"
label="&cmd.clearList.label;"
accesskey="&cmd.clearList.accesskey;"/>
</menupopup>
<richlistbox id="downloadsListBox"
+ style="width: &downloads.width;"
class="plain"
flex="1"
context="downloadsContextMenu"
onkeypress="DownloadsView.onDownloadKeyPress(event);"
oncontextmenu="DownloadsView.onDownloadContextMenu(event);"
ondragstart="DownloadsView.onDownloadDragStart(event);"/>
<button id="downloadsHistory"
--- a/browser/components/places/content/browserPlacesViews.js
+++ b/browser/components/places/content/browserPlacesViews.js
@@ -1599,17 +1599,17 @@ PlacesToolbar.prototype = {
translateX += this._rootElt.lastChild.getBoundingClientRect().right;
else {
translateX += this._rootElt.childNodes[dropPoint.beforeIndex]
.getBoundingClientRect().left;
}
}
}
- ind.style.MozTransform = "translate(" + Math.round(translateX) + "px)";
+ ind.style.transform = "translate(" + Math.round(translateX) + "px)";
ind.style.MozMarginStart = (-ind.clientWidth) + "px";
ind.collapsed = false;
// Clear out old folder information.
this._clearOverFolder();
}
aEvent.preventDefault();
--- a/browser/components/shell/src/nsWindowsShellService.cpp
+++ b/browser/components/shell/src/nsWindowsShellService.cpp
@@ -189,29 +189,26 @@ static SETTING gDDESettings[] = {
{ MAKE_KEY_NAME1("Software\\Classes\\FirefoxURL", SOD) },
// Protocol Handlers
{ MAKE_KEY_NAME1("Software\\Classes\\FTP", SOD) },
{ MAKE_KEY_NAME1("Software\\Classes\\HTTP", SOD) },
{ MAKE_KEY_NAME1("Software\\Classes\\HTTPS", SOD) }
};
-// See Bug 770883
-#if 0
#if defined(MOZ_MAINTENANCE_SERVICE)
#define ONLY_SERVICE_LAUNCHING
#include "updatehelper.h"
#include "updatehelper.cpp"
static const char *kPrefetchClearedPref =
"app.update.service.lastVersionPrefetchCleared";
static nsCOMPtr<nsIThread> sThread;
#endif
-#endif
nsresult
GetHelperPath(nsAutoString& aPath)
{
nsresult rv;
nsCOMPtr<nsIProperties> directoryService =
do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
@@ -999,18 +996,16 @@ nsWindowsShellService::SetDesktopBackgro
NS_ENSURE_SUCCESS(rv, rv);
return regKey->Close();
}
nsWindowsShellService::nsWindowsShellService() :
mCheckedThisSession(false)
{
-// See Bug 770883
-#if 0
#if defined(MOZ_MAINTENANCE_SERVICE)
// Check to make sure the service is installed
PRUint32 installed = 0;
nsCOMPtr<nsIWindowsRegKey> regKey =
do_CreateInstance("@mozilla.org/windows-registry-key;1");
if (!regKey ||
NS_FAILED(regKey->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE,
@@ -1045,38 +1040,32 @@ nsWindowsShellService::nsWindowsShellSer
// service command.
mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
if (mTimer) {
mTimer->InitWithFuncCallback(
nsWindowsShellService::LaunchPrefetchClearCommand,
nsnull, CLEAR_PREFETCH_TIMEOUT_MS, nsITimer::TYPE_ONE_SHOT);
}
#endif
-#endif
}
nsWindowsShellService::~nsWindowsShellService()
{
-// See Bug 770883
-#if 0
#if defined(MOZ_MAINTENANCE_SERVICE)
if (mTimer) {
mTimer->Cancel();
mTimer = nsnull;
}
if (sThread) {
sThread->Shutdown();
sThread = nsnull;
}
#endif
-#endif
}
-// See Bug 770883
-#if 0
#if defined(MOZ_MAINTENANCE_SERVICE)
class ClearPrefetchEvent : public nsRunnable {
public:
ClearPrefetchEvent()
{
}
@@ -1089,29 +1078,26 @@ public:
// If this command fails, it is not critical as prefetch will be cleared
// on the next software update.
StartServiceCommand(NS_ARRAY_LENGTH(updaterServiceArgv),
updaterServiceArgv);
return NS_OK;
}
};
#endif
-#endif
/**
* For faster startup we attempt to clear the prefetch if the maintenance
* service is installed. Please see the definition of ClearPrefetch()
* in toolkit/components/maintenanceservice/prefetch.cpp for more info.
* For now the only application that gets prefetch cleaned is Firefox
* since we have not done performance checking for other applications.
* This is done on every update but also there is a one time operation done
* from within the program for first time installs.
*/
-// See Bug 770883
-#if 0
#if defined(MOZ_MAINTENANCE_SERVICE)
void
nsWindowsShellService::LaunchPrefetchClearCommand(nsITimer *aTimer, void*)
{
// Make sure we don't call this again from the application, it will be
// called on each application update instead.
nsCOMPtr<nsIPrefBranch> prefBranch;
nsCOMPtr<nsIPrefService> prefs =
@@ -1126,17 +1112,16 @@ nsWindowsShellService::LaunchPrefetchCle
// main thread, so start an event on another thread to handle the operation
NS_NewThread(getter_AddRefs(sThread));
if (sThread) {
nsCOMPtr<nsIRunnable> prefetchEvent = new ClearPrefetchEvent();
sThread->Dispatch(prefetchEvent, NS_DISPATCH_NORMAL);
}
}
#endif
-#endif
NS_IMETHODIMP
nsWindowsShellService::OpenApplicationWithURI(nsIFile* aApplication,
const nsACString& aURI)
{
nsresult rv;
nsCOMPtr<nsIProcess> process =
do_CreateInstance("@mozilla.org/process/util;1", &rv);
--- a/browser/components/tabview/groupitems.js
+++ b/browser/components/tabview/groupitems.js
@@ -614,17 +614,17 @@ GroupItem.prototype = Utils.extend(new I
GroupItems.updateGroupCloseButtons();
}
if (this.hidden || (options && options.immediately)) {
destroyGroup();
} else {
iQ(this.container).animate({
opacity: 0,
- "-moz-transform": "scale(.3)",
+ "transform": "scale(.3)",
}, {
duration: 170,
complete: destroyGroup
});
}
this.deleteData();
},
@@ -636,17 +636,17 @@ GroupItem.prototype = Utils.extend(new I
if (this._children.length > 0) {
this._unfreezeItemSize();
this._children.forEach(function(child) {
iQ(child.container).hide();
});
iQ(this.container).animate({
opacity: 0,
- "-moz-transform": "scale(.3)",
+ "transform": "scale(.3)",
}, {
duration: 170,
complete: function() {
iQ(this).hide();
}
});
this.droppable(false);
@@ -722,24 +722,24 @@ GroupItem.prototype = Utils.extend(new I
UI.setActive(self);
self._sendToSubscribers("groupShown");
};
let $container = iQ(this.container).show();
if (!options || !options.immediately) {
$container.animate({
- "-moz-transform": "scale(1)",
+ "transform": "scale(1)",
"opacity": 1
}, {
duration: 170,
complete: finalize
});
} else {
- $container.css({"-moz-transform": "none", opacity: 1});
+ $container.css({"transform": "none", opacity: 1});
finalize();
}
GroupItems.updateGroupCloseButtons();
},
// ----------
// Function: closeHidden
@@ -870,25 +870,25 @@ GroupItem.prototype = Utils.extend(new I
let undoClose = iQ("<span/>")
.addClass("close")
.attr("title", tabviewString("groupItem.discardClosedGroup"))
.appendTo(this.$undoContainer);
this.$undoContainer.css({
left: this.bounds.left + this.bounds.width/2 - iQ(self.$undoContainer).width()/2,
top: this.bounds.top + this.bounds.height/2 - iQ(self.$undoContainer).height()/2,
- "-moz-transform": "scale(.1)",
+ "transform": "scale(.1)",
opacity: 0
});
this.hidden = true;
// hide group item and show undo container.
setTimeout(function() {
self.$undoContainer.animate({
- "-moz-transform": "scale(1)",
+ "transform": "scale(1)",
"opacity": 1
}, {
easing: "tabviewBounce",
duration: 170,
complete: function() {
self._sendToSubscribers("groupHidden");
}
});
--- a/browser/components/tabview/items.js
+++ b/browser/components/tabview/items.js
@@ -249,17 +249,17 @@ Item.prototype = {
return this.zIndex;
},
// ----------
// Function: setRotation
// Rotates the object to the given number of degrees.
setRotation: function Item_setRotation(degrees) {
var value = degrees ? "rotate(%deg)".replace(/%/, degrees) : null;
- iQ(this.container).css({"-moz-transform": value});
+ iQ(this.container).css({"transform": value});
},
// ----------
// Function: setParent
// Sets the receiver's parent to the given <Item>.
setParent: function Item_setParent(parent) {
this.parent = parent;
this.removeTrenches();
--- a/browser/components/tabview/tabitems.js
+++ b/browser/components/tabview/tabitems.js
@@ -530,17 +530,17 @@ TabItem.prototype = Utils.extend(new Ite
UI.setActive(this);
TabItems._update(this.tab, {force: true});
// Zoom in!
let tab = this.tab;
function onZoomDone() {
- $canvas.css({ '-moz-transform': null });
+ $canvas.css({ 'transform': null });
$tabEl.removeClass("front");
UI.goToTab(tab);
// tab might not be selected because hideTabView() is invoked after
// UI.goToTab() so we need to setup everything for the gBrowser.selectedTab
if (tab != gBrowser.selectedTab) {
UI.onTabSelect(gBrowser.selectedTab);
@@ -558,18 +558,18 @@ TabItem.prototype = Utils.extend(new Ite
if (animateZoom) {
let transform = this.getZoomTransform();
TabItems.pausePainting();
if (this.parent && this.parent.expanded)
$tabEl.removeClass("stack-trayed");
$tabEl.addClass("front");
$canvas
- .css({ '-moz-transform-origin': transform.transformOrigin })
- .animate({ '-moz-transform': transform.transform }, {
+ .css({ 'transform-origin': transform.transformOrigin })
+ .animate({ 'transform': transform.transform }, {
duration: 230,
easing: 'fast',
complete: function() {
onZoomDone();
setTimeout(function() {
TabItems.resumePainting();
}, 0);
@@ -588,17 +588,17 @@ TabItem.prototype = Utils.extend(new Ite
// Parameters:
// complete - a function to call after the zoom down animation
zoomOut: function TabItem_zoomOut(complete) {
let $tab = this.$container, $canvas = this.$canvas;
var self = this;
let onZoomDone = function onZoomDone() {
$tab.removeClass("front");
- $canvas.css("-moz-transform", null);
+ $canvas.css("transform", null);
if (typeof complete == "function")
complete();
};
UI.setActive(this);
TabItems._update(this.tab, {force: true});
@@ -607,21 +607,21 @@ TabItem.prototype = Utils.extend(new Ite
let animateZoom = gPrefBranch.getBoolPref("animate_zoom");
if (animateZoom) {
// The scaleCheat of 2 here is a clever way to speed up the zoom-out
// code. See getZoomTransform() below.
let transform = this.getZoomTransform(2);
TabItems.pausePainting();
$canvas.css({
- '-moz-transform': transform.transform,
- '-moz-transform-origin': transform.transformOrigin
+ 'transform': transform.transform,
+ 'transform-origin': transform.transformOrigin
});
- $canvas.animate({ "-moz-transform": "scale(1.0)" }, {
+ $canvas.animate({ "transform": "scale(1.0)" }, {
duration: 300,
easing: 'cubic-bezier', // note that this is legal easing, even without parameters
complete: function() {
TabItems.resumePainting();
onZoomDone();
}
});
} else {
--- a/browser/components/tabview/test/browser_tabview_bug624931.js
+++ b/browser/components/tabview/test/browser_tabview_bug624931.js
@@ -33,26 +33,26 @@ function onTabViewWindowLoaded() {
function checkForFrontAddition(aEvent) {
if (aEvent.attrName == "class" &&
aEvent.target.classList.contains("front")) {
frontChanged = true;
}
}
function checkForTransformAddition(aEvent) {
- if (aEvent.attrName == "style" && aEvent.target.style.MozTransform) {
+ if (aEvent.attrName == "style" && aEvent.target.style.transform) {
transformChanged = true;
}
}
function onTabViewHidden() {
window.removeEventListener("tabviewhidden", onTabViewHidden, false);
ok(frontChanged, "the CSS class 'front' was added while zooming in");
- ok(transformChanged, "the CSS class '-moz-transform' was modified while " +
+ ok(transformChanged, "the CSS class 'transform' was modified while " +
"zooming in");
frontChanged = transformChanged = false;
tab.$container[0].removeEventListener("DOMAttrModified",
checkForFrontAddition, false);
tab.$container[0].addEventListener("DOMAttrModified", checkForFrontRemoval,
false);
--- a/browser/config/mozconfigs/macosx-universal/nightly
+++ b/browser/config/mozconfigs/macosx-universal/nightly
@@ -18,12 +18,11 @@ export MOZILLA_OFFICIAL=1
export MOZ_TELEMETRY_REPORTING=1
mk_add_options MOZ_MAKE_FLAGS="-j12"
ac_add_options --with-macbundlename-prefix=Firefox
# Treat warnings as errors in directories with FAIL_ON_WARNINGS.
ac_add_options --enable-warnings-as-errors
-ac_add_options --with-ccache
# Package js shell.
export MOZ_PACKAGE_JSSHELL=1
--- a/browser/config/mozconfigs/macosx32/debug
+++ b/browser/config/mozconfigs/macosx32/debug
@@ -6,12 +6,11 @@ ENABLE_MARIONETTE=1
# Enable parallel compiling
mk_add_options MOZ_MAKE_FLAGS="-j12"
# Needed to enable breakpad in application.ini
export MOZILLA_OFFICIAL=1
ac_add_options --with-macbundlename-prefix=Firefox
-ac_add_options --with-ccache
# Package js shell.
export MOZ_PACKAGE_JSSHELL=1
--- a/browser/config/mozconfigs/macosx64/debug
+++ b/browser/config/mozconfigs/macosx64/debug
@@ -11,12 +11,11 @@ mk_add_options MOZ_MAKE_FLAGS="-j12"
# Needed to enable breakpad in application.ini
export MOZILLA_OFFICIAL=1
ac_add_options --with-macbundlename-prefix=Firefox
# Treat warnings as errors in directories with FAIL_ON_WARNINGS.
ac_add_options --enable-warnings-as-errors
-ac_add_options --with-ccache
# Package js shell.
export MOZ_PACKAGE_JSSHELL=1
--- a/browser/config/tooltool-manifests/linux32/clang.manifest
+++ b/browser/config/tooltool-manifests/linux32/clang.manifest
@@ -1,15 +1,17 @@
[
-{"clang_version": "r159509"},
+{
+"clang_version": "r160364"
+},
{
"size": 47,
"digest": "2005a41fe97a5e00997063705f39d42b6a43b1cf7ba306cbc7b1513de34cdcd050fc6326efa2107f19ba0cc67914745dbf13154fa748010a93cf072481ef4aaa",
"algorithm": "sha512",
"filename": "setup.sh"
},
{
-"size": 74071397,
-"digest": "390e499161e8b5e91c179f3352ecbb07431e3d5a190298de7f338b48fe3807f3ddbeca72d8df39d11f89864fb1135f14500471faa741d3886b875b8e2b1d4416",
+"size": 65680370,
+"digest": "5d343ea80cb0ace0f2a1683466015679dfacd0ae5584a89f001710c6d665f9fbd757edef5b1bd440f234553f9dbad06c8d1eed74b71a3d11e04e7f4e2c929628",
"algorithm": "sha512",
"filename": "clang.tar.bz2"
}
]
--- a/browser/config/tooltool-manifests/linux64/clang.manifest
+++ b/browser/config/tooltool-manifests/linux64/clang.manifest
@@ -1,15 +1,17 @@
[
-{"clang_version": "r159509"},
+{
+"clang_version": "r160364"
+},
{
"size": 47,
"digest": "2005a41fe97a5e00997063705f39d42b6a43b1cf7ba306cbc7b1513de34cdcd050fc6326efa2107f19ba0cc67914745dbf13154fa748010a93cf072481ef4aaa",
"algorithm": "sha512",
"filename": "setup.sh"
},
{
-"size": 72518043,
-"digest": "447dac319a8d7fa902cc065b758440bf6d4a7a98104362162fbdb0479a44d9b84c5878506f09be4398053a6ddc07935c7fb4f71e59b178073876fb5f90a45219",
+"size": 61020656,
+"digest": "822dc6f076309b1e45ef0ea77f88b9f5b73c8f1d0bb9c52147a2f99c4bdb67272442c9e89ab88bdadc94e2dead5e5cafc5ccea8211f42919b5ff9242bf2844d5",
"algorithm": "sha512",
"filename": "clang.tar.bz2"
}
]
old mode 120000
new mode 100644
--- a/browser/config/tooltool-manifests/macosx32/clang.manifest
+++ b/browser/config/tooltool-manifests/macosx32/clang.manifest
@@ -1,1 +1,17 @@
-../macosx64/clang.manifest
\ No newline at end of file
+[
+{
+"clang_version": "r160364"
+},
+{
+"size": 47,
+"digest": "2005a41fe97a5e00997063705f39d42b6a43b1cf7ba306cbc7b1513de34cdcd050fc6326efa2107f19ba0cc67914745dbf13154fa748010a93cf072481ef4aaa",
+"algorithm": "sha512",
+"filename": "setup.sh"
+},
+{
+"size": 54422251,
+"digest": "8208645d24ac87975a091ff66a90c20589ff8945936ed9b16ca81976c59bf1166ed9f79709698d435480774fba8ed9f9f178dc189305c86162acac8fda19830e",
+"algorithm": "sha512",
+"filename": "clang.tar.bz2"
+}
+]
--- a/browser/config/tooltool-manifests/macosx32/releng.manifest
+++ b/browser/config/tooltool-manifests/macosx32/releng.manifest
@@ -1,1 +1,17 @@
-[]
+[
+{
+"clang_version": "r160364"
+},
+{
+"size": 47,
+"digest": "2005a41fe97a5e00997063705f39d42b6a43b1cf7ba306cbc7b1513de34cdcd050fc6326efa2107f19ba0cc67914745dbf13154fa748010a93cf072481ef4aaa",
+"algorithm": "sha512",
+"filename": "setup.sh"
+},
+{
+"size": 54422251,
+"digest": "8208645d24ac87975a091ff66a90c20589ff8945936ed9b16ca81976c59bf1166ed9f79709698d435480774fba8ed9f9f178dc189305c86162acac8fda19830e",
+"algorithm": "sha512",
+"filename": "clang.tar.bz2"
+}
+]
--- a/browser/config/tooltool-manifests/macosx64/clang.manifest
+++ b/browser/config/tooltool-manifests/macosx64/clang.manifest
@@ -1,15 +1,17 @@
[
-{"clang_version": "r159509"},
+{
+"clang_version": "r160364"
+},
{
"size": 47,
"digest": "2005a41fe97a5e00997063705f39d42b6a43b1cf7ba306cbc7b1513de34cdcd050fc6326efa2107f19ba0cc67914745dbf13154fa748010a93cf072481ef4aaa",
"algorithm": "sha512",
"filename": "setup.sh"
},
{
-"size": 63679229,
-"digest": "5257503e537b8d440b17e40aa06f0f70f1b124129c02f10e45b46ac642fc4170bfa77ae737a8bcac3ed7602ccd934a88cbe349986eb971d66a6fb553ae31f13c",
+"size": 54422251,
+"digest": "8208645d24ac87975a091ff66a90c20589ff8945936ed9b16ca81976c59bf1166ed9f79709698d435480774fba8ed9f9f178dc189305c86162acac8fda19830e",
"algorithm": "sha512",
"filename": "clang.tar.bz2"
}
]
--- a/browser/config/tooltool-manifests/macosx64/releng.manifest
+++ b/browser/config/tooltool-manifests/macosx64/releng.manifest
@@ -1,1 +1,17 @@
-[]
+[
+{
+"clang_version": "r160364"
+},
+{
+"size": 47,
+"digest": "2005a41fe97a5e00997063705f39d42b6a43b1cf7ba306cbc7b1513de34cdcd050fc6326efa2107f19ba0cc67914745dbf13154fa748010a93cf072481ef4aaa",
+"algorithm": "sha512",
+"filename": "setup.sh"
+},
+{
+"size": 54422251,
+"digest": "8208645d24ac87975a091ff66a90c20589ff8945936ed9b16ca81976c59bf1166ed9f79709698d435480774fba8ed9f9f178dc189305c86162acac8fda19830e",
+"algorithm": "sha512",
+"filename": "clang.tar.bz2"
+}
+]
--- a/browser/config/version.txt
+++ b/browser/config/version.txt
@@ -1,1 +1,1 @@
-16.0a1
+17.0a1
--- a/browser/devtools/commandline/GcliCommands.jsm
+++ b/browser/devtools/commandline/GcliCommands.jsm
@@ -2,16 +2,19 @@
* 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/. */
let EXPORTED_SYMBOLS = [ "GcliCommands" ];
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+const XMLHttpRequest =
+ Components.Constructor("@mozilla.org/xmlextras/xmlhttprequest;1");
+
Cu.import("resource:///modules/devtools/gcli.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "HUDService",
"resource:///modules/HUDService.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
@@ -21,16 +24,29 @@ XPCOMUtils.defineLazyModuleGetter(this,
"resource:///modules/devtools/LayoutHelpers.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "console",
"resource:///modules/devtools/Console.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
"resource://gre/modules/AddonManager.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "js_beautify",
+ "resource:///modules/devtools/Jsbeautify.jsm");
+
+XPCOMUtils.defineLazyGetter(this, "Debugger", function() {
+ let JsDebugger = {};
+ Components.utils.import("resource://gre/modules/jsdebugger.jsm", JsDebugger);
+
+ let global = Components.utils.getGlobalForObject({});
+ JsDebugger.addDebuggerToGlobal(global);
+
+ return global.Debugger;
+});
+
let prefSvc = "@mozilla.org/preferences-service;1";
XPCOMUtils.defineLazyGetter(this, "prefBranch", function() {
let prefService = Cc[prefSvc].getService(Ci.nsIPrefService);
return prefService.getBranch(null).QueryInterface(Ci.nsIPrefBranch2);
});
Cu.import("resource:///modules/devtools/GcliTiltCommands.jsm", {});
Cu.import("resource:///modules/devtools/GcliCookieCommands.jsm", {});
@@ -120,24 +136,26 @@ function loadCommandFile(aFile, aSandbox
}
/**
* 'cmd' command
*/
gcli.addCommand({
name: "cmd",
description: gcli.lookup("cmdDesc"),
+ hidden: true
});
/**
* 'cmd refresh' command
*/
gcli.addCommand({
name: "cmd refresh",
description: gcli.lookup("cmdRefreshDesc"),
+ hidden: true,
exec: function Command_cmdRefresh(args, context) {
GcliCommands.refreshAutoCommands(context.environment.chromeDocument.defaultView);
}
});
/**
* 'echo' command
*/
@@ -147,16 +165,17 @@ gcli.addCommand({
params: [
{
name: "message",
type: "string",
description: gcli.lookup("echoMessageDesc")
}
],
returnType: "string",
+ hidden: true,
exec: function Command_echo(args, context) {
return args.message;
}
});
/**
* 'screenshot' command
@@ -276,16 +295,97 @@ gcli.addCommand({
let source = ioService.newURI(data, "UTF8", null);
persist.saveURI(source, null, null, null, null, file);
return "Saved to " + filename;
}
});
+let callLogDebuggers = [];
+
+/**
+ * 'calllog' command
+ */
+gcli.addCommand({
+ name: "calllog",
+ description: gcli.lookup("calllogDesc")
+})
+
+/**
+ * 'calllog start' command
+ */
+gcli.addCommand({
+ name: "calllog start",
+ description: gcli.lookup("calllogStartDesc"),
+
+ exec: function(args, context) {
+ let contentWindow = context.environment.contentDocument.defaultView;
+
+ let dbg = new Debugger(contentWindow);
+ dbg.onEnterFrame = function(frame) {
+ // BUG 773652 - Make the output from the GCLI calllog command nicer
+ contentWindow.console.log("Method call: " + this.callDescription(frame));
+ }.bind(this);
+
+ callLogDebuggers.push(dbg);
+
+ let tab = context.environment.chromeDocument.defaultView.gBrowser.selectedTab;
+ HUDService.activateHUDForContext(tab);
+
+ return gcli.lookup("calllogStartReply");
+ },
+
+ callDescription: function(frame) {
+ let name = "<anonymous>";
+ if (frame.callee.name) {
+ name = frame.callee.name;
+ }
+ else {
+ let desc = frame.callee.getOwnPropertyDescriptor("displayName");
+ if (desc && desc.value && typeof desc.value == "string") {
+ name = desc.value;
+ }
+ }
+
+ let args = frame.arguments.map(this.valueToString).join(", ");
+ return name + "(" + args + ")";
+ },
+
+ valueToString: function(value) {
+ if (typeof value !== "object" || value === null) {
+ return uneval(value);
+ }
+ return "[object " + value.class + "]";
+ }
+});
+
+/**
+ * 'calllog stop' command
+ */
+gcli.addCommand({
+ name: "calllog stop",
+ description: gcli.lookup("calllogStopDesc"),
+
+ exec: function(args, context) {
+ let numDebuggers = callLogDebuggers.length;
+ if (numDebuggers == 0) {
+ return gcli.lookup("calllogStopNoLogging");
+ }
+
+ for (let dbg of callLogDebuggers) {
+ dbg.onEnterFrame = undefined;
+ }
+ callLogDebuggers = [];
+
+ return gcli.lookupFormat("calllogStopReply", [ numDebuggers ]);
+ }
+});
+
+
/**
* 'console' command
*/
gcli.addCommand({
name: "console",
description: gcli.lookup("consoleDesc"),
manual: gcli.lookup("consoleManual")
});
@@ -992,16 +1092,143 @@ gcli.addCommand({
// been finished.
let promise = context.createPromise();
let types = aArgs.type == "all" ? null : [aArgs.type];
AddonManager.getAddonsByTypes(types, list.bind(promise, aArgs.type));
return promise;
}
});
+
+/**
+ * 'dbg' command
+ */
+gcli.addCommand({
+ name: "dbg",
+ description: gcli.lookup("dbgDesc"),
+ manual: gcli.lookup("dbgManual")
+});
+
+
+/**
+ * 'dbg interrupt' command
+ */
+gcli.addCommand({
+ name: "dbg interrupt",
+ description: gcli.lookup("dbgInterrupt"),
+ params: [],
+ exec: function(args, context) {
+ let win = context.environment.chromeDocument.defaultView;
+ let dbg = win.DebuggerUI.getDebugger();
+
+ if (dbg) {
+ let controller = dbg.contentWindow.DebuggerController;
+ let thread = controller.activeThread;
+ if (!thread.paused) {
+ thread.interrupt();
+ }
+ }
+ }
+});
+
+/**
+ * 'dbg continue' command
+ */
+gcli.addCommand({
+ name: "dbg continue",
+ description: gcli.lookup("dbgContinue"),
+ params: [],
+ exec: function(args, context) {
+ let win = context.environment.chromeDocument.defaultView;
+ let dbg = win.DebuggerUI.getDebugger();
+
+ if (dbg) {
+ let controller = dbg.contentWindow.DebuggerController;
+ let thread = controller.activeThread;
+ if (thread.paused) {
+ thread.resume();
+ }
+ }
+ }
+});
+
+
+/**
+ * 'dbg step' command
+ */
+gcli.addCommand({
+ name: "dbg step",
+ description: gcli.lookup("dbgStepDesc"),
+ manual: gcli.lookup("dbgStepManual")
+});
+
+
+/**
+ * 'dbg step over' command
+ */
+gcli.addCommand({
+ name: "dbg step over",
+ description: gcli.lookup("dbgStepOverDesc"),
+ params: [],
+ exec: function(args, context) {
+ let win = context.environment.chromeDocument.defaultView;
+ let dbg = win.DebuggerUI.getDebugger();
+
+ if (dbg) {
+ let controller = dbg.contentWindow.DebuggerController;
+ let thread = controller.activeThread;
+ if (thread.paused) {
+ thread.stepOver();
+ }
+ }
+ }
+});
+
+/**
+ * 'dbg step in' command
+ */
+gcli.addCommand({
+ name: 'dbg step in',
+ description: gcli.lookup("dbgStepInDesc"),
+ params: [],
+ exec: function(args, context) {
+ let win = context.environment.chromeDocument.defaultView;
+ let dbg = win.DebuggerUI.getDebugger();
+
+ if (dbg) {
+ let controller = dbg.contentWindow.DebuggerController;
+ let thread = controller.activeThread;
+ if (thread.paused) {
+ thread.stepIn();
+ }
+ }
+ }
+});
+
+/**
+ * 'dbg step over' command
+ */
+gcli.addCommand({
+ name: 'dbg step out',
+ description: gcli.lookup("dbgStepOutDesc"),
+ params: [],
+ exec: function(args, context) {
+ let win = context.environment.chromeDocument.defaultView;
+ let dbg = win.DebuggerUI.getDebugger();
+
+ if (dbg) {
+ let controller = dbg.contentWindow.DebuggerController;
+ let thread = controller.activeThread;
+ if (thread.paused) {
+ thread.stepOut();
+ }
+ }
+ }
+});
+
// We need a list of addon names for the enable and disable commands. Because
// getting the name list is async we do not add the commands until we have the
// list.
AddonManager.getAllAddons(function addonAsync(aAddons) {
// We listen for installs to keep our addon list up to date. There is no need
// to listen for uninstalls because uninstalled addons are simply disabled
// until restart (to enable undo functionality).
AddonManager.addAddonListener({
@@ -1192,8 +1419,131 @@ AddonManager.getAllAddons(function addon
name: 'height',
type: 'number',
description: gcli.lookup("resizePageArgHeightDesc"),
},
],
exec: gcli_cmd_resize
});
})();
+
+/**
+ * jsb command.
+ */
+gcli.addCommand({
+ name: 'jsb',
+ description: gcli.lookup('jsbDesc'),
+ returnValue:'string',
+ hidden: true,
+ params: [
+ {
+ name: 'url',
+ type: 'string',
+ description: gcli.lookup('jsbUrlDesc'),
+ manual: 'The URL of the JS to prettify'
+ },
+ {
+ name: 'indentSize',
+ type: 'number',
+ description: gcli.lookup('jsbIndentSizeDesc'),
+ manual: gcli.lookup('jsbIndentSizeManual'),
+ defaultValue: 2
+ },
+ {
+ name: 'indentChar',
+ type: {
+ name: 'selection',
+ lookup: [{name: "space", value: " "}, {name: "tab", value: "\t"}]
+ },
+ description: gcli.lookup('jsbIndentCharDesc'),
+ manual: gcli.lookup('jsbIndentCharManual'),
+ defaultValue: ' ',
+ },
+ {
+ name: 'preserveNewlines',
+ type: 'boolean',
+ description: gcli.lookup('jsbPreserveNewlinesDesc'),
+ manual: gcli.lookup('jsbPreserveNewlinesManual'),
+ defaultValue: true
+ },
+ {
+ name: 'preserveMaxNewlines',
+ type: 'number',
+ description: gcli.lookup('jsbPreserveMaxNewlinesDesc'),
+ manual: gcli.lookup('jsbPreserveMaxNewlinesManual'),
+ defaultValue: -1
+ },
+ {
+ name: 'jslintHappy',
+ type: 'boolean',
+ description: gcli.lookup('jsbJslintHappyDesc'),
+ manual: gcli.lookup('jsbJslintHappyManual'),
+ defaultValue: false
+ },
+ {
+ name: 'braceStyle',
+ type: {
+ name: 'selection',
+ data: ['collapse', 'expand', 'end-expand', 'expand-strict']
+ },
+ description: gcli.lookup('jsbBraceStyleDesc'),
+ manual: gcli.lookup('jsbBraceStyleManual'),
+ defaultValue: "collapse"
+ },
+ {
+ name: 'spaceBeforeConditional',
+ type: 'boolean',
+ description: gcli.lookup('jsbSpaceBeforeConditionalDesc'),
+ manual: gcli.lookup('jsbSpaceBeforeConditionalManual'),
+ defaultValue: true
+ },
+ {
+ name: 'unescapeStrings',
+ type: 'boolean',
+ description: gcli.lookup('jsbUnescapeStringsDesc'),
+ manual: gcli.lookup('jsbUnescapeStringsManual'),
+ defaultValue: false
+ }
+ ],
+ exec: function(args, context) {
+ let opts = {
+ indent_size: args.indentSize,
+ indent_char: args.indentChar,
+ preserve_newlines: args.preserveNewlines,
+ max_preserve_newlines: args.preserveMaxNewlines == -1 ?
+ undefined : args.preserveMaxNewlines,
+ jslint_happy: args.jslintHappy,
+ brace_style: args.braceStyle,
+ space_before_conditional: args.spaceBeforeConditional,
+ unescape_strings: args.unescapeStrings
+ }
+
+ let xhr = new XMLHttpRequest();
+
+ try {
+ xhr.open("GET", args.url, true);
+ } catch(e) {
+ return gcli.lookup('jsbInvalidURL');
+ }
+
+ let promise = context.createPromise();
+
+ xhr.onreadystatechange = function(aEvt) {
+ if (xhr.readyState == 4) {
+ if (xhr.status == 200 || xhr.status == 0) {
+ let browserDoc = context.environment.chromeDocument;
+ let browserWindow = browserDoc.defaultView;
+ let browser = browserWindow.gBrowser;
+
+ browser.selectedTab = browser.addTab("data:text/plain;base64," +
+ browserWindow.btoa(js_beautify(xhr.responseText, opts)));
+ promise.resolve();
+ }
+ else {
+ promise.resolve("Unable to load page to beautify: " + args.url + " " +
+ xhr.status + " " + xhr.statusText);
+ }
+ };
+ }
+ xhr.send(null);
+ return promise;
+ }
+});
--- a/browser/devtools/commandline/GcliCookieCommands.jsm
+++ b/browser/devtools/commandline/GcliCookieCommands.jsm
@@ -32,19 +32,18 @@ var cookieListHtml = "" +
" <th>" + gcli.lookup("cookieListOutKey") + "</th>" +
" <th>" + gcli.lookup("cookieListOutValue") + "</th>" +
" <th>" + gcli.lookup("cookieListOutActions") + "</th>" +
" </tr>" +
" <tr foreach='cookie in ${cookies}'>" +
" <td>${cookie.key}</td>" +
" <td>${cookie.value}</td>" +
" <td>" +
- " <span class='gcli-out-shortcut'" +
- " onclick='${onclick}' ondblclick='${ondblclick}'" +
- " data-command='cookie set ${cookie.key}'" +
+ " <span class='gcli-out-shortcut' onclick='${onclick}'" +
+ " data-command='cookie set ${cookie.key} '" +
" >" + gcli.lookup("cookieListOutEdit") + "</span>" +
" <span class='gcli-out-shortcut'" +
" onclick='${onclick}' ondblclick='${ondblclick}'" +
" data-command='cookie remove ${cookie.key}'" +
" >" + gcli.lookup("cookieListOutRemove") + "</span>" +
" </td>" +
" </tr>" +
"</table>" +
--- a/browser/devtools/commandline/gcli.jsm
+++ b/browser/devtools/commandline/gcli.jsm
@@ -1674,18 +1674,18 @@ exports.shutdown = function() {
* - data: An array of strings - alternative to 'lookup' where the valid values
* are strings. i.e. there is no mapping between what is typed and the value
* that is used by the program
* - stringifyProperty: Conversion from value to string is generally a process
* of looking through all the valid options for a matching value, and using
* the associated name. However the name maybe available directly from the
* value using a property lookup. Setting 'stringifyProperty' allows
* SelectionType to take this shortcut.
- * - cacheable : If lookup is a function, then we normally assume that
- * the values fetched can change. Setting 'cacheable' enables internal
+ * - cacheable: If lookup is a function, then we normally assume that
+ * the values fetched can change. Setting 'cacheable:true' enables internal
* caching.
*/
function SelectionType(typeSpec) {
if (typeSpec) {
Object.keys(typeSpec).forEach(function(key) {
this[key] = typeSpec[key];
}, this);
}
@@ -1769,58 +1769,67 @@ SelectionType.prototype._dataToLookup =
* @param arg The initial input to match
* @return A trimmed array of string:value pairs
*/
SelectionType.prototype._findPredictions = function(arg) {
var predictions = [];
var lookup = this.getLookup();
var i, option;
var maxPredictions = Conversion.maxPredictions;
+ var match = arg.text.toLowerCase();
// If the arg has a suffix then we're kind of 'done'. Only an exact match
// will do.
if (arg.suffix.length > 0) {
for (i = 0; i < lookup.length && predictions.length < maxPredictions; i++) {
option = lookup[i];
if (option.name === arg.text) {
this._addToPredictions(predictions, option, arg);
}
}
return predictions;
}
+ // Cache lower case versions of all the option names
+ for (i = 0; i < lookup.length; i++) {
+ option = lookup[i];
+ if (option._gcliLowerName == null) {
+ option._gcliLowerName = option.name.toLowerCase();
+ }
+ }
+
// Start with prefix matching
for (i = 0; i < lookup.length && predictions.length < maxPredictions; i++) {
option = lookup[i];
- if (option.name.indexOf(arg.text) === 0) {
+ if (option._gcliLowerName.indexOf(match) === 0) {
this._addToPredictions(predictions, option, arg);
}
}
// Try infix matching if we get less half max matched
if (predictions.length < (maxPredictions / 2)) {
for (i = 0; i < lookup.length && predictions.length < maxPredictions; i++) {
option = lookup[i];
- if (option.name.indexOf(arg.text) !== -1) {
+ if (option._gcliLowerName.indexOf(match) !== -1) {
if (predictions.indexOf(option) === -1) {
this._addToPredictions(predictions, option, arg);
}
}
}
}
// Try fuzzy matching if we don't get a prefix match
if (false && predictions.length === 0) {
var speller = new Speller();
var names = lookup.map(function(opt) {
return opt.name;
});
speller.train(names);
- var corrected = speller.correct(arg.text);
+ var corrected = speller.correct(match);
if (corrected) {
lookup.forEach(function(opt) {
if (opt.name === corrected) {
predictions.push(opt);
}
}, this);
}
}
@@ -1847,19 +1856,18 @@ SelectionType.prototype.parse = function
}
// This is something of a hack it basically allows us to tell the
// setting type to forget its last setting hack.
if (this.noMatch) {
this.noMatch();
}
- var value = predictions[0].value;
-
if (predictions[0].name === arg.text) {
+ var value = predictions[0].value;
return new Conversion(value, arg, Status.VALID, '', predictions);
}
return new Conversion(undefined, arg, Status.INCOMPLETE, '', predictions);
};
/**
* For selections, up is down and black is white. It's like this, given a list
@@ -8415,17 +8423,19 @@ function getListTemplateData(args, conte
return false;
}
if (!args.search && command.name.indexOf(' ') != -1) {
// We don't show sub commands with plain 'help'
return false;
}
return true;
});
- matchingCommands.sort();
+ matchingCommands.sort(function(c1, c2) {
+ return c1.name.localeCompare(c2.name);
+ });
var heading;
if (matchingCommands.length === 0) {
heading = l10n.lookupFormat('helpListNone', [ args.search ]);
}
else if (args.search == null) {
heading = l10n.lookup('helpListAll');
}
@@ -8488,17 +8498,19 @@ function getManTemplateData(command, con
};
Object.defineProperty(manTemplateData, 'subcommands', {
get: function() {
var matching = canon.getCommands().filter(function(subcommand) {
return subcommand.name.indexOf(command.name) === 0 &&
subcommand.name !== command.name;
});
- matching.sort();
+ matching.sort(function(c1, c2) {
+ return c1.name.localeCompare(c2.name);
+ });
return matching;
},
enumerable: true
});
return manTemplateData;
}
--- a/browser/devtools/commandline/test/Makefile.in
+++ b/browser/devtools/commandline/test/Makefile.in
@@ -9,33 +9,38 @@ srcdir = @srcdir@
VPATH = @srcdir@
relativesrcdir = browser/devtools/commandline/test
include $(DEPTH)/config/autoconf.mk
MOCHITEST_BROWSER_FILES = \
browser_gcli_addon.js \
browser_gcli_break.js \
+ browser_gcli_calllog.js \
browser_gcli_commands.js \
browser_gcli_cookie.js \
+ browser_gcli_dbg.js \
browser_gcli_edit.js \
browser_gcli_inspect.js \
browser_gcli_integrate.js \
+ browser_gcli_jsb.js \
browser_gcli_pagemod_export.js \
browser_gcli_pref.js \
browser_gcli_responsivemode.js \
browser_gcli_restart.js \
browser_gcli_settings.js \
browser_gcli_web.js \
head.js \
$(NULL)
MOCHITEST_BROWSER_FILES += \
browser_gcli_break.html \
browser_gcli_inspect.html \
+ resources_dbg.html \
resources_inpage.js \
resources_inpage1.css \
resources_inpage2.css \
+ resources_jsb_script.js \
resources.html \
$(NULL)
include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/browser/devtools/commandline/test/browser_gcli_calllog.js
@@ -0,0 +1,74 @@
+/* Any copyright is dedicated to the Public Domain.
+* http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that the calllog commands works as they should
+
+let imported = {};
+Components.utils.import("resource:///modules/HUDService.jsm", imported);
+
+const TEST_URI = "data:text/html;charset=utf-8,gcli-calllog";
+
+function test() {
+ DeveloperToolbarTest.test(TEST_URI, function(browser, tab) {
+ testCallLogStatus();
+ testCallLogExec();
+ finish();
+ });
+}
+
+function testCallLogStatus() {
+ DeveloperToolbarTest.checkInputStatus({
+ typed: "calllog",
+ status: "ERROR"
+ });
+
+ DeveloperToolbarTest.checkInputStatus({
+ typed: "calllog start",
+ status: "VALID",
+ emptyParameters: [ ]
+ });
+
+ DeveloperToolbarTest.checkInputStatus({
+ typed: "calllog start",
+ status: "VALID",
+ emptyParameters: [ ]
+ });
+}
+
+function testCallLogExec() {
+ DeveloperToolbarTest.exec({
+ typed: "calllog stop",
+ args: { },
+ outputMatch: /No call logging/,
+ });
+
+ DeveloperToolbarTest.exec({
+ typed: "calllog start",
+ args: { },
+ outputMatch: /Call logging started/,
+ });
+
+ let hud = imported.HUDService.getHudByWindow(content);
+ ok(hud.hudId in imported.HUDService.hudReferences, "console open");
+
+ DeveloperToolbarTest.exec({
+ typed: "calllog stop",
+ args: { },
+ outputMatch: /Stopped call logging/,
+ });
+
+ DeveloperToolbarTest.exec({
+ typed: "console clear",
+ args: {},
+ blankOutput: true,
+ });
+
+ let labels = hud.jsterm.outputNode.querySelectorAll(".webconsole-msg-output");
+ is(labels.length, 0, "no output in console");
+
+ DeveloperToolbarTest.exec({
+ typed: "console close",
+ args: {},
+ blankOutput: true,
+ });
+}
--- a/browser/devtools/commandline/test/browser_gcli_commands.js
+++ b/browser/devtools/commandline/test/browser_gcli_commands.js
@@ -14,21 +14,23 @@ function test() {
testConsole(tab);
imported = undefined;
finish();
});
}
function testEcho() {
+ /*
DeveloperToolbarTest.exec({
typed: "echo message",
args: { message: "message" },
outputMatch: /^message$/,
});
+ */
}
function testConsole(tab) {
DeveloperToolbarTest.exec({
typed: "console open",
args: {},
blankOutput: true,
});
new file mode 100644
--- /dev/null
+++ b/browser/devtools/commandline/test/browser_gcli_dbg.js
@@ -0,0 +1,67 @@
+function test() {
+ const TEST_URI = TEST_BASE_HTTP + "resources_dbg.html";
+
+ DeveloperToolbarTest.test(TEST_URI, function GAT_test() {
+ let pane = DebuggerUI.toggleDebugger();
+ ok(pane, "toggleDebugger() should return a pane.");
+ let frame = pane._frame;
+
+ frame.addEventListener("Debugger:Connecting", function dbgConnected(aEvent) {
+ frame.removeEventListener("Debugger:Connecting", dbgConnected, true);
+
+ // Wait for the initial resume...
+ aEvent.target.ownerDocument.defaultView.gClient
+ .addOneTimeListener("resumed", function() {
+
+ info("Starting tests.");
+
+ let contentDoc = content.window.document;
+ let output = contentDoc.querySelector("input[type=text]");
+ let btnDoit = contentDoc.querySelector("input[type=button]");
+
+ cmd("dbg interrupt", function() {
+ ok(true, "debugger is paused");
+ pane.contentWindow.gClient.addOneTimeListener("resumed", function() {
+ ok(true, "debugger continued");
+ pane.contentWindow.gClient.addOneTimeListener("paused", function() {
+ cmd("dbg step in", function() {
+ cmd("dbg step in", function() {
+ cmd("dbg step in", function() {
+ is(output.value, "step in", "debugger stepped in");
+ cmd("dbg step over", function() {
+ is(output.value, "step over", "debugger stepped over");
+ cmd("dbg step out", function() {
+ is(output.value, "step out", "debugger stepped out");
+ cmd("dbg continue", function() {
+ cmd("dbg continue", function() {
+ is(output.value, "dbg continue", "debugger continued");
+ pane.contentWindow.gClient.close(function() {
+ finish();
+ });
+ });
+ });
+ });
+ });
+ });
+ });
+ });
+ });
+ EventUtils.sendMouseEvent({type:"click"}, btnDoit);
+ });
+ DeveloperToolbarTest.exec({
+ typed: "dbg continue",
+ blankOutput: true
+ });
+ });
+ });
+
+ function cmd(aTyped, aCallback) {
+ pane.contentWindow.gClient.addOneTimeListener("paused", aCallback);
+ DeveloperToolbarTest.exec({
+ typed: aTyped,
+ blankOutput: true
+ });
+ }
+ });
+ });
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/commandline/test/browser_gcli_jsb.js
@@ -0,0 +1,48 @@
+function test() {
+ const TEST_URI = "http://example.com/browser/browser/devtools/commandline/" +
+ "test/resources_jsb_script.js";
+
+ DeveloperToolbarTest.test("about:blank", function GJT_test() {
+ /* Commented out by bug 774057, re-enable with un-hidden jsb command
+ DeveloperToolbarTest.exec({
+ typed: "jsb AAA",
+ outputMatch: /valid/
+ });
+
+ gBrowser.addTabsProgressListener({
+ onProgressChange: function GJT_onProgressChange(aBrowser) {
+ gBrowser.removeTabsProgressListener(this);
+
+ let win = aBrowser._contentWindow;
+ let uri = win.document.location.href;
+ let result = win.atob(uri.replace(/.*,/, ""));
+
+ result = result.replace(/[\r\n]]/g, "\n");
+
+ checkResult(result);
+ finish();
+ }
+ });
+
+ info("Checking beautification");
+ DeveloperToolbarTest.checkInputStatus({
+ typed: "jsb " + TEST_URI + " 4 space true -1 false collapse true false",
+ status: "VALID"
+ });
+ DeveloperToolbarTest.exec({ completed: false });
+
+ function checkResult(aResult) {
+ let correct = "function somefunc() {\n" +
+ " for (let n = 0; n < 500; n++) {\n" +
+ " if (n % 2 == 1) {\n" +
+ " console.log(n);\n" +
+ " console.log(n + 1);\n" +
+ " }\n" +
+ " }\n" +
+ "}";
+ is(aResult, correct, "JS has been correctly prettified");
+ }
+ */
+ finish();
+ });
+}
--- a/browser/devtools/commandline/test/browser_gcli_web.js
+++ b/browser/devtools/commandline/test/browser_gcli_web.js
@@ -2955,18 +2955,35 @@ exports.testCompleted = function(options
args: {
key: { value: 'key', status: 'VALID' },
value: { value: 'value', status: 'VALID' },
path: { value: 'path', status: 'VALID' },
domain: { value: 'domain', status: 'VALID' },
secure: { value: false, status: 'VALID' }
}
});
-
- // Expand out to christmas tree command line
+};
+
+exports.testCase = function(options) {
+ helpers.setInput('tsg AA');
+ helpers.check({
+ input: 'tsg AA',
+ markup: 'VVVVII',
+ directTabText: '',
+ arrowTabText: 'aaa',
+ status: 'ERROR',
+ emptyParameters: [ ],
+ args: {
+ solo: { value: undefined, text: 'AA', status: 'INCOMPLETE' },
+ txt1: { value: undefined, status: 'VALID' },
+ bool: { value: undefined, status: 'VALID' },
+ txt2: { value: undefined, status: 'VALID' },
+ num: { value: undefined, status: 'VALID' }
+ }
+ });
};
exports.testIncomplete = function(options) {
var requisition = options.display.requisition;
helpers.setInput('tsm a a -');
helpers.check({
args: {
new file mode 100644
--- /dev/null
+++ b/browser/devtools/commandline/test/resources_dbg.html
@@ -0,0 +1,44 @@
+<html>
+<head>
+ <script type="application/javascript;version=1.7"/>
+ let output;
+
+ function init() {
+ output = document.querySelector("input");
+ output.value = "";
+ }
+
+ function doit() {
+ debugger;
+ stepIntoMe(); // step in
+
+ output.value = "dbg continue";
+ debugger;
+ }
+
+ function stepIntoMe() {
+ output.value = "step in"; // step in
+ stepOverMe(); // step over
+ let x = 0; // step out
+ output.value = "step out";
+ }
+
+ function stepOverMe() {
+ output.value = "step over";
+ }
+ </script>
+</head>
+<body onload="init()">
+ <input type="text" value=""/>
+ <input type="button" value="DOIT" onclick="doit()"/>
+ <br />
+ Use this file to test the following commands:
+ <ul>
+ <li>dbg interrupt</li>
+ <li>dbg continue</li>
+ <li>dbg step over</li>
+ <li>dbg step in</li>
+ <li>dbg step out</li>
+ </ul>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/devtools/commandline/test/resources_jsb_script.js
@@ -0,0 +1,1 @@
+function somefunc(){for(let n=0;n<500;n++){if(n%2==1){console.log(n);console.log(n+1);}}}
--- a/browser/devtools/debugger/debugger-controller.js
+++ b/browser/devtools/debugger/debugger-controller.js
@@ -47,16 +47,17 @@ let DebuggerController = {
return;
}
this._isInitialized = true;
window.removeEventListener("DOMContentLoaded", this._startupDebugger, true);
DebuggerView.initializePanes();
DebuggerView.initializeEditor();
DebuggerView.StackFrames.initialize();
+ DebuggerView.Breakpoints.initialize();
DebuggerView.Properties.initialize();
DebuggerView.Scripts.initialize();
DebuggerView.showCloseButton(!this._isRemoteDebugger && !this._isChromeDebugger);
this.dispatchEvent("Debugger:Loaded");
this._connect();
},
@@ -70,16 +71,17 @@ let DebuggerController = {
}
this._isDestroyed = true;
window.removeEventListener("unload", this._shutdownDebugger, true);
DebuggerView.destroyPanes();
DebuggerView.destroyEditor();
DebuggerView.Scripts.destroy();
DebuggerView.StackFrames.destroy();
+ DebuggerView.Breakpoints.destroy();
DebuggerView.Properties.destroy();
DebuggerController.Breakpoints.destroy();
DebuggerController.SourceScripts.disconnect();
DebuggerController.StackFrames.disconnect();
DebuggerController.ThreadState.disconnect();
this.dispatchEvent("Debugger:Unloaded");
@@ -108,18 +110,18 @@ let DebuggerController = {
let prompt = new RemoteDebuggerPrompt();
let result = prompt.show(!!this._remoteConnectionTimeout);
// If the connection was not established before the user canceled the
// prompt, close the remote debugger.
if (!result && !DebuggerController.activeThread) {
this.dispatchEvent("Debugger:Close");
return false;
}
- Prefs.remoteHost = prompt.uri.host;
- Prefs.remotePort = prompt.uri.port;
+ Prefs.remoteHost = prompt.remote.host;
+ Prefs.remotePort = prompt.remote.port;
}
// If this debugger is connecting remotely to a server, we need to check
// after a while if the connection actually succeeded.
this._remoteConnectionTry = ++this._remoteConnectionTry || 1;
this._remoteConnectionTimeout = window.setTimeout(function() {
// If we couldn't connect to any server yet, try again...
if (!DebuggerController.activeThread) {
@@ -255,17 +257,18 @@ let DebuggerController = {
return window._remoteFlag;
},
/**
* Returns true if this is a chrome debugger instance.
* @return boolean
*/
get _isChromeDebugger() {
- return !window.parent.content && !this._isRemoteDebugger;
+ // Directly accessing window.parent.content may throw in some cases.
+ return !("content" in window.parent) && !this._isRemoteDebugger;
},
/**
* Attempts to quit the current process if allowed.
*/
_quitApp: function DC__quitApp() {
let canceled = Cc["@mozilla.org/supports-PRBool;1"]
.createInstance(Ci.nsISupportsPRBool);
@@ -389,18 +392,16 @@ StackFrames.prototype = {
* Watch the given thread client.
*
* @param function aCallback
* The next function in the initialization sequence.
*/
connect: function SF_connect(aCallback) {
window.addEventListener("Debugger:FetchedVariables", this._onFetchedVars, false);
- this._onFramesCleared();
-
this.activeThread.addListener("paused", this._onPaused);
this.activeThread.addListener("resumed", this._onResume);
this.activeThread.addListener("framesadded", this._onFrames);
this.activeThread.addListener("framescleared", this._onFramesCleared);
this.updatePauseOnExceptions(this.pauseOnExceptions);
aCallback && aCallback();
@@ -500,22 +501,57 @@ StackFrames.prototype = {
if (!frame) {
return;
}
let url = frame.where.url;
let line = frame.where.line;
let editor = DebuggerView.editor;
- // Move the editor's caret to the proper line.
- if (DebuggerView.Scripts.isSelected(url) && line) {
- editor.setDebugLocation(line - 1);
- } else {
- editor.setDebugLocation(-1);
+ this.updateEditorToLocation(url, line, true);
+ },
+
+ /**
+ * Update the source editor's current caret and debug location based on
+ * a specified url and line.
+ *
+ * @param string aUrl
+ * The target source url.
+ * @param number aLine
+ * The target line number in the source.
+ * @param boolean aNoSwitch
+ * Pass true to not switch to the script if not currently selected.
+ * @param boolean aNoCaretFlag
+ * Pass true to not set the caret location at the specified line.
+ * @param boolean aNoDebugFlag
+ * Pass true to not set the debug location at the specified line.
+ */
+ updateEditorToLocation:
+ function SF_updateEditorToLocation(aUrl, aLine, aNoSwitch, aNoCaretFlag, aNoDebugFlag) {
+ let editor = DebuggerView.editor;
+
+ function set() {
+ if (!aNoCaretFlag) {
+ editor.setCaretPosition(aLine - 1);
+ }
+ if (!aNoDebugFlag) {
+ editor.setDebugLocation(aLine - 1);
+ }
}
+
+ // Move the editor's caret to the proper url and line.
+ if (DebuggerView.Scripts.isSelected(aUrl)) {
+ return set();
+ }
+ if (!aNoSwitch && DebuggerView.Scripts.contains(aUrl)) {
+ DebuggerView.Scripts.selectScript(aUrl);
+ return set();
+ }
+ editor.setCaretPosition(-1);
+ editor.setDebugLocation(-1);
},
/**
* Inform the debugger client whether the debuggee should be paused whenever
* an exception is thrown.
*
* @param boolean aFlag
* The new value of the flag: true for pausing, false otherwise.
@@ -544,30 +580,19 @@ StackFrames.prototype = {
let frame = this.activeThread.cachedFrames[aDepth];
if (!frame) {
return;
}
let url = frame.where.url;
let line = frame.where.line;
- let editor = DebuggerView.editor;
// Move the editor's caret to the proper line.
- if (DebuggerView.Scripts.isSelected(url) && line) {
- editor.setCaretPosition(line - 1);
- editor.setDebugLocation(line - 1);
- }
- else if (DebuggerView.Scripts.contains(url)) {
- DebuggerView.Scripts.selectScript(url);
- editor.setCaretPosition(line - 1);
- }
- else {
- editor.setDebugLocation(-1);
- }
+ this.updateEditorToLocation(url, line);
// Start recording any added variables or properties in any scope.
DebuggerView.Properties.createHierarchyStore();
// Clear existing scopes and create each one dynamically.
DebuggerView.Properties.empty();
if (frame.environment) {
@@ -749,17 +774,17 @@ StackFrames.prototype = {
/**
* Adds the specified stack frame to the list.
*
* @param Debugger.Frame aFrame
* The new frame to add.
*/
_addFrame: function SF__addFrame(aFrame) {
let depth = aFrame.depth;
- let label = DebuggerController.SourceScripts._getScriptLabel(aFrame.where.url);
+ let label = DebuggerController.SourceScripts.getScriptLabel(aFrame.where.url);
let startText = this._getFrameTitle(aFrame);
let endText = label + ":" + aFrame.where.line;
let frame = DebuggerView.StackFrames.addFrame(depth, startText, endText);
if (frame) {
frame.debuggerFrame = aFrame;
}
@@ -876,39 +901,44 @@ SourceScripts.prototype = {
*/
_onNewScript: function SS__onNewScript(aNotification, aPacket) {
// Ignore scripts generated from 'clientEvaluate' packets.
if (aPacket.url == "debugger eval code") {
return;
}
this._addScript({ url: aPacket.url, startLine: aPacket.startLine }, true);
- // If there are any stored breakpoints for this script, display them again.
- for each (let bp in DebuggerController.Breakpoints.store) {
- if (bp.location.url == aPacket.url) {
- DebuggerController.Breakpoints.displayBreakpoint(bp.location);
+
+ // If there are any stored breakpoints for this script, display them again,
+ // both in the editor and the pane.
+ for each (let breakpoint in DebuggerController.Breakpoints.store) {
+ if (breakpoint.location.url == aPacket.url) {
+ DebuggerController.Breakpoints.displayBreakpoint(breakpoint);
}
}
},
/**
* Handler for the thread client's scriptsadded notification.
*/
_onScriptsAdded: function SS__onScriptsAdded() {
for each (let script in this.activeThread.cachedScripts) {
this._addScript(script, false);
}
DebuggerView.Scripts.commitScripts();
+ DebuggerController.Breakpoints.updatePaneBreakpoints();
},
/**
* Handler for the thread client's scriptscleared notification.
*/
_onScriptsCleared: function SS__onScriptsCleared() {
DebuggerView.Scripts.empty();
+ DebuggerView.Breakpoints.emptyText();
+ DebuggerView.editor.setText("");
},
/**
* Sets the proper editor mode (JS or HTML) according to the specified
* content type, or by determining the type from the URL.
*
* @param string aUrl
* The script URL.
@@ -961,17 +991,17 @@ SourceScripts.prototype = {
* The script URL.
* @param string aLabel [optional]
* The resulting label at each step.
* @param number aSeq [optional]
* The current iteration step.
* @return string
* The resulting label at the final step.
*/
- _trimURL: function SS__trimURL(aUrl, aLabel, aSeq) {
+ _trimUrl: function SS__trimUrl(aUrl, aLabel, aSeq) {
if (!(aUrl instanceof Ci.nsIURL)) {
try {
// Use an nsIURL to parse all the url path parts.
aUrl = Services.io.newURI(aUrl, null, null).QueryInterface(Ci.nsIURL);
} catch (e) {
// This doesn't look like a url, or nsIURL can't handle it.
return aUrl;
}
@@ -1006,65 +1036,65 @@ SourceScripts.prototype = {
return aLabel;
}
}
// Append the url query.
if (aSeq === 1) {
let query = aUrl.query;
if (query) {
- return this._trimURL(aUrl, aLabel + "?" + query, aSeq + 1);
+ return this._trimUrl(aUrl, aLabel + "?" + query, aSeq + 1);
}
aSeq++;
}
// Append the url reference.
if (aSeq === 2) {
let ref = aUrl.ref;
if (ref) {
- return this._trimURL(aUrl, aLabel + "#" + aUrl.ref, aSeq + 1);
+ return this._trimUrl(aUrl, aLabel + "#" + aUrl.ref, aSeq + 1);
}
aSeq++;
}
// Prepend the url directory.
if (aSeq === 3) {
let dir = aUrl.directory;
if (dir) {
- return this._trimURL(aUrl, dir.replace(/^\//, "") + aLabel, aSeq + 1);
+ return this._trimUrl(aUrl, dir.replace(/^\//, "") + aLabel, aSeq + 1);
}
aSeq++;
}
// Prepend the hostname and port number.
if (aSeq === 4) {
let host = aUrl.hostPort;
if (host) {
- return this._trimURL(aUrl, host + "/" + aLabel, aSeq + 1);
+ return this._trimUrl(aUrl, host + "/" + aLabel, aSeq + 1);
}
aSeq++;
}
// Use the whole url spec but ignoring the reference.
if (aSeq === 5) {
- return this._trimURL(aUrl, aUrl.specIgnoringRef, aSeq + 1);
+ return this._trimUrl(aUrl, aUrl.specIgnoringRef, aSeq + 1);
}
// Give up.
return aUrl.spec;
},
/**
* Gets a unique, simplified label from a script url.
*
* @param string aUrl
* The script url.
* @param string aHref
* The content location href to be used. If unspecified, it will
* default to the script url prepath.
* @return string
* The simplified label.
*/
- _getScriptLabel: function SS__getScriptLabel(aUrl, aHref) {
- return this._labelsCache[aUrl] || (this._labelsCache[aUrl] = this._trimURL(aUrl));
+ getScriptLabel: function SS_getScriptLabel(aUrl, aHref) {
+ return this._labelsCache[aUrl] || (this._labelsCache[aUrl] = this._trimUrl(aUrl));
},
/**
* Clears the labels cache, populated by SS_getScriptLabel.
* This should be done every time the content location changes.
*/
_clearLabelsCache: function SS__clearLabelsCache() {
this._labelsCache = {};
@@ -1075,17 +1105,17 @@ SourceScripts.prototype = {
*
* @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, aForceFlag) {
DebuggerView.Scripts.addScript(
- this._getScriptLabel(aScript.url), aScript, aForceFlag);
+ this.getScriptLabel(aScript.url), aScript, aForceFlag);
},
/**
* Load the editor with the script text if available, otherwise fire an event
* to load and display the script text.
*
* @param object aScript
* The script object coming from the active thread.
@@ -1228,16 +1258,33 @@ SourceScripts.prototype = {
script.text = aSourceText;
script.contentType = aContentType;
element.setUserData("sourceScript", script, null);
this.showScript(script, aOptions);
},
/**
+ * Gets the text in a source editor's specified line.
+ *
+ * @param number aLine [optional]
+ * The line to get the text from.
+ * If unspecified, it defaults to the current caret position line.
+ * @return string
+ * The specified line text
+ */
+ getLineText: function SS_getLineText(aLine) {
+ let editor = DebuggerView.editor;
+ let line = aLine || editor.getCaretPosition().line;
+ let start = editor.getLineStart(line);
+ let end = editor.getLineEnd(line);
+ return editor.getText(start, end);
+ },
+
+ /**
* Log an error message in the error console when a script fails to load.
*
* @param string aUrl
* The URL of the source script.
* @param string aStatus
* The failure status code.
*/
_logError: function SS__logError(aUrl, aStatus) {
@@ -1390,102 +1437,141 @@ Breakpoints.prototype = {
if (breakpoint.location.url == url) {
this.editor.addBreakpoint(breakpoint.location.line - 1);
}
}
this._skipEditorBreakpointChange = false;
},
/**
+ * Update the breakpoints in the pane view. This function is invoked when the
+ * scripts are added (typically after a page navigation).
+ */
+ updatePaneBreakpoints: function BP_updatePaneBreakpoints() {
+ let url = DebuggerView.Scripts.selected;
+ if (!url) {
+ return;
+ }
+
+ this._skipEditorBreakpointChange = true;
+ for each (let breakpoint in this.store) {
+ if (DebuggerView.Scripts.contains(breakpoint.location.url)) {
+ this.displayBreakpoint(breakpoint, true);
+ }
+ }
+ this._skipEditorBreakpointChange = false;
+ },
+
+ /**
* Add a breakpoint.
*
* @param object aLocation
* The location where you want the breakpoint. This object must have
* two properties:
* - url - the URL of the script.
* - line - the line number (starting from 1).
* @param function [aCallback]
* Optional function to invoke once the breakpoint is added. The
* callback is invoked with two arguments:
* - aBreakpointClient - the BreakpointActor client object, if the
* breakpoint has been added successfully.
* - aResponseError - if there was any error.
* @param boolean [aNoEditorUpdate=false]
* Tells if you want to skip editor updates. Typically the editor is
* updated to visually indicate that a breakpoint has been added.
+ * @param boolean [aNoPaneUpdate=false]
+ * Tells if you want to skip any breakpoint pane updates.
*/
addBreakpoint:
- function BP_addBreakpoint(aLocation, aCallback, aNoEditorUpdate) {
+ function BP_addBreakpoint(aLocation, aCallback, aNoEditorUpdate, aNoPaneUpdate) {
let breakpoint = this.getBreakpoint(aLocation.url, aLocation.line);
if (breakpoint) {
aCallback && aCallback(breakpoint);
return;
}
this.activeThread.setBreakpoint(aLocation, function(aResponse, aBpClient) {
this.store[aBpClient.actor] = aBpClient;
- this.displayBreakpoint(aLocation, aNoEditorUpdate);
+ this.displayBreakpoint(aBpClient, aNoEditorUpdate, aNoPaneUpdate);
aCallback && aCallback(aBpClient, aResponse.error);
}.bind(this));
},
/**
* Update the editor to display the specified breakpoint in the gutter.
*
- * @param object aLocation
- * The location where you want the breakpoint. This object must have
- * two properties:
- * - url - the URL of the script.
- * - line - the line number (starting from 1).
+ * @param object aBreakpoint
+ * The breakpoint you want to display.
* @param boolean [aNoEditorUpdate=false]
* Tells if you want to skip editor updates. Typically the editor is
* updated to visually indicate that a breakpoint has been added.
+ * @param boolean [aNoPaneUpdate=false]
+ * Tells if you want to skip any breakpoint pane updates.
*/
- displayBreakpoint: function BP_displayBreakpoint(aLocation, aNoEditorUpdate) {
+ displayBreakpoint:
+ function BP_displayBreakpoint(aBreakpoint, aNoEditorUpdate, aNoPaneUpdate) {
if (!aNoEditorUpdate) {
let url = DebuggerView.Scripts.selected;
- if (url == aLocation.url) {
+ if (url == aBreakpoint.location.url) {
this._skipEditorBreakpointChange = true;
- this.editor.addBreakpoint(aLocation.line - 1);
+ this.editor.addBreakpoint(aBreakpoint.location.line - 1);
this._skipEditorBreakpointChange = false;
}
}
+ if (!aNoPaneUpdate) {
+ let { url: url, line: line } = aBreakpoint.location;
+
+ if (!aBreakpoint.lineText || !aBreakpoint.lineInfo) {
+ let scripts = DebuggerController.SourceScripts;
+ aBreakpoint.lineText = scripts.getLineText(line - 1);
+ aBreakpoint.lineInfo = scripts.getScriptLabel(url) + ":" + line;
+ }
+ DebuggerView.Breakpoints.addBreakpoint(
+ aBreakpoint.actor,
+ aBreakpoint.lineInfo,
+ aBreakpoint.lineText, url, line);
+ }
},
/**
* Remove a breakpoint.
*
* @param object aBreakpoint
* The breakpoint you want to remove.
* @param function [aCallback]
* Optional function to invoke once the breakpoint is removed. The
* callback is invoked with one argument: the breakpoint location
* object which holds the url and line properties.
* @param boolean [aNoEditorUpdate=false]
* Tells if you want to skip editor updates. Typically the editor is
* updated to visually indicate that a breakpoint has been removed.
+ * @param boolean [aNoPaneUpdate=false]
+ * Tells if you want to skip any breakpoint pane updates.
*/
removeBreakpoint:
- function BP_removeBreakpoint(aBreakpoint, aCallback, aNoEditorUpdate) {
+ function BP_removeBreakpoint(aBreakpoint, aCallback, aNoEditorUpdate, aNoPaneUpdate) {
if (!(aBreakpoint.actor in this.store)) {
aCallback && aCallback(aBreakpoint.location);
return;
}
aBreakpoint.remove(function() {
delete this.store[aBreakpoint.actor];
if (!aNoEditorUpdate) {
let url = DebuggerView.Scripts.selected;
if (url == aBreakpoint.location.url) {
this._skipEditorBreakpointChange = true;
this.editor.removeBreakpoint(aBreakpoint.location.line - 1);
this._skipEditorBreakpointChange = false;
}
}
+ if (!aNoPaneUpdate) {
+ DebuggerView.Breakpoints.removeBreakpoint(aBreakpoint.actor);
+ }
aCallback && aCallback(aBreakpoint.location);
}.bind(this));
},
/**
* Get the breakpoint object at the given location.
*
--- a/browser/devtools/debugger/debugger-view.js
+++ b/browser/devtools/debugger/debugger-view.js
@@ -1,33 +1,34 @@
/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const PROPERTY_VIEW_FLASH_DURATION = 400; // ms
+const BREAKPOINT_LINE_TOOLTIP_MAX_SIZE = 1000;
/**
* Object mediating visual changes and event listeners between the debugger and
* the html view.
*/
let DebuggerView = {
/**
* An instance of SourceEditor.
*/
editor: null,
/**
* Initializes UI properties for all the displayed panes.
*/
initializePanes: function DV_initializePanes() {
- let stackframes = document.getElementById("stackframes");
+ let stackframes = document.getElementById("stackframes+breakpoints");
stackframes.setAttribute("width", Prefs.stackframesWidth);
let variables = document.getElementById("variables");
variables.setAttribute("width", Prefs.variablesWidth);
},
/**
* Initializes the SourceEditor instance.
@@ -46,17 +47,17 @@ let DebuggerView = {
this.editor = new SourceEditor();
this.editor.init(placeholder, config, this._onEditorLoad.bind(this));
},
/**
* Removes the displayed panes and saves any necessary state.
*/
destroyPanes: function DV_destroyPanes() {
- let stackframes = document.getElementById("stackframes");
+ let stackframes = document.getElementById("stackframes+breakpoints");
Prefs.stackframesWidth = stackframes.getAttribute("width");
let variables = document.getElementById("variables");
Prefs.variablesWidth = variables.getAttribute("width");
},
/**
* Removes the SourceEditor instance and added breakpoints.
@@ -84,53 +85,55 @@ let DebuggerView = {
};
/**
* A simple way of displaying a "Connect to..." prompt.
*/
function RemoteDebuggerPrompt() {
/**
- * The remote uri the user wants to connect to.
+ * The remote host and port the user wants to connect to.
*/
- this.uri = null;
+ this.remote = {};
}
RemoteDebuggerPrompt.prototype = {
/**
* Shows the prompt and sets the uri using the user input.
*
* @param boolean aIsReconnectingFlag
* True to show the reconnect message instead.
*/
show: function RDP_show(aIsReconnectingFlag) {
let check = { value: Prefs.remoteAutoConnect };
- let input = { value: "http://" + Prefs.remoteHost +
- ":" + Prefs.remotePort + "/" };
+ let input = { value: Prefs.remoteHost + ":" + Prefs.remotePort };
+ let parts;
while (true) {
let result = Services.prompt.prompt(null,
L10N.getStr("remoteDebuggerPromptTitle"),
L10N.getStr(aIsReconnectingFlag
? "remoteDebuggerReconnectMessage"
: "remoteDebuggerPromptMessage"), input,
L10N.getStr("remoteDebuggerPromptCheck"), check);
Prefs.remoteAutoConnect = check.value;
- try {
- let uri = Services.io.newURI(input.value, null, null);
- let url = uri.QueryInterface(Ci.nsIURL);
+ if (!result) {
+ return false;
+ }
+ if ((parts = input.value.split(":")).length === 2) {
+ let [host, port] = parts;
- // If a url could be successfully retrieved, then the uri is correct.
- this.uri = uri;
- return result;
+ if (host.length && port.length) {
+ this.remote = { host: host, port: port };
+ return true;
+ }
}
- catch(e) { }
}
}
};
/**
* Functions handling the scripts UI.
*/
function ScriptsView() {
@@ -140,16 +143,18 @@ function ScriptsView() {
}
ScriptsView.prototype = {
/**
* Removes all elements from the scripts container, leaving it empty.
*/
empty: function DVS_empty() {
+ this._scripts.selectedIndex = -1;
+
while (this._scripts.firstChild) {
this._scripts.removeChild(this._scripts.firstChild);
}
},
/**
* Removes the input in the searchbox and unhides all the scripts.
*/
@@ -410,17 +415,22 @@ ScriptsView.prototype = {
return [file, line, token];
},
/**
* The click listener for the scripts container.
*/
_onScriptsChange: function DVS__onScriptsChange() {
- let script = this._scripts.selectedItem.getUserData("sourceScript");
+ let selectedItem = this._scripts.selectedItem;
+ if (!selectedItem) {
+ return;
+ }
+
+ let script = selectedItem.getUserData("sourceScript");
this._preferredScript = script;
DebuggerController.SourceScripts.showScript(script);
},
/**
* The search listener for the scripts search box.
*/
_onScriptsSearch: function DVS__onScriptsSearch(e) {
@@ -454,17 +464,17 @@ ScriptsView.prototype = {
else {
item.hidden = true;
}
}
}
if (line > -1) {
editor.setCaretPosition(line - 1);
}
- if (token) {
+ if (token.length) {
let offset = editor.find(token, { ignoreCase: true });
if (offset > -1) {
editor.setSelection(offset, offset + token.length)
}
}
},
/**
@@ -473,16 +483,20 @@ ScriptsView.prototype = {
_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 token = this._getSearchboxInfo()[2];
+ if (!token.length) {
+ return;
+ }
+
let editor = DebuggerView.editor;
let offset = editor.findNext(true);
if (offset > -1) {
editor.setSelection(offset, offset + token.length)
}
}
},
@@ -796,16 +810,17 @@ StackFramesView.prototype = {
stepOver.addEventListener("click", this._onStepOverClick, false);
stepIn.addEventListener("click", this._onStepInClick, false);
stepOut.addEventListener("click", this._onStepOutClick, false);
frames.addEventListener("click", this._onFramesClick, false);
frames.addEventListener("scroll", this._onFramesScroll, false);
window.addEventListener("resize", this._onFramesScroll, false);
this._frames = frames;
+ this.emptyText();
},
/**
* Destruction function, called when the debugger is shut down.
*/
destroy: function DVF_destroy() {
let close = document.getElementById("close");
let pauseOnExceptions = document.getElementById("pause-exceptions");
@@ -827,25 +842,565 @@ StackFramesView.prototype = {
frames.removeEventListener("scroll", this._onFramesScroll, false);
window.removeEventListener("resize", this._onFramesScroll, false);
this._frames = null;
}
};
/**
+ * Functions handling the breakpoints view.
+ */
+function BreakpointsView() {
+ this._onBreakpointClick = this._onBreakpointClick.bind(this);
+ this._onBreakpointCheckboxChange = this._onBreakpointCheckboxChange.bind(this);
+}
+
+BreakpointsView.prototype = {
+
+ /**
+ * Removes all elements from the breakpoints container, leaving it empty.
+ */
+ empty: function DVB_empty() {
+ let firstChild;
+ while (firstChild = this._breakpoints.firstChild) {
+ this._destroyContextMenu(firstChild);
+ this._breakpoints.removeChild(firstChild);
+ }
+ },
+
+ /**
+ * Removes all elements from the breakpoints container, and adds a child node
+ * with an empty text note attached.
+ */
+ emptyText: function DVB_emptyText() {
+ // Make sure the container is empty first.
+ this.empty();
+
+ let item = document.createElement("label");
+
+ // The empty node should look grayed out to avoid confusion.
+ item.className = "list-item empty";
+ item.setAttribute("value", L10N.getStr("emptyBreakpointsText"));
+
+ this._breakpoints.appendChild(item);
+ },
+
+ /**
+ * Checks whether the breakpoint with the specified script URL and line is
+ * among the breakpoints known to the debugger and shown in the list, and
+ * returns the matched result or null if nothing is found.
+ *
+ * @param string aUrl
+ * The original breakpoint script url.
+ * @param number aLine
+ * The original breakpoint script line.
+ * @return object | null
+ * The queried breakpoint
+ */
+ getBreakpoint: function DVB_getBreakpoint(aUrl, aLine) {
+ return this._breakpoints.getElementsByAttribute("location", aUrl + ":" + aLine)[0];
+ },
+
+ /**
+ * Removes a breakpoint only from the breakpoints container.
+ * This doesn't remove the breakpoint from the DebuggerController!
+ *
+ * @param string aId
+ * A breakpoint identifier specified by the debugger.
+ */
+ removeBreakpoint: function DVB_removeBreakpoint(aId) {
+ let breakpoint = document.getElementById("breakpoint-" + aId);
+
+ // Make sure we have something to remove.
+ if (!breakpoint) {
+ return;
+ }
+ this._destroyContextMenu(breakpoint);
+ this._breakpoints.removeChild(breakpoint);
+
+ if (!this.count) {
+ this.emptyText();
+ }
+ },
+
+ /**
+ * Adds a breakpoint to the breakpoints container.
+ * If the breakpoint already exists (was previously added), null is returned.
+ * If it's already added but disabled, it will be enabled and null is returned.
+ * Otherwise, the newly created element is returned.
+ *
+ * @param string aId
+ * A breakpoint identifier specified by the debugger.
+ * @param string aLineInfo
+ * The script line information to be displayed in the list.
+ * @param string aLineText
+ * The script line text to be displayed in the list.
+ * @param string aUrl
+ * The original breakpoint script url.
+ * @param number aLine
+ * The original breakpoint script line.
+ * @return object
+ * The newly created html node representing the added breakpoint.
+ */
+ addBreakpoint: function DVB_addBreakpoint(aId, aLineInfo, aLineText, aUrl, aLine) {
+ // Make sure we don't duplicate anything.
+ if (document.getElementById("breakpoint-" + aId)) {
+ return null;
+ }
+ // Remove the empty list text if it was there.
+ if (!this.count) {
+ this.empty();
+ }
+
+ // If the breakpoint was already added but disabled, enable it now.
+ let breakpoint = this.getBreakpoint(aUrl, aLine);
+ if (breakpoint) {
+ breakpoint.id = "breakpoint-" + aId;
+ breakpoint.breakpointActor = aId;
+ breakpoint.getElementsByTagName("checkbox")[0].setAttribute("checked", "true");
+ return;
+ }
+
+ breakpoint = document.createElement("box");
+ let bkpCheckbox = document.createElement("checkbox");
+ let bkpLineInfo = document.createElement("label");
+ let bkpLineText = document.createElement("label");
+
+ // Create a list item to be added to the stackframes container.
+ breakpoint.id = "breakpoint-" + aId;
+ breakpoint.className = "dbg-breakpoint list-item";
+ breakpoint.setAttribute("location", aUrl + ":" + aLine);
+ breakpoint.breakpointUrl = aUrl;
+ breakpoint.breakpointLine = aLine;
+ breakpoint.breakpointActor = aId;
+
+ aLineInfo = aLineInfo.trim();
+ aLineText = aLineText.trim();
+
+ // A checkbox specifies if the breakpoint is enabled or not.
+ bkpCheckbox.setAttribute("checked", "true");
+ bkpCheckbox.addEventListener("click", this._onBreakpointCheckboxChange, false);
+
+ // This list should display the line info and text for the breakpoint.
+ bkpLineInfo.className = "dbg-breakpoint-info plain";
+ bkpLineText.className = "dbg-breakpoint-text plain";
+ bkpLineInfo.setAttribute("value", aLineInfo);
+ bkpLineText.setAttribute("value", aLineText);
+ bkpLineInfo.setAttribute("crop", "end");
+ bkpLineText.setAttribute("crop", "end");
+ bkpLineText.setAttribute("tooltiptext", aLineText.substr(0, BREAKPOINT_LINE_TOOLTIP_MAX_SIZE));
+
+ // Create a context menu for the breakpoint.
+ let menupopupId = this._createContextMenu(breakpoint);
+ breakpoint.setAttribute("contextmenu", menupopupId);
+
+ let state = document.createElement("vbox");
+ state.className = "state";
+ state.appendChild(bkpCheckbox);
+
+ let content = document.createElement("vbox");
+ content.className = "content";
+ content.setAttribute("flex", "1");
+ content.appendChild(bkpLineInfo);
+ content.appendChild(bkpLineText);
+
+ breakpoint.appendChild(state);
+ breakpoint.appendChild(content);
+
+ this._breakpoints.appendChild(breakpoint);
+
+ // Return the element for later use if necessary.
+ return breakpoint;
+ },
+
+ /**
+ * Enables a breakpoint.
+ *
+ * @param object aBreakpoint
+ * An element representing a breakpoint.
+ * @param function aCallback
+ * Optional function to invoke once the breakpoint is enabled.
+ * @param boolean aNoCheckboxUpdate
+ * Pass true to not update the checkbox checked state.
+ * This is usually necessary when the checked state will be updated
+ * automatically (e.g: on a checkbox click).
+ */
+ enableBreakpoint:
+ function DVB_enableBreakpoint(aTarget, aCallback, aNoCheckboxUpdate) {
+ let { breakpointUrl: url, breakpointLine: line } = aTarget;
+ let breakpoint = DebuggerController.Breakpoints.getBreakpoint(url, line)
+
+ if (!breakpoint) {
+ if (!aNoCheckboxUpdate) {
+ aTarget.getElementsByTagName("checkbox")[0].setAttribute("checked", "true");
+ }
+ DebuggerController.Breakpoints.
+ addBreakpoint({ url: url, line: line }, aCallback);
+
+ return true;
+ }
+ return false;
+ },
+
+ /**
+ * Disables a breakpoint.
+ *
+ * @param object aTarget
+ * An element representing a breakpoint.
+ * @param function aCallback
+ * Optional function to invoke once the breakpoint is disabled.
+ * @param boolean aNoCheckboxUpdate
+ * Pass true to not update the checkbox checked state.
+ * This is usually necessary when the checked state will be updated
+ * automatically (e.g: on a checkbox click).
+ */
+ disableBreakpoint:
+ function DVB_disableBreakpoint(aTarget, aCallback, aNoCheckboxUpdate) {
+ let { breakpointUrl: url, breakpointLine: line } = aTarget;
+ let breakpoint = DebuggerController.Breakpoints.getBreakpoint(url, line)
+
+ if (breakpoint) {
+ if (!aNoCheckboxUpdate) {
+ aTarget.getElementsByTagName("checkbox")[0].removeAttribute("checked");
+ }
+ DebuggerController.Breakpoints.
+ removeBreakpoint(breakpoint, aCallback, false, true);
+
+ return true;
+ }
+ return false;
+ },
+
+ /**
+ * Gets the current number of added breakpoints.
+ */
+ get count() {
+ return this._breakpoints.getElementsByClassName("dbg-breakpoint").length;
+ },
+
+ /**
+ * Iterates through all the added breakpoints.
+ *
+ * @param function aCallback
+ * Function called for each element.
+ */
+ _iterate: function DVB_iterate(aCallback) {
+ Array.forEach(Array.slice(this._breakpoints.childNodes), aCallback);
+ },
+
+ /**
+ * Gets the real breakpoint target when an event is handled.
+ * @return object
+ */
+ _getBreakpointTarget: function DVB__getBreakpointTarget(aEvent) {
+ let target = aEvent.target;
+
+ while (target) {
+ if (target.breakpointActor) {
+ return target;
+ }
+ target = target.parentNode;
+ }
+ },
+
+ /**
+ * Listener handling the breakpoint click event.
+ */
+ _onBreakpointClick: function DVB__onBreakpointClick(aEvent) {
+ let target = this._getBreakpointTarget(aEvent);
+ let { breakpointUrl: url, breakpointLine: line } = target;
+
+ DebuggerController.StackFrames.updateEditorToLocation(url, line, 0, 0, 1);
+ },
+
+ /**
+ * Listener handling the breakpoint checkbox change event.
+ */
+ _onBreakpointCheckboxChange: function DVB__onBreakpointCheckboxChange(aEvent) {
+ aEvent.stopPropagation();
+
+ let target = this._getBreakpointTarget(aEvent);
+ let { breakpointUrl: url, breakpointLine: line } = target;
+
+ if (aEvent.target.getAttribute("checked") === "true") {
+ this.disableBreakpoint(target, null, true);
+ } else {
+ this.enableBreakpoint(target, null, true);
+ }
+ },
+
+ /**
+ * Listener handling the "enableSelf" menuitem command.
+ *
+ * @param object aTarget
+ * The corresponding breakpoint element.
+ */
+ _onEnableSelf: function DVB__onEnableSelf(aTarget) {
+ if (!aTarget) {
+ return;
+ }
+ if (this.enableBreakpoint(aTarget)) {
+ aTarget.enableSelf.menuitem.setAttribute("hidden", "true");
+ aTarget.disableSelf.menuitem.removeAttribute("hidden");
+ }
+ },
+
+ /**
+ * Listener handling the "disableSelf" menuitem command.
+ *
+ * @param object aTarget
+ * The corresponding breakpoint element.
+ */
+ _onDisableSelf: function DVB__onDisableSelf(aTarget) {
+ if (!aTarget) {
+ return;
+ }
+ if (this.disableBreakpoint(aTarget)) {
+ aTarget.enableSelf.menuitem.removeAttribute("hidden");
+ aTarget.disableSelf.menuitem.setAttribute("hidden", "true");
+ }
+ },
+
+ /**
+ * Listener handling the "deleteSelf" menuitem command.
+ *
+ * @param object aTarget
+ * The corresponding breakpoint element.
+ */
+ _onDeleteSelf: function DVB__onDeleteSelf(aTarget) {
+ let { breakpointUrl: url, breakpointLine: line } = aTarget;
+ let breakpoint = DebuggerController.Breakpoints.getBreakpoint(url, line)
+
+ if (aTarget) {
+ this.removeBreakpoint(aTarget.breakpointActor);
+ }
+ if (breakpoint) {
+ DebuggerController.Breakpoints.removeBreakpoint(breakpoint);
+ }
+ },
+
+ /**
+ * Listener handling the "enableOthers" menuitem command.
+ *
+ * @param object aTarget
+ * The corresponding breakpoint element.
+ */
+ _onEnableOthers: function DVB__onEnableOthers(aTarget) {
+ this._iterate(function(element) {
+ if (element !== aTarget) {
+ this._onEnableSelf(element);
+ }
+ }.bind(this));
+ },
+
+ /**
+ * Listener handling the "disableOthers" menuitem command.
+ *
+ * @param object aTarget
+ * The corresponding breakpoint element.
+ */
+ _onDisableOthers: function DVB__onDisableOthers(aTarget) {
+ this._iterate(function(element) {
+ if (element !== aTarget) {
+ this._onDisableSelf(element);
+ }
+ }.bind(this));
+ },
+
+ /**
+ * Listener handling the "deleteOthers" menuitem command.
+ *
+ * @param object aTarget
+ * The corresponding breakpoint element.
+ */
+ _onDeleteOthers: function DVB__onDeleteOthers(aTarget) {
+ this._iterate(function(element) {
+ if (element !== aTarget) {
+ this._onDeleteSelf(element);
+ }
+ }.bind(this));
+ },
+
+ /**
+ * Listener handling the "disableAll" menuitem command.
+ *
+ * @param object aTarget
+ * The corresponding breakpoint element.
+ */
+ _onEnableAll: function DVB__onEnableAll(aTarget) {
+ this._onEnableOthers(aTarget);
+ this._onEnableSelf(aTarget);
+ },
+
+ /**
+ * Listener handling the "disableAll" menuitem command.
+ *
+ * @param object aTarget
+ * The corresponding breakpoint element.
+ */
+ _onDisableAll: function DVB__onDisableAll(aTarget) {
+ this._onDisableOthers(aTarget);
+ this._onDisableSelf(aTarget);
+ },
+
+ /**
+ * Listener handling the "deleteAll" menuitem command.
+ *
+ * @param object aTarget
+ * The corresponding breakpoint element.
+ */
+ _onDeleteAll: function DVB__onDeleteAll(aTarget) {
+ this._onDeleteOthers(aTarget);
+ this._onDeleteSelf(aTarget);
+ },
+
+ /**
+ * The cached breakpoints container.
+ */
+ _breakpoints: null,
+
+ /**
+ * Creates a breakpoint context menu.
+ *
+ * @param object aBreakpoint
+ * An element representing a breakpoint.
+ * @return string
+ * The popup id.
+ */
+ _createContextMenu: function DVB_createContextMenu(aBreakpoint) {
+ let commandsetId = "breakpointMenuCommands-" + aBreakpoint.id;
+ let menupopupId = "breakpointContextMenu-" + aBreakpoint.id;
+
+ let commandsset = document.createElement("commandsset");
+ commandsset.setAttribute("id", commandsetId);
+
+ let menupopup = document.createElement("menupopup");
+ menupopup.setAttribute("id", menupopupId);
+
+ /**
+ * Creates a menu item specified by a name with the appropriate attributes
+ * (label and command handler).
+ *
+ * @param string aName
+ * A global identifier for the menu item.
+ * @param boolean aHiddenFlag
+ * True if this menuitem should be hidden.
+ */
+ function createMenuItem(aName, aHiddenFlag) {
+ let menuitem = document.createElement("menuitem");
+ let command = document.createElement("command");
+
+ let func = this["_on" + aName.charAt(0).toUpperCase() + aName.slice(1)];
+ let label = L10N.getStr("breakpointMenuItem." + aName);
+
+ let prefix = "bp-cMenu-";
+ let commandId = prefix + aName + "-" + aBreakpoint.id + "-command";
+ let menuitemId = prefix + aName + "-" + aBreakpoint.id + "-menuitem";
+
+ command.setAttribute("id", commandId);
+ command.setAttribute("label", label);
+ command.addEventListener("command", func.bind(this, aBreakpoint), true);
+
+ menuitem.setAttribute("id", menuitemId);
+ menuitem.setAttribute("command", commandId);
+ menuitem.setAttribute("hidden", aHiddenFlag);
+
+ commandsset.appendChild(command);
+ menupopup.appendChild(menuitem);
+
+ aBreakpoint[aName] = {
+ menuitem: menuitem,
+ command: command
+ };
+ }
+
+ /**
+ * Creates a simple menu separator element and appends it to the current
+ * menupopup hierarchy.
+ */
+ function createMenuSeparator() {
+ let menuseparator = document.createElement("menuseparator");
+ menupopup.appendChild(menuseparator);
+ }
+
+ createMenuItem.call(this, "enableSelf", true);
+ createMenuItem.call(this, "disableSelf");
+ createMenuItem.call(this, "deleteSelf");
+ createMenuSeparator();
+ createMenuItem.call(this, "enableOthers");
+ createMenuItem.call(this, "disableOthers");
+ createMenuItem.call(this, "deleteOthers");
+ createMenuSeparator();
+ createMenuItem.call(this, "enableAll");
+ createMenuItem.call(this, "disableAll");
+ createMenuSeparator();
+ createMenuItem.call(this, "deleteAll");
+
+ let popupset = document.getElementById("debugger-popups");
+ popupset.appendChild(menupopup);
+ document.documentElement.appendChild(commandsset);
+
+ return menupopupId;
+ },
+
+ /**
+ * Destroys a breakpoint context menu.
+ *
+ * @param object aBreakpoint
+ * An element representing a breakpoint.
+ */
+ _destroyContextMenu: function DVB__destroyContextMenu(aBreakpoint) {
+ let commandsetId = "breakpointMenuCommands-" + aBreakpoint.id;
+ let menupopupId = "breakpointContextMenu-" + aBreakpoint.id;
+
+ let commandset = document.getElementById(commandsetId);
+ let menupopup = document.getElementById(menupopupId);
+
+ if (commandset) {
+ commandset.parentNode.removeChild(commandset);
+ }
+ if (menupopup) {
+ menupopup.parentNode.removeChild(menupopup);
+ }
+ },
+
+ /**
+ * Initialization function, called when the debugger is initialized.
+ */
+ initialize: function DVB_initialize() {
+ let breakpoints = document.getElementById("breakpoints");
+ breakpoints.addEventListener("click", this._onBreakpointClick, false);
+
+ this._breakpoints = breakpoints;
+ this.emptyText();
+ },
+
+ /**
+ * Destruction function, called when the debugger is shut down.
+ */
+ destroy: function DVB_destroy() {
+ let breakpoints = this._breakpoints;
+ breakpoints.removeEventListener("click", this._onBreakpointClick, false);
+
+ this._breakpoints = null;
+ }
+};
+
+/**
* Functions handling the properties view.
*/
function PropertiesView() {
this.addScope = this._addScope.bind(this);
this._addVar = this._addVar.bind(this);
this._addProperties = this._addProperties.bind(this);
}
PropertiesView.prototype = {
+
/**
* A monotonically-increasing counter, that guarantees the uniqueness of scope
* IDs.
*/
_idCount: 1,
/**
* Adds a scope to contain any inspected variables.
@@ -1912,19 +2467,20 @@ PropertiesView.prototype = {
* The cached variable properties container.
*/
_vars: null,
/**
* Initialization function, called when the debugger is initialized.
*/
initialize: function DVP_initialize() {
- this.createHierarchyStore();
+ this._vars = document.getElementById("variables");
- this._vars = document.getElementById("variables");
+ this.emptyText();
+ this.createHierarchyStore();
},
/**
* Destruction function, called when the debugger is shut down.
*/
destroy: function DVP_destroy() {
this._currHierarchy = null;
this._prevHierarchy = null;
@@ -1932,16 +2488,17 @@ PropertiesView.prototype = {
}
};
/**
* Preliminary setup for the DebuggerView object.
*/
DebuggerView.Scripts = new ScriptsView();
DebuggerView.StackFrames = new StackFramesView();
+DebuggerView.Breakpoints = new BreakpointsView();
DebuggerView.Properties = new PropertiesView();
/**
* Export the source editor to the global scope for easier access in tests.
*/
Object.defineProperty(window, "editor", {
get: function() { return DebuggerView.editor; }
});
--- a/browser/devtools/debugger/debugger.css
+++ b/browser/devtools/debugger/debugger.css
@@ -8,16 +8,30 @@
* Stack frames
*/
#stackframes {
overflow: auto;
}
/**
+ * Breakpoints view
+ */
+
+#breakpoints {
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.dbg-breakpoint > .state,
+.dbg-breakpoint > .content {
+ overflow: hidden;
+}
+
+/**
* Properties elements
*/
#variables {
overflow: auto;
}
/**
--- a/browser/devtools/debugger/debugger.xul
+++ b/browser/devtools/debugger/debugger.xul
@@ -75,16 +75,20 @@
<spacer flex="1"/>
#ifndef XP_MACOSX
<toolbarbutton id="close"
tooltiptext="&debuggerUI.closeButton.tooltip;"
class="devtools-closebutton"/>
#endif
</toolbar>
<hbox id="dbg-content" flex="1">
- <vbox id="stackframes"/>
- <splitter id="stack-script-splitter" class="devtools-side-splitter"/>
+ <vbox id="stackframes+breakpoints">
+ <vbox id="stackframes" flex="1"/>
+ <splitter class="devtools-horizontal-splitter"/>
+ <vbox id="breakpoints"/>
+ </vbox>
+ <splitter class="devtools-side-splitter"/>
<vbox id="editor" flex="1"/>
- <splitter id="script-properties-splitter" class="devtools-side-splitter"/>
+ <splitter class="devtools-side-splitter"/>
<vbox id="variables"/>
</hbox>
</vbox>
</window>
--- a/browser/devtools/debugger/test/Makefile.in
+++ b/browser/devtools/debugger/test/Makefile.in
@@ -37,32 +37,35 @@ MOCHITEST_BROWSER_TESTS = \
browser_dbg_panesize.js \
browser_dbg_panesize-inner.js \
browser_dbg_stack-01.js \
browser_dbg_stack-02.js \
browser_dbg_stack-03.js \
browser_dbg_stack-04.js \
browser_dbg_stack-05.js \
browser_dbg_location-changes.js \
+ browser_dbg_location-changes-blank.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) \
browser_dbg_clean-exit.js \
browser_dbg_bug723069_editor-breakpoints.js \
+ browser_dbg_bug723071_editor-breakpoints-pane.js \
browser_dbg_bug731394_editor-contextmenu.js \
browser_dbg_displayName.js \
browser_dbg_iframes.js \
browser_dbg_pause-exceptions.js \
browser_dbg_multiple-windows.js \
browser_dbg_menustatus.js \
browser_dbg_bfcache.js \
+ browser_dbg_breakpoint-new-script.js \
head.js \
$(NULL)
MOCHITEST_BROWSER_PAGES = \
browser_dbg_tab1.html \
browser_dbg_tab2.html \
browser_dbg_debuggerstatement.html \
browser_dbg_stack.html \
@@ -71,13 +74,14 @@ MOCHITEST_BROWSER_PAGES = \
test-script-switching-02.js \
browser_dbg_frame-parameters.html \
browser_dbg_update-editor-mode.html \
test-editor-mode \
browser_dbg_displayName.html \
browser_dbg_iframes.html \
browser_dbg_with-frame.html \
browser_dbg_pause-exceptions.html \
+ browser_dbg_breakpoint-new-script.html \
$(NULL)
MOCHITEST_BROWSER_FILES_PARTS = MOCHITEST_BROWSER_TESTS MOCHITEST_BROWSER_PAGES
include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_breakpoint-new-script.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script type="text/javascript">
+function runDebuggerStatement() {
+ debugger;
+}
+function myFunction() {
+ var a = 1;
+ debugger;
+}
+</script>
+</head>
+<body>
+
+<button type="button" onclick="myFunction()">Run</button>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_breakpoint-new-script.js
@@ -0,0 +1,94 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Bug 771452: make sure that setting a breakpoint in an inline script doesn't
+// add it twice.
+
+const TAB_URL = EXAMPLE_URL + "browser_dbg_breakpoint-new-script.html";
+
+var gPane = null;
+var gTab = null;
+var gDebugger = null;
+var gDebuggee = null;
+
+function test()
+{
+ debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
+ gTab = aTab;
+ gPane = aPane;
+ gDebugger = gPane.contentWindow;
+ gDebuggee = aDebuggee;
+
+ testAddBreakpoint();
+ });
+}
+
+function testAddBreakpoint()
+{
+ gDebugger.addEventListener("Debugger:FetchedVariables", function test() {
+ gDebugger.removeEventListener("Debugger:FetchedVariables", test, false);
+ executeSoon(function() {
+ var frames = gDebugger.DebuggerView.StackFrames._frames;
+
+ is(gDebugger.DebuggerController.activeThread.state, "paused",
+ "The debugger statement was reached.");
+
+ is(frames.querySelectorAll(".dbg-stackframe").length, 1,
+ "Should have one frame.");
+
+ let location = { url: TAB_URL, line: 9 };
+ gPane.addBreakpoint(location, function (aResponse, bpClient) {
+ testResume();
+ });
+ });
+ }, false);
+
+ gDebuggee.runDebuggerStatement();
+}
+
+function testResume()
+{
+ gDebugger.DebuggerController.activeThread.addOneTimeListener("resumed", function test() {
+ gDebugger.DebuggerController.activeThread.addOneTimeListener("paused", function test() {
+ executeSoon(testBreakpointHit);
+ }, false);
+
+ EventUtils.sendMouseEvent({ type: "click" },
+ content.document.querySelector("button"),
+ content.window);
+
+ });
+
+ gDebugger.DebuggerController.activeThread.resume();
+}
+
+function testBreakpointHit()
+{
+ var frames = gDebugger.DebuggerView.StackFrames._frames;
+
+ is(gDebugger.DebuggerController.activeThread.state, "paused",
+ "The breakpoint was hit.");
+
+ resumeAndFinish();
+}
+
+function resumeAndFinish() {
+ let thread = gDebugger.DebuggerController.activeThread;
+ thread.addOneTimeListener("paused", function test(aEvent, aPacket) {
+ is(aPacket.why.type, "debuggerStatement", "Execution has advanced to the next line.");
+ isnot(aPacket.why.type, "breakpoint", "No ghost breakpoint was hit.");
+
+ closeDebuggerAndFinish();
+ });
+
+ thread.resume();
+}
+
+registerCleanupFunction(function() {
+ removeTab(gTab);
+ gPane = null;
+ gTab = null;
+ gDebugger = null;
+ gDebuggee = null;
+});
--- a/browser/devtools/debugger/test/browser_dbg_bug723069_editor-breakpoints.js
+++ b/browser/devtools/debugger/test/browser_dbg_bug723069_editor-breakpoints.js
@@ -16,16 +16,17 @@ let gScripts = null;
let gEditor = null;
let gBreakpoints = null;
function test()
{
let tempScope = {};
Cu.import("resource:///modules/source-editor.jsm", tempScope);
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;
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_bug723071_editor-breakpoints-pane.js
@@ -0,0 +1,281 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Bug 723071: test adding a pane to display the list of breakpoints across
+ * all scripts in the debuggee.
+ */
+
+const TAB_URL = EXAMPLE_URL + "browser_dbg_script-switching.html";
+
+let gPane = null;
+let gTab = null;
+let gDebuggee = null;
+let gDebugger = null;
+let gScripts = null;
+let gBreakpoints = null;
+let gBreakpointsElement = null;
+
+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.contentWindow;
+ resumed = true;
+
+ gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
+ framesAdded = true;
+ executeSoon(startTest);
+ });
+
+ executeSoon(function() {
+ gDebuggee.firstCall();
+ });
+ });
+
+ function onScriptShown(aEvent)
+ {
+ 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);
+ }
+ }
+
+ let breakpointsAdded = 0;
+ let breakpointsDisabled = 0;
+ let breakpointsRemoved = 0;
+
+ function performTest()
+ {
+ gScripts = gDebugger.DebuggerView.Scripts;
+
+ is(gDebugger.DebuggerController.activeThread.state, "paused",
+ "Should only be getting stack frames while paused.");
+
+ is(gScripts._scripts.itemCount, 2, "Found the expected number of scripts.");
+
+ let editor = gDebugger.editor;
+
+ isnot(editor.getText().indexOf("debugger"), -1,
+ "The correct script was loaded initially.");
+ isnot(gScripts.selected, gScripts.scriptLocations[0],
+ "the correct script is selected");
+
+ gBreakpoints = gPane.breakpoints;
+ is(Object.keys(gBreakpoints), 0, "no breakpoints");
+ ok(!gPane.getBreakpoint("chocolate", 3), "getBreakpoint('chocolate', 3) returns falsey");
+
+ is(editor.getBreakpoints().length, 0, "no breakpoints in the editor");
+
+ gBreakpointsElement = gDebugger.DebuggerView.Breakpoints._breakpoints;
+ is(gBreakpointsElement.childNodes.length, 1,
+ "The breakpoints pane should be empty, but showing a " +
+ "'no breakpoints' information message.");
+ is(gBreakpointsElement.childNodes.length,
+ gBreakpointsElement.querySelectorAll(".list-item.empty").length,
+ "Found junk in the breakpoints container.");
+
+ addBreakpoints(function() {
+ is(breakpointsAdded, 3,
+ "Should have added 3 breakpoints so far.");
+ is(breakpointsDisabled, 0,
+ "Shouldn't have disabled anything so far.");
+ is(breakpointsRemoved, 0,
+ "Shouldn't have removed anything so far.");
+
+ is(gBreakpointsElement.childNodes.length,
+ gBreakpointsElement.querySelectorAll(".dbg-breakpoint").length,
+ "Found junk in the breakpoints container.");
+
+ disableBreakpoints(function() {
+ is(breakpointsAdded, 3,
+ "Should still have 3 breakpoints added so far.");
+ is(breakpointsDisabled, 3,
+ "Should have 3 disabled breakpoints.");
+ is(breakpointsRemoved, 0,
+ "Shouldn't have removed anything so far.");
+
+ is(gBreakpointsElement.childNodes.length, breakpointsAdded,
+ "Should have the same number of breakpoints in the pane.");
+ is(gBreakpointsElement.childNodes.length, breakpointsDisabled,
+ "Should have the same number of disabled breakpoints.");
+
+ addBreakpoints(function() {
+ is(breakpointsAdded, 3,
+ "Should still have only 3 breakpoints added so far.");
+ is(breakpointsDisabled, 3,
+ "Should still have 3 disabled breakpoints.");
+ is(breakpointsRemoved, 0,
+ "Shouldn't have removed anything so far.");
+
+ is(gBreakpointsElement.childNodes.length, breakpointsAdded,
+ "Since half of the breakpoints already existed, but disabled, " +
+ "only half of the added breakpoints are actually in the pane.");
+ is(gBreakpointsElement.childNodes.length,
+ gBreakpointsElement.querySelectorAll(".dbg-breakpoint").length,
+ "Found junk in the breakpoints container.");
+
+ removeBreakpoints(function() {
+ is(breakpointsRemoved, 3,
+ "Should have 3 removed breakpoints.");
+
+ is(gBreakpointsElement.childNodes.length, 1,
+ "The breakpoints pane should be empty, but showing a " +
+ "'no breakpoints' information message.");
+ is(gBreakpointsElement.childNodes.length,
+ gBreakpointsElement.querySelectorAll(".list-item.empty").length,
+ "Found junk in the breakpoints container.");
+
+ finish();
+ });
+ });
+ });
+ }, true);
+
+ function addBreakpoints(callback, increment)
+ {
+ let line;
+
+ executeSoon(function()
+ {
+ line = 4;
+ gPane.addBreakpoint({url: gScripts.selected, line: line},
+ function(cl, err) {
+ onBreakpointAdd.call({ increment: increment, line: line }, cl, err);
+
+ line = 5;
+ gPane.addBreakpoint({url: gScripts.selected, line: line},
+ function(cl, err) {
+ onBreakpointAdd.call({ increment: increment, line: line }, cl, err);
+
+ line = 6;
+ gPane.addBreakpoint({url: gScripts.selected, line: line},
+ function(cl, err) {
+ onBreakpointAdd.call({ increment: increment, line: line }, cl, err);
+
+ executeSoon(function() {
+ callback();
+ });
+ });
+ });
+ });
+ });
+ }
+
+ function disableBreakpoints(callback)
+ {
+ let nodes = Array.slice(gBreakpointsElement.childNodes);
+ info("Nodes to disable: " + breakpointsAdded);
+ is(nodes.length, breakpointsAdded,
+ "The number of nodes to disable is incorrect.");
+
+ Array.forEach(nodes, function(bkp) {
+ info("Disabling breakpoint: " + bkp.id);
+
+ gDebugger.DebuggerView.Breakpoints.disableBreakpoint(bkp, function() {
+ if (++breakpointsDisabled !== breakpointsAdded) {
+ return;
+ }
+ executeSoon(function() {
+ callback();
+ });
+ });
+ });
+ }
+
+ function removeBreakpoints(callback)
+ {
+ let nodes = Array.slice(gBreakpointsElement.childNodes);
+ info("Nodes to remove: " + breakpointsAdded);
+ is(nodes.length, breakpointsAdded,
+ "The number of nodes to remove is incorrect.");
+
+ Array.forEach(nodes, function(bkp) {
+ info("Removing breakpoint: " + bkp.id);
+
+ let [url, line, actor] =
+ [bkp.breakpointUrl, bkp.breakpointLine, bkp.breakpointActor];
+
+ gDebugger.DebuggerView.Breakpoints.removeBreakpoint(actor);
+ gPane.removeBreakpoint(gPane.getBreakpoint(url, line), function() {
+ if (++breakpointsRemoved !== breakpointsAdded) {
+ return;
+ }
+ executeSoon(function() {
+ callback();
+ });
+ });
+ });
+ }
+
+ function onBreakpointAdd(aBreakpointClient, aResponseError)
+ {
+ if (this.increment) {
+ breakpointsAdded++;
+ }
+
+ is(gBreakpointsElement.childNodes.length, breakpointsAdded, this.increment
+ ? "Should have added a breakpoint in the pane."
+ : "Should have the same number of breakpoints in the pane.");
+
+ let id = "breakpoint-" + aBreakpointClient.actor;
+ let bkp = gDebugger.document.getElementById(id);
+ let info = bkp.getElementsByClassName("dbg-breakpoint-info")[0];
+ let text = bkp.getElementsByClassName("dbg-breakpoint-text")[0];
+ let check = bkp.querySelector("checkbox");
+
+ is(bkp.id, id,
+ "Breakpoint element " + id + " found succesfully.");
+ is(info.getAttribute("value"), getExpectedBreakpointInfo(this.line),
+ "The expected information wasn't found in the breakpoint element.");
+ is(text.getAttribute("value"), getExpectedLineText(this.line).trim(),
+ "The expected line text wasn't found in the breakpoint element.");
+ is(check.getAttribute("checked"), "true",
+ "The breakpoint enable checkbox is checked as expected.");
+ }
+
+ function getExpectedBreakpointInfo(line) {
+ let url = gDebugger.DebuggerView.Scripts.selected;
+ let label = gDebugger.DebuggerController.SourceScripts.getScriptLabel(url);
+ return label + ":" + line;
+ }
+
+ function getExpectedLineText(line) {
+ return gDebugger.DebuggerController.SourceScripts.getLineText(line - 1);
+ }
+ }
+
+ registerCleanupFunction(function() {
+ is(Object.keys(gBreakpoints).length, 0, "no breakpoint in the debugger");
+ ok(!gPane.getBreakpoint(gScripts.scriptLocations[0], 5),
+ "getBreakpoint(scriptLocations[0], 5) returns no breakpoint");
+
+ is(breakpointsAdded, 3, "correct number of breakpoints have been added");
+ is(breakpointsDisabled, 3, "correct number of breakpoints have been disabled");
+ is(breakpointsRemoved, 3, "correct number of breakpoints have been removed");
+ removeTab(gTab);
+ gPane = null;
+ gTab = null;
+ gDebuggee = null;
+ gDebugger = null;
+ gScripts = null;
+ gBreakpoints = null;
+ gBreakpointsElement = null;
+ });
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_location-changes-blank.js
@@ -0,0 +1,84 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Make sure that changing the tab location URL to a page with no scripts works.
+ */
+
+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.contentWindow;
+
+ testSimpleCall();
+ });
+}
+
+function testSimpleCall() {
+ gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
+ Services.tm.currentThread.dispatch({
+ run: function() {
+ var frames = gDebugger.DebuggerView.StackFrames._frames,
+ childNodes = frames.childNodes;
+
+ is(gDebugger.DebuggerController.activeThread.state, "paused",
+ "Should only be getting stack frames while paused.");
+
+ is(frames.querySelectorAll(".dbg-stackframe").length, 1,
+ "Should have only one frame.");
+
+ is(childNodes.length, frames.querySelectorAll(".dbg-stackframe").length,
+ "All children should be frames.");
+
+ isnot(gDebugger.DebuggerView.Scripts.selected, null,
+ "There should be a selected script.");
+ isnot(gDebugger.editor.getText().length, 0,
+ "The source editor should have some text displayed.");
+
+ testLocationChange();
+ }
+ }, 0);
+ });
+
+ gDebuggee.simpleCall();
+}
+
+function testLocationChange()
+{
+ gDebugger.DebuggerController.activeThread.resume(function() {
+ gDebugger.DebuggerController.client.addOneTimeListener("tabNavigated", function(aEvent, aPacket) {
+ ok(true, "tabNavigated event was fired.");
+ gDebugger.DebuggerController.client.addOneTimeListener("tabAttached", function(aEvent, aPacket) {
+ ok(true, "Successfully reattached to the tab again.");
+
+ // Wait for the initial resume...
+ gDebugger.gClient.addOneTimeListener("resumed", function() {
+ is(gDebugger.DebuggerView.Scripts.selected, null,
+ "There should be no selected script.");
+ is(gDebugger.editor.getText().length, 0,
+ "The source editor not have any text displayed.");
+
+ closeDebuggerAndFinish();
+ });
+ });
+ });
+ content.location = "about:blank";
+ });
+}
+
+registerCleanupFunction(function() {
+ removeTab(gTab);
+ gPane = null;
+ gTab = null;
+ gDebuggee = null;
+ gDebugger = null;
+});
--- a/browser/devtools/debugger/test/browser_dbg_panesize-inner.js
+++ b/browser/devtools/debugger/test/browser_dbg_panesize-inner.js
@@ -28,17 +28,17 @@ function test() {
frame.addEventListener("Debugger:Loaded", function dbgLoaded() {
frame.removeEventListener("Debugger:Loaded", dbgLoaded, true);
ok(content.Prefs.stackframesWidth,
"The debugger preferences should have a saved stackframesWidth value.");
ok(content.Prefs.variablesWidth,
"The debugger preferences should have a saved variablesWidth value.");
- stackframes = content.document.getElementById("stackframes");
+ stackframes = content.document.getElementById("stackframes+breakpoints");
variables = content.document.getElementById("variables");
is(content.Prefs.stackframesWidth, stackframes.getAttribute("width"),
"The stackframes pane width should be the same as the preferred value.");
is(content.Prefs.variablesWidth, variables.getAttribute("width"),
"The variables pane width should be the same as the preferred value.");
stackframes.setAttribute("width", someWidth1);
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-01.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-01.js
@@ -63,17 +63,17 @@ function testScriptLabelShortening() {
{ href: "resource://random/", leaf: "script_t3_1.js#id?a=1&b=2" },
{ href: "resource://random/", leaf: "script_t3_2.js?a=1&b=2#id" },
{ href: "resource://random/", leaf: "script_t3_3.js&a=1&b=2#id" }
];
urls.forEach(function(url) {
executeSoon(function() {
let loc = url.href + url.leaf;
- vs.addScript(ss._getScriptLabel(loc, url.href), { url: loc }, true);
+ vs.addScript(ss.getScriptLabel(loc, url.href), { url: loc }, true);
});
});
executeSoon(function() {
info("Script labels:");
info(vs.scriptLabels.toSource());
info("Script locations:");
--- a/browser/devtools/debugger/test/browser_dbg_scripts-searching-01.js
+++ b/browser/devtools/debugger/test/browser_dbg_scripts-searching-01.js
@@ -119,16 +119,33 @@ function testScriptSearching() {
write("#__i do not exist__");
ok(gEditor.getCaretPosition().line == 8 &&
gEditor.getCaretPosition().col == 2 + token.length,
"The editor didn't remain at the correct token. (12)");
token = "debugger";
+ write("#" + token);
+ ok(gEditor.getCaretPosition().line == 2 &&
+ gEditor.getCaretPosition().col == 44 + token.length,
+ "The editor didn't jump to the correct token. (12.1)");
+
+ clear();
+ EventUtils.sendKey("RETURN");
+ ok(gEditor.getCaretPosition().line == 2 &&
+ gEditor.getCaretPosition().col == 44 + token.length,
+ "The editor shouldn't jump to another token. (12.2)");
+
+ EventUtils.sendKey("ENTER");
+ ok(gEditor.getCaretPosition().line == 2 &&
+ gEditor.getCaretPosition().col == 44 + token.length,
+ "The editor shouldn't jump to another token. (12.3)");
+
+
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#" + token);
ok(gEditor.getCaretPosition().line == 2 &&
gEditor.getCaretPosition().col == 44 + token.length,
--- a/browser/devtools/debugger/test/browser_dbg_scripts-sorting.js
+++ b/browser/devtools/debugger/test/browser_dbg_scripts-sorting.js
@@ -62,41 +62,41 @@ function addScriptsAndCheckOrder(method,
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.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);
+ 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.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);
+ vs.addScript(ss.getScriptLabel(loc, url.href), { url: loc }, true);
}
break;
}
executeSoon(function() {
checkScriptsOrder(method);
callback();
});
new file mode 100644
--- /dev/null
+++ b/browser/devtools/shared/Jsbeautify.jsm
@@ -0,0 +1,1317 @@
+/* Any copyright is dedicated to the Public Domain.
+* http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*
+JS Beautifier written by Einar Lielmanis, <einar@jsbeautifier.org>
+ http://jsbeautifier.org/
+
+Originally converted to javascript by Vital, <vital76@gmail.com>
+"End braces on own line" added by Chris J. Shull, <chrisjshull@gmail.com>
+
+You are free to use this in any way you want, in case you find this useful or
+working for you.
+
+Usage:
+js_beautify(js_source_text);
+js_beautify(js_source_text, options);
+
+The options are:
+indent_size (default 4) — indentation size.
+indent_char (default space) — character to indent with.
+preserve_newlines (default true) — whether existing line breaks should be
+ preserved.
+max_preserve_newlines (default unlimited) - maximum number of line breaks to be
+ preserved in one chunk.
+jslint_happy (default false) — if true, then jslint-stricter mode is
+ enforced.
+
+ jslint_happy !jslint_happy
+ ---------------------------------
+ function () function()
+
+brace_style (default "collapse") - "collapse" | "expand" | "end-expand" |
+ "expand-strict"
+ put braces on the same line as control statements (default), or put braces
+ on own line (Allman / ANSI style), or just put end braces on own line.
+
+ expand-strict: put brace on own line even in such cases:
+
+ var a =
+ {
+ a: 5,
+ b: 6
+ }
+ This mode may break your scripts - e.g "return { a: 1 }" will be broken into
+ two lines, so beware.
+
+space_before_conditional (default true) - should the space before conditional
+statement be added, "if(true)" vs "if (true)",
+
+unescape_strings (default false) - should printable characters in
+strings encoded in \xNN notation be unescaped, "example" vs
+"\x65\x78\x61\x6d\x70\x6c\x65"
+
+e.g
+
+js_beautify(js_source_text, {
+ 'indent_size': 1,
+ 'indent_char': '\t'
<