Merge fx-team to m-c
@ -4904,17 +4904,16 @@
|
||||
|
||||
<method name="_calcMouseTargetRect">
|
||||
<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 = {
|
||||
top: rect.top,
|
||||
bottom: rect.bottom,
|
||||
left: alignRight ? window.innerWidth - rect.width : 0,
|
||||
right: alignRight ? window.innerWidth : rect.width
|
||||
top: panelRect.top,
|
||||
bottom: panelRect.bottom,
|
||||
left: alignRight ? containerRect.right - panelRect.width : containerRect.left,
|
||||
right: alignRight ? containerRect.right : containerRect.left + panelRect.width
|
||||
};
|
||||
]]></body>
|
||||
</method>
|
||||
|
@ -1127,7 +1127,7 @@ var ViewMenu = {
|
||||
* Set up the content of the view menu.
|
||||
*/
|
||||
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 viewSortAscending = document.getElementById("viewSortAscending");
|
||||
|
@ -384,7 +384,7 @@
|
||||
<treecol label="&col.url.label;" id="placesContentUrl" anonid="url" flex="5"
|
||||
persist="width hidden ordinal sortActive sortDirection"/>
|
||||
<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"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol label="&col.visitcount.label;" id="placesContentVisitCount" anonid="visitCount" flex="1" hidden="true"
|
||||
|
@ -2585,7 +2585,6 @@ let SessionStoreInternal = {
|
||||
restoreHistory:
|
||||
function ssi_restoreHistory(aWindow, aTabs, aTabData, aIdMap, aDocIdentMap,
|
||||
aRestoreImmediately) {
|
||||
var _this = this;
|
||||
// if the tab got removed before being completely restored, then skip it
|
||||
while (aTabs.length > 0 && !(this._canRestoreTabHistory(aTabs[0]))) {
|
||||
aTabs.shift();
|
||||
@ -2654,9 +2653,11 @@ let SessionStoreInternal = {
|
||||
tab.dispatchEvent(event);
|
||||
|
||||
// Restore the history in the next tab
|
||||
aWindow.setTimeout(function(){
|
||||
_this.restoreHistory(aWindow, aTabs, aTabData, aIdMap, aDocIdentMap,
|
||||
aRestoreImmediately);
|
||||
aWindow.setTimeout(() => {
|
||||
if (!aWindow.closed) {
|
||||
this.restoreHistory(aWindow, aTabs, aTabData, aIdMap, aDocIdentMap,
|
||||
aRestoreImmediately);
|
||||
}
|
||||
}, 0);
|
||||
|
||||
// This could cause us to ignore MAX_CONCURRENT_TAB_RESTORES a bit, but
|
||||
|
@ -61,6 +61,9 @@ let UI = {
|
||||
|
||||
this.template = new Template(document.body, this.store, Utils.l10n);
|
||||
this.template.start();
|
||||
|
||||
this._onSimulatorConnected = this._onSimulatorConnected.bind(this);
|
||||
this._onSimulatorDisconnected = this._onSimulatorDisconnected.bind(this);
|
||||
},
|
||||
|
||||
useFloatingScrollbarsIfNeeded: function() {
|
||||
@ -136,16 +139,25 @@ let UI = {
|
||||
this.connection.log("Simulator ready. Connecting.");
|
||||
this.connection.port = port;
|
||||
this.connection.host = "localhost";
|
||||
this.connection.once("connected", () => {
|
||||
this.connection.log("Connected to simulator.");
|
||||
this.connection.keepConnecting = false;
|
||||
});
|
||||
this.connection.once("connected",
|
||||
this._onSimulatorConnected);
|
||||
this.connection.once("disconnected",
|
||||
this._onSimulatorDisconnected);
|
||||
this.connection.keepConnecting = true;
|
||||
this.connection.connect();
|
||||
});
|
||||
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) {
|
||||
let device = Devices.getByName(name);
|
||||
device.connect().then((port) => {
|
||||
|
@ -46,7 +46,7 @@ function testSetBreakpoint() {
|
||||
let deferred = promise.defer();
|
||||
|
||||
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,
|
||||
"Should be able to set a breakpoint in a js file.");
|
||||
ok(!aResponse.actualLocation,
|
||||
|
40
browser/devtools/devtools-clhandler.js
Normal 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]);
|
2
browser/devtools/devtools-clhandler.manifest
Normal 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}
|
@ -168,7 +168,7 @@ InspectorPanel.prototype = {
|
||||
// as default selected, else set documentElement
|
||||
return walker.getRootNode().then(aRootNode => {
|
||||
rootNode = aRootNode;
|
||||
return walker.querySelector(aRootNode, this.selectionCssSelector);
|
||||
return walker.querySelector(rootNode, this.selectionCssSelector);
|
||||
}).then(front => {
|
||||
if (front) {
|
||||
return front;
|
||||
|
@ -43,5 +43,7 @@ MOCHITEST_BROWSER_FILES := \
|
||||
browser_inspector_select_last_selected.html \
|
||||
browser_inspector_select_last_selected2.html \
|
||||
browser_inspector_basic_highlighter.js \
|
||||
browser_inspector_dead_node_exception.html \
|
||||
browser_inspector_dead_node_exception.js \
|
||||
head.js \
|
||||
$(NULL)
|
||||
|
@ -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>
|
@ -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";
|
||||
});
|
||||
});
|
||||
}
|
@ -24,3 +24,8 @@ DIRS += [
|
||||
'fontinspector',
|
||||
'app-manager',
|
||||
]
|
||||
|
||||
EXTRA_COMPONENTS += [
|
||||
'devtools-clhandler.js',
|
||||
'devtools-clhandler.manifest',
|
||||
]
|
||||
|
@ -254,13 +254,14 @@ HUD_SERVICE.prototype =
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
connect().then(getTarget).then(openWindow).then((aWindow) =>
|
||||
connect().then(getTarget).then(openWindow).then((aWindow) => {
|
||||
this.openBrowserConsole(target, aWindow, aWindow)
|
||||
.then((aBrowserConsole) => {
|
||||
this._browserConsoleID = aBrowserConsole.hudId;
|
||||
this._browserConsoleDefer.resolve(aBrowserConsole);
|
||||
this._browserConsoleDefer = null;
|
||||
}));
|
||||
})
|
||||
}, console.error);
|
||||
|
||||
return this._browserConsoleDefer.promise;
|
||||
},
|
||||
|
@ -62,7 +62,7 @@ function consoleOpened(hud)
|
||||
let text = output.textContent;
|
||||
chromeConsole = text.indexOf("bug587757a");
|
||||
contentConsole = text.indexOf("bug587757b");
|
||||
execValue = text.indexOf("browser.xul");
|
||||
execValue = text.indexOf("webconsole.xul");
|
||||
exception = text.indexOf("foobarExceptionBug587757");
|
||||
xhrRequest = text.indexOf("test-console.html");
|
||||
}
|
||||
|
@ -24,7 +24,10 @@ function test()
|
||||
ok(hud, "browser console opened");
|
||||
|
||||
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()
|
||||
|
@ -81,7 +81,7 @@
|
||||
<!ENTITY col.name.label "Name">
|
||||
<!ENTITY col.tags.label "Tags">
|
||||
<!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.keyword.label "Keyword">
|
||||
<!ENTITY col.description.label "Description">
|
||||
|
@ -25,24 +25,28 @@ menuOpenLivemarkOrigin.label=Open "%S"
|
||||
|
||||
sortByName=Sort '%S' by Name
|
||||
sortByNameGeneric=Sort by Name
|
||||
view.sortBy.name.label=Sort by Name
|
||||
view.sortBy.name.accesskey=N
|
||||
view.sortBy.url.label=Sort by Location
|
||||
view.sortBy.url.accesskey=L
|
||||
view.sortBy.date.label=Sort by Visit Date
|
||||
view.sortBy.date.accesskey=V
|
||||
view.sortBy.visitCount.label=Sort by Visit Count
|
||||
view.sortBy.visitCount.accesskey=C
|
||||
view.sortBy.keyword.label=Sort by Keyword
|
||||
view.sortBy.keyword.accesskey=K
|
||||
view.sortBy.description.label=Sort by Description
|
||||
view.sortBy.description.accesskey=D
|
||||
view.sortBy.dateAdded.label=Sort by Added
|
||||
view.sortBy.dateAdded.accesskey=e
|
||||
view.sortBy.lastModified.label=Sort by Last Modified
|
||||
view.sortBy.lastModified.accesskey=M
|
||||
view.sortBy.tags.label=Sort by Tags
|
||||
view.sortBy.tags.accesskey=T
|
||||
# LOCALIZATION NOTE (view.sortBy.1.name.label): sortBy properties are versioned.
|
||||
# When any of these changes, all of the properties must be bumped, and the
|
||||
# change must be annotated here. Both label and accesskey must be updated.
|
||||
# - version 1: changed view.sortBy.1.date.
|
||||
view.sortBy.1.name.label=Sort by Name
|
||||
view.sortBy.1.name.accesskey=N
|
||||
view.sortBy.1.url.label=Sort by Location
|
||||
view.sortBy.1.url.accesskey=L
|
||||
view.sortBy.1.date.label=Sort by Most Recent Visit
|
||||
view.sortBy.1.date.accesskey=V
|
||||
view.sortBy.1.visitCount.label=Sort by Visit Count
|
||||
view.sortBy.1.visitCount.accesskey=C
|
||||
view.sortBy.1.keyword.label=Sort by Keyword
|
||||
view.sortBy.1.keyword.accesskey=K
|
||||
view.sortBy.1.description.label=Sort by Description
|
||||
view.sortBy.1.description.accesskey=D
|
||||
view.sortBy.1.dateAdded.label=Sort by Added
|
||||
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
|
||||
searchHistory=Search History
|
||||
|
@ -77,6 +77,13 @@ var ContentAreaObserver = {
|
||||
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
|
||||
*/
|
||||
@ -122,6 +129,8 @@ var ContentAreaObserver = {
|
||||
this.styles["window-height"].height = newHeight + "px";
|
||||
this.styles["window-height"].maxHeight = newHeight + "px";
|
||||
|
||||
this._updateViewState();
|
||||
|
||||
this.updateContentArea(newWidth, this._getContentHeightForWindow(newHeight));
|
||||
this._disatchBrowserEvent("SizeChanged");
|
||||
},
|
||||
@ -280,6 +289,15 @@ var ContentAreaObserver = {
|
||||
* 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) {
|
||||
if (aAmount == 0) {
|
||||
this._deckTransitioning = false;
|
||||
|
@ -102,7 +102,6 @@ var BrowserUI = {
|
||||
window.addEventListener("MozImprecisePointer", this, true);
|
||||
|
||||
Services.prefs.addObserver("browser.cache.disk_cache_ssl", this, false);
|
||||
Services.obs.addObserver(this, "metro_viewstate_changed", false);
|
||||
|
||||
// Init core UI modules
|
||||
ContextUI.init();
|
||||
@ -112,9 +111,6 @@ var BrowserUI = {
|
||||
SettingsCharm.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
|
||||
// the first page is shown, then dispatch a UIReadyDelayed event.
|
||||
messageManager.addMessageListener("pageshow", function onPageShow() {
|
||||
@ -178,6 +174,7 @@ var BrowserUI = {
|
||||
messageManager.removeMessageListener("Browser:MozApplicationManifest", OfflineApps);
|
||||
|
||||
PanelUI.uninit();
|
||||
FlyoutPanelsUI.uninit();
|
||||
Downloads.uninit();
|
||||
SettingsCharm.uninit();
|
||||
messageManager.removeMessageListener("Content:StateChange", this);
|
||||
@ -592,13 +589,6 @@ var BrowserUI = {
|
||||
this.changeDebugPort(Services.prefs.getIntPref(aData));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case "metro_viewstate_changed":
|
||||
this._adjustDOMforViewState(aData);
|
||||
if (aData == "snapped") {
|
||||
FlyoutPanelsUI.hide();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
},
|
||||
@ -638,28 +628,6 @@ var BrowserUI = {
|
||||
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) {
|
||||
let url = this.getDisplayURI(aBrowser);
|
||||
|
||||
|
@ -130,14 +130,7 @@ var Browser = {
|
||||
window.controllers.appendController(this);
|
||||
window.controllers.appendController(BrowserUI);
|
||||
|
||||
let os = Services.obs;
|
||||
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);
|
||||
Services.obs.addObserver(SessionHistoryObserver, "browser:purge-session-history", false);
|
||||
|
||||
window.QueryInterface(Ci.nsIDOMChromeWindow).browserDOMWindow = new nsBrowserAccess();
|
||||
|
||||
@ -247,14 +240,7 @@ var Browser = {
|
||||
messageManager.removeMessageListener("Browser:PluginClickToPlayClicked", this);
|
||||
messageManager.removeMessageListener("Browser:TapOnSelection", this);
|
||||
|
||||
var os = Services.obs;
|
||||
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");
|
||||
Services.obs.removeObserver(SessionHistoryObserver, "browser:purge-session-history");
|
||||
|
||||
window.controllers.removeController(this);
|
||||
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) {
|
||||
return Browser.getNotificationBox(aBrowser);
|
||||
}
|
||||
|
@ -172,6 +172,7 @@
|
||||
|
||||
<stack id="stack" flex="1">
|
||||
<observes element="bcast_urlbarState" attribute="*"/>
|
||||
<observes element="bcast_windowState" attribute="*"/>
|
||||
<!-- Page Area -->
|
||||
<vbox id="page">
|
||||
<vbox id="tray" class="tray-toolbar" observes="bcast_windowState" >
|
||||
|
@ -36,6 +36,12 @@ let FlyoutPanelsUI = {
|
||||
return sandbox[name];
|
||||
});
|
||||
});
|
||||
|
||||
Services.obs.addObserver(this, "metro_viewstate_changed", false);
|
||||
},
|
||||
|
||||
uninit: function () {
|
||||
Services.obs.removeObserver(this, "metro_viewstate_changed");
|
||||
},
|
||||
|
||||
show: function(aToShow) {
|
||||
@ -73,6 +79,16 @@ let FlyoutPanelsUI = {
|
||||
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) {
|
||||
if (this._currentFlyout) {
|
||||
this._currentFlyout._topmostElement.dispatchEvent(aEvent);
|
||||
|
@ -13,8 +13,8 @@
|
||||
* @param aRoot Bookmark root to show in the view.
|
||||
*/
|
||||
function BookmarksView(aSet, aLimit, aRoot, aFilterUnpinned) {
|
||||
this._set = aSet;
|
||||
this._set.controller = this;
|
||||
View.call(this, aSet);
|
||||
|
||||
this._inBatch = false; // batch up grid updates to avoid redundant arrangeItems calls
|
||||
|
||||
this._limit = aLimit;
|
||||
@ -25,12 +25,10 @@ function BookmarksView(aSet, aLimit, aRoot, aFilterUnpinned) {
|
||||
this._changes = new BookmarkChangeListener(this);
|
||||
this._pinHelper = new ItemPinHelper("metro.bookmarks.unpinned");
|
||||
this._bookmarkService.addObserver(this._changes, false);
|
||||
Services.obs.addObserver(this, "metro_viewstate_changed", false);
|
||||
StartUI.chromeWin.addEventListener('MozAppbarDismissing', this, false);
|
||||
StartUI.chromeWin.addEventListener('BookmarksNeedsRefresh', this, false);
|
||||
window.addEventListener("TabClose", this, true);
|
||||
|
||||
this._adjustDOMforViewState();
|
||||
this.root = aRoot;
|
||||
}
|
||||
|
||||
@ -62,11 +60,11 @@ BookmarksView.prototype = Util.extend(Object.create(View.prototype), {
|
||||
|
||||
destruct: function bv_destruct() {
|
||||
this._bookmarkService.removeObserver(this._changes);
|
||||
Services.obs.removeObserver(this, "metro_viewstate_changed");
|
||||
if (StartUI.chromeWin) {
|
||||
StartUI.chromeWin.removeEventListener('MozAppbarDismissing', this, false);
|
||||
StartUI.chromeWin.removeEventListener('BookmarksNeedsRefresh', this, false);
|
||||
}
|
||||
View.prototype.destruct.call(this);
|
||||
},
|
||||
|
||||
handleItemClick: function bv_handleItemClick(aItem) {
|
||||
@ -275,15 +273,6 @@ BookmarksView.prototype = Util.extend(Object.create(View.prototype), {
|
||||
this._sendNeedsRefresh();
|
||||
},
|
||||
|
||||
// nsIObservers
|
||||
observe: function (aSubject, aTopic, aState) {
|
||||
switch(aTopic) {
|
||||
case "metro_viewstate_changed":
|
||||
this.onViewStateChange(aState);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
handleEvent: function bv_handleEvent(aEvent) {
|
||||
switch (aEvent.type){
|
||||
case "MozAppbarDismissing":
|
||||
|
@ -5,8 +5,8 @@
|
||||
'use strict';
|
||||
|
||||
function HistoryView(aSet, aLimit, aFilterUnpinned) {
|
||||
this._set = aSet;
|
||||
this._set.controller = this;
|
||||
View.call(this, aSet);
|
||||
|
||||
this._inBatch = 0;
|
||||
|
||||
this._limit = aLimit;
|
||||
@ -16,12 +16,9 @@ function HistoryView(aSet, aLimit, aFilterUnpinned) {
|
||||
|
||||
this._pinHelper = new ItemPinHelper("metro.history.unpinned");
|
||||
this._historyService.addObserver(this, false);
|
||||
Services.obs.addObserver(this, "metro_viewstate_changed", false);
|
||||
StartUI.chromeWin.addEventListener('MozAppbarDismissing', this, false);
|
||||
StartUI.chromeWin.addEventListener('HistoryNeedsRefresh', this, false);
|
||||
window.addEventListener("TabClose", this, true);
|
||||
|
||||
this._adjustDOMforViewState();
|
||||
}
|
||||
|
||||
HistoryView.prototype = Util.extend(Object.create(View.prototype), {
|
||||
@ -30,11 +27,11 @@ HistoryView.prototype = Util.extend(Object.create(View.prototype), {
|
||||
|
||||
destruct: function destruct() {
|
||||
this._historyService.removeObserver(this);
|
||||
Services.obs.removeObserver(this, "metro_viewstate_changed");
|
||||
if (StartUI.chromeWin) {
|
||||
StartUI.chromeWin.removeEventListener('MozAppbarDismissing', this, false);
|
||||
StartUI.chromeWin.removeEventListener('HistoryNeedsRefresh', this, false);
|
||||
}
|
||||
View.prototype.destruct.call(this);
|
||||
},
|
||||
|
||||
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
|
||||
|
||||
onBeginUpdateBatch: function() {
|
||||
|
@ -20,8 +20,8 @@ Components.utils.import("resource://services-sync/main.js");
|
||||
* You may only have one UI access point at this time.
|
||||
*/
|
||||
function RemoteTabsView(aSet, aSetUIAccessList) {
|
||||
this._set = aSet;
|
||||
this._set.controller = this;
|
||||
View.call(this, aSet);
|
||||
|
||||
this._uiAccessElements = aSetUIAccessList;
|
||||
|
||||
// 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:start-over", this);
|
||||
|
||||
Services.obs.addObserver(this, "metro_viewstate_changed", false);
|
||||
|
||||
if (this.isSyncEnabled() ) {
|
||||
this.populateGrid();
|
||||
}
|
||||
else {
|
||||
this.setUIAccessVisible(false);
|
||||
}
|
||||
this._adjustDOMforViewState();
|
||||
}
|
||||
|
||||
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) {
|
||||
switch (topic) {
|
||||
case "metro_viewstate_changed":
|
||||
this.onViewStateChange(data);
|
||||
break;
|
||||
case "weave:service:sync:finish":
|
||||
this.populateGrid();
|
||||
break;
|
||||
@ -102,9 +96,9 @@ RemoteTabsView.prototype = Util.extend(Object.create(View.prototype), {
|
||||
},
|
||||
|
||||
destruct: function destruct() {
|
||||
Services.obs.removeObserver(this, "metro_viewstate_changed");
|
||||
Weave.Svc.Obs.remove("weave:engine:sync:finish", this);
|
||||
Weave.Svc.Obs.remove("weave:service:logout:start-over", this);
|
||||
View.prototype.destruct.call(this);
|
||||
},
|
||||
|
||||
isSyncEnabled: function isSyncEnabled() {
|
||||
|
@ -26,7 +26,7 @@ var StartUI = {
|
||||
document.getElementById("bcast_preciseInput").setAttribute("input",
|
||||
this.chromeWin.InputSourceHelper.isPrecise ? "precise" : "imprecise");
|
||||
|
||||
this._adjustDOMforViewState();
|
||||
this._adjustDOMforViewState(this.chromeWin.ContentAreaObserver.viewstate);
|
||||
|
||||
TopSitesStartView.init();
|
||||
BookmarksStartView.init();
|
||||
@ -81,6 +81,10 @@ var StartUI = {
|
||||
section.setAttribute("expanded", "true");
|
||||
},
|
||||
|
||||
_adjustDOMforViewState: function(aState) {
|
||||
document.getElementById("bcast_windowState").setAttribute("viewstate", aState);
|
||||
},
|
||||
|
||||
handleEvent: function handleEvent(aEvent) {
|
||||
switch (aEvent.type) {
|
||||
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) {
|
||||
switch (aTopic) {
|
||||
case "metro_viewstate_changed":
|
||||
|
@ -11,8 +11,8 @@ Cu.import("resource://gre/modules/PageThumbs.jsm");
|
||||
Cu.import("resource:///modules/colorUtils.jsm");
|
||||
|
||||
function TopSitesView(aGrid, aMaxSites) {
|
||||
this._set = aGrid;
|
||||
this._set.controller = this;
|
||||
View.call(this, aGrid);
|
||||
|
||||
this._topSitesMax = aMaxSites;
|
||||
|
||||
// clean up state when the appbar closes
|
||||
@ -23,9 +23,6 @@ function TopSitesView(aGrid, aMaxSites) {
|
||||
|
||||
PageThumbs.addExpirationFilter(this);
|
||||
Services.obs.addObserver(this, "Metro:RefreshTopsiteThumbnail", false);
|
||||
Services.obs.addObserver(this, "metro_viewstate_changed", false);
|
||||
|
||||
this._adjustDOMforViewState();
|
||||
|
||||
NewTabUtils.allPages.register(this);
|
||||
TopSites.prepareCache().then(function(){
|
||||
@ -43,12 +40,12 @@ TopSitesView.prototype = Util.extend(Object.create(View.prototype), {
|
||||
|
||||
destruct: function destruct() {
|
||||
Services.obs.removeObserver(this, "Metro:RefreshTopsiteThumbnail");
|
||||
Services.obs.removeObserver(this, "metro_viewstate_changed");
|
||||
PageThumbs.removeExpirationFilter(this);
|
||||
NewTabUtils.allPages.unregister(this);
|
||||
if (StartUI.chromeWin) {
|
||||
StartUI.chromeWin.removeEventListener('MozAppbarDismissing', this, false);
|
||||
}
|
||||
View.prototype.destruct.call(this);
|
||||
},
|
||||
|
||||
handleItemClick: function tabview_handleItemClick(aItem) {
|
||||
@ -137,6 +134,7 @@ TopSitesView.prototype = Util.extend(Object.create(View.prototype), {
|
||||
case "MozAppbarDismissing":
|
||||
// clean up when the context appbar is dismissed - we don't remember selections
|
||||
this._lastSelectedSites = null;
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
@ -234,6 +232,13 @@ TopSitesView.prototype = Util.extend(Object.create(View.prototype), {
|
||||
|
||||
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
|
||||
let tileType = this._set.getAttribute("tiletype");
|
||||
for (let item of this._set.children) {
|
||||
@ -251,9 +256,6 @@ TopSitesView.prototype = Util.extend(Object.create(View.prototype), {
|
||||
case "Metro:RefreshTopsiteThumbnail":
|
||||
this.forceReloadOfThumbnail(aState);
|
||||
break;
|
||||
case "metro_viewstate_changed":
|
||||
this.onViewStateChange(aState);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -111,7 +111,7 @@ gTests.push({
|
||||
},
|
||||
tearDown: function() {
|
||||
BookmarksTestHelper.restore();
|
||||
restoreViewstate();
|
||||
yield restoreViewstate();
|
||||
}
|
||||
});
|
||||
|
||||
@ -160,6 +160,6 @@ gTests.push({
|
||||
tearDown: function() {
|
||||
BookmarksTestHelper.restore();
|
||||
HistoryTestHelper.restore();
|
||||
restoreViewstate();
|
||||
yield restoreViewstate();
|
||||
}
|
||||
});
|
||||
|
@ -47,7 +47,7 @@ const mochitestPath = splitPath.join('/') + '/';
|
||||
|
||||
function isLandscapeMode()
|
||||
{
|
||||
return (Services.metro.snappedState == Ci.nsIWinMetroUtils.fullScreenLandscape);
|
||||
return Elements.windowState.getAttribute("viewstate") == "landscape";
|
||||
}
|
||||
|
||||
function setDevPixelEqualToPx()
|
||||
|
@ -20,7 +20,7 @@ function setSnappedViewstate() {
|
||||
browser.style.borderRight = padding + "px solid gray";
|
||||
|
||||
// Communicate viewstate change
|
||||
Services.obs.notifyObservers(null, 'metro_viewstate_changed', 'snapped');
|
||||
ContentAreaObserver._updateViewState("snapped");
|
||||
|
||||
// Make sure it renders the new mode properly
|
||||
yield waitForMs(0);
|
||||
@ -36,16 +36,15 @@ function setPortraitViewstate() {
|
||||
|
||||
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
|
||||
yield waitForMs(0);
|
||||
}
|
||||
|
||||
function restoreViewstate() {
|
||||
ok(isLandscapeMode(), "restoreViewstate expects landscape mode to work.");
|
||||
|
||||
Services.obs.notifyObservers(null, 'metro_viewstate_changed', 'landscape');
|
||||
ContentAreaObserver._updateViewState("landscape");
|
||||
ok(isLandscapeMode(), "restoreViewstate should restore landscape mode.");
|
||||
|
||||
Browser.selectedBrowser.style.removeProperty("border-right");
|
||||
|
||||
|
@ -6,10 +6,7 @@
|
||||
{"index":1,"title":"@firefox_about@", "type":"text/x-moz-place", "uri":"http://www.mozilla.org/@AB_CD@/about/",
|
||||
"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/",
|
||||
"iconUri":"chrome://branding/content/favicon32.png"
|
||||
},
|
||||
{"index":3,"title":"@firefox_community@", "type":"text/x-moz-place", "uri":"http://www.mozilla.org/@AB_CD@/contribute/",
|
||||
{"index":2,"title":"@firefox_community@", "type":"text/x-moz-place", "uri":"http://www.mozilla.org/@AB_CD@/contribute/",
|
||||
"iconUri":"chrome://branding/content/favicon32.png"
|
||||
}
|
||||
]
|
||||
|
@ -23,10 +23,23 @@ function makeURI(aURL, aOriginCharset, aBaseURI) {
|
||||
// --------------------------------
|
||||
// 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 = {
|
||||
destruct: function () {
|
||||
Services.obs.removeObserver(this.viewStateObserver, "metro_viewstate_changed");
|
||||
},
|
||||
|
||||
_adjustDOMforViewState: function _adjustDOMforViewState(aState) {
|
||||
if (this._set) {
|
||||
if (undefined == aState)
|
||||
@ -44,10 +57,6 @@ View.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
onViewStateChange: function (aState) {
|
||||
this._adjustDOMforViewState(aState);
|
||||
},
|
||||
|
||||
_updateFavicon: function pv__updateFavicon(aItem, aUri) {
|
||||
if ("string" == typeof aUri) {
|
||||
aUri = makeURI(aUri);
|
||||
|
@ -377,6 +377,9 @@ pref("geo.wifi.uri", "https://www.googleapis.com/geolocation/v1/geolocate?key=%G
|
||||
// JS error console
|
||||
pref("devtools.errorconsole.enabled", false);
|
||||
|
||||
// snapped view
|
||||
pref("browser.ui.snapped.maxWidth", 600);
|
||||
|
||||
// kinetic tweakables
|
||||
pref("browser.ui.kinetic.updateInterval", 16);
|
||||
pref("browser.ui.kinetic.exponentialC", 1400);
|
||||
|
@ -375,6 +375,7 @@ documenttab[selected] .documenttab-selection {
|
||||
background-position: left 6px center;
|
||||
}
|
||||
|
||||
#stack[viewstate="snapped"] > .overlay-button,
|
||||
#stack[keyboardVisible] > .overlay-button,
|
||||
#stack[autocomplete] > .overlay-button,
|
||||
#stack[fullscreen] > .overlay-button,
|
||||
|
@ -181,9 +181,6 @@ abstract public class GeckoApp
|
||||
protected TabsPanel mTabsPanel;
|
||||
protected ButtonToast mToast;
|
||||
|
||||
// Handles notification messages from javascript
|
||||
protected NotificationHelper mNotificationHelper;
|
||||
|
||||
protected LayerView mLayerView;
|
||||
private AbsoluteLayout mPluginContainer;
|
||||
|
||||
@ -1235,7 +1232,6 @@ abstract public class GeckoApp
|
||||
|
||||
// Set up tabs panel.
|
||||
mTabsPanel = (TabsPanel) findViewById(R.id.tabs_panel);
|
||||
mNotificationHelper = new NotificationHelper(this);
|
||||
mToast = new ButtonToast(findViewById(R.id.toast));
|
||||
|
||||
// Determine whether we should restore tabs.
|
||||
@ -1796,11 +1792,6 @@ abstract public class GeckoApp
|
||||
alertCookie = "";
|
||||
}
|
||||
handleNotification(ACTION_ALERT_CALLBACK, alertName, alertCookie);
|
||||
|
||||
if (intent.hasExtra(NotificationHelper.NOTIFICATION_ID)) {
|
||||
String id = intent.getStringExtra(NotificationHelper.NOTIFICATION_ID);
|
||||
mNotificationHelper.hideNotification(id);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -2045,8 +2036,6 @@ abstract public class GeckoApp
|
||||
mPromptService.destroy();
|
||||
if (mTextSelection != null)
|
||||
mTextSelection.destroy();
|
||||
if (mNotificationHelper != null)
|
||||
mNotificationHelper.destroy();
|
||||
|
||||
if (SmsManager.getInstance() != null) {
|
||||
SmsManager.getInstance().stop();
|
||||
|
@ -1330,12 +1330,6 @@ public class GeckoAppShell
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -98,6 +98,7 @@ public class GeckoApplication extends Application {
|
||||
public void onCreate() {
|
||||
HardwareUtils.init(getApplicationContext());
|
||||
Clipboard.init(getApplicationContext());
|
||||
NotificationHelper.init(getApplicationContext());
|
||||
GeckoLoader.loadMozGlue();
|
||||
super.onCreate();
|
||||
}
|
||||
|
@ -649,7 +649,7 @@ RES_DRAWABLE_MDPI = \
|
||||
res/drawable-mdpi/ic_menu_bookmark_add.png \
|
||||
res/drawable-mdpi/ic_menu_bookmark_remove.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_guest.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_reading_list_empty.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_selected.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_remove.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_guest.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_uncheck.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_doorhanger.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_bookmark_add.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_forward.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_uncheck.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_doorhanger.png \
|
||||
res/drawable-xhdpi/tab_indicator_divider.9.png \
|
||||
|
@ -14,6 +14,8 @@ import org.json.JSONObject;
|
||||
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
@ -24,21 +26,59 @@ import android.util.Log;
|
||||
import java.util.Set;
|
||||
import java.util.HashSet;
|
||||
|
||||
public class NotificationHelper implements GeckoEventListener {
|
||||
public final class NotificationHelper implements GeckoEventListener {
|
||||
public static final String NOTIFICATION_ID = "NotificationHelper_ID";
|
||||
private static final String LOGTAG = "GeckoNotificationManager";
|
||||
private Context mContext;
|
||||
private Set<String> mShowing;
|
||||
private static final String HELPER_NOTIFICATION = "helperNotif";
|
||||
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;
|
||||
mShowing = new HashSet<String>();
|
||||
registerEventListener("Notification:Show");
|
||||
registerEventListener("Notification:Hide");
|
||||
registerReceiver(context);
|
||||
}
|
||||
|
||||
private void registerEventListener(String event) {
|
||||
GeckoAppShell.getEventDispatcher().registerEventListener(event, this);
|
||||
private static void registerEventListener(String event) {
|
||||
GeckoAppShell.getEventDispatcher().registerEventListener(event, mInstance);
|
||||
}
|
||||
|
||||
@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) {
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext);
|
||||
|
||||
// These attributes are required
|
||||
final String id;
|
||||
try {
|
||||
builder.setContentTitle(message.getString("title"));
|
||||
builder.setContentText(message.getString("text"));
|
||||
id = message.getString("id");
|
||||
builder.setContentTitle(message.getString(TITLE_ATTR));
|
||||
builder.setContentText(message.getString(TEXT_ATTR));
|
||||
id = message.getString(ID_ATTR);
|
||||
} catch (JSONException ex) {
|
||||
Log.i(LOGTAG, "Error parsing", ex);
|
||||
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));
|
||||
|
||||
JSONArray light = message.optJSONArray("light");
|
||||
JSONArray light = message.optJSONArray(LIGHT_ATTR);
|
||||
if (light != null && light.length() == 3) {
|
||||
try {
|
||||
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);
|
||||
|
||||
if (message.has("when")) {
|
||||
int when = message.optInt("when");
|
||||
if (message.has(WHEN_ATTR)) {
|
||||
int when = message.optInt(WHEN_ATTR);
|
||||
builder.setWhen(when);
|
||||
}
|
||||
|
||||
if (message.has("largeicon")) {
|
||||
Bitmap b = BitmapUtils.getBitmapFromDataURI(message.optString("largeicon"));
|
||||
if (message.has(LARGE_ICON_ATTR)) {
|
||||
Bitmap b = BitmapUtils.getBitmapFromDataURI(message.optString(LARGE_ICON_ATTR));
|
||||
builder.setLargeIcon(b);
|
||||
}
|
||||
|
||||
// We currently don't support a callback when these are clicked.
|
||||
// Instead we just open fennec.
|
||||
Intent notificationIntent = new Intent(GeckoApp.ACTION_ALERT_CALLBACK);
|
||||
String app = mContext.getClass().getName();
|
||||
notificationIntent.setClassName(AppConstants.ANDROID_PACKAGE_NAME, app);
|
||||
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
// if this isn't an ongoing notification, add the id to the intent so that we
|
||||
// can remove the notification from our list of active notifications if its clicked
|
||||
if (!ongoing) {
|
||||
notificationIntent.putExtra(NOTIFICATION_ID, id);
|
||||
if (message.has(PROGRESS_VALUE_ATTR) &&
|
||||
message.has(PROGRESS_MAX_ATTR)) {
|
||||
try {
|
||||
final int progress = message.getInt(PROGRESS_VALUE_ATTR);
|
||||
final int progressMax = message.getInt(PROGRESS_MAX_ATTR);
|
||||
builder.setProgress(progressMax, progress, false);
|
||||
} catch (JSONException ex) {
|
||||
Log.i(LOGTAG, "Error parsing", ex);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
PendingIntent deletePendingIntent = buildNotificationPendingIntent(message, CLEARED_EVENT);
|
||||
builder.setDeleteIntent(deletePendingIntent);
|
||||
|
||||
GeckoAppShell.sNotificationClient.add(id.hashCode(), builder.build());
|
||||
if (!mShowing.contains(id)) {
|
||||
@ -134,8 +275,4 @@ public class NotificationHelper implements GeckoEventListener {
|
||||
hideNotification(id);
|
||||
}
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
clearAll();
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 377 B After Width: | Height: | Size: 377 B |
After Width: | Height: | Size: 763 B |
After Width: | Height: | Size: 362 B |
After Width: | Height: | Size: 670 B |
BIN
mobile/android/base/resources/drawable-hdpi/pause.png
Normal file
After Width: | Height: | Size: 450 B |
BIN
mobile/android/base/resources/drawable-hdpi/play.png
Normal file
After Width: | Height: | Size: 611 B |
Before Width: | Height: | Size: 277 B After Width: | Height: | Size: 277 B |
After Width: | Height: | Size: 497 B |
After Width: | Height: | Size: 252 B |
After Width: | Height: | Size: 463 B |
BIN
mobile/android/base/resources/drawable-mdpi/pause.png
Normal file
After Width: | Height: | Size: 380 B |
BIN
mobile/android/base/resources/drawable-mdpi/play.png
Normal file
After Width: | Height: | Size: 479 B |
Before Width: | Height: | Size: 594 B After Width: | Height: | Size: 594 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 389 B |
After Width: | Height: | Size: 853 B |
BIN
mobile/android/base/resources/drawable-xhdpi/pause.png
Normal file
After Width: | Height: | Size: 467 B |
BIN
mobile/android/base/resources/drawable-xhdpi/play.png
Normal file
After Width: | Height: | Size: 550 B |
@ -62,6 +62,11 @@
|
||||
android:icon="@drawable/ic_menu_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>
|
||||
|
||||
</item>
|
||||
@ -75,11 +80,6 @@
|
||||
android:icon="@drawable/ic_menu_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"
|
||||
android:icon="@drawable/ic_menu_guest"
|
||||
android:visible="false"
|
||||
|
@ -63,6 +63,11 @@
|
||||
android:icon="@drawable/ic_menu_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>
|
||||
|
||||
</item>
|
||||
@ -76,11 +81,6 @@
|
||||
android:icon="@drawable/ic_menu_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"
|
||||
android:icon="@drawable/ic_menu_guest"
|
||||
android:visible="false"
|
||||
|
@ -63,6 +63,11 @@
|
||||
android:icon="@drawable/ic_menu_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>
|
||||
|
||||
</item>
|
||||
@ -76,11 +81,6 @@
|
||||
android:icon="@drawable/ic_menu_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"
|
||||
android:icon="@drawable/ic_menu_guest"
|
||||
android:visible="false"
|
||||
|
@ -2,6 +2,7 @@
|
||||
package @ANDROID_PACKAGE_NAME@.tests;
|
||||
|
||||
import @ANDROID_PACKAGE_NAME@.*;
|
||||
import android.os.Build;
|
||||
|
||||
abstract class PixelTest extends BaseTest {
|
||||
private static final long PAINT_CLEAR_DELAY = 10000; // milliseconds
|
||||
@ -9,7 +10,11 @@ abstract class PixelTest extends BaseTest {
|
||||
protected final PaintedSurface loadAndGetPainted(String url) {
|
||||
Actions.RepeatedEventExpecter paintExpecter = mActions.expectPaint();
|
||||
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.unregisterListener();
|
||||
PaintedSurface p = mDriver.getPaintedSurface();
|
||||
|
@ -143,12 +143,18 @@ public class testSettingsMenuItems extends PixelTest {
|
||||
ClassLoader classLoader = getActivity().getClassLoader();
|
||||
Class appConstants = classLoader.loadClass("org.mozilla.gecko.AppConstants");
|
||||
|
||||
// Text reflow
|
||||
Field textReflowField = appConstants.getField("RELEASE_BUILD");
|
||||
boolean textReflow = textReflowField.getBoolean(appConstants);
|
||||
if (textReflow) {
|
||||
// Preferences dependent on RELEASE_BUILD
|
||||
Field releaseBuildField = appConstants.getField("RELEASE_BUILD");
|
||||
boolean releaseBuild = releaseBuildField.getBoolean(appConstants);
|
||||
if (!releaseBuild) {
|
||||
// Text reflow - only built if *not* release build
|
||||
String[] textReflowUi = { "Text reflow" };
|
||||
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
|
||||
@ -167,14 +173,6 @@ public class testSettingsMenuItems extends PixelTest {
|
||||
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
|
||||
Field telemetryField = appConstants.getField("MOZ_TELEMETRY_REPORTING");
|
||||
boolean telemetry = telemetryField.getBoolean(appConstants);
|
||||
|
@ -5136,10 +5136,16 @@ var FormAssistant = {
|
||||
|
||||
// Reset invalid submit state on each pageshow
|
||||
case "pageshow":
|
||||
let target = aEvent.originalTarget;
|
||||
let selectedDocument = BrowserApp.selectedBrowser.contentDocument;
|
||||
if (target == selectedDocument || target.ownerDocument == selectedDocument)
|
||||
this._invalidSubmit = false;
|
||||
if (!this._invalidSubmit)
|
||||
return;
|
||||
|
||||
let selectedBrowser = BrowserApp.selectedBrowser;
|
||||
if (selectedBrowser) {
|
||||
let selectedDocument = selectedBrowser.contentDocument;
|
||||
let target = aEvent.originalTarget;
|
||||
if (target == selectedDocument || target.ownerDocument == selectedDocument)
|
||||
this._invalidSubmit = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -10,6 +10,27 @@ function dump(a) {
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
@ -18,6 +39,7 @@ var Downloads = {
|
||||
_dlmgr: null,
|
||||
_progressAlert: null,
|
||||
_privateDownloads: [],
|
||||
_showingPrompt: false,
|
||||
|
||||
_getLocalFile: function dl__getLocalFile(aFileURI) {
|
||||
// if this is a URL, get the file from that
|
||||
@ -36,6 +58,7 @@ var Downloads = {
|
||||
this._progressAlert = new AlertDownloadProgressListener();
|
||||
this._dlmgr.addPrivacyAwareListener(this._progressAlert);
|
||||
Services.obs.addObserver(this, "last-pb-context-exited", true);
|
||||
Services.obs.addObserver(this, "Notification:Event", true);
|
||||
},
|
||||
|
||||
openDownload: function dl_openDownload(aDownload) {
|
||||
@ -60,61 +83,113 @@ var Downloads = {
|
||||
OS.File.remove(f.path);
|
||||
},
|
||||
|
||||
showAlert: function dl_showAlert(aDownload, aMessage, aTitle, aIcon) {
|
||||
let self = this;
|
||||
getNotificationIdFromDownload: function dl_getNotificationIdFromDownload(aDownload) {
|
||||
return aDownload.target.spec.replace("file:", "download:");
|
||||
},
|
||||
|
||||
// Use this flag to make sure we only show one prompt at a time
|
||||
let cancelPrompt = false;
|
||||
|
||||
// Callback for tapping on the alert popup
|
||||
let observer = {
|
||||
observe: function (aSubject, aTopic, aData) {
|
||||
if (aTopic == "alertclickcallback") {
|
||||
if (aDownload.state == Ci.nsIDownloadManager.DOWNLOAD_FINISHED) {
|
||||
// Only open the downloaded file if the download is complete
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
showNotification: function dl_showNotification(aDownload, aMessage, aTitle, aOptions) {
|
||||
let msg = {
|
||||
type: "Notification:Show",
|
||||
id: this.getNotificationIdFromDownload(aDownload),
|
||||
title: aTitle,
|
||||
smallIcon: URI_GENERIC_ICON_DOWNLOAD,
|
||||
text: aMessage,
|
||||
ongoing: false,
|
||||
cookie: aDownload.guid
|
||||
};
|
||||
|
||||
if (!aIcon)
|
||||
aIcon = URI_GENERIC_ICON_DOWNLOAD;
|
||||
|
||||
if (aDownload.isPrivate) {
|
||||
this._privateDownloads.push(aDownload);
|
||||
if (aOptions && aOptions.icon) {
|
||||
msg.smallIcon = aOptions.icon;
|
||||
}
|
||||
|
||||
var notifier = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
|
||||
notifier.showAlertNotification(aIcon, aTitle, aMessage, true, "", observer,
|
||||
aDownload.target.spec.replace("file:", "download:"));
|
||||
if (aOptions && aOptions.percentComplete) {
|
||||
msg.progress_value = aOptions.percentComplete;
|
||||
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
|
||||
observe: function dl_observe(aSubject, aTopic, aData) {
|
||||
let alertsService = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
|
||||
let progressListener = alertsService.QueryInterface(Ci.nsIAlertsProgressListener);
|
||||
let download;
|
||||
while ((download = this._privateDownloads.pop())) {
|
||||
try {
|
||||
let notificationName = download.target.spec.replace("file:", "download:");
|
||||
progressListener.onCancel(notificationName);
|
||||
} catch (e) {
|
||||
dump("Error removing private download: " + e);
|
||||
switch (aTopic) {
|
||||
case "Notification:Event": {
|
||||
let data = JSON.parse(aData);
|
||||
let guid = data.cookie;
|
||||
this._dlmgr.getDownloadByGUID(guid, (function(status, download) {
|
||||
if (Components.isSuccessCode(status))
|
||||
this.handleNotificationEvent(data, download);
|
||||
}).bind(this));
|
||||
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))
|
||||
throw Components.results.NS_ERROR_NO_INTERFACE;
|
||||
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) { }
|
||||
let contentLength = aDownload.size;
|
||||
if (availableSpace > 0 && contentLength > 0 && contentLength > availableSpace) {
|
||||
Downloads.showAlert(aDownload, strings.GetStringFromName("alertDownloadsNoSpace"),
|
||||
Downloads.showNotification(aDownload, strings.GetStringFromName("alertDownloadsNoSpace"),
|
||||
strings.GetStringFromName("alertDownloadsSize"));
|
||||
|
||||
aDownload.cancel();
|
||||
}
|
||||
|
||||
@ -153,10 +233,11 @@ AlertDownloadProgressListener.prototype = {
|
||||
// Undetermined progress is not supported yet
|
||||
return;
|
||||
}
|
||||
let alertsService = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
|
||||
let progressListener = alertsService.QueryInterface(Ci.nsIAlertsProgressListener);
|
||||
let notificationName = aDownload.target.spec.replace("file:", "download:");
|
||||
progressListener.onProgress(notificationName, aDownload.percentComplete, 100);
|
||||
Downloads.showNotification(aDownload, aDownload.percentComplete + "%",
|
||||
aDownload.displayName, { percentComplete: aDownload.percentComplete,
|
||||
ongoing: true,
|
||||
actions: [PAUSE_ACTION, CANCEL_ACTION] });
|
||||
|
||||
},
|
||||
|
||||
onDownloadStateChange: function(aState, aDownload) {
|
||||
@ -164,19 +245,21 @@ AlertDownloadProgressListener.prototype = {
|
||||
switch (state) {
|
||||
case Ci.nsIDownloadManager.DOWNLOAD_QUEUED:
|
||||
NativeWindow.toast.show(Strings.browser.GetStringFromName("alertDownloadsToast"), "long");
|
||||
Downloads.showAlert(aDownload, Strings.browser.GetStringFromName("alertDownloadsStart2"),
|
||||
Downloads.showNotification(aDownload, Strings.browser.GetStringFromName("alertDownloadsStart2"),
|
||||
aDownload.displayName);
|
||||
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_CANCELED:
|
||||
case Ci.nsIDownloadManager.DOWNLOAD_BLOCKED_PARENTAL:
|
||||
case Ci.nsIDownloadManager.DOWNLOAD_DIRTY:
|
||||
case Ci.nsIDownloadManager.DOWNLOAD_FINISHED: {
|
||||
let alertsService = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
|
||||
let progressListener = alertsService.QueryInterface(Ci.nsIAlertsProgressListener);
|
||||
let notificationName = aDownload.target.spec.replace("file:", "download:");
|
||||
progressListener.onCancel(notificationName);
|
||||
|
||||
Downloads.removeNotification(aDownload);
|
||||
if (aDownload.isPrivate) {
|
||||
let index = this._privateDownloads.indexOf(aDownload);
|
||||
if (index != -1) {
|
||||
@ -185,7 +268,7 @@ AlertDownloadProgressListener.prototype = {
|
||||
}
|
||||
|
||||
if (state == Ci.nsIDownloadManager.DOWNLOAD_FINISHED) {
|
||||
Downloads.showAlert(aDownload, Strings.browser.GetStringFromName("alertDownloadsDone2"),
|
||||
Downloads.showNotification(aDownload, Strings.browser.GetStringFromName("alertDownloadsDone2"),
|
||||
aDownload.displayName);
|
||||
}
|
||||
break;
|
||||
|
@ -16,6 +16,9 @@ alertCantOpenDownload=Can't open file. Tap to save it.
|
||||
alertDownloadsSize=Download too big
|
||||
alertDownloadsNoSpace=Not enough storage space
|
||||
alertDownloadsToast=Download started…
|
||||
alertDownloadsPause=Pause
|
||||
alertDownloadsResume=Resume
|
||||
alertDownloadsCancel=Cancel
|
||||
|
||||
alertFullScreenToast=Press BACK to leave full-screen mode
|
||||
|
||||
|
@ -9,9 +9,8 @@ let Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "FormHistory",
|
||||
"resource://gre/modules/FormHistory.jsm");
|
||||
Cu.import("resource://gre/modules/LoadContextInfo.jsm");
|
||||
Cu.import("resource://gre/modules/FormHistory.jsm");
|
||||
|
||||
function dump(a) {
|
||||
Services.console.logStringMessage(a);
|
||||
|
@ -313,7 +313,7 @@ this.DownloadIntegration = {
|
||||
* @return {Promise}
|
||||
* @resolves The nsIFile of downloads directory.
|
||||
*/
|
||||
getUserDownloadsDirectory: function DI_getUserDownloadsDirectory() {
|
||||
getPreferredDownloadsDirectory: function DI_getPreferredDownloadsDirectory() {
|
||||
return Task.spawn(function() {
|
||||
let directory = null;
|
||||
let prefValue = 1;
|
||||
@ -356,7 +356,7 @@ this.DownloadIntegration = {
|
||||
return Task.spawn(function() {
|
||||
let directory = null;
|
||||
#ifdef XP_MACOSX
|
||||
directory = yield this.getUserDownloadsDirectory();
|
||||
directory = yield this.getPreferredDownloadsDirectory();
|
||||
#elifdef ANDROID
|
||||
directory = yield this.getSystemDownloadsDirectory();
|
||||
#else
|
||||
|
@ -273,8 +273,8 @@ this.Downloads = {
|
||||
* @return {Promise}
|
||||
* @resolves The nsIFile of downloads directory.
|
||||
*/
|
||||
getUserDownloadsDirectory: function D_getUserDownloadsDirectory() {
|
||||
return DownloadIntegration.getUserDownloadsDirectory();
|
||||
getPreferredDownloadsDirectory: function D_getPreferredDownloadsDirectory() {
|
||||
return DownloadIntegration.getPreferredDownloadsDirectory();
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -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.
|
||||
*/
|
||||
add_task(function test_getUserDownloadsDirectory()
|
||||
add_task(function test_getPreferredDownloadsDirectory()
|
||||
{
|
||||
let folderListPrefName = "browser.download.folderList";
|
||||
let dirPrefName = "browser.download.dir";
|
||||
@ -132,20 +132,20 @@ add_task(function test_getUserDownloadsDirectory()
|
||||
// Should return the system downloads directory.
|
||||
Services.prefs.setIntPref(folderListPrefName, 1);
|
||||
let systemDir = yield DownloadIntegration.getSystemDownloadsDirectory();
|
||||
let downloadDir = yield DownloadIntegration.getUserDownloadsDirectory();
|
||||
let downloadDir = yield DownloadIntegration.getPreferredDownloadsDirectory();
|
||||
do_check_true(downloadDir instanceof Ci.nsIFile);
|
||||
do_check_eq(downloadDir.path, systemDir.path);
|
||||
|
||||
// Should return the desktop directory.
|
||||
Services.prefs.setIntPref(folderListPrefName, 0);
|
||||
downloadDir = yield DownloadIntegration.getUserDownloadsDirectory();
|
||||
downloadDir = yield DownloadIntegration.getPreferredDownloadsDirectory();
|
||||
do_check_true(downloadDir instanceof Ci.nsIFile);
|
||||
do_check_eq(downloadDir.path, Services.dirsvc.get("Desk", Ci.nsIFile).path);
|
||||
|
||||
// Should return the system downloads directory because the dir preference
|
||||
// is not set.
|
||||
Services.prefs.setIntPref(folderListPrefName, 2);
|
||||
let downloadDir = yield DownloadIntegration.getUserDownloadsDirectory();
|
||||
let downloadDir = yield DownloadIntegration.getPreferredDownloadsDirectory();
|
||||
do_check_true(downloadDir instanceof Ci.nsIFile);
|
||||
do_check_eq(downloadDir.path, systemDir.path);
|
||||
|
||||
@ -154,7 +154,7 @@ add_task(function test_getUserDownloadsDirectory()
|
||||
let tempDir = Services.dirsvc.get("TmpD", Ci.nsIFile);
|
||||
tempDir.append(time);
|
||||
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_eq(downloadDir.path, tempDir.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(time);
|
||||
Services.prefs.setComplexValue("browser.download.dir", Ci.nsIFile, tempDir);
|
||||
downloadDir = yield DownloadIntegration.getUserDownloadsDirectory();
|
||||
downloadDir = yield DownloadIntegration.getPreferredDownloadsDirectory();
|
||||
do_check_eq(downloadDir.path, systemDir.path);
|
||||
|
||||
// Should return the system downloads directory because the folderList
|
||||
// preference is invalid
|
||||
Services.prefs.setIntPref(folderListPrefName, 999);
|
||||
let downloadDir = yield DownloadIntegration.getUserDownloadsDirectory();
|
||||
let downloadDir = yield DownloadIntegration.getPreferredDownloadsDirectory();
|
||||
do_check_eq(downloadDir.path, systemDir.path);
|
||||
|
||||
cleanup();
|
||||
@ -188,8 +188,8 @@ add_task(function test_getTemporaryDownloadsDirectory()
|
||||
do_check_true(downloadDir instanceof Ci.nsIFile);
|
||||
|
||||
if ("nsILocalFileMac" in Ci) {
|
||||
let userDownloadDir = yield DownloadIntegration.getUserDownloadsDirectory();
|
||||
do_check_eq(downloadDir.path, userDownloadDir.path);
|
||||
let preferredDownloadDir = yield DownloadIntegration.getPreferredDownloadsDirectory();
|
||||
do_check_eq(downloadDir.path, preferredDownloadDir.path);
|
||||
} else {
|
||||
let tempDir = Services.dirsvc.get("TmpD", Ci.nsIFile);
|
||||
do_check_eq(downloadDir.path, tempDir.path);
|
||||
|
@ -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.
|
||||
*/
|
||||
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);
|
||||
});
|
||||
|
||||
|
@ -214,6 +214,11 @@ exports.winIsAbsolute = winIsAbsolute;
|
||||
let normalize = function(path) {
|
||||
let stack = [];
|
||||
|
||||
if (!path.startsWith("\\\\")) {
|
||||
// Normalize "/" to "\\"
|
||||
path = path.replace(/\//g, "\\");
|
||||
}
|
||||
|
||||
// Remove the drive (we will put it back at the end)
|
||||
let root = this.winGetDrive(path);
|
||||
if (root) {
|
||||
@ -223,9 +228,6 @@ let normalize = function(path) {
|
||||
// Remember whether we need to restore a leading "\\" or drive name.
|
||||
let absolute = this.winIsAbsolute(path);
|
||||
|
||||
// Normalize "/" to "\\"
|
||||
path = path.replace("/", "\\");
|
||||
|
||||
// And now, fill |stack| from the components,
|
||||
// popping whenever there is a ".."
|
||||
path.split("\\").forEach(function loop(v) {
|
||||
|
@ -88,6 +88,21 @@ function run_test()
|
||||
do_check_eq(Win.winGetDrive("c:\\abc"), "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_check_eq(Win.basename("\\\\a\\b"), "b");
|
||||
do_check_eq(Win.basename("\\\\a\\b\\"), "");
|
||||
|
@ -19,7 +19,7 @@ this.LayoutHelpers = LayoutHelpers = function(aTopLevelWindow) {
|
||||
this._topDocShell = aTopLevelWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShell);
|
||||
}
|
||||
};
|
||||
|
||||
LayoutHelpers.prototype = {
|
||||
|
||||
@ -366,18 +366,28 @@ LayoutHelpers.prototype = {
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
if (this.isTopLevelWindow(win)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get the docShell for that window
|
||||
let docShell = win.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShell);
|
||||
|
||||
if (docShell.isBrowserOrApp) {
|
||||
// Get the docShell's same-type parent ignoring mozBrowser and mozApp
|
||||
// boundaries
|
||||
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 allIframes = parentDoc.querySelectorAll("iframe");
|
||||
for (let f of allIframes) {
|
||||
@ -385,6 +395,7 @@ LayoutHelpers.prototype = {
|
||||
return f;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
} else {
|
||||
return win.frameElement;
|
||||
|
@ -724,25 +724,26 @@ var ProgressListener = Class({
|
||||
this.webProgress = null;
|
||||
},
|
||||
|
||||
onStateChange: makeInfallible(function stateChange(progress, request, flag, status) {
|
||||
onStateChange: makeInfallible(function stateChange(progress, request, flags, status) {
|
||||
if (!this.webProgress) {
|
||||
console.warn("got an onStateChange after destruction");
|
||||
return;
|
||||
}
|
||||
|
||||
let isWindow = flag & Ci.nsIWebProgressListener.STATE_IS_WINDOW;
|
||||
let isDocument = flag & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT;
|
||||
let isWindow = flags & Ci.nsIWebProgressListener.STATE_IS_WINDOW;
|
||||
let isDocument = flags & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT;
|
||||
if (!(isWindow || isDocument)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isDocument && (flag & Ci.nsIWebProgressListener.STATE_START)) {
|
||||
if (isDocument && (flags & Ci.nsIWebProgressListener.STATE_START)) {
|
||||
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);
|
||||
}
|
||||
}),
|
||||
|
||||
onProgressChange: function() {},
|
||||
onSecurityChange: function() {},
|
||||
onStatusChange: function() {},
|
||||
@ -1736,7 +1737,8 @@ var WalkerActor = protocol.ActorClass({
|
||||
|
||||
onFrameLoad: function(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.rootNode = this.document();
|
||||
this.queueMutation({
|
||||
|
@ -2446,37 +2446,37 @@ SourceActor.prototype = {
|
||||
* 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
|
||||
* debugging.
|
||||
*
|
||||
* Note that the source map is modified in place.
|
||||
*/
|
||||
_invertSourceMap: function SA__invertSourceMap({ code, map }) {
|
||||
const smc = new SourceMapConsumer(map.toJSON());
|
||||
const invertedMap = new SourceMapGenerator({
|
||||
file: this._url
|
||||
});
|
||||
// XXX bug 918802: Monkey punch the source map consumer, because iterating
|
||||
// over all mappings and inverting each of them, and then creating a new
|
||||
// SourceMapConsumer is *way* too slow.
|
||||
|
||||
smc.eachMapping(m => {
|
||||
if (!m.originalLine || !m.originalColumn) {
|
||||
return;
|
||||
}
|
||||
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);
|
||||
});
|
||||
map.setSourceContent(this._url, code);
|
||||
const consumer = new SourceMapConsumer.fromSourceMap(map);
|
||||
const getOrigPos = consumer.originalPositionFor.bind(consumer);
|
||||
const getGenPos = consumer.generatedPositionFor.bind(consumer);
|
||||
|
||||
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 {
|
||||
code: code,
|
||||
map: new SourceMapConsumer(invertedMap.toJSON())
|
||||
map: consumer
|
||||
};
|
||||
},
|
||||
|
||||
@ -2491,7 +2491,7 @@ SourceActor.prototype = {
|
||||
// Compose the source maps
|
||||
this._sourceMap = SourceMapGenerator.fromSourceMap(this._sourceMap);
|
||||
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._generatedSource);
|
||||
} else {
|
||||
|
@ -622,13 +622,10 @@ StyleSheetActor.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
if (channel instanceof Ci.nsIPrivateBrowsingChannel) {
|
||||
let loadContext = this.window
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
channel.loadGroup = this.window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsILoadContext);
|
||||
channel.setPrivate(loadContext.usePrivateBrowsing);
|
||||
}
|
||||
.QueryInterface(Ci.nsIDocumentLoader)
|
||||
.loadGroup;
|
||||
channel.loadFlags = channel.LOAD_FROM_CACHE;
|
||||
channel.asyncOpen(streamListener, null);
|
||||
},
|
||||
|
@ -185,8 +185,18 @@ function BrowserTabList(aConnection)
|
||||
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) {
|
||||
return aWindow.gBrowser.selectedBrowser;
|
||||
return aWindow.gBrowser ? aWindow.gBrowser.selectedBrowser : null;
|
||||
};
|
||||
|
||||
BrowserTabList.prototype._getChildren = function(aWindow) {
|
||||
@ -209,6 +219,9 @@ BrowserTabList.prototype.getList = function() {
|
||||
// Iterate over all navigator:browser XUL windows.
|
||||
for (let win of allAppShellDOMWindows(DebuggerServer.chromeWindowType)) {
|
||||
let selectedBrowser = this._getSelectedBrowser(win);
|
||||
if (!selectedBrowser) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// For each tab in this XUL window, ensure that we have an actor for
|
||||
// it, reusing existing actors where possible. We actually iterate
|
||||
|
@ -118,10 +118,18 @@ WebConsoleActor.prototype =
|
||||
conn: null,
|
||||
|
||||
/**
|
||||
* The content window we work with.
|
||||
* The window we work with.
|
||||
* @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.
|
||||
|
@ -117,6 +117,32 @@ define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'sou
|
||||
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.
|
||||
*/
|
||||
@ -212,37 +238,7 @@ define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'sou
|
||||
}
|
||||
}
|
||||
|
||||
this._originalMappings.sort(this._compareOriginalPositions);
|
||||
};
|
||||
|
||||
/**
|
||||
* 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;
|
||||
this._originalMappings.sort(util.compareByOriginalPositions);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -295,7 +291,7 @@ define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'sou
|
||||
this._generatedMappings,
|
||||
"generatedLine",
|
||||
"generatedColumn",
|
||||
this._compareGeneratedPositions);
|
||||
util.compareByGeneratedPositions);
|
||||
|
||||
if (mapping) {
|
||||
var source = util.getArg(mapping, 'source', null);
|
||||
@ -389,7 +385,7 @@ define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'sou
|
||||
this._originalMappings,
|
||||
"originalLine",
|
||||
"originalColumn",
|
||||
this._compareOriginalPositions);
|
||||
util.compareByOriginalPositions);
|
||||
|
||||
if (mapping) {
|
||||
return {
|
||||
@ -574,6 +570,93 @@ define('source-map/util', ['require', 'exports', 'module' , ], function(require,
|
||||
}
|
||||
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; -*- */
|
||||
/*
|
||||
@ -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
|
||||
// return null.
|
||||
var mid = Math.floor((aHigh - aLow) / 2) + aLow;
|
||||
var cmp = aCompare(aNeedle, aHaystack[mid]);
|
||||
var cmp = aCompare(aNeedle, aHaystack[mid], true);
|
||||
if (cmp === 0) {
|
||||
// Found the element we are looking for.
|
||||
return aHaystack[mid];
|
||||
@ -1033,8 +1116,10 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so
|
||||
}
|
||||
|
||||
this._mappings.push({
|
||||
generated: generated,
|
||||
original: original,
|
||||
generatedLine: generated.line,
|
||||
generatedColumn: generated.column,
|
||||
originalLine: original != null && original.line,
|
||||
originalColumn: original != null && original.column,
|
||||
source: source,
|
||||
name: name
|
||||
});
|
||||
@ -1083,13 +1168,11 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so
|
||||
if (!aSourceFile) {
|
||||
aSourceFile = aSourceMapConsumer.file;
|
||||
}
|
||||
|
||||
var sourceRoot = this._sourceRoot;
|
||||
// Make "aSourceFile" relative if an absolute Url is passed.
|
||||
if (sourceRoot) {
|
||||
aSourceFile = util.relative(sourceRoot, aSourceFile);
|
||||
}
|
||||
|
||||
// Applying the SourceMap can add and remove items from the sources and
|
||||
// the names array.
|
||||
var newSources = new ArraySet();
|
||||
@ -1097,13 +1180,12 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so
|
||||
|
||||
// Find mappings for the "aSourceFile"
|
||||
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.
|
||||
var original = aSourceMapConsumer.originalPositionFor({
|
||||
line: mapping.original.line,
|
||||
column: mapping.original.column
|
||||
line: mapping.originalLine,
|
||||
column: mapping.originalColumn
|
||||
});
|
||||
|
||||
if (original.source !== null) {
|
||||
// Copy mapping
|
||||
if (sourceRoot) {
|
||||
@ -1111,8 +1193,8 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so
|
||||
} else {
|
||||
mapping.source = original.source;
|
||||
}
|
||||
mapping.original.line = original.line;
|
||||
mapping.original.column = original.column;
|
||||
mapping.originalLine = original.line;
|
||||
mapping.originalColumn = original.column;
|
||||
if (original.name !== null && mapping.name !== null) {
|
||||
// Only use the identifier name if it's an identifier
|
||||
// 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
|
||||
* 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
|
||||
// performant to maintain the sorting as we insert them, rather than as we
|
||||
// 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++) {
|
||||
mapping = this._mappings[i];
|
||||
|
||||
if (mapping.generated.line !== previousGeneratedLine) {
|
||||
if (mapping.generatedLine !== previousGeneratedLine) {
|
||||
previousGeneratedColumn = 0;
|
||||
while (mapping.generated.line !== previousGeneratedLine) {
|
||||
while (mapping.generatedLine !== previousGeneratedLine) {
|
||||
result += ';';
|
||||
previousGeneratedLine++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (i > 0) {
|
||||
if (!cmpMapping(mapping, this._mappings[i - 1])) {
|
||||
if (!util.compareByGeneratedPositions(mapping, this._mappings[i - 1])) {
|
||||
continue;
|
||||
}
|
||||
result += ',';
|
||||
}
|
||||
}
|
||||
|
||||
result += base64VLQ.encode(mapping.generated.column
|
||||
result += base64VLQ.encode(mapping.generatedColumn
|
||||
- previousGeneratedColumn);
|
||||
previousGeneratedColumn = mapping.generated.column;
|
||||
previousGeneratedColumn = mapping.generatedColumn;
|
||||
|
||||
if (mapping.source && mapping.original) {
|
||||
if (mapping.source) {
|
||||
result += base64VLQ.encode(this._sources.indexOf(mapping.source)
|
||||
- previousSource);
|
||||
previousSource = this._sources.indexOf(mapping.source);
|
||||
|
||||
// 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 = mapping.original.line - 1;
|
||||
previousOriginalLine = mapping.originalLine - 1;
|
||||
|
||||
result += base64VLQ.encode(mapping.original.column
|
||||
result += base64VLQ.encode(mapping.originalColumn
|
||||
- previousOriginalColumn);
|
||||
previousOriginalColumn = mapping.original.column;
|
||||
previousOriginalColumn = mapping.originalColumn;
|
||||
|
||||
if (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;
|
||||
};
|
||||
|
||||
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.
|
||||
*/
|
||||
@ -1284,16 +1362,9 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so
|
||||
map.sourceRoot = this._sourceRoot;
|
||||
}
|
||||
if (this._sourcesContents) {
|
||||
map.sourcesContent = map.sources.map(function (source) {
|
||||
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);
|
||||
map.sourcesContent = this._generateSourcesContent(map.sources, map.sourceRoot);
|
||||
}
|
||||
|
||||
return map;
|
||||
};
|
||||
|
||||
@ -1500,7 +1571,9 @@ define('source-map/source-node', ['require', 'exports', 'module' , 'source-map/
|
||||
* @param aFn The traversal function.
|
||||
*/
|
||||
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) {
|
||||
chunk.walk(aFn);
|
||||
}
|
||||
@ -1512,7 +1585,7 @@ define('source-map/source-node', ['require', 'exports', 'module' , 'source-map/
|
||||
name: this.name });
|
||||
}
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1578,14 +1651,16 @@ define('source-map/source-node', ['require', 'exports', 'module' , 'source-map/
|
||||
*/
|
||||
SourceNode.prototype.walkSourceContents =
|
||||
function SourceNode_walkSourceContents(aFn) {
|
||||
this.children.forEach(function (chunk) {
|
||||
if (chunk instanceof SourceNode) {
|
||||
chunk.walkSourceContents(aFn);
|
||||
for (var i = 0, len = this.children.length; i < len; i++) {
|
||||
if (this.children[i] instanceof SourceNode) {
|
||||
this.children[i].walkSourceContents(aFn);
|
||||
}
|
||||
}, this);
|
||||
Object.keys(this.sourceContents).forEach(function (sourceFileKey) {
|
||||
aFn(util.fromSetString(sourceFileKey), this.sourceContents[sourceFileKey]);
|
||||
}, this);
|
||||
}
|
||||
|
||||
var sources = Object.keys(this.sourceContents);
|
||||
for (var i = 0, len = sources.length; i < len; i++) {
|
||||
aFn(util.fromSetString(sources[i]), this.sourceContents[sources[i]]);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -214,7 +214,8 @@ define('test/source-map/util', ['require', 'exports', 'module' , 'lib/source-ma
|
||||
expectedMap.sourceRoot,
|
||||
"sourceRoot mismatch: " +
|
||||
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) {
|
||||
assert.equal(actualMap.sourcesContent.length,
|
||||
expectedMap.sourcesContent.length,
|
||||
@ -343,6 +344,93 @@ define('lib/source-map/util', ['require', 'exports', 'module' , ], function(requ
|
||||
}
|
||||
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; -*- */
|
||||
/*
|
||||
|
@ -393,6 +393,66 @@ define("test/source-map/test-source-map-consumer", ["require", "exports", "modul
|
||||
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() {
|
||||
runSourceMapTests('test/source-map/test-source-map-consumer', do_throw);
|
||||
|
@ -262,11 +262,17 @@ define("test/source-map/test-source-node", ["require", "exports", "module"], fun
|
||||
|
||||
exports['test .fromStringWithSourceMap() merging duplicate mappings'] = function (assert, util) {
|
||||
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", "var Test = "), 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 */"]);
|
||||
new SourceNode(1, 0, "a.js", "(function"),
|
||||
new SourceNode(1, 0, "a.js", "() {\n"),
|
||||
" ",
|
||||
new SourceNode(1, 0, "a.js", "var Test = "),
|
||||
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({
|
||||
file: 'foo.js'
|
||||
});
|
||||
|
@ -52,7 +52,7 @@ WebappTabList.prototype = Object.create(BrowserTabList.prototype);
|
||||
WebappTabList.prototype.constructor = WebappTabList;
|
||||
|
||||
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
|
||||
// picked up when we iterate over all windows.
|
||||
|
@ -12,27 +12,13 @@
|
||||
* implementation of this interface for non-Windows systems, for testing and
|
||||
* development purposes only.
|
||||
*/
|
||||
[scriptable, uuid(c5a654c8-2443-47ce-9322-d150af3ca526)]
|
||||
[scriptable, uuid(fa6750a2-f0fe-411c-af23-1cd6d2fdeceb)]
|
||||
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 */
|
||||
const long handPreferenceLeft = 0;
|
||||
const long handPreferenceRight = 1;
|
||||
|
||||
/**
|
||||
* Determines the current snapped state.
|
||||
*/
|
||||
readonly attribute long snappedState;
|
||||
|
||||
/**
|
||||
* Determine if the current browser is running in the metro immersive
|
||||
* environment.
|
||||
@ -49,12 +35,6 @@ interface nsIWinMetroUtils : nsISupports
|
||||
*/
|
||||
readonly attribute AString activationURI;
|
||||
|
||||
/**
|
||||
* Attempts to unsnap the application from snapped state to filled state
|
||||
*/
|
||||
void unsnap();
|
||||
|
||||
|
||||
/**
|
||||
* Show the settings flyout
|
||||
*/
|
||||
|
@ -109,7 +109,6 @@ FrameworkView::ActivateView()
|
||||
LogFunction();
|
||||
|
||||
UpdateWidgetSizeAndPosition();
|
||||
MetroUtils::GetViewState(mViewState);
|
||||
|
||||
nsIntRegion region(nsIntRect(0, 0, mWindowBounds.width, mWindowBounds.height));
|
||||
mWidget->Paint(region);
|
||||
@ -386,36 +385,6 @@ FrameworkView::OnWindowClosed(ICoreWindow* aSender, ICoreWindowEventArgs* aArgs)
|
||||
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
|
||||
FrameworkView::OnWindowSizeChanged(ICoreWindow* aSender, IWindowSizeChangedEventArgs* aArgs)
|
||||
{
|
||||
@ -431,7 +400,6 @@ FrameworkView::OnWindowSizeChanged(ICoreWindow* aSender, IWindowSizeChangedEvent
|
||||
mWindowBounds = MetroUtils::LogToPhys(logicalBounds);
|
||||
|
||||
UpdateWidgetSizeAndPosition();
|
||||
FireViewStateObservers();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
@ -195,7 +195,6 @@ private:
|
||||
static Rect sKeyboardRect;
|
||||
bool mWinVisible;
|
||||
bool mWinActiveState;
|
||||
ApplicationViewState mViewState;
|
||||
};
|
||||
|
||||
} } }
|
||||
|
@ -363,32 +363,6 @@ nsWinMetroUtils::ShowNativeToast(const nsAString &aTitle,
|
||||
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
|
||||
nsWinMetroUtils::ShowSettingsFlyout()
|
||||
{
|
||||
|