Merge fx-team to m-c

This commit is contained in:
Wes Kocher 2013-09-24 20:06:37 -07:00
commit 9f40060dc3
88 changed files with 1096 additions and 600 deletions

View File

@ -4904,17 +4904,16 @@
<method name="_calcMouseTargetRect"> <method name="_calcMouseTargetRect">
<body><![CDATA[ <body><![CDATA[
let alignRight = false; let container = this.parentNode;
let alignRight = (getComputedStyle(container).direction == "rtl");
let panelRect = this.getBoundingClientRect();
let containerRect = container.getBoundingClientRect();
if (getComputedStyle(document.documentElement).direction == "rtl")
alignRight = !alignRight;
let rect = this.getBoundingClientRect();
this._mouseTargetRect = { this._mouseTargetRect = {
top: rect.top, top: panelRect.top,
bottom: rect.bottom, bottom: panelRect.bottom,
left: alignRight ? window.innerWidth - rect.width : 0, left: alignRight ? containerRect.right - panelRect.width : containerRect.left,
right: alignRight ? window.innerWidth : rect.width right: alignRight ? containerRect.right : containerRect.left + panelRect.width
}; };
]]></body> ]]></body>
</method> </method>

View File

@ -1127,7 +1127,7 @@ var ViewMenu = {
* Set up the content of the view menu. * Set up the content of the view menu.
*/ */
populateSortMenu: function VM_populateSortMenu(event) { populateSortMenu: function VM_populateSortMenu(event) {
this.fillWithColumns(event, "viewUnsorted", "directionSeparator", "radio", "view.sortBy."); this.fillWithColumns(event, "viewUnsorted", "directionSeparator", "radio", "view.sortBy.1.");
var sortColumn = this._getSortColumn(); var sortColumn = this._getSortColumn();
var viewSortAscending = document.getElementById("viewSortAscending"); var viewSortAscending = document.getElementById("viewSortAscending");

View File

@ -384,7 +384,7 @@
<treecol label="&col.url.label;" id="placesContentUrl" anonid="url" flex="5" <treecol label="&col.url.label;" id="placesContentUrl" anonid="url" flex="5"
persist="width hidden ordinal sortActive sortDirection"/> persist="width hidden ordinal sortActive sortDirection"/>
<splitter class="tree-splitter"/> <splitter class="tree-splitter"/>
<treecol label="&col.lastvisit.label;" id="placesContentDate" anonid="date" flex="1" hidden="true" <treecol label="&col.mostrecentvisit.label;" id="placesContentDate" anonid="date" flex="1" hidden="true"
persist="width hidden ordinal sortActive sortDirection"/> persist="width hidden ordinal sortActive sortDirection"/>
<splitter class="tree-splitter"/> <splitter class="tree-splitter"/>
<treecol label="&col.visitcount.label;" id="placesContentVisitCount" anonid="visitCount" flex="1" hidden="true" <treecol label="&col.visitcount.label;" id="placesContentVisitCount" anonid="visitCount" flex="1" hidden="true"

View File

@ -2585,7 +2585,6 @@ let SessionStoreInternal = {
restoreHistory: restoreHistory:
function ssi_restoreHistory(aWindow, aTabs, aTabData, aIdMap, aDocIdentMap, function ssi_restoreHistory(aWindow, aTabs, aTabData, aIdMap, aDocIdentMap,
aRestoreImmediately) { aRestoreImmediately) {
var _this = this;
// if the tab got removed before being completely restored, then skip it // if the tab got removed before being completely restored, then skip it
while (aTabs.length > 0 && !(this._canRestoreTabHistory(aTabs[0]))) { while (aTabs.length > 0 && !(this._canRestoreTabHistory(aTabs[0]))) {
aTabs.shift(); aTabs.shift();
@ -2654,9 +2653,11 @@ let SessionStoreInternal = {
tab.dispatchEvent(event); tab.dispatchEvent(event);
// Restore the history in the next tab // Restore the history in the next tab
aWindow.setTimeout(function(){ aWindow.setTimeout(() => {
_this.restoreHistory(aWindow, aTabs, aTabData, aIdMap, aDocIdentMap, if (!aWindow.closed) {
aRestoreImmediately); this.restoreHistory(aWindow, aTabs, aTabData, aIdMap, aDocIdentMap,
aRestoreImmediately);
}
}, 0); }, 0);
// This could cause us to ignore MAX_CONCURRENT_TAB_RESTORES a bit, but // This could cause us to ignore MAX_CONCURRENT_TAB_RESTORES a bit, but

View File

@ -61,6 +61,9 @@ let UI = {
this.template = new Template(document.body, this.store, Utils.l10n); this.template = new Template(document.body, this.store, Utils.l10n);
this.template.start(); this.template.start();
this._onSimulatorConnected = this._onSimulatorConnected.bind(this);
this._onSimulatorDisconnected = this._onSimulatorDisconnected.bind(this);
}, },
useFloatingScrollbarsIfNeeded: function() { useFloatingScrollbarsIfNeeded: function() {
@ -136,16 +139,25 @@ let UI = {
this.connection.log("Simulator ready. Connecting."); this.connection.log("Simulator ready. Connecting.");
this.connection.port = port; this.connection.port = port;
this.connection.host = "localhost"; this.connection.host = "localhost";
this.connection.once("connected", () => { this.connection.once("connected",
this.connection.log("Connected to simulator."); this._onSimulatorConnected);
this.connection.keepConnecting = false; this.connection.once("disconnected",
}); this._onSimulatorDisconnected);
this.connection.keepConnecting = true; this.connection.keepConnecting = true;
this.connection.connect(); this.connection.connect();
}); });
document.body.classList.remove("show-simulators"); document.body.classList.remove("show-simulators");
}, },
_onSimulatorConnected: function() {
this.connection.log("Connected to simulator.");
this.connection.keepConnecting = false;
},
_onSimulatorDisconnected: function() {
this.connection.off("connected", this._onSimulatorConnected);
},
connectToAdbDevice: function(name) { connectToAdbDevice: function(name) {
let device = Devices.getByName(name); let device = Devices.getByName(name);
device.connect().then((port) => { device.connect().then((port) => {

View File

@ -46,7 +46,7 @@ function testSetBreakpoint() {
let deferred = promise.defer(); let deferred = promise.defer();
gDebugger.gThreadClient.interrupt(aResponse => { gDebugger.gThreadClient.interrupt(aResponse => {
gDebugger.gThreadClient.setBreakpoint({ url: JS_URL, line: 30, column: 10 }, aResponse => { gDebugger.gThreadClient.setBreakpoint({ url: JS_URL, line: 30, column: 21 }, aResponse => {
ok(!aResponse.error, ok(!aResponse.error,
"Should be able to set a breakpoint in a js file."); "Should be able to set a breakpoint in a js file.");
ok(!aResponse.actualLocation, ok(!aResponse.actualLocation,

View File

@ -0,0 +1,40 @@
/* 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/. */
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
function devtoolsCommandlineHandler() {
}
devtoolsCommandlineHandler.prototype = {
handle: function(cmdLine) {
if (!cmdLine.handleFlag("jsconsole", false)) {
return;
}
Cu.import("resource://gre/modules/Services.jsm");
let window = Services.wm.getMostRecentWindow("devtools:webconsole");
if (!window) {
let devtools = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
// Load the browser devtools main module as the loader's main module.
devtools.main("main");
let hudservice = devtools.require("devtools/webconsole/hudservice");
let console = Cu.import("resource://gre/modules/devtools/Console.jsm", {}).console;
hudservice.toggleBrowserConsole().then(null, console.error);
} else {
window.focus(); // the Browser Console was already open
}
if (cmdLine.state == Ci.nsICommandLine.STATE_REMOTE_AUTO) {
cmdLine.preventDefault = true;
}
},
helpInfo : " -jsconsole Open the Browser Console.\n",
classID: Components.ID("{9e9a9283-0ce9-4e4a-8f1c-ba129a032c32}"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsICommandLineHandler]),
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([devtoolsCommandlineHandler]);

View File

@ -0,0 +1,2 @@
component {9e9a9283-0ce9-4e4a-8f1c-ba129a032c32} devtools-clhandler.js
contract @mozilla.org/toolkit/console-clh;1 {9e9a9283-0ce9-4e4a-8f1c-ba129a032c32}

View File

@ -168,7 +168,7 @@ InspectorPanel.prototype = {
// as default selected, else set documentElement // as default selected, else set documentElement
return walker.getRootNode().then(aRootNode => { return walker.getRootNode().then(aRootNode => {
rootNode = aRootNode; rootNode = aRootNode;
return walker.querySelector(aRootNode, this.selectionCssSelector); return walker.querySelector(rootNode, this.selectionCssSelector);
}).then(front => { }).then(front => {
if (front) { if (front) {
return front; return front;

View File

@ -43,5 +43,7 @@ MOCHITEST_BROWSER_FILES := \
browser_inspector_select_last_selected.html \ browser_inspector_select_last_selected.html \
browser_inspector_select_last_selected2.html \ browser_inspector_select_last_selected2.html \
browser_inspector_basic_highlighter.js \ browser_inspector_basic_highlighter.js \
browser_inspector_dead_node_exception.html \
browser_inspector_dead_node_exception.js \
head.js \ head.js \
$(NULL) $(NULL)

View File

@ -0,0 +1,35 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>iframe creation/deletion test</title>
</head>
<body>
<div id="yay"></div>
<script type="text/javascript">
var yay = document.querySelector("#yay");
yay.textContent = "nothing";
var iframe = document.createElement('iframe');
document.body.appendChild(iframe);
iframe.parentNode.removeChild(iframe);
yay.textContent = "before events";
document.addEventListener("DOMContentLoaded", function() {
var iframe = document.createElement('iframe');
document.body.appendChild(iframe);
iframe.parentNode.removeChild(iframe);
yay.textContent = "DOMContentLoaded";
});
window.addEventListener("load", function() {
var iframe = document.createElement('iframe');
document.body.appendChild(iframe);
iframe.parentNode.removeChild(iframe);
yay.textContent = "load";
});
</script>
</body>
</html>

View File

@ -0,0 +1,56 @@
/* Any copyright", " is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
function test() {
let contentTab, contentDoc, inspector;
waitForExplicitFinish();
// Create a tab
contentTab = gBrowser.selectedTab = gBrowser.addTab();
// Open the toolbox's inspector first
let target = TargetFactory.forTab(gBrowser.selectedTab);
gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
inspector = toolbox.getCurrentPanel();
inspector.selection.setNode(content.document.querySelector("body"));
inspector.once("inspector-updated", () => {
is(inspector.selection.node.tagName, "BODY", "Inspector displayed");
// Then load our test page
gBrowser.selectedBrowser.addEventListener("load", function onload() {
gBrowser.selectedBrowser.removeEventListener("load", onload, true);
contentDoc = content.document;
// The content doc contains a script that creates an iframe and deletes
// it immediately after. This is what used to make the inspector go
// blank when navigating to that page.
var iframe = contentDoc.createElement("iframe");
contentDoc.body.appendChild(iframe);
iframe.parentNode.removeChild(iframe);
is(contentDoc.querySelector("iframe"), null, "Iframe has been removed");
inspector.once("markuploaded", () => {
// Assert that the markup-view is displayed and works
is(contentDoc.querySelector("iframe"), null, "Iframe has been removed");
is(contentDoc.getElementById("yay").textContent, "load", "Load event fired");
inspector.selection.setNode(contentDoc.getElementById("yay"));
inspector.once("inspector-updated", () => {
ok(inspector.selection.node, "Inspector still displayed");
// Clean up and go
contentTab, contentDoc, inspector = null;
gBrowser.removeTab(contentTab);
finish();
});
});
}, true);
content.location = "http://mochi.test:8888/browser/browser/devtools/" +
"inspector/test/browser_inspector_dead_node_exception.html";
});
});
}

View File

@ -24,3 +24,8 @@ DIRS += [
'fontinspector', 'fontinspector',
'app-manager', 'app-manager',
] ]
EXTRA_COMPONENTS += [
'devtools-clhandler.js',
'devtools-clhandler.manifest',
]

View File

@ -254,13 +254,14 @@ HUD_SERVICE.prototype =
return deferred.promise; return deferred.promise;
} }
connect().then(getTarget).then(openWindow).then((aWindow) => connect().then(getTarget).then(openWindow).then((aWindow) => {
this.openBrowserConsole(target, aWindow, aWindow) this.openBrowserConsole(target, aWindow, aWindow)
.then((aBrowserConsole) => { .then((aBrowserConsole) => {
this._browserConsoleID = aBrowserConsole.hudId; this._browserConsoleID = aBrowserConsole.hudId;
this._browserConsoleDefer.resolve(aBrowserConsole); this._browserConsoleDefer.resolve(aBrowserConsole);
this._browserConsoleDefer = null; this._browserConsoleDefer = null;
})); })
}, console.error);
return this._browserConsoleDefer.promise; return this._browserConsoleDefer.promise;
}, },

View File

@ -62,7 +62,7 @@ function consoleOpened(hud)
let text = output.textContent; let text = output.textContent;
chromeConsole = text.indexOf("bug587757a"); chromeConsole = text.indexOf("bug587757a");
contentConsole = text.indexOf("bug587757b"); contentConsole = text.indexOf("bug587757b");
execValue = text.indexOf("browser.xul"); execValue = text.indexOf("webconsole.xul");
exception = text.indexOf("foobarExceptionBug587757"); exception = text.indexOf("foobarExceptionBug587757");
xhrRequest = text.indexOf("test-console.html"); xhrRequest = text.indexOf("test-console.html");
} }

View File

@ -24,7 +24,10 @@ function test()
ok(hud, "browser console opened"); ok(hud, "browser console opened");
hud.jsterm.clearOutput(); hud.jsterm.clearOutput();
hud.jsterm.execute("foobarzTezt = content.document", onAddVariable); hud.jsterm.execute("Cu = Components.utils;" +
"Cu.import('resource://gre/modules/Services.jsm');" +
"chromeWindow = Services.wm.getMostRecentWindow('navigator:browser');" +
"foobarzTezt = chromeWindow.content.document", onAddVariable);
} }
function onAddVariable() function onAddVariable()

View File

@ -81,7 +81,7 @@
<!ENTITY col.name.label "Name"> <!ENTITY col.name.label "Name">
<!ENTITY col.tags.label "Tags"> <!ENTITY col.tags.label "Tags">
<!ENTITY col.url.label "Location"> <!ENTITY col.url.label "Location">
<!ENTITY col.lastvisit.label "Visit Date"> <!ENTITY col.mostrecentvisit.label "Most Recent Visit">
<!ENTITY col.visitcount.label "Visit Count"> <!ENTITY col.visitcount.label "Visit Count">
<!ENTITY col.keyword.label "Keyword"> <!ENTITY col.keyword.label "Keyword">
<!ENTITY col.description.label "Description"> <!ENTITY col.description.label "Description">

View File

@ -25,24 +25,28 @@ menuOpenLivemarkOrigin.label=Open "%S"
sortByName=Sort '%S' by Name sortByName=Sort '%S' by Name
sortByNameGeneric=Sort by Name sortByNameGeneric=Sort by Name
view.sortBy.name.label=Sort by Name # LOCALIZATION NOTE (view.sortBy.1.name.label): sortBy properties are versioned.
view.sortBy.name.accesskey=N # When any of these changes, all of the properties must be bumped, and the
view.sortBy.url.label=Sort by Location # change must be annotated here. Both label and accesskey must be updated.
view.sortBy.url.accesskey=L # - version 1: changed view.sortBy.1.date.
view.sortBy.date.label=Sort by Visit Date view.sortBy.1.name.label=Sort by Name
view.sortBy.date.accesskey=V view.sortBy.1.name.accesskey=N
view.sortBy.visitCount.label=Sort by Visit Count view.sortBy.1.url.label=Sort by Location
view.sortBy.visitCount.accesskey=C view.sortBy.1.url.accesskey=L
view.sortBy.keyword.label=Sort by Keyword view.sortBy.1.date.label=Sort by Most Recent Visit
view.sortBy.keyword.accesskey=K view.sortBy.1.date.accesskey=V
view.sortBy.description.label=Sort by Description view.sortBy.1.visitCount.label=Sort by Visit Count
view.sortBy.description.accesskey=D view.sortBy.1.visitCount.accesskey=C
view.sortBy.dateAdded.label=Sort by Added view.sortBy.1.keyword.label=Sort by Keyword
view.sortBy.dateAdded.accesskey=e view.sortBy.1.keyword.accesskey=K
view.sortBy.lastModified.label=Sort by Last Modified view.sortBy.1.description.label=Sort by Description
view.sortBy.lastModified.accesskey=M view.sortBy.1.description.accesskey=D
view.sortBy.tags.label=Sort by Tags view.sortBy.1.dateAdded.label=Sort by Added
view.sortBy.tags.accesskey=T view.sortBy.1.dateAdded.accesskey=e
view.sortBy.1.lastModified.label=Sort by Last Modified
view.sortBy.1.lastModified.accesskey=M
view.sortBy.1.tags.label=Sort by Tags
view.sortBy.1.tags.accesskey=T
searchBookmarks=Search Bookmarks searchBookmarks=Search Bookmarks
searchHistory=Search History searchHistory=Search History

View File

@ -77,6 +77,13 @@ var ContentAreaObserver = {
return this._deckTransitioning; return this._deckTransitioning;
}, },
get viewstate() {
if (this.width < Services.prefs.getIntPref("browser.ui.snapped.maxWidth")) {
return "snapped";
}
return (this.height > this.width) ? "portrait" : "landscape";
},
/* /*
* Public apis * Public apis
*/ */
@ -122,6 +129,8 @@ var ContentAreaObserver = {
this.styles["window-height"].height = newHeight + "px"; this.styles["window-height"].height = newHeight + "px";
this.styles["window-height"].maxHeight = newHeight + "px"; this.styles["window-height"].maxHeight = newHeight + "px";
this._updateViewState();
this.updateContentArea(newWidth, this._getContentHeightForWindow(newHeight)); this.updateContentArea(newWidth, this._getContentHeightForWindow(newHeight));
this._disatchBrowserEvent("SizeChanged"); this._disatchBrowserEvent("SizeChanged");
}, },
@ -280,6 +289,15 @@ var ContentAreaObserver = {
* Internal helpers * Internal helpers
*/ */
_updateViewState: function (aState) {
let oldViewstate = Elements.windowState.getAttribute("viewstate");
let viewstate = aState || this.viewstate;
if (viewstate != oldViewstate) {
Elements.windowState.setAttribute("viewstate", viewstate);
Services.obs.notifyObservers(null, "metro_viewstate_changed", viewstate);
}
},
_shiftBrowserDeck: function _shiftBrowserDeck(aAmount) { _shiftBrowserDeck: function _shiftBrowserDeck(aAmount) {
if (aAmount == 0) { if (aAmount == 0) {
this._deckTransitioning = false; this._deckTransitioning = false;

View File

@ -102,7 +102,6 @@ var BrowserUI = {
window.addEventListener("MozImprecisePointer", this, true); window.addEventListener("MozImprecisePointer", this, true);
Services.prefs.addObserver("browser.cache.disk_cache_ssl", this, false); Services.prefs.addObserver("browser.cache.disk_cache_ssl", this, false);
Services.obs.addObserver(this, "metro_viewstate_changed", false);
// Init core UI modules // Init core UI modules
ContextUI.init(); ContextUI.init();
@ -112,9 +111,6 @@ var BrowserUI = {
SettingsCharm.init(); SettingsCharm.init();
NavButtonSlider.init(); NavButtonSlider.init();
// show the right toolbars, awesomescreen, etc for the os viewstate
BrowserUI._adjustDOMforViewState();
// We can delay some initialization until after startup. We wait until // We can delay some initialization until after startup. We wait until
// the first page is shown, then dispatch a UIReadyDelayed event. // the first page is shown, then dispatch a UIReadyDelayed event.
messageManager.addMessageListener("pageshow", function onPageShow() { messageManager.addMessageListener("pageshow", function onPageShow() {
@ -178,6 +174,7 @@ var BrowserUI = {
messageManager.removeMessageListener("Browser:MozApplicationManifest", OfflineApps); messageManager.removeMessageListener("Browser:MozApplicationManifest", OfflineApps);
PanelUI.uninit(); PanelUI.uninit();
FlyoutPanelsUI.uninit();
Downloads.uninit(); Downloads.uninit();
SettingsCharm.uninit(); SettingsCharm.uninit();
messageManager.removeMessageListener("Content:StateChange", this); messageManager.removeMessageListener("Content:StateChange", this);
@ -592,13 +589,6 @@ var BrowserUI = {
this.changeDebugPort(Services.prefs.getIntPref(aData)); this.changeDebugPort(Services.prefs.getIntPref(aData));
break; break;
} }
break;
case "metro_viewstate_changed":
this._adjustDOMforViewState(aData);
if (aData == "snapped") {
FlyoutPanelsUI.hide();
}
break; break;
} }
}, },
@ -638,28 +628,6 @@ var BrowserUI = {
pullDesktopControlledPrefType(Ci.nsIPrefBranch.PREF_STRING, "setCharPref"); pullDesktopControlledPrefType(Ci.nsIPrefBranch.PREF_STRING, "setCharPref");
}, },
_adjustDOMforViewState: function(aState) {
let currViewState = aState;
if (!currViewState && Services.metro.immersive) {
switch (Services.metro.snappedState) {
case Ci.nsIWinMetroUtils.fullScreenLandscape:
currViewState = "landscape";
break;
case Ci.nsIWinMetroUtils.fullScreenPortrait:
currViewState = "portrait";
break;
case Ci.nsIWinMetroUtils.filled:
currViewState = "filled";
break;
case Ci.nsIWinMetroUtils.snapped:
currViewState = "snapped";
break;
}
}
Elements.windowState.setAttribute("viewstate", currViewState);
},
_titleChanged: function(aBrowser) { _titleChanged: function(aBrowser) {
let url = this.getDisplayURI(aBrowser); let url = this.getDisplayURI(aBrowser);

View File

@ -130,14 +130,7 @@ var Browser = {
window.controllers.appendController(this); window.controllers.appendController(this);
window.controllers.appendController(BrowserUI); window.controllers.appendController(BrowserUI);
let os = Services.obs; Services.obs.addObserver(SessionHistoryObserver, "browser:purge-session-history", false);
os.addObserver(SessionHistoryObserver, "browser:purge-session-history", false);
os.addObserver(ActivityObserver, "application-background", false);
os.addObserver(ActivityObserver, "application-foreground", false);
os.addObserver(ActivityObserver, "system-active", false);
os.addObserver(ActivityObserver, "system-idle", false);
os.addObserver(ActivityObserver, "system-display-on", false);
os.addObserver(ActivityObserver, "system-display-off", false);
window.QueryInterface(Ci.nsIDOMChromeWindow).browserDOMWindow = new nsBrowserAccess(); window.QueryInterface(Ci.nsIDOMChromeWindow).browserDOMWindow = new nsBrowserAccess();
@ -247,14 +240,7 @@ var Browser = {
messageManager.removeMessageListener("Browser:PluginClickToPlayClicked", this); messageManager.removeMessageListener("Browser:PluginClickToPlayClicked", this);
messageManager.removeMessageListener("Browser:TapOnSelection", this); messageManager.removeMessageListener("Browser:TapOnSelection", this);
var os = Services.obs; Services.obs.removeObserver(SessionHistoryObserver, "browser:purge-session-history");
os.removeObserver(SessionHistoryObserver, "browser:purge-session-history");
os.removeObserver(ActivityObserver, "application-background");
os.removeObserver(ActivityObserver, "application-foreground");
os.removeObserver(ActivityObserver, "system-active");
os.removeObserver(ActivityObserver, "system-idle");
os.removeObserver(ActivityObserver, "system-display-on");
os.removeObserver(ActivityObserver, "system-display-off");
window.controllers.removeController(this); window.controllers.removeController(this);
window.controllers.removeController(BrowserUI); window.controllers.removeController(BrowserUI);
@ -1394,37 +1380,6 @@ var SessionHistoryObserver = {
} }
}; };
var ActivityObserver = {
_inBackground : false,
_notActive : false,
_isDisplayOff : false,
_timeoutID: 0,
observe: function ao_observe(aSubject, aTopic, aData) {
if (aTopic == "application-background") {
this._inBackground = true;
} else if (aTopic == "application-foreground") {
this._inBackground = false;
} else if (aTopic == "system-idle") {
this._notActive = true;
} else if (aTopic == "system-active") {
this._notActive = false;
} else if (aTopic == "system-display-on") {
this._isDisplayOff = false;
} else if (aTopic == "system-display-off") {
this._isDisplayOff = true;
}
let activeTabState = !this._inBackground && !this._notActive && !this._isDisplayOff;
if (this._timeoutID)
clearTimeout(this._timeoutID);
if (Browser.selectedTab.active != activeTabState) {
// On Maemo all backgrounded applications getting portrait orientation
// so if browser had landscape mode then we need timeout in order
// to finish last rotate/paint operation and have nice lookine browser in TS
this._timeoutID = setTimeout(function() { Browser.selectedTab.active = activeTabState; }, activeTabState ? 0 : kSetInactiveStateTimeout);
}
}
};
function getNotificationBox(aBrowser) { function getNotificationBox(aBrowser) {
return Browser.getNotificationBox(aBrowser); return Browser.getNotificationBox(aBrowser);
} }

View File

@ -172,6 +172,7 @@
<stack id="stack" flex="1"> <stack id="stack" flex="1">
<observes element="bcast_urlbarState" attribute="*"/> <observes element="bcast_urlbarState" attribute="*"/>
<observes element="bcast_windowState" attribute="*"/>
<!-- Page Area --> <!-- Page Area -->
<vbox id="page"> <vbox id="page">
<vbox id="tray" class="tray-toolbar" observes="bcast_windowState" > <vbox id="tray" class="tray-toolbar" observes="bcast_windowState" >

View File

@ -36,6 +36,12 @@ let FlyoutPanelsUI = {
return sandbox[name]; return sandbox[name];
}); });
}); });
Services.obs.addObserver(this, "metro_viewstate_changed", false);
},
uninit: function () {
Services.obs.removeObserver(this, "metro_viewstate_changed");
}, },
show: function(aToShow) { show: function(aToShow) {
@ -73,6 +79,16 @@ let FlyoutPanelsUI = {
return this._currentFlyout ? true : false; return this._currentFlyout ? true : false;
}, },
observe: function (aSubject, aTopic, aData) {
switch (aTopic) {
case "metro_viewstate_changed":
if (aData == "snapped") {
this.hide();
}
break;
}
},
dispatchEvent: function(aEvent) { dispatchEvent: function(aEvent) {
if (this._currentFlyout) { if (this._currentFlyout) {
this._currentFlyout._topmostElement.dispatchEvent(aEvent); this._currentFlyout._topmostElement.dispatchEvent(aEvent);

View File

@ -13,8 +13,8 @@
* @param aRoot Bookmark root to show in the view. * @param aRoot Bookmark root to show in the view.
*/ */
function BookmarksView(aSet, aLimit, aRoot, aFilterUnpinned) { function BookmarksView(aSet, aLimit, aRoot, aFilterUnpinned) {
this._set = aSet; View.call(this, aSet);
this._set.controller = this;
this._inBatch = false; // batch up grid updates to avoid redundant arrangeItems calls this._inBatch = false; // batch up grid updates to avoid redundant arrangeItems calls
this._limit = aLimit; this._limit = aLimit;
@ -25,12 +25,10 @@ function BookmarksView(aSet, aLimit, aRoot, aFilterUnpinned) {
this._changes = new BookmarkChangeListener(this); this._changes = new BookmarkChangeListener(this);
this._pinHelper = new ItemPinHelper("metro.bookmarks.unpinned"); this._pinHelper = new ItemPinHelper("metro.bookmarks.unpinned");
this._bookmarkService.addObserver(this._changes, false); this._bookmarkService.addObserver(this._changes, false);
Services.obs.addObserver(this, "metro_viewstate_changed", false);
StartUI.chromeWin.addEventListener('MozAppbarDismissing', this, false); StartUI.chromeWin.addEventListener('MozAppbarDismissing', this, false);
StartUI.chromeWin.addEventListener('BookmarksNeedsRefresh', this, false); StartUI.chromeWin.addEventListener('BookmarksNeedsRefresh', this, false);
window.addEventListener("TabClose", this, true); window.addEventListener("TabClose", this, true);
this._adjustDOMforViewState();
this.root = aRoot; this.root = aRoot;
} }
@ -62,11 +60,11 @@ BookmarksView.prototype = Util.extend(Object.create(View.prototype), {
destruct: function bv_destruct() { destruct: function bv_destruct() {
this._bookmarkService.removeObserver(this._changes); this._bookmarkService.removeObserver(this._changes);
Services.obs.removeObserver(this, "metro_viewstate_changed");
if (StartUI.chromeWin) { if (StartUI.chromeWin) {
StartUI.chromeWin.removeEventListener('MozAppbarDismissing', this, false); StartUI.chromeWin.removeEventListener('MozAppbarDismissing', this, false);
StartUI.chromeWin.removeEventListener('BookmarksNeedsRefresh', this, false); StartUI.chromeWin.removeEventListener('BookmarksNeedsRefresh', this, false);
} }
View.prototype.destruct.call(this);
}, },
handleItemClick: function bv_handleItemClick(aItem) { handleItemClick: function bv_handleItemClick(aItem) {
@ -275,15 +273,6 @@ BookmarksView.prototype = Util.extend(Object.create(View.prototype), {
this._sendNeedsRefresh(); this._sendNeedsRefresh();
}, },
// nsIObservers
observe: function (aSubject, aTopic, aState) {
switch(aTopic) {
case "metro_viewstate_changed":
this.onViewStateChange(aState);
break;
}
},
handleEvent: function bv_handleEvent(aEvent) { handleEvent: function bv_handleEvent(aEvent) {
switch (aEvent.type){ switch (aEvent.type){
case "MozAppbarDismissing": case "MozAppbarDismissing":

View File

@ -5,8 +5,8 @@
'use strict'; 'use strict';
function HistoryView(aSet, aLimit, aFilterUnpinned) { function HistoryView(aSet, aLimit, aFilterUnpinned) {
this._set = aSet; View.call(this, aSet);
this._set.controller = this;
this._inBatch = 0; this._inBatch = 0;
this._limit = aLimit; this._limit = aLimit;
@ -16,12 +16,9 @@ function HistoryView(aSet, aLimit, aFilterUnpinned) {
this._pinHelper = new ItemPinHelper("metro.history.unpinned"); this._pinHelper = new ItemPinHelper("metro.history.unpinned");
this._historyService.addObserver(this, false); this._historyService.addObserver(this, false);
Services.obs.addObserver(this, "metro_viewstate_changed", false);
StartUI.chromeWin.addEventListener('MozAppbarDismissing', this, false); StartUI.chromeWin.addEventListener('MozAppbarDismissing', this, false);
StartUI.chromeWin.addEventListener('HistoryNeedsRefresh', this, false); StartUI.chromeWin.addEventListener('HistoryNeedsRefresh', this, false);
window.addEventListener("TabClose", this, true); window.addEventListener("TabClose", this, true);
this._adjustDOMforViewState();
} }
HistoryView.prototype = Util.extend(Object.create(View.prototype), { HistoryView.prototype = Util.extend(Object.create(View.prototype), {
@ -30,11 +27,11 @@ HistoryView.prototype = Util.extend(Object.create(View.prototype), {
destruct: function destruct() { destruct: function destruct() {
this._historyService.removeObserver(this); this._historyService.removeObserver(this);
Services.obs.removeObserver(this, "metro_viewstate_changed");
if (StartUI.chromeWin) { if (StartUI.chromeWin) {
StartUI.chromeWin.removeEventListener('MozAppbarDismissing', this, false); StartUI.chromeWin.removeEventListener('MozAppbarDismissing', this, false);
StartUI.chromeWin.removeEventListener('HistoryNeedsRefresh', this, false); StartUI.chromeWin.removeEventListener('HistoryNeedsRefresh', this, false);
} }
View.prototype.destruct.call(this);
}, },
handleItemClick: function tabview_handleItemClick(aItem) { handleItemClick: function tabview_handleItemClick(aItem) {
@ -223,15 +220,6 @@ HistoryView.prototype = Util.extend(Object.create(View.prototype), {
} }
}, },
// nsIObservers
observe: function (aSubject, aTopic, aState) {
switch(aTopic) {
case "metro_viewstate_changed":
this.onViewStateChange(aState);
break;
}
},
// nsINavHistoryObserver & helpers // nsINavHistoryObserver & helpers
onBeginUpdateBatch: function() { onBeginUpdateBatch: function() {

View File

@ -20,8 +20,8 @@ Components.utils.import("resource://services-sync/main.js");
* You may only have one UI access point at this time. * You may only have one UI access point at this time.
*/ */
function RemoteTabsView(aSet, aSetUIAccessList) { function RemoteTabsView(aSet, aSetUIAccessList) {
this._set = aSet; View.call(this, aSet);
this._set.controller = this;
this._uiAccessElements = aSetUIAccessList; this._uiAccessElements = aSetUIAccessList;
// Sync uses special voodoo observers. // Sync uses special voodoo observers.
@ -29,15 +29,12 @@ function RemoteTabsView(aSet, aSetUIAccessList) {
Weave.Svc.Obs.add("weave:service:sync:finish", this); Weave.Svc.Obs.add("weave:service:sync:finish", this);
Weave.Svc.Obs.add("weave:service:start-over", this); Weave.Svc.Obs.add("weave:service:start-over", this);
Services.obs.addObserver(this, "metro_viewstate_changed", false);
if (this.isSyncEnabled() ) { if (this.isSyncEnabled() ) {
this.populateGrid(); this.populateGrid();
} }
else { else {
this.setUIAccessVisible(false); this.setUIAccessVisible(false);
} }
this._adjustDOMforViewState();
} }
RemoteTabsView.prototype = Util.extend(Object.create(View.prototype), { RemoteTabsView.prototype = Util.extend(Object.create(View.prototype), {
@ -51,9 +48,6 @@ RemoteTabsView.prototype = Util.extend(Object.create(View.prototype), {
observe: function(subject, topic, data) { observe: function(subject, topic, data) {
switch (topic) { switch (topic) {
case "metro_viewstate_changed":
this.onViewStateChange(data);
break;
case "weave:service:sync:finish": case "weave:service:sync:finish":
this.populateGrid(); this.populateGrid();
break; break;
@ -102,9 +96,9 @@ RemoteTabsView.prototype = Util.extend(Object.create(View.prototype), {
}, },
destruct: function destruct() { destruct: function destruct() {
Services.obs.removeObserver(this, "metro_viewstate_changed");
Weave.Svc.Obs.remove("weave:engine:sync:finish", this); Weave.Svc.Obs.remove("weave:engine:sync:finish", this);
Weave.Svc.Obs.remove("weave:service:logout:start-over", this); Weave.Svc.Obs.remove("weave:service:logout:start-over", this);
View.prototype.destruct.call(this);
}, },
isSyncEnabled: function isSyncEnabled() { isSyncEnabled: function isSyncEnabled() {

View File

@ -26,7 +26,7 @@ var StartUI = {
document.getElementById("bcast_preciseInput").setAttribute("input", document.getElementById("bcast_preciseInput").setAttribute("input",
this.chromeWin.InputSourceHelper.isPrecise ? "precise" : "imprecise"); this.chromeWin.InputSourceHelper.isPrecise ? "precise" : "imprecise");
this._adjustDOMforViewState(); this._adjustDOMforViewState(this.chromeWin.ContentAreaObserver.viewstate);
TopSitesStartView.init(); TopSitesStartView.init();
BookmarksStartView.init(); BookmarksStartView.init();
@ -81,6 +81,10 @@ var StartUI = {
section.setAttribute("expanded", "true"); section.setAttribute("expanded", "true");
}, },
_adjustDOMforViewState: function(aState) {
document.getElementById("bcast_windowState").setAttribute("viewstate", aState);
},
handleEvent: function handleEvent(aEvent) { handleEvent: function handleEvent(aEvent) {
switch (aEvent.type) { switch (aEvent.type) {
case "MozPrecisePointer": case "MozPrecisePointer":
@ -106,33 +110,6 @@ var StartUI = {
} }
}, },
_adjustDOMforViewState: function(aState) {
let currViewState = aState;
if (!currViewState && Services.metro.immersive) {
switch (Services.metro.snappedState) {
case Ci.nsIWinMetroUtils.fullScreenLandscape:
currViewState = "landscape";
break;
case Ci.nsIWinMetroUtils.fullScreenPortrait:
currViewState = "portrait";
break;
case Ci.nsIWinMetroUtils.filled:
currViewState = "filled";
break;
case Ci.nsIWinMetroUtils.snapped:
currViewState = "snapped";
break;
}
}
document.getElementById("bcast_windowState").setAttribute("viewstate", currViewState);
if (currViewState == "snapped") {
document.getElementById("start-topsites-grid").removeAttribute("tiletype");
} else {
document.getElementById("start-topsites-grid").setAttribute("tiletype", "thumbnail");
}
},
observe: function (aSubject, aTopic, aData) { observe: function (aSubject, aTopic, aData) {
switch (aTopic) { switch (aTopic) {
case "metro_viewstate_changed": case "metro_viewstate_changed":

View File

@ -11,8 +11,8 @@ Cu.import("resource://gre/modules/PageThumbs.jsm");
Cu.import("resource:///modules/colorUtils.jsm"); Cu.import("resource:///modules/colorUtils.jsm");
function TopSitesView(aGrid, aMaxSites) { function TopSitesView(aGrid, aMaxSites) {
this._set = aGrid; View.call(this, aGrid);
this._set.controller = this;
this._topSitesMax = aMaxSites; this._topSitesMax = aMaxSites;
// clean up state when the appbar closes // clean up state when the appbar closes
@ -23,9 +23,6 @@ function TopSitesView(aGrid, aMaxSites) {
PageThumbs.addExpirationFilter(this); PageThumbs.addExpirationFilter(this);
Services.obs.addObserver(this, "Metro:RefreshTopsiteThumbnail", false); Services.obs.addObserver(this, "Metro:RefreshTopsiteThumbnail", false);
Services.obs.addObserver(this, "metro_viewstate_changed", false);
this._adjustDOMforViewState();
NewTabUtils.allPages.register(this); NewTabUtils.allPages.register(this);
TopSites.prepareCache().then(function(){ TopSites.prepareCache().then(function(){
@ -43,12 +40,12 @@ TopSitesView.prototype = Util.extend(Object.create(View.prototype), {
destruct: function destruct() { destruct: function destruct() {
Services.obs.removeObserver(this, "Metro:RefreshTopsiteThumbnail"); Services.obs.removeObserver(this, "Metro:RefreshTopsiteThumbnail");
Services.obs.removeObserver(this, "metro_viewstate_changed");
PageThumbs.removeExpirationFilter(this); PageThumbs.removeExpirationFilter(this);
NewTabUtils.allPages.unregister(this); NewTabUtils.allPages.unregister(this);
if (StartUI.chromeWin) { if (StartUI.chromeWin) {
StartUI.chromeWin.removeEventListener('MozAppbarDismissing', this, false); StartUI.chromeWin.removeEventListener('MozAppbarDismissing', this, false);
} }
View.prototype.destruct.call(this);
}, },
handleItemClick: function tabview_handleItemClick(aItem) { handleItemClick: function tabview_handleItemClick(aItem) {
@ -137,6 +134,7 @@ TopSitesView.prototype = Util.extend(Object.create(View.prototype), {
case "MozAppbarDismissing": case "MozAppbarDismissing":
// clean up when the context appbar is dismissed - we don't remember selections // clean up when the context appbar is dismissed - we don't remember selections
this._lastSelectedSites = null; this._lastSelectedSites = null;
break;
} }
}, },
@ -234,6 +232,13 @@ TopSitesView.prototype = Util.extend(Object.create(View.prototype), {
View.prototype._adjustDOMforViewState.call(this, aState); View.prototype._adjustDOMforViewState.call(this, aState);
// Don't show thumbnails in snapped view.
if (aState == "snapped") {
document.getElementById("start-topsites-grid").removeAttribute("tiletype");
} else {
document.getElementById("start-topsites-grid").setAttribute("tiletype", "thumbnail");
}
// propogate tiletype changes down to tile children // propogate tiletype changes down to tile children
let tileType = this._set.getAttribute("tiletype"); let tileType = this._set.getAttribute("tiletype");
for (let item of this._set.children) { for (let item of this._set.children) {
@ -251,9 +256,6 @@ TopSitesView.prototype = Util.extend(Object.create(View.prototype), {
case "Metro:RefreshTopsiteThumbnail": case "Metro:RefreshTopsiteThumbnail":
this.forceReloadOfThumbnail(aState); this.forceReloadOfThumbnail(aState);
break; break;
case "metro_viewstate_changed":
this.onViewStateChange(aState);
break;
} }
}, },

View File

@ -111,7 +111,7 @@ gTests.push({
}, },
tearDown: function() { tearDown: function() {
BookmarksTestHelper.restore(); BookmarksTestHelper.restore();
restoreViewstate(); yield restoreViewstate();
} }
}); });
@ -160,6 +160,6 @@ gTests.push({
tearDown: function() { tearDown: function() {
BookmarksTestHelper.restore(); BookmarksTestHelper.restore();
HistoryTestHelper.restore(); HistoryTestHelper.restore();
restoreViewstate(); yield restoreViewstate();
} }
}); });

View File

@ -47,7 +47,7 @@ const mochitestPath = splitPath.join('/') + '/';
function isLandscapeMode() function isLandscapeMode()
{ {
return (Services.metro.snappedState == Ci.nsIWinMetroUtils.fullScreenLandscape); return Elements.windowState.getAttribute("viewstate") == "landscape";
} }
function setDevPixelEqualToPx() function setDevPixelEqualToPx()

View File

@ -20,7 +20,7 @@ function setSnappedViewstate() {
browser.style.borderRight = padding + "px solid gray"; browser.style.borderRight = padding + "px solid gray";
// Communicate viewstate change // Communicate viewstate change
Services.obs.notifyObservers(null, 'metro_viewstate_changed', 'snapped'); ContentAreaObserver._updateViewState("snapped");
// Make sure it renders the new mode properly // Make sure it renders the new mode properly
yield waitForMs(0); yield waitForMs(0);
@ -36,16 +36,15 @@ function setPortraitViewstate() {
browser.style.borderRight = padding + "px solid gray"; browser.style.borderRight = padding + "px solid gray";
Services.obs.notifyObservers(null, 'metro_viewstate_changed', 'portrait'); ContentAreaObserver._updateViewState("portrait");
// Make sure it renders the new mode properly // Make sure it renders the new mode properly
yield waitForMs(0); yield waitForMs(0);
} }
function restoreViewstate() { function restoreViewstate() {
ok(isLandscapeMode(), "restoreViewstate expects landscape mode to work."); ContentAreaObserver._updateViewState("landscape");
ok(isLandscapeMode(), "restoreViewstate should restore landscape mode.");
Services.obs.notifyObservers(null, 'metro_viewstate_changed', 'landscape');
Browser.selectedBrowser.style.removeProperty("border-right"); Browser.selectedBrowser.style.removeProperty("border-right");

View File

@ -6,10 +6,7 @@
{"index":1,"title":"@firefox_about@", "type":"text/x-moz-place", "uri":"http://www.mozilla.org/@AB_CD@/about/", {"index":1,"title":"@firefox_about@", "type":"text/x-moz-place", "uri":"http://www.mozilla.org/@AB_CD@/about/",
"iconUri":"chrome://branding/content/favicon32.png" "iconUri":"chrome://branding/content/favicon32.png"
}, },
{"index":2,"title":"@getting_started@", "type":"text/x-moz-place", "uri":"http://www.mozilla.org/@AB_CD@/firefox/central/", {"index":2,"title":"@firefox_community@", "type":"text/x-moz-place", "uri":"http://www.mozilla.org/@AB_CD@/contribute/",
"iconUri":"chrome://branding/content/favicon32.png"
},
{"index":3,"title":"@firefox_community@", "type":"text/x-moz-place", "uri":"http://www.mozilla.org/@AB_CD@/contribute/",
"iconUri":"chrome://branding/content/favicon32.png" "iconUri":"chrome://branding/content/favicon32.png"
} }
] ]

View File

@ -23,10 +23,23 @@ function makeURI(aURL, aOriginCharset, aBaseURI) {
// -------------------------------- // --------------------------------
// View prototype for shared functionality // View prototype for shared functionality
function View() { function View(aSet) {
this._set = aSet;
this._set.controller = this;
this.viewStateObserver = {
observe: (aSubject, aTopic, aData) => this._adjustDOMforViewState(aData)
};
Services.obs.addObserver(this.viewStateObserver, "metro_viewstate_changed", false);
this._adjustDOMforViewState();
} }
View.prototype = { View.prototype = {
destruct: function () {
Services.obs.removeObserver(this.viewStateObserver, "metro_viewstate_changed");
},
_adjustDOMforViewState: function _adjustDOMforViewState(aState) { _adjustDOMforViewState: function _adjustDOMforViewState(aState) {
if (this._set) { if (this._set) {
if (undefined == aState) if (undefined == aState)
@ -44,10 +57,6 @@ View.prototype = {
} }
}, },
onViewStateChange: function (aState) {
this._adjustDOMforViewState(aState);
},
_updateFavicon: function pv__updateFavicon(aItem, aUri) { _updateFavicon: function pv__updateFavicon(aItem, aUri) {
if ("string" == typeof aUri) { if ("string" == typeof aUri) {
aUri = makeURI(aUri); aUri = makeURI(aUri);

View File

@ -377,6 +377,9 @@ pref("geo.wifi.uri", "https://www.googleapis.com/geolocation/v1/geolocate?key=%G
// JS error console // JS error console
pref("devtools.errorconsole.enabled", false); pref("devtools.errorconsole.enabled", false);
// snapped view
pref("browser.ui.snapped.maxWidth", 600);
// kinetic tweakables // kinetic tweakables
pref("browser.ui.kinetic.updateInterval", 16); pref("browser.ui.kinetic.updateInterval", 16);
pref("browser.ui.kinetic.exponentialC", 1400); pref("browser.ui.kinetic.exponentialC", 1400);

View File

@ -375,6 +375,7 @@ documenttab[selected] .documenttab-selection {
background-position: left 6px center; background-position: left 6px center;
} }
#stack[viewstate="snapped"] > .overlay-button,
#stack[keyboardVisible] > .overlay-button, #stack[keyboardVisible] > .overlay-button,
#stack[autocomplete] > .overlay-button, #stack[autocomplete] > .overlay-button,
#stack[fullscreen] > .overlay-button, #stack[fullscreen] > .overlay-button,

View File

@ -181,9 +181,6 @@ abstract public class GeckoApp
protected TabsPanel mTabsPanel; protected TabsPanel mTabsPanel;
protected ButtonToast mToast; protected ButtonToast mToast;
// Handles notification messages from javascript
protected NotificationHelper mNotificationHelper;
protected LayerView mLayerView; protected LayerView mLayerView;
private AbsoluteLayout mPluginContainer; private AbsoluteLayout mPluginContainer;
@ -1235,7 +1232,6 @@ abstract public class GeckoApp
// Set up tabs panel. // Set up tabs panel.
mTabsPanel = (TabsPanel) findViewById(R.id.tabs_panel); mTabsPanel = (TabsPanel) findViewById(R.id.tabs_panel);
mNotificationHelper = new NotificationHelper(this);
mToast = new ButtonToast(findViewById(R.id.toast)); mToast = new ButtonToast(findViewById(R.id.toast));
// Determine whether we should restore tabs. // Determine whether we should restore tabs.
@ -1796,11 +1792,6 @@ abstract public class GeckoApp
alertCookie = ""; alertCookie = "";
} }
handleNotification(ACTION_ALERT_CALLBACK, alertName, alertCookie); handleNotification(ACTION_ALERT_CALLBACK, alertName, alertCookie);
if (intent.hasExtra(NotificationHelper.NOTIFICATION_ID)) {
String id = intent.getStringExtra(NotificationHelper.NOTIFICATION_ID);
mNotificationHelper.hideNotification(id);
}
} }
@Override @Override
@ -2045,8 +2036,6 @@ abstract public class GeckoApp
mPromptService.destroy(); mPromptService.destroy();
if (mTextSelection != null) if (mTextSelection != null)
mTextSelection.destroy(); mTextSelection.destroy();
if (mNotificationHelper != null)
mNotificationHelper.destroy();
if (SmsManager.getInstance() != null) { if (SmsManager.getInstance() != null) {
SmsManager.getInstance().stop(); SmsManager.getInstance().stop();

View File

@ -1330,12 +1330,6 @@ public class GeckoAppShell
return; return;
} }
} }
// Also send a notification to the observer service
// New listeners should register for these notifications since they will be called even if
// Gecko has been killed and restared between when your notification was shown and when the
// user clicked on it.
sendEventToGecko(GeckoEvent.createBroadcastEvent("Notification:Clicked", aAlertCookie));
closeNotification(aAlertName); closeNotification(aAlertName);
} }

View File

@ -98,6 +98,7 @@ public class GeckoApplication extends Application {
public void onCreate() { public void onCreate() {
HardwareUtils.init(getApplicationContext()); HardwareUtils.init(getApplicationContext());
Clipboard.init(getApplicationContext()); Clipboard.init(getApplicationContext());
NotificationHelper.init(getApplicationContext());
GeckoLoader.loadMozGlue(); GeckoLoader.loadMozGlue();
super.onCreate(); super.onCreate();
} }

View File

@ -649,7 +649,7 @@ RES_DRAWABLE_MDPI = \
res/drawable-mdpi/ic_menu_bookmark_add.png \ res/drawable-mdpi/ic_menu_bookmark_add.png \
res/drawable-mdpi/ic_menu_bookmark_remove.png \ res/drawable-mdpi/ic_menu_bookmark_remove.png \
res/drawable-mdpi/ic_menu_character_encoding.png \ res/drawable-mdpi/ic_menu_character_encoding.png \
res/drawable-mdpi/ic_menu_close_all_tabs.png \ res/drawable-mdpi/close.png \
res/drawable-mdpi/ic_menu_forward.png \ res/drawable-mdpi/ic_menu_forward.png \
res/drawable-mdpi/ic_menu_guest.png \ res/drawable-mdpi/ic_menu_guest.png \
res/drawable-mdpi/ic_menu_new_private_tab.png \ res/drawable-mdpi/ic_menu_new_private_tab.png \
@ -671,6 +671,8 @@ RES_DRAWABLE_MDPI = \
res/drawable-mdpi/icon_pageaction.png \ res/drawable-mdpi/icon_pageaction.png \
res/drawable-mdpi/icon_reading_list_empty.png \ res/drawable-mdpi/icon_reading_list_empty.png \
res/drawable-mdpi/progress_spinner.png \ res/drawable-mdpi/progress_spinner.png \
res/drawable-mdpi/play.png \
res/drawable-mdpi/pause.png \
res/drawable-mdpi/tab_indicator_divider.9.png \ res/drawable-mdpi/tab_indicator_divider.9.png \
res/drawable-mdpi/tab_indicator_selected.9.png \ res/drawable-mdpi/tab_indicator_selected.9.png \
res/drawable-mdpi/tab_indicator_selected_focused.9.png \ res/drawable-mdpi/tab_indicator_selected_focused.9.png \
@ -763,7 +765,7 @@ RES_DRAWABLE_HDPI = \
res/drawable-hdpi/ic_menu_bookmark_add.png \ res/drawable-hdpi/ic_menu_bookmark_add.png \
res/drawable-hdpi/ic_menu_bookmark_remove.png \ res/drawable-hdpi/ic_menu_bookmark_remove.png \
res/drawable-hdpi/ic_menu_character_encoding.png \ res/drawable-hdpi/ic_menu_character_encoding.png \
res/drawable-hdpi/ic_menu_close_all_tabs.png \ res/drawable-hdpi/close.png \
res/drawable-hdpi/ic_menu_forward.png \ res/drawable-hdpi/ic_menu_forward.png \
res/drawable-hdpi/ic_menu_guest.png \ res/drawable-hdpi/ic_menu_guest.png \
res/drawable-hdpi/ic_menu_new_private_tab.png \ res/drawable-hdpi/ic_menu_new_private_tab.png \
@ -818,6 +820,8 @@ RES_DRAWABLE_HDPI = \
res/drawable-hdpi/menu_item_more.png \ res/drawable-hdpi/menu_item_more.png \
res/drawable-hdpi/menu_item_uncheck.png \ res/drawable-hdpi/menu_item_uncheck.png \
res/drawable-hdpi/pin.png \ res/drawable-hdpi/pin.png \
res/drawable-hdpi/play.png \
res/drawable-hdpi/pause.png \
res/drawable-hdpi/shield.png \ res/drawable-hdpi/shield.png \
res/drawable-hdpi/shield_doorhanger.png \ res/drawable-hdpi/shield_doorhanger.png \
res/drawable-hdpi/tabs_normal.png \ res/drawable-hdpi/tabs_normal.png \
@ -862,7 +866,7 @@ RES_DRAWABLE_XHDPI = \
res/drawable-xhdpi/ic_menu_addons_filler.png \ res/drawable-xhdpi/ic_menu_addons_filler.png \
res/drawable-xhdpi/ic_menu_bookmark_add.png \ res/drawable-xhdpi/ic_menu_bookmark_add.png \
res/drawable-xhdpi/ic_menu_bookmark_remove.png \ res/drawable-xhdpi/ic_menu_bookmark_remove.png \
res/drawable-xhdpi/ic_menu_close_all_tabs.png \ res/drawable-xhdpi/close.png \
res/drawable-xhdpi/ic_menu_character_encoding.png \ res/drawable-xhdpi/ic_menu_character_encoding.png \
res/drawable-xhdpi/ic_menu_forward.png \ res/drawable-xhdpi/ic_menu_forward.png \
res/drawable-xhdpi/ic_menu_guest.png \ res/drawable-xhdpi/ic_menu_guest.png \
@ -917,6 +921,8 @@ RES_DRAWABLE_XHDPI = \
res/drawable-xhdpi/menu_item_more.png \ res/drawable-xhdpi/menu_item_more.png \
res/drawable-xhdpi/menu_item_uncheck.png \ res/drawable-xhdpi/menu_item_uncheck.png \
res/drawable-xhdpi/pin.png \ res/drawable-xhdpi/pin.png \
res/drawable-xhdpi/play.png \
res/drawable-xhdpi/pause.png \
res/drawable-xhdpi/shield.png \ res/drawable-xhdpi/shield.png \
res/drawable-xhdpi/shield_doorhanger.png \ res/drawable-xhdpi/shield_doorhanger.png \
res/drawable-xhdpi/tab_indicator_divider.9.png \ res/drawable-xhdpi/tab_indicator_divider.9.png \

View File

@ -14,6 +14,8 @@ import org.json.JSONObject;
import android.app.NotificationManager; import android.app.NotificationManager;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.IntentFilter;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.graphics.Bitmap; import android.graphics.Bitmap;
@ -24,21 +26,59 @@ import android.util.Log;
import java.util.Set; import java.util.Set;
import java.util.HashSet; import java.util.HashSet;
public class NotificationHelper implements GeckoEventListener { public final class NotificationHelper implements GeckoEventListener {
public static final String NOTIFICATION_ID = "NotificationHelper_ID"; public static final String NOTIFICATION_ID = "NotificationHelper_ID";
private static final String LOGTAG = "GeckoNotificationManager"; private static final String LOGTAG = "GeckoNotificationManager";
private Context mContext; private static final String HELPER_NOTIFICATION = "helperNotif";
private Set<String> mShowing; private static final String HELPER_BROADCAST_ACTION = "helperBroadcastAction";
public NotificationHelper(Context context) { // Attributes mandatory to be used while sending a notification from js.
private static final String TITLE_ATTR = "title";
private static final String TEXT_ATTR = "text";
private static final String ID_ATTR = "id";
private static final String SMALLICON_ATTR = "smallIcon";
// Attributes that can be used while sending a notification from js.
private static final String PROGRESS_VALUE_ATTR = "progress_value";
private static final String PROGRESS_MAX_ATTR = "progress_max";
private static final String LIGHT_ATTR = "light";
private static final String ONGOING_ATTR = "ongoing";
private static final String WHEN_ATTR = "when";
private static final String LARGE_ICON_ATTR = "largeIcon";
private static final String COOKIE_ATTR = "cookie";
private static final String EVENT_TYPE_ATTR = "eventType";
private static final String ACTIONS_ATTR = "actions";
private static final String ACTION_ATTR = "actionKind";
private static final String ACTION_TITLE_ATTR = "title";
private static final String ACTION_ICON_ATTR = "icon";
private static final String NOTIFICATION_SCHEME = "moz-notification";
private static final String CLICK_EVENT = "notification-clicked";
private static final String CLEARED_EVENT = "notification-cleared";
private static Context mContext;
private static Set<String> mShowing;
private static BroadcastReceiver mReceiver;
private static NotificationHelper mInstance;
private NotificationHelper() {
}
public static void init(Context context) {
if (mInstance != null) {
Log.w(LOGTAG, "NotificationHelper.init() called twice!");
}
mInstance = new NotificationHelper();
mContext = context; mContext = context;
mShowing = new HashSet<String>(); mShowing = new HashSet<String>();
registerEventListener("Notification:Show"); registerEventListener("Notification:Show");
registerEventListener("Notification:Hide"); registerEventListener("Notification:Hide");
registerReceiver(context);
} }
private void registerEventListener(String event) { private static void registerEventListener(String event) {
GeckoAppShell.getEventDispatcher().registerEventListener(event, this); GeckoAppShell.getEventDispatcher().registerEventListener(event, mInstance);
} }
@Override @Override
@ -50,24 +90,107 @@ public class NotificationHelper implements GeckoEventListener {
} }
} }
public boolean isHelperIntent(Intent i) {
return i.getBooleanExtra(HELPER_NOTIFICATION, false);
}
private static void registerReceiver(Context context) {
IntentFilter filter = new IntentFilter(HELPER_BROADCAST_ACTION);
// Scheme is needed, otherwise only broadcast with no data will be catched.
filter.addDataScheme(NOTIFICATION_SCHEME);
mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
mInstance.handleNotificationIntent(intent);
}
};
context.registerReceiver(mReceiver, filter);
}
private void handleNotificationIntent(Intent i) {
final Uri data = i.getData();
if (data == null) {
Log.w(LOGTAG, "handleNotificationEvent: empty data");
return;
}
final String id = data.getQueryParameter(ID_ATTR);
final String notificationType = data.getQueryParameter(EVENT_TYPE_ATTR);
if (id == null || notificationType == null) {
Log.w(LOGTAG, "handleNotificationEvent: invalid intent parameters");
return;
}
// In case the user swiped out the notification, we empty the id
// set.
if (CLEARED_EVENT.equals(notificationType)) {
mShowing.remove(id);
}
// If the notification was clicked, we are closing it.
if (CLICK_EVENT.equals(notificationType) && !i.getBooleanExtra(ONGOING_ATTR, false)) {
hideNotification(id);
}
String cookie = data.getQueryParameter(COOKIE_ATTR);
if (GeckoThread.checkLaunchState(GeckoThread.LaunchState.GeckoRunning)) {
JSONObject args = new JSONObject();
try {
args.put(NOTIFICATION_ID, id);
if (cookie != null) {
args.put(COOKIE_ATTR, cookie);
}
args.put(EVENT_TYPE_ATTR, notificationType);
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Notification:Event", args.toString()));
} catch (JSONException e) {
Log.w(LOGTAG, "Error building JSON notification arguments.", e);
}
}
}
private PendingIntent buildNotificationPendingIntent(JSONObject message, String type) {
Intent notificationIntent = new Intent(HELPER_BROADCAST_ACTION);
final boolean ongoing = message.optBoolean(ONGOING_ATTR);
notificationIntent.putExtra(ONGOING_ATTR, ongoing);
Uri.Builder b = new Uri.Builder();
b.scheme(NOTIFICATION_SCHEME).appendQueryParameter(EVENT_TYPE_ATTR, type);
try {
final String id = message.getString(ID_ATTR);
b.appendQueryParameter(ID_ATTR, id);
if (message.has(COOKIE_ATTR)) {
b.appendQueryParameter(COOKIE_ATTR,
message.getString(COOKIE_ATTR));
}
} catch (JSONException ex) {
Log.i(LOGTAG, "buildNotificationPendingIntent, error parsing", ex);
}
final Uri dataUri = b.build();
notificationIntent.setData(dataUri);
notificationIntent.putExtra(HELPER_NOTIFICATION, true);
PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
return pi;
}
private void showNotification(JSONObject message) { private void showNotification(JSONObject message) {
NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext); NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext);
// These attributes are required // These attributes are required
final String id; final String id;
try { try {
builder.setContentTitle(message.getString("title")); builder.setContentTitle(message.getString(TITLE_ATTR));
builder.setContentText(message.getString("text")); builder.setContentText(message.getString(TEXT_ATTR));
id = message.getString("id"); id = message.getString(ID_ATTR);
} catch (JSONException ex) { } catch (JSONException ex) {
Log.i(LOGTAG, "Error parsing", ex); Log.i(LOGTAG, "Error parsing", ex);
return; return;
} }
Uri imageUri = Uri.parse(message.optString("smallicon")); Uri imageUri = Uri.parse(message.optString(SMALLICON_ATTR));
builder.setSmallIcon(BitmapUtils.getResource(imageUri, R.drawable.ic_status_logo)); builder.setSmallIcon(BitmapUtils.getResource(imageUri, R.drawable.ic_status_logo));
JSONArray light = message.optJSONArray("light"); JSONArray light = message.optJSONArray(LIGHT_ATTR);
if (light != null && light.length() == 3) { if (light != null && light.length() == 3) {
try { try {
builder.setLights(light.getInt(0), builder.setLights(light.getInt(0),
@ -78,33 +201,51 @@ public class NotificationHelper implements GeckoEventListener {
} }
} }
boolean ongoing = message.optBoolean("ongoing"); boolean ongoing = message.optBoolean(ONGOING_ATTR);
builder.setOngoing(ongoing); builder.setOngoing(ongoing);
if (message.has("when")) { if (message.has(WHEN_ATTR)) {
int when = message.optInt("when"); int when = message.optInt(WHEN_ATTR);
builder.setWhen(when); builder.setWhen(when);
} }
if (message.has("largeicon")) { if (message.has(LARGE_ICON_ATTR)) {
Bitmap b = BitmapUtils.getBitmapFromDataURI(message.optString("largeicon")); Bitmap b = BitmapUtils.getBitmapFromDataURI(message.optString(LARGE_ICON_ATTR));
builder.setLargeIcon(b); builder.setLargeIcon(b);
} }
// We currently don't support a callback when these are clicked. if (message.has(PROGRESS_VALUE_ATTR) &&
// Instead we just open fennec. message.has(PROGRESS_MAX_ATTR)) {
Intent notificationIntent = new Intent(GeckoApp.ACTION_ALERT_CALLBACK); try {
String app = mContext.getClass().getName(); final int progress = message.getInt(PROGRESS_VALUE_ATTR);
notificationIntent.setClassName(AppConstants.ANDROID_PACKAGE_NAME, app); final int progressMax = message.getInt(PROGRESS_MAX_ATTR);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); builder.setProgress(progressMax, progress, false);
// if this isn't an ongoing notification, add the id to the intent so that we } catch (JSONException ex) {
// can remove the notification from our list of active notifications if its clicked Log.i(LOGTAG, "Error parsing", ex);
if (!ongoing) { }
notificationIntent.putExtra(NOTIFICATION_ID, id);
} }
PendingIntent pi = PendingIntent.getActivity(mContext, 0, notificationIntent, 0); JSONArray actions = message.optJSONArray(ACTIONS_ATTR);
if (actions != null) {
try {
for (int i = 0; i < actions.length(); i++) {
JSONObject action = actions.getJSONObject(i);
final PendingIntent pending = buildNotificationPendingIntent(message, action.getString(ACTION_ATTR));
final String actionTitle = action.getString(ACTION_TITLE_ATTR);
final Uri actionImage = Uri.parse(action.optString(ACTION_ICON_ATTR));
builder.addAction(BitmapUtils.getResource(actionImage, R.drawable.ic_status_logo),
actionTitle,
pending);
}
} catch (JSONException ex) {
Log.i(LOGTAG, "Error parsing", ex);
}
}
PendingIntent pi = buildNotificationPendingIntent(message, CLICK_EVENT);
builder.setContentIntent(pi); builder.setContentIntent(pi);
PendingIntent deletePendingIntent = buildNotificationPendingIntent(message, CLEARED_EVENT);
builder.setDeleteIntent(deletePendingIntent);
GeckoAppShell.sNotificationClient.add(id.hashCode(), builder.build()); GeckoAppShell.sNotificationClient.add(id.hashCode(), builder.build());
if (!mShowing.contains(id)) { if (!mShowing.contains(id)) {
@ -134,8 +275,4 @@ public class NotificationHelper implements GeckoEventListener {
hideNotification(id); hideNotification(id);
} }
} }
public void destroy() {
clearAll();
}
} }

View File

Before

Width:  |  Height:  |  Size: 377 B

After

Width:  |  Height:  |  Size: 377 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 763 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 670 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 611 B

View File

Before

Width:  |  Height:  |  Size: 277 B

After

Width:  |  Height:  |  Size: 277 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 497 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 463 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 380 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 479 B

View File

Before

Width:  |  Height:  |  Size: 594 B

After

Width:  |  Height:  |  Size: 594 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 389 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 853 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 467 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 550 B

View File

@ -62,6 +62,11 @@
android:icon="@drawable/ic_menu_apps" android:icon="@drawable/ic_menu_apps"
android:title="@string/apps"/> android:title="@string/apps"/>
<item android:id="@+id/new_guest_session"
android:icon="@drawable/ic_menu_guest"
android:visible="false"
android:title="@string/new_guest_session"/>
</menu> </menu>
</item> </item>
@ -75,11 +80,6 @@
android:icon="@drawable/ic_menu_settings" android:icon="@drawable/ic_menu_settings"
android:title="@string/settings" /> android:title="@string/settings" />
<item android:id="@+id/new_guest_session"
android:icon="@drawable/ic_menu_guest"
android:visible="false"
android:title="@string/new_guest_session"/>
<item android:id="@+id/exit_guest_session" <item android:id="@+id/exit_guest_session"
android:icon="@drawable/ic_menu_guest" android:icon="@drawable/ic_menu_guest"
android:visible="false" android:visible="false"

View File

@ -63,6 +63,11 @@
android:icon="@drawable/ic_menu_apps" android:icon="@drawable/ic_menu_apps"
android:title="@string/apps"/> android:title="@string/apps"/>
<item android:id="@+id/new_guest_session"
android:icon="@drawable/ic_menu_guest"
android:visible="false"
android:title="@string/new_guest_session"/>
</menu> </menu>
</item> </item>
@ -76,11 +81,6 @@
android:icon="@drawable/ic_menu_settings" android:icon="@drawable/ic_menu_settings"
android:title="@string/settings" /> android:title="@string/settings" />
<item android:id="@+id/new_guest_session"
android:icon="@drawable/ic_menu_guest"
android:visible="false"
android:title="@string/new_guest_session"/>
<item android:id="@+id/exit_guest_session" <item android:id="@+id/exit_guest_session"
android:icon="@drawable/ic_menu_guest" android:icon="@drawable/ic_menu_guest"
android:visible="false" android:visible="false"

View File

@ -63,6 +63,11 @@
android:icon="@drawable/ic_menu_apps" android:icon="@drawable/ic_menu_apps"
android:title="@string/apps"/> android:title="@string/apps"/>
<item android:id="@+id/new_guest_session"
android:icon="@drawable/ic_menu_guest"
android:visible="false"
android:title="@string/new_guest_session"/>
</menu> </menu>
</item> </item>
@ -76,11 +81,6 @@
android:icon="@drawable/ic_menu_settings" android:icon="@drawable/ic_menu_settings"
android:title="@string/settings" /> android:title="@string/settings" />
<item android:id="@+id/new_guest_session"
android:icon="@drawable/ic_menu_guest"
android:visible="false"
android:title="@string/new_guest_session"/>
<item android:id="@+id/exit_guest_session" <item android:id="@+id/exit_guest_session"
android:icon="@drawable/ic_menu_guest" android:icon="@drawable/ic_menu_guest"
android:visible="false" android:visible="false"

View File

@ -2,6 +2,7 @@
package @ANDROID_PACKAGE_NAME@.tests; package @ANDROID_PACKAGE_NAME@.tests;
import @ANDROID_PACKAGE_NAME@.*; import @ANDROID_PACKAGE_NAME@.*;
import android.os.Build;
abstract class PixelTest extends BaseTest { abstract class PixelTest extends BaseTest {
private static final long PAINT_CLEAR_DELAY = 10000; // milliseconds private static final long PAINT_CLEAR_DELAY = 10000; // milliseconds
@ -9,7 +10,11 @@ abstract class PixelTest extends BaseTest {
protected final PaintedSurface loadAndGetPainted(String url) { protected final PaintedSurface loadAndGetPainted(String url) {
Actions.RepeatedEventExpecter paintExpecter = mActions.expectPaint(); Actions.RepeatedEventExpecter paintExpecter = mActions.expectPaint();
loadUrl(url); loadUrl(url);
verifyHomePagerHidden(); // Skip this on the Tegras (Android 2.2) since they are too slow,
// which results in too many intermittent failures.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
verifyHomePagerHidden();
}
paintExpecter.blockUntilClear(PAINT_CLEAR_DELAY); paintExpecter.blockUntilClear(PAINT_CLEAR_DELAY);
paintExpecter.unregisterListener(); paintExpecter.unregisterListener();
PaintedSurface p = mDriver.getPaintedSurface(); PaintedSurface p = mDriver.getPaintedSurface();

View File

@ -143,12 +143,18 @@ public class testSettingsMenuItems extends PixelTest {
ClassLoader classLoader = getActivity().getClassLoader(); ClassLoader classLoader = getActivity().getClassLoader();
Class appConstants = classLoader.loadClass("org.mozilla.gecko.AppConstants"); Class appConstants = classLoader.loadClass("org.mozilla.gecko.AppConstants");
// Text reflow // Preferences dependent on RELEASE_BUILD
Field textReflowField = appConstants.getField("RELEASE_BUILD"); Field releaseBuildField = appConstants.getField("RELEASE_BUILD");
boolean textReflow = textReflowField.getBoolean(appConstants); boolean releaseBuild = releaseBuildField.getBoolean(appConstants);
if (textReflow) { if (!releaseBuild) {
// Text reflow - only built if *not* release build
String[] textReflowUi = { "Text reflow" }; String[] textReflowUi = { "Text reflow" };
settingsMap.get("Display").add(textReflowUi); settingsMap.get("Display").add(textReflowUi);
// Anonymous cell tower/wifi collection - only built if *not* release build
String[] networkReportingUi = { "Mozilla location services", "Help improve geolocation services for the Open Web by letting " + BRAND_NAME + " collect and send anonymous cellular tower data" };
settingsMap.get("Mozilla").add(networkReportingUi);
} }
// Automatic updates // Automatic updates
@ -167,14 +173,6 @@ public class testSettingsMenuItems extends PixelTest {
settingsMap.get("Mozilla").add(crashReporterUi); settingsMap.get("Mozilla").add(crashReporterUi);
} }
// Anonymous cell tower/wifi collection; built if *not* a RELEASE_BUILD
Field releaseBuildField = appConstants.getField("RELEASE_BUILD");
boolean releaseBuild = releaseBuildField.getBoolean(appConstants);
if (!releaseBuild) {
String[] networkReportingUi = { "Mozilla location services", "Help improve geolocation services for the Open Web by letting " + BRAND_NAME + " collect and send anonymous cellular tower data" };
settingsMap.get("Mozilla").add(networkReportingUi);
}
// Telemetry // Telemetry
Field telemetryField = appConstants.getField("MOZ_TELEMETRY_REPORTING"); Field telemetryField = appConstants.getField("MOZ_TELEMETRY_REPORTING");
boolean telemetry = telemetryField.getBoolean(appConstants); boolean telemetry = telemetryField.getBoolean(appConstants);

View File

@ -5136,10 +5136,16 @@ var FormAssistant = {
// Reset invalid submit state on each pageshow // Reset invalid submit state on each pageshow
case "pageshow": case "pageshow":
let target = aEvent.originalTarget; if (!this._invalidSubmit)
let selectedDocument = BrowserApp.selectedBrowser.contentDocument; return;
if (target == selectedDocument || target.ownerDocument == selectedDocument)
this._invalidSubmit = false; let selectedBrowser = BrowserApp.selectedBrowser;
if (selectedBrowser) {
let selectedDocument = selectedBrowser.contentDocument;
let target = aEvent.originalTarget;
if (target == selectedDocument || target.ownerDocument == selectedDocument)
this._invalidSubmit = false;
}
} }
}, },

View File

@ -10,6 +10,27 @@ function dump(a) {
} }
const URI_GENERIC_ICON_DOWNLOAD = "drawable://alert_download"; const URI_GENERIC_ICON_DOWNLOAD = "drawable://alert_download";
const URI_PAUSE_ICON = "drawable://pause";
const URI_CANCEL_ICON = "drawable://close";
const URI_RESUME_ICON = "drawable://play";
const PAUSE_ACTION = {
actionKind : "pause",
title : Strings.browser.GetStringFromName("alertDownloadsPause"),
icon : URI_PAUSE_ICON
};
const CANCEL_ACTION = {
actionKind : "cancel",
title : Strings.browser.GetStringFromName("alertDownloadsCancel"),
icon : URI_CANCEL_ICON
};
const RESUME_ACTION = {
actionKind : "resume",
title : Strings.browser.GetStringFromName("alertDownloadsResume"),
icon : URI_RESUME_ICON
};
XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
@ -18,6 +39,7 @@ var Downloads = {
_dlmgr: null, _dlmgr: null,
_progressAlert: null, _progressAlert: null,
_privateDownloads: [], _privateDownloads: [],
_showingPrompt: false,
_getLocalFile: function dl__getLocalFile(aFileURI) { _getLocalFile: function dl__getLocalFile(aFileURI) {
// if this is a URL, get the file from that // if this is a URL, get the file from that
@ -36,6 +58,7 @@ var Downloads = {
this._progressAlert = new AlertDownloadProgressListener(); this._progressAlert = new AlertDownloadProgressListener();
this._dlmgr.addPrivacyAwareListener(this._progressAlert); this._dlmgr.addPrivacyAwareListener(this._progressAlert);
Services.obs.addObserver(this, "last-pb-context-exited", true); Services.obs.addObserver(this, "last-pb-context-exited", true);
Services.obs.addObserver(this, "Notification:Event", true);
}, },
openDownload: function dl_openDownload(aDownload) { openDownload: function dl_openDownload(aDownload) {
@ -60,61 +83,113 @@ var Downloads = {
OS.File.remove(f.path); OS.File.remove(f.path);
}, },
showAlert: function dl_showAlert(aDownload, aMessage, aTitle, aIcon) { getNotificationIdFromDownload: function dl_getNotificationIdFromDownload(aDownload) {
let self = this; return aDownload.target.spec.replace("file:", "download:");
},
// Use this flag to make sure we only show one prompt at a time showNotification: function dl_showNotification(aDownload, aMessage, aTitle, aOptions) {
let cancelPrompt = false; let msg = {
type: "Notification:Show",
// Callback for tapping on the alert popup id: this.getNotificationIdFromDownload(aDownload),
let observer = { title: aTitle,
observe: function (aSubject, aTopic, aData) { smallIcon: URI_GENERIC_ICON_DOWNLOAD,
if (aTopic == "alertclickcallback") { text: aMessage,
if (aDownload.state == Ci.nsIDownloadManager.DOWNLOAD_FINISHED) { ongoing: false,
// Only open the downloaded file if the download is complete cookie: aDownload.guid
self.openDownload(aDownload);
} else if (aDownload.state == Ci.nsIDownloadManager.DOWNLOAD_DOWNLOADING &&
!cancelPrompt) {
cancelPrompt = true;
// Open a prompt that offers a choice to cancel the download
let title = Strings.browser.GetStringFromName("downloadCancelPromptTitle");
let message = Strings.browser.GetStringFromName("downloadCancelPromptMessage");
let flags = Services.prompt.BUTTON_POS_0 * Services.prompt.BUTTON_TITLE_YES +
Services.prompt.BUTTON_POS_1 * Services.prompt.BUTTON_TITLE_NO;
let choice = Services.prompt.confirmEx(null, title, message, flags,
null, null, null, null, {});
if (choice == 0)
self.cancelDownload(aDownload);
cancelPrompt = false;
}
}
}
}; };
if (!aIcon) if (aOptions && aOptions.icon) {
aIcon = URI_GENERIC_ICON_DOWNLOAD; msg.smallIcon = aOptions.icon;
if (aDownload.isPrivate) {
this._privateDownloads.push(aDownload);
} }
var notifier = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService); if (aOptions && aOptions.percentComplete) {
notifier.showAlertNotification(aIcon, aTitle, aMessage, true, "", observer, msg.progress_value = aOptions.percentComplete;
aDownload.target.spec.replace("file:", "download:")); msg.progress_max = 100;
}
if (aOptions && aOptions.actions) {
msg.actions = aOptions.actions;
}
if (aOptions && aOptions.ongoing) {
msg.ongoing = aOptions.ongoing;
}
this._bridge.handleGeckoMessage(JSON.stringify(msg));
},
removeNotification: function dl_removeNotification(aDownload) {
let msg = {
type: "Notification:Hide",
id: this.getNotificationIdFromDownload(aDownload)
};
this._bridge.handleGeckoMessage(JSON.stringify(msg));
},
showCancelConfirmPrompt: function dl_showCancelConfirmPrompt(aDownload) {
if (this._showingPrompt)
return;
this._showingPrompt = true;
// Open a prompt that offers a choice to cancel the download
let title = Strings.browser.GetStringFromName("downloadCancelPromptTitle");
let message = Strings.browser.GetStringFromName("downloadCancelPromptMessage");
let flags = Services.prompt.BUTTON_POS_0 * Services.prompt.BUTTON_TITLE_YES +
Services.prompt.BUTTON_POS_1 * Services.prompt.BUTTON_TITLE_NO;
let choice = Services.prompt.confirmEx(null, title, message, flags,
null, null, null, null, {});
if (choice == 0)
this.cancelDownload(aDownload);
this._showingPrompt = false;
},
handleClickEvent: function dl_handleClickEvent(aDownload) {
// Only open the downloaded file if the download is complete
if (aDownload.state == Ci.nsIDownloadManager.DOWNLOAD_FINISHED)
this.openDownload(aDownload);
else if (aDownload.state == Ci.nsIDownloadManager.DOWNLOAD_DOWNLOADING ||
aDownload.state == Ci.nsIDownloadManager.DOWNLOAD_PAUSED)
this.showCancelConfirmPrompt(aDownload);
},
handleNotificationEvent: function dl_handleNotificationEvent(aNotifData, aDownload) {
switch (aNotifData.eventType) {
case "notification-clicked":
this.handleClickEvent(aDownload);
break;
case "cancel":
this.cancelDownload(aDownload);
break;
case "pause":
aDownload.pause();
break;
case "resume":
aDownload.resume();
break;
case "notification-cleared":
// notification cleared by the user
break;
}
}, },
// observer for last-pb-context-exited // observer for last-pb-context-exited
observe: function dl_observe(aSubject, aTopic, aData) { observe: function dl_observe(aSubject, aTopic, aData) {
let alertsService = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService); switch (aTopic) {
let progressListener = alertsService.QueryInterface(Ci.nsIAlertsProgressListener); case "Notification:Event": {
let download; let data = JSON.parse(aData);
while ((download = this._privateDownloads.pop())) { let guid = data.cookie;
try { this._dlmgr.getDownloadByGUID(guid, (function(status, download) {
let notificationName = download.target.spec.replace("file:", "download:"); if (Components.isSuccessCode(status))
progressListener.onCancel(notificationName); this.handleNotificationEvent(data, download);
} catch (e) { }).bind(this));
dump("Error removing private download: " + e); break;
}
case "last-pb-context-exited": {
let download;
while ((download = this._privateDownloads.pop())) {
try {
Downloads.removeNotification(download);
} catch (e) {
dump("Error removing private download: " + e);
}
}
break;
} }
} }
}, },
@ -125,6 +200,12 @@ var Downloads = {
!aIID.equals(Ci.nsISupportsWeakReference)) !aIID.equals(Ci.nsISupportsWeakReference))
throw Components.results.NS_ERROR_NO_INTERFACE; throw Components.results.NS_ERROR_NO_INTERFACE;
return this; return this;
},
get _bridge() {
delete this._bridge;
return this._bridge = Cc["@mozilla.org/android/bridge;1"].getService(Ci.nsIAndroidBridge);
} }
}; };
@ -143,9 +224,8 @@ AlertDownloadProgressListener.prototype = {
} catch(ex) { } } catch(ex) { }
let contentLength = aDownload.size; let contentLength = aDownload.size;
if (availableSpace > 0 && contentLength > 0 && contentLength > availableSpace) { if (availableSpace > 0 && contentLength > 0 && contentLength > availableSpace) {
Downloads.showAlert(aDownload, strings.GetStringFromName("alertDownloadsNoSpace"), Downloads.showNotification(aDownload, strings.GetStringFromName("alertDownloadsNoSpace"),
strings.GetStringFromName("alertDownloadsSize")); strings.GetStringFromName("alertDownloadsSize"));
aDownload.cancel(); aDownload.cancel();
} }
@ -153,10 +233,11 @@ AlertDownloadProgressListener.prototype = {
// Undetermined progress is not supported yet // Undetermined progress is not supported yet
return; return;
} }
let alertsService = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService); Downloads.showNotification(aDownload, aDownload.percentComplete + "%",
let progressListener = alertsService.QueryInterface(Ci.nsIAlertsProgressListener); aDownload.displayName, { percentComplete: aDownload.percentComplete,
let notificationName = aDownload.target.spec.replace("file:", "download:"); ongoing: true,
progressListener.onProgress(notificationName, aDownload.percentComplete, 100); actions: [PAUSE_ACTION, CANCEL_ACTION] });
}, },
onDownloadStateChange: function(aState, aDownload) { onDownloadStateChange: function(aState, aDownload) {
@ -164,19 +245,21 @@ AlertDownloadProgressListener.prototype = {
switch (state) { switch (state) {
case Ci.nsIDownloadManager.DOWNLOAD_QUEUED: case Ci.nsIDownloadManager.DOWNLOAD_QUEUED:
NativeWindow.toast.show(Strings.browser.GetStringFromName("alertDownloadsToast"), "long"); NativeWindow.toast.show(Strings.browser.GetStringFromName("alertDownloadsToast"), "long");
Downloads.showAlert(aDownload, Strings.browser.GetStringFromName("alertDownloadsStart2"), Downloads.showNotification(aDownload, Strings.browser.GetStringFromName("alertDownloadsStart2"),
aDownload.displayName); aDownload.displayName);
break; break;
case Ci.nsIDownloadManager.DOWNLOAD_PAUSED:
Downloads.showNotification(aDownload, aDownload.percentComplete + "%",
aDownload.displayName, { percentComplete: aDownload.percentComplete,
ongoing: true,
actions: [RESUME_ACTION, CANCEL_ACTION] });
break;
case Ci.nsIDownloadManager.DOWNLOAD_FAILED: case Ci.nsIDownloadManager.DOWNLOAD_FAILED:
case Ci.nsIDownloadManager.DOWNLOAD_CANCELED: case Ci.nsIDownloadManager.DOWNLOAD_CANCELED:
case Ci.nsIDownloadManager.DOWNLOAD_BLOCKED_PARENTAL: case Ci.nsIDownloadManager.DOWNLOAD_BLOCKED_PARENTAL:
case Ci.nsIDownloadManager.DOWNLOAD_DIRTY: case Ci.nsIDownloadManager.DOWNLOAD_DIRTY:
case Ci.nsIDownloadManager.DOWNLOAD_FINISHED: { case Ci.nsIDownloadManager.DOWNLOAD_FINISHED: {
let alertsService = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService); Downloads.removeNotification(aDownload);
let progressListener = alertsService.QueryInterface(Ci.nsIAlertsProgressListener);
let notificationName = aDownload.target.spec.replace("file:", "download:");
progressListener.onCancel(notificationName);
if (aDownload.isPrivate) { if (aDownload.isPrivate) {
let index = this._privateDownloads.indexOf(aDownload); let index = this._privateDownloads.indexOf(aDownload);
if (index != -1) { if (index != -1) {
@ -185,7 +268,7 @@ AlertDownloadProgressListener.prototype = {
} }
if (state == Ci.nsIDownloadManager.DOWNLOAD_FINISHED) { if (state == Ci.nsIDownloadManager.DOWNLOAD_FINISHED) {
Downloads.showAlert(aDownload, Strings.browser.GetStringFromName("alertDownloadsDone2"), Downloads.showNotification(aDownload, Strings.browser.GetStringFromName("alertDownloadsDone2"),
aDownload.displayName); aDownload.displayName);
} }
break; break;

View File

@ -16,6 +16,9 @@ alertCantOpenDownload=Can't open file. Tap to save it.
alertDownloadsSize=Download too big alertDownloadsSize=Download too big
alertDownloadsNoSpace=Not enough storage space alertDownloadsNoSpace=Not enough storage space
alertDownloadsToast=Download started… alertDownloadsToast=Download started…
alertDownloadsPause=Pause
alertDownloadsResume=Resume
alertDownloadsCancel=Cancel
alertFullScreenToast=Press BACK to leave full-screen mode alertFullScreenToast=Press BACK to leave full-screen mode

View File

@ -9,9 +9,8 @@ let Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/LoadContextInfo.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "FormHistory", Cu.import("resource://gre/modules/FormHistory.jsm");
"resource://gre/modules/FormHistory.jsm");
function dump(a) { function dump(a) {
Services.console.logStringMessage(a); Services.console.logStringMessage(a);

View File

@ -313,7 +313,7 @@ this.DownloadIntegration = {
* @return {Promise} * @return {Promise}
* @resolves The nsIFile of downloads directory. * @resolves The nsIFile of downloads directory.
*/ */
getUserDownloadsDirectory: function DI_getUserDownloadsDirectory() { getPreferredDownloadsDirectory: function DI_getPreferredDownloadsDirectory() {
return Task.spawn(function() { return Task.spawn(function() {
let directory = null; let directory = null;
let prefValue = 1; let prefValue = 1;
@ -356,7 +356,7 @@ this.DownloadIntegration = {
return Task.spawn(function() { return Task.spawn(function() {
let directory = null; let directory = null;
#ifdef XP_MACOSX #ifdef XP_MACOSX
directory = yield this.getUserDownloadsDirectory(); directory = yield this.getPreferredDownloadsDirectory();
#elifdef ANDROID #elifdef ANDROID
directory = yield this.getSystemDownloadsDirectory(); directory = yield this.getSystemDownloadsDirectory();
#else #else

View File

@ -273,8 +273,8 @@ this.Downloads = {
* @return {Promise} * @return {Promise}
* @resolves The nsIFile of downloads directory. * @resolves The nsIFile of downloads directory.
*/ */
getUserDownloadsDirectory: function D_getUserDownloadsDirectory() { getPreferredDownloadsDirectory: function D_getPreferredDownloadsDirectory() {
return DownloadIntegration.getUserDownloadsDirectory(); return DownloadIntegration.getPreferredDownloadsDirectory();
}, },
/** /**

View File

@ -116,10 +116,10 @@ add_task(function test_getSystemDownloadsDirectory()
}); });
/** /**
* Tests that the getUserDownloadsDirectory returns a valid nsFile * Tests that the getPreferredDownloadsDirectory returns a valid nsFile
* download directory object. * download directory object.
*/ */
add_task(function test_getUserDownloadsDirectory() add_task(function test_getPreferredDownloadsDirectory()
{ {
let folderListPrefName = "browser.download.folderList"; let folderListPrefName = "browser.download.folderList";
let dirPrefName = "browser.download.dir"; let dirPrefName = "browser.download.dir";
@ -132,20 +132,20 @@ add_task(function test_getUserDownloadsDirectory()
// Should return the system downloads directory. // Should return the system downloads directory.
Services.prefs.setIntPref(folderListPrefName, 1); Services.prefs.setIntPref(folderListPrefName, 1);
let systemDir = yield DownloadIntegration.getSystemDownloadsDirectory(); let systemDir = yield DownloadIntegration.getSystemDownloadsDirectory();
let downloadDir = yield DownloadIntegration.getUserDownloadsDirectory(); let downloadDir = yield DownloadIntegration.getPreferredDownloadsDirectory();
do_check_true(downloadDir instanceof Ci.nsIFile); do_check_true(downloadDir instanceof Ci.nsIFile);
do_check_eq(downloadDir.path, systemDir.path); do_check_eq(downloadDir.path, systemDir.path);
// Should return the desktop directory. // Should return the desktop directory.
Services.prefs.setIntPref(folderListPrefName, 0); Services.prefs.setIntPref(folderListPrefName, 0);
downloadDir = yield DownloadIntegration.getUserDownloadsDirectory(); downloadDir = yield DownloadIntegration.getPreferredDownloadsDirectory();
do_check_true(downloadDir instanceof Ci.nsIFile); do_check_true(downloadDir instanceof Ci.nsIFile);
do_check_eq(downloadDir.path, Services.dirsvc.get("Desk", Ci.nsIFile).path); do_check_eq(downloadDir.path, Services.dirsvc.get("Desk", Ci.nsIFile).path);
// Should return the system downloads directory because the dir preference // Should return the system downloads directory because the dir preference
// is not set. // is not set.
Services.prefs.setIntPref(folderListPrefName, 2); Services.prefs.setIntPref(folderListPrefName, 2);
let downloadDir = yield DownloadIntegration.getUserDownloadsDirectory(); let downloadDir = yield DownloadIntegration.getPreferredDownloadsDirectory();
do_check_true(downloadDir instanceof Ci.nsIFile); do_check_true(downloadDir instanceof Ci.nsIFile);
do_check_eq(downloadDir.path, systemDir.path); do_check_eq(downloadDir.path, systemDir.path);
@ -154,7 +154,7 @@ add_task(function test_getUserDownloadsDirectory()
let tempDir = Services.dirsvc.get("TmpD", Ci.nsIFile); let tempDir = Services.dirsvc.get("TmpD", Ci.nsIFile);
tempDir.append(time); tempDir.append(time);
Services.prefs.setComplexValue("browser.download.dir", Ci.nsIFile, tempDir); Services.prefs.setComplexValue("browser.download.dir", Ci.nsIFile, tempDir);
downloadDir = yield DownloadIntegration.getUserDownloadsDirectory(); downloadDir = yield DownloadIntegration.getPreferredDownloadsDirectory();
do_check_true(downloadDir instanceof Ci.nsIFile); do_check_true(downloadDir instanceof Ci.nsIFile);
do_check_eq(downloadDir.path, tempDir.path); do_check_eq(downloadDir.path, tempDir.path);
do_check_true(yield OS.File.exists(downloadDir.path)); do_check_true(yield OS.File.exists(downloadDir.path));
@ -166,13 +166,13 @@ add_task(function test_getUserDownloadsDirectory()
tempDir.append("dir_not_exist"); tempDir.append("dir_not_exist");
tempDir.append(time); tempDir.append(time);
Services.prefs.setComplexValue("browser.download.dir", Ci.nsIFile, tempDir); Services.prefs.setComplexValue("browser.download.dir", Ci.nsIFile, tempDir);
downloadDir = yield DownloadIntegration.getUserDownloadsDirectory(); downloadDir = yield DownloadIntegration.getPreferredDownloadsDirectory();
do_check_eq(downloadDir.path, systemDir.path); do_check_eq(downloadDir.path, systemDir.path);
// Should return the system downloads directory because the folderList // Should return the system downloads directory because the folderList
// preference is invalid // preference is invalid
Services.prefs.setIntPref(folderListPrefName, 999); Services.prefs.setIntPref(folderListPrefName, 999);
let downloadDir = yield DownloadIntegration.getUserDownloadsDirectory(); let downloadDir = yield DownloadIntegration.getPreferredDownloadsDirectory();
do_check_eq(downloadDir.path, systemDir.path); do_check_eq(downloadDir.path, systemDir.path);
cleanup(); cleanup();
@ -188,8 +188,8 @@ add_task(function test_getTemporaryDownloadsDirectory()
do_check_true(downloadDir instanceof Ci.nsIFile); do_check_true(downloadDir instanceof Ci.nsIFile);
if ("nsILocalFileMac" in Ci) { if ("nsILocalFileMac" in Ci) {
let userDownloadDir = yield DownloadIntegration.getUserDownloadsDirectory(); let preferredDownloadDir = yield DownloadIntegration.getPreferredDownloadsDirectory();
do_check_eq(downloadDir.path, userDownloadDir.path); do_check_eq(downloadDir.path, preferredDownloadDir.path);
} else { } else {
let tempDir = Services.dirsvc.get("TmpD", Ci.nsIFile); let tempDir = Services.dirsvc.get("TmpD", Ci.nsIFile);
do_check_eq(downloadDir.path, tempDir.path); do_check_eq(downloadDir.path, tempDir.path);

View File

@ -146,12 +146,12 @@ add_task(function test_getSystemDownloadsDirectory()
}); });
/** /**
* Tests that the getUserDownloadsDirectory returns a valid nsFile * Tests that the getPreferredDownloadsDirectory returns a valid nsFile
* download directory object. * download directory object.
*/ */
add_task(function test_getUserDownloadsDirectory() add_task(function test_getPreferredDownloadsDirectory()
{ {
let downloadDir = yield Downloads.getUserDownloadsDirectory(); let downloadDir = yield Downloads.getPreferredDownloadsDirectory();
do_check_true(downloadDir instanceof Ci.nsIFile); do_check_true(downloadDir instanceof Ci.nsIFile);
}); });

View File

@ -214,6 +214,11 @@ exports.winIsAbsolute = winIsAbsolute;
let normalize = function(path) { let normalize = function(path) {
let stack = []; let stack = [];
if (!path.startsWith("\\\\")) {
// Normalize "/" to "\\"
path = path.replace(/\//g, "\\");
}
// Remove the drive (we will put it back at the end) // Remove the drive (we will put it back at the end)
let root = this.winGetDrive(path); let root = this.winGetDrive(path);
if (root) { if (root) {
@ -223,9 +228,6 @@ let normalize = function(path) {
// Remember whether we need to restore a leading "\\" or drive name. // Remember whether we need to restore a leading "\\" or drive name.
let absolute = this.winIsAbsolute(path); let absolute = this.winIsAbsolute(path);
// Normalize "/" to "\\"
path = path.replace("/", "\\");
// And now, fill |stack| from the components, // And now, fill |stack| from the components,
// popping whenever there is a ".." // popping whenever there is a ".."
path.split("\\").forEach(function loop(v) { path.split("\\").forEach(function loop(v) {

View File

@ -88,6 +88,21 @@ function run_test()
do_check_eq(Win.winGetDrive("c:\\abc"), "c:"); do_check_eq(Win.winGetDrive("c:\\abc"), "c:");
do_check_eq(Win.winGetDrive("c:\\abc\\d\\e\\f\\g"), "c:"); do_check_eq(Win.winGetDrive("c:\\abc\\d\\e\\f\\g"), "c:");
do_print("Forwardslash-separated, no drive");
do_check_eq(Win.normalize("/a/b/c"), "\\a\\b\\c");
do_check_eq(Win.normalize("/a/b////c"), "\\a\\b\\c");
do_check_eq(Win.normalize("/a/b/c///"), "\\a\\b\\c");
do_check_eq(Win.normalize("/a/b/c/../../../d/e/f"), "\\d\\e\\f");
do_check_eq(Win.normalize("a/b/c/../../../d/e/f"), "d\\e\\f");
do_print("Forwardslash-separated, with a drive");
do_check_eq(Win.normalize("c:/a/b/c"), "c:\\a\\b\\c");
do_check_eq(Win.normalize("c:/a/b////c"), "c:\\a\\b\\c");
do_check_eq(Win.normalize("c:////a/b/c"), "c:\\a\\b\\c");
do_check_eq(Win.normalize("c:/a/b/c///"), "c:\\a\\b\\c");
do_check_eq(Win.normalize("c:/a/b/c/../../../d/e/f"), "c:\\d\\e\\f");
do_check_eq(Win.normalize("c:a/b/c/../../../d/e/f"), "c:d\\e\\f");
do_print("Backslash-separated, UNC-style"); do_print("Backslash-separated, UNC-style");
do_check_eq(Win.basename("\\\\a\\b"), "b"); do_check_eq(Win.basename("\\\\a\\b"), "b");
do_check_eq(Win.basename("\\\\a\\b\\"), ""); do_check_eq(Win.basename("\\\\a\\b\\"), "");

View File

@ -19,7 +19,7 @@ this.LayoutHelpers = LayoutHelpers = function(aTopLevelWindow) {
this._topDocShell = aTopLevelWindow.QueryInterface(Ci.nsIInterfaceRequestor) this._topDocShell = aTopLevelWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation) .getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell); .QueryInterface(Ci.nsIDocShell);
} };
LayoutHelpers.prototype = { LayoutHelpers.prototype = {
@ -366,18 +366,28 @@ LayoutHelpers.prototype = {
/** /**
* like win.frameElement, but goes through mozbrowsers and mozapps iframes. * like win.frameElement, but goes through mozbrowsers and mozapps iframes.
*
* @param DOMWindow win The window to get the frame for
* @return DOMElement The element(only <iframe> for now) in which the window
* is embedded. A null return from this function does not imply that it is a
* top level window. Use isTopLevelWindow(win) if needed.
*/ */
getFrameElement: function LH_getFrameElement(win) { getFrameElement: function LH_getFrameElement(win) {
if (this.isTopLevelWindow(win)) { if (this.isTopLevelWindow(win)) {
return null; return null;
} }
// Get the docShell for that window
let docShell = win.QueryInterface(Ci.nsIInterfaceRequestor) let docShell = win.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation) .getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell); .QueryInterface(Ci.nsIDocShell);
if (docShell.isBrowserOrApp) { if (docShell.isBrowserOrApp) {
// Get the docShell's same-type parent ignoring mozBrowser and mozApp
// boundaries
let parentDocShell = docShell.getSameTypeParentIgnoreBrowserAndAppBoundaries(); let parentDocShell = docShell.getSameTypeParentIgnoreBrowserAndAppBoundaries();
// Once we have a parent, get all its iframes and loop through them
// to find `win`. If we do, it means win is a nested iframe
let parentDoc = parentDocShell.contentViewer.DOMDocument; let parentDoc = parentDocShell.contentViewer.DOMDocument;
let allIframes = parentDoc.querySelectorAll("iframe"); let allIframes = parentDoc.querySelectorAll("iframe");
for (let f of allIframes) { for (let f of allIframes) {
@ -385,6 +395,7 @@ LayoutHelpers.prototype = {
return f; return f;
} }
} }
return null; return null;
} else { } else {
return win.frameElement; return win.frameElement;

View File

@ -724,25 +724,26 @@ var ProgressListener = Class({
this.webProgress = null; this.webProgress = null;
}, },
onStateChange: makeInfallible(function stateChange(progress, request, flag, status) { onStateChange: makeInfallible(function stateChange(progress, request, flags, status) {
if (!this.webProgress) { if (!this.webProgress) {
console.warn("got an onStateChange after destruction"); console.warn("got an onStateChange after destruction");
return; return;
} }
let isWindow = flag & Ci.nsIWebProgressListener.STATE_IS_WINDOW; let isWindow = flags & Ci.nsIWebProgressListener.STATE_IS_WINDOW;
let isDocument = flag & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT; let isDocument = flags & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT;
if (!(isWindow || isDocument)) { if (!(isWindow || isDocument)) {
return; return;
} }
if (isDocument && (flag & Ci.nsIWebProgressListener.STATE_START)) { if (isDocument && (flags & Ci.nsIWebProgressListener.STATE_START)) {
events.emit(this, "windowchange-start", progress.DOMWindow); events.emit(this, "windowchange-start", progress.DOMWindow);
} }
if (isWindow && (flag & Ci.nsIWebProgressListener.STATE_STOP)) { if (isWindow && (flags & Ci.nsIWebProgressListener.STATE_STOP)) {
events.emit(this, "windowchange-stop", progress.DOMWindow); events.emit(this, "windowchange-stop", progress.DOMWindow);
} }
}), }),
onProgressChange: function() {}, onProgressChange: function() {},
onSecurityChange: function() {}, onSecurityChange: function() {},
onStatusChange: function() {}, onStatusChange: function() {},
@ -1736,7 +1737,8 @@ var WalkerActor = protocol.ActorClass({
onFrameLoad: function(window) { onFrameLoad: function(window) {
let frame = this.layoutHelpers.getFrameElement(window); let frame = this.layoutHelpers.getFrameElement(window);
if (!frame && !this.rootDoc) { let isTopLevel = this.layoutHelpers.isTopLevelWindow(window);
if (!frame && !this.rootDoc && isTopLevel) {
this.rootDoc = window.document; this.rootDoc = window.document;
this.rootNode = this.document(); this.rootNode = this.document();
this.queueMutation({ this.queueMutation({

View File

@ -2446,37 +2446,37 @@ SourceActor.prototype = {
* source map from b to a. We need to do this because the source map we get * source map from b to a. We need to do this because the source map we get
* from _generatePrettyCodeAndMap goes the opposite way we want it to for * from _generatePrettyCodeAndMap goes the opposite way we want it to for
* debugging. * debugging.
*
* Note that the source map is modified in place.
*/ */
_invertSourceMap: function SA__invertSourceMap({ code, map }) { _invertSourceMap: function SA__invertSourceMap({ code, map }) {
const smc = new SourceMapConsumer(map.toJSON()); // XXX bug 918802: Monkey punch the source map consumer, because iterating
const invertedMap = new SourceMapGenerator({ // over all mappings and inverting each of them, and then creating a new
file: this._url // SourceMapConsumer is *way* too slow.
});
smc.eachMapping(m => { map.setSourceContent(this._url, code);
if (!m.originalLine || !m.originalColumn) { const consumer = new SourceMapConsumer.fromSourceMap(map);
return; const getOrigPos = consumer.originalPositionFor.bind(consumer);
} const getGenPos = consumer.generatedPositionFor.bind(consumer);
const invertedMapping = {
source: m.source,
name: m.name,
original: {
line: m.generatedLine,
column: m.generatedColumn
},
generated: {
line: m.originalLine,
column: m.originalColumn
}
};
invertedMap.addMapping(invertedMapping);
});
invertedMap.setSourceContent(this._url, code); consumer.originalPositionFor = ({ line, column }) => {
const location = getGenPos({
line: line,
column: column,
source: this._url
});
location.source = this._url;
return location;
};
consumer.generatedPositionFor = ({ line, column }) => getOrigPos({
line: line,
column: column
});
return { return {
code: code, code: code,
map: new SourceMapConsumer(invertedMap.toJSON()) map: consumer
}; };
}, },
@ -2491,7 +2491,7 @@ SourceActor.prototype = {
// Compose the source maps // Compose the source maps
this._sourceMap = SourceMapGenerator.fromSourceMap(this._sourceMap); this._sourceMap = SourceMapGenerator.fromSourceMap(this._sourceMap);
this._sourceMap.applySourceMap(map, this._url); this._sourceMap.applySourceMap(map, this._url);
this._sourceMap = new SourceMapConsumer(this._sourceMap.toJSON()); this._sourceMap = SourceMapConsumer.fromSourceMap(this._sourceMap);
this._threadActor.sources.saveSourceMap(this._sourceMap, this._threadActor.sources.saveSourceMap(this._sourceMap,
this._generatedSource); this._generatedSource);
} else { } else {

View File

@ -622,13 +622,10 @@ StyleSheetActor.prototype = {
} }
}; };
if (channel instanceof Ci.nsIPrivateBrowsingChannel) { channel.loadGroup = this.window.QueryInterface(Ci.nsIInterfaceRequestor)
let loadContext = this.window
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation) .getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsILoadContext); .QueryInterface(Ci.nsIDocumentLoader)
channel.setPrivate(loadContext.usePrivateBrowsing); .loadGroup;
}
channel.loadFlags = channel.LOAD_FROM_CACHE; channel.loadFlags = channel.LOAD_FROM_CACHE;
channel.asyncOpen(streamListener, null); channel.asyncOpen(streamListener, null);
}, },

View File

@ -185,8 +185,18 @@ function BrowserTabList(aConnection)
BrowserTabList.prototype.constructor = BrowserTabList; BrowserTabList.prototype.constructor = BrowserTabList;
/**
* Get the selected browser for the given navigator:browser window.
* @private
* @param aWindow nsIChromeWindow
* The navigator:browser window for which you want the selected browser.
* @return nsIDOMElement|null
* The currently selected xul:browser element, if any. Note that the
* browser window might not be loaded yet - the function will return
* |null| in such cases.
*/
BrowserTabList.prototype._getSelectedBrowser = function(aWindow) { BrowserTabList.prototype._getSelectedBrowser = function(aWindow) {
return aWindow.gBrowser.selectedBrowser; return aWindow.gBrowser ? aWindow.gBrowser.selectedBrowser : null;
}; };
BrowserTabList.prototype._getChildren = function(aWindow) { BrowserTabList.prototype._getChildren = function(aWindow) {
@ -209,6 +219,9 @@ BrowserTabList.prototype.getList = function() {
// Iterate over all navigator:browser XUL windows. // Iterate over all navigator:browser XUL windows.
for (let win of allAppShellDOMWindows(DebuggerServer.chromeWindowType)) { for (let win of allAppShellDOMWindows(DebuggerServer.chromeWindowType)) {
let selectedBrowser = this._getSelectedBrowser(win); let selectedBrowser = this._getSelectedBrowser(win);
if (!selectedBrowser) {
continue;
}
// For each tab in this XUL window, ensure that we have an actor for // For each tab in this XUL window, ensure that we have an actor for
// it, reusing existing actors where possible. We actually iterate // it, reusing existing actors where possible. We actually iterate

View File

@ -118,10 +118,18 @@ WebConsoleActor.prototype =
conn: null, conn: null,
/** /**
* The content window we work with. * The window we work with.
* @type nsIDOMWindow * @type nsIDOMWindow
*/ */
get window() this.parentActor.window, get window() {
if (this.parentActor.isRootActor) {
// Try to find the Browser Console window, otherwise use the window of
// the root actor.
let window = Services.wm.getMostRecentWindow("devtools:webconsole");
return window || this.parentActor.window;
}
return this.parentActor.window;
},
/** /**
* The ConsoleServiceListener instance. * The ConsoleServiceListener instance.

View File

@ -117,6 +117,32 @@ define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'sou
this._parseMappings(mappings, sourceRoot); this._parseMappings(mappings, sourceRoot);
} }
/**
* Create a SourceMapConsumer from a SourceMapGenerator.
*
* @param SourceMapGenerator aSourceMap
* The source map that will be consumed.
* @returns SourceMapConsumer
*/
SourceMapConsumer.fromSourceMap =
function SourceMapConsumer_fromSourceMap(aSourceMap) {
var smc = Object.create(SourceMapConsumer.prototype);
smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true);
smc._sources = ArraySet.fromArray(aSourceMap._sources.toArray(), true);
smc.sourceRoot = aSourceMap._sourceRoot;
smc.sourcesContent = aSourceMap._generateSourcesContent(smc._sources.toArray(),
smc.sourceRoot);
smc.file = aSourceMap._file;
smc._generatedMappings = aSourceMap._mappings.slice()
.sort(util.compareByGeneratedPositions);
smc._originalMappings = aSourceMap._mappings.slice()
.sort(util.compareByOriginalPositions);
return smc;
};
/** /**
* The version of the source mapping spec that we are consuming. * The version of the source mapping spec that we are consuming.
*/ */
@ -212,37 +238,7 @@ define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'sou
} }
} }
this._originalMappings.sort(this._compareOriginalPositions); this._originalMappings.sort(util.compareByOriginalPositions);
};
/**
* Comparator between two mappings where the original positions are compared.
*/
SourceMapConsumer.prototype._compareOriginalPositions =
function SourceMapConsumer_compareOriginalPositions(mappingA, mappingB) {
if (mappingA.source > mappingB.source) {
return 1;
}
else if (mappingA.source < mappingB.source) {
return -1;
}
else {
var cmp = mappingA.originalLine - mappingB.originalLine;
return cmp === 0
? mappingA.originalColumn - mappingB.originalColumn
: cmp;
}
};
/**
* Comparator between two mappings where the generated positions are compared.
*/
SourceMapConsumer.prototype._compareGeneratedPositions =
function SourceMapConsumer_compareGeneratedPositions(mappingA, mappingB) {
var cmp = mappingA.generatedLine - mappingB.generatedLine;
return cmp === 0
? mappingA.generatedColumn - mappingB.generatedColumn
: cmp;
}; };
/** /**
@ -295,7 +291,7 @@ define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'sou
this._generatedMappings, this._generatedMappings,
"generatedLine", "generatedLine",
"generatedColumn", "generatedColumn",
this._compareGeneratedPositions); util.compareByGeneratedPositions);
if (mapping) { if (mapping) {
var source = util.getArg(mapping, 'source', null); var source = util.getArg(mapping, 'source', null);
@ -389,7 +385,7 @@ define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'sou
this._originalMappings, this._originalMappings,
"originalLine", "originalLine",
"originalColumn", "originalColumn",
this._compareOriginalPositions); util.compareByOriginalPositions);
if (mapping) { if (mapping) {
return { return {
@ -574,6 +570,93 @@ define('source-map/util', ['require', 'exports', 'module' , ], function(require,
} }
exports.relative = relative; exports.relative = relative;
function strcmp(aStr1, aStr2) {
var s1 = aStr1 || "";
var s2 = aStr2 || "";
return (s1 > s2) - (s1 < s2);
}
/**
* Comparator between two mappings where the original positions are compared.
*
* Optionally pass in `true` as `onlyCompareGenerated` to consider two
* mappings with the same original source/line/column, but different generated
* line and column the same. Useful when searching for a mapping with a
* stubbed out mapping.
*/
function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) {
var cmp;
cmp = strcmp(mappingA.source, mappingB.source);
if (cmp) {
return cmp;
}
cmp = mappingA.originalLine - mappingB.originalLine;
if (cmp) {
return cmp;
}
cmp = mappingA.originalColumn - mappingB.originalColumn;
if (cmp || onlyCompareOriginal) {
return cmp;
}
cmp = strcmp(mappingA.name, mappingB.name);
if (cmp) {
return cmp;
}
cmp = mappingA.generatedLine - mappingB.generatedLine;
if (cmp) {
return cmp;
}
return mappingA.generatedColumn - mappingB.generatedColumn;
};
exports.compareByOriginalPositions = compareByOriginalPositions;
/**
* Comparator between two mappings where the generated positions are
* compared.
*
* Optionally pass in `true` as `onlyCompareGenerated` to consider two
* mappings with the same generated line and column, but different
* source/name/original line and column the same. Useful when searching for a
* mapping with a stubbed out mapping.
*/
function compareByGeneratedPositions(mappingA, mappingB, onlyCompareGenerated) {
var cmp;
cmp = mappingA.generatedLine - mappingB.generatedLine;
if (cmp) {
return cmp;
}
cmp = mappingA.generatedColumn - mappingB.generatedColumn;
if (cmp || onlyCompareGenerated) {
return cmp;
}
cmp = strcmp(mappingA.source, mappingB.source);
if (cmp) {
return cmp;
}
cmp = mappingA.originalLine - mappingB.originalLine;
if (cmp) {
return cmp;
}
cmp = mappingA.originalColumn - mappingB.originalColumn;
if (cmp) {
return cmp;
}
return strcmp(mappingA.name, mappingB.name);
};
exports.compareByGeneratedPositions = compareByGeneratedPositions;
}); });
/* -*- Mode: js; js-indent-level: 2; -*- */ /* -*- Mode: js; js-indent-level: 2; -*- */
/* /*
@ -604,7 +687,7 @@ define('source-map/binary-search', ['require', 'exports', 'module' , ], function
// element which is less than the one we are searching for, so we // element which is less than the one we are searching for, so we
// return null. // return null.
var mid = Math.floor((aHigh - aLow) / 2) + aLow; var mid = Math.floor((aHigh - aLow) / 2) + aLow;
var cmp = aCompare(aNeedle, aHaystack[mid]); var cmp = aCompare(aNeedle, aHaystack[mid], true);
if (cmp === 0) { if (cmp === 0) {
// Found the element we are looking for. // Found the element we are looking for.
return aHaystack[mid]; return aHaystack[mid];
@ -1033,8 +1116,10 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so
} }
this._mappings.push({ this._mappings.push({
generated: generated, generatedLine: generated.line,
original: original, generatedColumn: generated.column,
originalLine: original != null && original.line,
originalColumn: original != null && original.column,
source: source, source: source,
name: name name: name
}); });
@ -1083,13 +1168,11 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so
if (!aSourceFile) { if (!aSourceFile) {
aSourceFile = aSourceMapConsumer.file; aSourceFile = aSourceMapConsumer.file;
} }
var sourceRoot = this._sourceRoot; var sourceRoot = this._sourceRoot;
// Make "aSourceFile" relative if an absolute Url is passed. // Make "aSourceFile" relative if an absolute Url is passed.
if (sourceRoot) { if (sourceRoot) {
aSourceFile = util.relative(sourceRoot, aSourceFile); aSourceFile = util.relative(sourceRoot, aSourceFile);
} }
// Applying the SourceMap can add and remove items from the sources and // Applying the SourceMap can add and remove items from the sources and
// the names array. // the names array.
var newSources = new ArraySet(); var newSources = new ArraySet();
@ -1097,13 +1180,12 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so
// Find mappings for the "aSourceFile" // Find mappings for the "aSourceFile"
this._mappings.forEach(function (mapping) { this._mappings.forEach(function (mapping) {
if (mapping.source === aSourceFile && mapping.original) { if (mapping.source === aSourceFile && mapping.originalLine) {
// Check if it can be mapped by the source map, then update the mapping. // Check if it can be mapped by the source map, then update the mapping.
var original = aSourceMapConsumer.originalPositionFor({ var original = aSourceMapConsumer.originalPositionFor({
line: mapping.original.line, line: mapping.originalLine,
column: mapping.original.column column: mapping.originalColumn
}); });
if (original.source !== null) { if (original.source !== null) {
// Copy mapping // Copy mapping
if (sourceRoot) { if (sourceRoot) {
@ -1111,8 +1193,8 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so
} else { } else {
mapping.source = original.source; mapping.source = original.source;
} }
mapping.original.line = original.line; mapping.originalLine = original.line;
mapping.original.column = original.column; mapping.originalColumn = original.column;
if (original.name !== null && mapping.name !== null) { if (original.name !== null && mapping.name !== null) {
// Only use the identifier name if it's an identifier // Only use the identifier name if it's an identifier
// in both SourceMaps // in both SourceMaps
@ -1180,24 +1262,6 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so
} }
}; };
function cmpLocation(loc1, loc2) {
var cmp = (loc1 && loc1.line) - (loc2 && loc2.line);
return cmp ? cmp : (loc1 && loc1.column) - (loc2 && loc2.column);
}
function strcmp(str1, str2) {
str1 = str1 || '';
str2 = str2 || '';
return (str1 > str2) - (str1 < str2);
}
function cmpMapping(mappingA, mappingB) {
return cmpLocation(mappingA.generated, mappingB.generated) ||
cmpLocation(mappingA.original, mappingB.original) ||
strcmp(mappingA.source, mappingB.source) ||
strcmp(mappingA.name, mappingB.name);
}
/** /**
* Serialize the accumulated mappings in to the stream of base 64 VLQs * Serialize the accumulated mappings in to the stream of base 64 VLQs
* specified by the source map format. * specified by the source map format.
@ -1218,44 +1282,44 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so
// via the ';' separators) will be all messed up. Note: it might be more // via the ';' separators) will be all messed up. Note: it might be more
// performant to maintain the sorting as we insert them, rather than as we // performant to maintain the sorting as we insert them, rather than as we
// serialize them, but the big O is the same either way. // serialize them, but the big O is the same either way.
this._mappings.sort(cmpMapping); this._mappings.sort(util.compareByGeneratedPositions);
for (var i = 0, len = this._mappings.length; i < len; i++) { for (var i = 0, len = this._mappings.length; i < len; i++) {
mapping = this._mappings[i]; mapping = this._mappings[i];
if (mapping.generated.line !== previousGeneratedLine) { if (mapping.generatedLine !== previousGeneratedLine) {
previousGeneratedColumn = 0; previousGeneratedColumn = 0;
while (mapping.generated.line !== previousGeneratedLine) { while (mapping.generatedLine !== previousGeneratedLine) {
result += ';'; result += ';';
previousGeneratedLine++; previousGeneratedLine++;
} }
} }
else { else {
if (i > 0) { if (i > 0) {
if (!cmpMapping(mapping, this._mappings[i - 1])) { if (!util.compareByGeneratedPositions(mapping, this._mappings[i - 1])) {
continue; continue;
} }
result += ','; result += ',';
} }
} }
result += base64VLQ.encode(mapping.generated.column result += base64VLQ.encode(mapping.generatedColumn
- previousGeneratedColumn); - previousGeneratedColumn);
previousGeneratedColumn = mapping.generated.column; previousGeneratedColumn = mapping.generatedColumn;
if (mapping.source && mapping.original) { if (mapping.source) {
result += base64VLQ.encode(this._sources.indexOf(mapping.source) result += base64VLQ.encode(this._sources.indexOf(mapping.source)
- previousSource); - previousSource);
previousSource = this._sources.indexOf(mapping.source); previousSource = this._sources.indexOf(mapping.source);
// lines are stored 0-based in SourceMap spec version 3 // lines are stored 0-based in SourceMap spec version 3
result += base64VLQ.encode(mapping.original.line - 1 result += base64VLQ.encode(mapping.originalLine - 1
- previousOriginalLine); - previousOriginalLine);
previousOriginalLine = mapping.original.line - 1; previousOriginalLine = mapping.originalLine - 1;
result += base64VLQ.encode(mapping.original.column result += base64VLQ.encode(mapping.originalColumn
- previousOriginalColumn); - previousOriginalColumn);
previousOriginalColumn = mapping.original.column; previousOriginalColumn = mapping.originalColumn;
if (mapping.name) { if (mapping.name) {
result += base64VLQ.encode(this._names.indexOf(mapping.name) result += base64VLQ.encode(this._names.indexOf(mapping.name)
@ -1268,6 +1332,20 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so
return result; return result;
}; };
SourceMapGenerator.prototype._generateSourcesContent =
function SourceMapGenerator_generateSourcesContent(aSources, aSourceRoot) {
return aSources.map(function (source) {
if (aSourceRoot) {
source = util.relative(aSourceRoot, source);
}
var key = util.toSetString(source);
return Object.prototype.hasOwnProperty.call(this._sourcesContents,
key)
? this._sourcesContents[key]
: null;
}, this);
};
/** /**
* Externalize the source map. * Externalize the source map.
*/ */
@ -1284,16 +1362,9 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so
map.sourceRoot = this._sourceRoot; map.sourceRoot = this._sourceRoot;
} }
if (this._sourcesContents) { if (this._sourcesContents) {
map.sourcesContent = map.sources.map(function (source) { map.sourcesContent = this._generateSourcesContent(map.sources, map.sourceRoot);
if (map.sourceRoot) {
source = util.relative(map.sourceRoot, source);
}
return Object.prototype.hasOwnProperty.call(
this._sourcesContents, util.toSetString(source))
? this._sourcesContents[util.toSetString(source)]
: null;
}, this);
} }
return map; return map;
}; };
@ -1500,7 +1571,9 @@ define('source-map/source-node', ['require', 'exports', 'module' , 'source-map/
* @param aFn The traversal function. * @param aFn The traversal function.
*/ */
SourceNode.prototype.walk = function SourceNode_walk(aFn) { SourceNode.prototype.walk = function SourceNode_walk(aFn) {
this.children.forEach(function (chunk) { var chunk;
for (var i = 0, len = this.children.length; i < len; i++) {
chunk = this.children[i];
if (chunk instanceof SourceNode) { if (chunk instanceof SourceNode) {
chunk.walk(aFn); chunk.walk(aFn);
} }
@ -1512,7 +1585,7 @@ define('source-map/source-node', ['require', 'exports', 'module' , 'source-map/
name: this.name }); name: this.name });
} }
} }
}, this); }
}; };
/** /**
@ -1578,14 +1651,16 @@ define('source-map/source-node', ['require', 'exports', 'module' , 'source-map/
*/ */
SourceNode.prototype.walkSourceContents = SourceNode.prototype.walkSourceContents =
function SourceNode_walkSourceContents(aFn) { function SourceNode_walkSourceContents(aFn) {
this.children.forEach(function (chunk) { for (var i = 0, len = this.children.length; i < len; i++) {
if (chunk instanceof SourceNode) { if (this.children[i] instanceof SourceNode) {
chunk.walkSourceContents(aFn); this.children[i].walkSourceContents(aFn);
} }
}, this); }
Object.keys(this.sourceContents).forEach(function (sourceFileKey) {
aFn(util.fromSetString(sourceFileKey), this.sourceContents[sourceFileKey]); var sources = Object.keys(this.sourceContents);
}, this); for (var i = 0, len = sources.length; i < len; i++) {
aFn(util.fromSetString(sources[i]), this.sourceContents[sources[i]]);
}
}; };
/** /**

View File

@ -214,7 +214,8 @@ define('test/source-map/util', ['require', 'exports', 'module' , 'lib/source-ma
expectedMap.sourceRoot, expectedMap.sourceRoot,
"sourceRoot mismatch: " + "sourceRoot mismatch: " +
actualMap.sourceRoot + " != " + expectedMap.sourceRoot); actualMap.sourceRoot + " != " + expectedMap.sourceRoot);
assert.equal(actualMap.mappings, expectedMap.mappings, "mappings mismatch"); assert.equal(actualMap.mappings, expectedMap.mappings,
"mappings mismatch:\nActual: " + actualMap.mappings + "\nExpected: " + expectedMap.mappings);
if (actualMap.sourcesContent) { if (actualMap.sourcesContent) {
assert.equal(actualMap.sourcesContent.length, assert.equal(actualMap.sourcesContent.length,
expectedMap.sourcesContent.length, expectedMap.sourcesContent.length,
@ -343,6 +344,93 @@ define('lib/source-map/util', ['require', 'exports', 'module' , ], function(requ
} }
exports.relative = relative; exports.relative = relative;
function strcmp(aStr1, aStr2) {
var s1 = aStr1 || "";
var s2 = aStr2 || "";
return (s1 > s2) - (s1 < s2);
}
/**
* Comparator between two mappings where the original positions are compared.
*
* Optionally pass in `true` as `onlyCompareGenerated` to consider two
* mappings with the same original source/line/column, but different generated
* line and column the same. Useful when searching for a mapping with a
* stubbed out mapping.
*/
function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) {
var cmp;
cmp = strcmp(mappingA.source, mappingB.source);
if (cmp) {
return cmp;
}
cmp = mappingA.originalLine - mappingB.originalLine;
if (cmp) {
return cmp;
}
cmp = mappingA.originalColumn - mappingB.originalColumn;
if (cmp || onlyCompareOriginal) {
return cmp;
}
cmp = strcmp(mappingA.name, mappingB.name);
if (cmp) {
return cmp;
}
cmp = mappingA.generatedLine - mappingB.generatedLine;
if (cmp) {
return cmp;
}
return mappingA.generatedColumn - mappingB.generatedColumn;
};
exports.compareByOriginalPositions = compareByOriginalPositions;
/**
* Comparator between two mappings where the generated positions are
* compared.
*
* Optionally pass in `true` as `onlyCompareGenerated` to consider two
* mappings with the same generated line and column, but different
* source/name/original line and column the same. Useful when searching for a
* mapping with a stubbed out mapping.
*/
function compareByGeneratedPositions(mappingA, mappingB, onlyCompareGenerated) {
var cmp;
cmp = mappingA.generatedLine - mappingB.generatedLine;
if (cmp) {
return cmp;
}
cmp = mappingA.generatedColumn - mappingB.generatedColumn;
if (cmp || onlyCompareGenerated) {
return cmp;
}
cmp = strcmp(mappingA.source, mappingB.source);
if (cmp) {
return cmp;
}
cmp = mappingA.originalLine - mappingB.originalLine;
if (cmp) {
return cmp;
}
cmp = mappingA.originalColumn - mappingB.originalColumn;
if (cmp) {
return cmp;
}
return strcmp(mappingA.name, mappingB.name);
};
exports.compareByGeneratedPositions = compareByGeneratedPositions;
}); });
/* -*- Mode: js; js-indent-level: 2; -*- */ /* -*- Mode: js; js-indent-level: 2; -*- */
/* /*

View File

@ -393,6 +393,66 @@ define("test/source-map/test-source-map-consumer", ["require", "exports", "modul
assert.equal(pos.column, 5); assert.equal(pos.column, 5);
}; };
exports['test SourceMapConsumer.fromSourceMap'] = function (assert, util) {
var smg = new SourceMapGenerator({
sourceRoot: 'http://example.com/',
file: 'foo.js'
});
smg.addMapping({
original: { line: 1, column: 1 },
generated: { line: 2, column: 2 },
source: 'bar.js'
});
smg.addMapping({
original: { line: 2, column: 2 },
generated: { line: 4, column: 4 },
source: 'baz.js',
name: 'dirtMcGirt'
});
smg.setSourceContent('baz.js', 'baz.js content');
var smc = SourceMapConsumer.fromSourceMap(smg);
assert.equal(smc.file, 'foo.js');
assert.equal(smc.sourceRoot, 'http://example.com/');
assert.equal(smc.sources.length, 2);
assert.equal(smc.sources[0], 'http://example.com/bar.js');
assert.equal(smc.sources[1], 'http://example.com/baz.js');
assert.equal(smc.sourceContentFor('baz.js'), 'baz.js content');
var pos = smc.originalPositionFor({
line: 2,
column: 2
});
assert.equal(pos.line, 1);
assert.equal(pos.column, 1);
assert.equal(pos.source, 'http://example.com/bar.js');
assert.equal(pos.name, null);
pos = smc.generatedPositionFor({
line: 1,
column: 1,
source: 'http://example.com/bar.js'
});
assert.equal(pos.line, 2);
assert.equal(pos.column, 2);
pos = smc.originalPositionFor({
line: 4,
column: 4
});
assert.equal(pos.line, 2);
assert.equal(pos.column, 2);
assert.equal(pos.source, 'http://example.com/baz.js');
assert.equal(pos.name, 'dirtMcGirt');
pos = smc.generatedPositionFor({
line: 2,
column: 2,
source: 'http://example.com/baz.js'
});
assert.equal(pos.line, 4);
assert.equal(pos.column, 4);
};
}); });
function run_test() { function run_test() {
runSourceMapTests('test/source-map/test-source-map-consumer', do_throw); runSourceMapTests('test/source-map/test-source-map-consumer', do_throw);

View File

@ -262,11 +262,17 @@ define("test/source-map/test-source-node", ["require", "exports", "module"], fun
exports['test .fromStringWithSourceMap() merging duplicate mappings'] = function (assert, util) { exports['test .fromStringWithSourceMap() merging duplicate mappings'] = function (assert, util) {
var input = new SourceNode(null, null, null, [ var input = new SourceNode(null, null, null, [
new SourceNode(1, 0, "a.js", "(function"), new SourceNode(1, 0, "a.js", "() {\n"), new SourceNode(1, 0, "a.js", "(function"),
" ", new SourceNode(1, 0, "a.js", "var Test = "), new SourceNode(1, 0, "b.js", "{};\n"), new SourceNode(1, 0, "a.js", "() {\n"),
new SourceNode(2, 0, "b.js", "Test"), new SourceNode(2, 0, "b.js", ".A", "A"), new SourceNode(2, 20, "b.js", " = { value: 1234 };\n", "A"), " ",
"}());\n", new SourceNode(1, 0, "a.js", "var Test = "),
"/* Generated Source */"]); new SourceNode(1, 0, "b.js", "{};\n"),
new SourceNode(2, 0, "b.js", "Test"),
new SourceNode(2, 0, "b.js", ".A", "A"),
new SourceNode(2, 20, "b.js", " = { value: 1234 };\n", "A"),
"}());\n",
"/* Generated Source */"
]);
input = input.toStringWithSourceMap({ input = input.toStringWithSourceMap({
file: 'foo.js' file: 'foo.js'
}); });

View File

@ -52,7 +52,7 @@ WebappTabList.prototype = Object.create(BrowserTabList.prototype);
WebappTabList.prototype.constructor = WebappTabList; WebappTabList.prototype.constructor = WebappTabList;
WebappTabList.prototype.getList = function() { WebappTabList.prototype.getList = function() {
let topXULWindow = windowMediator.getMostRecentWindow(this._windowType); let topXULWindow = Services.wm.getMostRecentWindow(this._windowType);
// As a sanity check, make sure all the actors presently in our map get // As a sanity check, make sure all the actors presently in our map get
// picked up when we iterate over all windows. // picked up when we iterate over all windows.

View File

@ -12,27 +12,13 @@
* implementation of this interface for non-Windows systems, for testing and * implementation of this interface for non-Windows systems, for testing and
* development purposes only. * development purposes only.
*/ */
[scriptable, uuid(c5a654c8-2443-47ce-9322-d150af3ca526)] [scriptable, uuid(fa6750a2-f0fe-411c-af23-1cd6d2fdeceb)]
interface nsIWinMetroUtils : nsISupports interface nsIWinMetroUtils : nsISupports
{ {
/* Fullscreen landscape orientation */
const long fullScreenLandscape = 0;
/* Larger snapped state */
const long filled = 1;
/* Smaller snapped state */
const long snapped = 2;
/* Fullscreen portrait orientation */
const long fullScreenPortrait = 3;
/* return constants for the handPreference property */ /* return constants for the handPreference property */
const long handPreferenceLeft = 0; const long handPreferenceLeft = 0;
const long handPreferenceRight = 1; const long handPreferenceRight = 1;
/**
* Determines the current snapped state.
*/
readonly attribute long snappedState;
/** /**
* Determine if the current browser is running in the metro immersive * Determine if the current browser is running in the metro immersive
* environment. * environment.
@ -49,12 +35,6 @@ interface nsIWinMetroUtils : nsISupports
*/ */
readonly attribute AString activationURI; readonly attribute AString activationURI;
/**
* Attempts to unsnap the application from snapped state to filled state
*/
void unsnap();
/** /**
* Show the settings flyout * Show the settings flyout
*/ */

View File

@ -109,7 +109,6 @@ FrameworkView::ActivateView()
LogFunction(); LogFunction();
UpdateWidgetSizeAndPosition(); UpdateWidgetSizeAndPosition();
MetroUtils::GetViewState(mViewState);
nsIntRegion region(nsIntRect(0, 0, mWindowBounds.width, mWindowBounds.height)); nsIntRegion region(nsIntRect(0, 0, mWindowBounds.width, mWindowBounds.height));
mWidget->Paint(region); mWidget->Paint(region);
@ -386,36 +385,6 @@ FrameworkView::OnWindowClosed(ICoreWindow* aSender, ICoreWindowEventArgs* aArgs)
return S_OK; return S_OK;
} }
void
FrameworkView::FireViewStateObservers()
{
ApplicationViewState state;
MetroUtils::GetViewState(state);
if (state == mViewState) {
return;
}
mViewState = state;
nsAutoString name;
switch (mViewState) {
case ApplicationViewState_FullScreenLandscape:
name.AssignLiteral("landscape");
break;
case ApplicationViewState_Filled:
name.AssignLiteral("filled");
break;
case ApplicationViewState_Snapped:
name.AssignLiteral("snapped");
break;
case ApplicationViewState_FullScreenPortrait:
name.AssignLiteral("portrait");
break;
default:
NS_WARNING("Unknown view state");
return;
};
MetroUtils::FireObserver("metro_viewstate_changed", name.get());
}
HRESULT HRESULT
FrameworkView::OnWindowSizeChanged(ICoreWindow* aSender, IWindowSizeChangedEventArgs* aArgs) FrameworkView::OnWindowSizeChanged(ICoreWindow* aSender, IWindowSizeChangedEventArgs* aArgs)
{ {
@ -431,7 +400,6 @@ FrameworkView::OnWindowSizeChanged(ICoreWindow* aSender, IWindowSizeChangedEvent
mWindowBounds = MetroUtils::LogToPhys(logicalBounds); mWindowBounds = MetroUtils::LogToPhys(logicalBounds);
UpdateWidgetSizeAndPosition(); UpdateWidgetSizeAndPosition();
FireViewStateObservers();
return S_OK; return S_OK;
} }

View File

@ -195,7 +195,6 @@ private:
static Rect sKeyboardRect; static Rect sKeyboardRect;
bool mWinVisible; bool mWinVisible;
bool mWinActiveState; bool mWinActiveState;
ApplicationViewState mViewState;
}; };
} } } } } }

View File

@ -363,32 +363,6 @@ nsWinMetroUtils::ShowNativeToast(const nsAString &aTitle,
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP
nsWinMetroUtils::GetSnappedState(int32_t *aSnappedState)
{
if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Desktop) {
NS_WARNING("GetSnappedState can't be called on the desktop.");
return NS_ERROR_FAILURE;
}
NS_ENSURE_ARG_POINTER(aSnappedState);
ApplicationViewState viewState;
AssertRetHRESULT(MetroUtils::GetViewState(viewState), NS_ERROR_UNEXPECTED);
*aSnappedState = (int32_t) viewState;
return NS_OK;
}
NS_IMETHODIMP
nsWinMetroUtils::Unsnap()
{
if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Desktop) {
NS_WARNING("Unsnap can't be called on the desktop.");
return NS_ERROR_FAILURE;
}
HRESULT hr = MetroUtils::TryUnsnap();
return SUCCEEDED(hr) ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP NS_IMETHODIMP
nsWinMetroUtils::ShowSettingsFlyout() nsWinMetroUtils::ShowSettingsFlyout()
{ {