merge fx-team to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2014-06-05 15:08:55 +02:00
Родитель 40ac2a36a8 527e49066e
Коммит 83702104a1
52 изменённых файлов: 786 добавлений и 391 удалений

Просмотреть файл

@ -45,6 +45,7 @@ svg|line.box-model-guide-bottom[hidden] {
html|*.highlighter-nodeinfobar-id,
html|*.highlighter-nodeinfobar-classes,
html|*.highlighter-nodeinfobar-pseudo-classes,
html|*.highlighter-nodeinfobar-dimensions,
html|*.highlighter-nodeinfobar-tagname {
-moz-user-select: text;
-moz-user-focus: normal;

Просмотреть файл

@ -210,7 +210,19 @@ let gPage = {
for (let site of gGrid.sites) {
if (site) {
site.captureIfMissing();
let {type} = site.link;
// Record which tile index a directory link was shown
let {directoryIndex, type} = site.link;
if (directoryIndex !== undefined) {
let tileIndex = site.cell.index;
// For telemetry, only handle the first 9 links in the first 9 cells
if (directoryIndex < 9) {
let shownId = "NEWTAB_PAGE_DIRECTORY_LINK" + directoryIndex + "_SHOWN";
Services.telemetry.getHistogramById(shownId).add(Math.min(9, tileIndex));
}
}
// Aggregate tile impression counts into directory types
if (type in directoryCount) {
directoryCount[type]++;
}

Просмотреть файл

@ -12,6 +12,70 @@ function test() {
}
function spawnTest() {
let lines = [
'Manifest has a character encoding of ISO-8859-1. Manifests must have the ' +
'utf-8 character encoding.',
'The first line of the manifest must be "CACHE MANIFEST" at line 1.',
'"CACHE MANIFEST" is only valid on the first line but was found at line 3.',
'images/sound-icon.png points to a resource that is not available at line 9.',
'images/background.png points to a resource that is not available at line 10.',
'/checking.cgi points to a resource that is not available at line 13.',
'Asterisk (*) incorrectly used in the NETWORK section at line 14. If a line ' +
'in the NETWORK section contains only a single asterisk character, then any ' +
'URI not listed in the manifest will be treated as if the URI was listed in ' +
'the NETWORK section. Otherwise such URIs will be treated as unavailable. ' +
'Other uses of the * character are prohibited',
'../rel.html points to a resource that is not available at line 17.',
'../../rel.html points to a resource that is not available at line 18.',
'../../../rel.html points to a resource that is not available at line 19.',
'../../../../rel.html points to a resource that is not available at line 20.',
'../../../../../rel.html points to a resource that is not available at line 21.',
'/../ is not a valid URI prefix at line 22.',
'/test.css points to a resource that is not available at line 23.',
'/test.js points to a resource that is not available at line 24.',
'test.png points to a resource that is not available at line 25.',
'/main/features.js points to a resource that is not available at line 27.',
'/main/settings/index.css points to a resource that is not available at line 28.',
'http://example.com/scene.jpg points to a resource that is not available at line 29.',
'/section1/blockedbyfallback.html points to a resource that is not available at line 30.',
'http://example.com/images/world.jpg points to a resource that is not available at line 31.',
'/section2/blockedbyfallback.html points to a resource that is not available at line 32.',
'/main/home points to a resource that is not available at line 34.',
'main/app.js points to a resource that is not available at line 35.',
'/settings/home points to a resource that is not available at line 37.',
'/settings/app.js points to a resource that is not available at line 38.',
'The file http://sub1.test1.example.com/browser/browser/devtools/' +
'commandline/test/browser_cmd_appcache_invalid_page3.html was modified ' +
'after http://sub1.test1.example.com/browser/browser/devtools/' +
'commandline/test/browser_cmd_appcache_invalid_appcache.appcache. Unless ' +
'the text in the manifest file is changed the cached version will be used ' +
'instead at line 39.',
'browser_cmd_appcache_invalid_page3.html has cache-control set to no-store. ' +
'This will prevent the application cache from storing the file at line 39.',
'http://example.com/logo.png points to a resource that is not available at line 40.',
'http://example.com/check.png points to a resource that is not available at line 41.',
'Spaces in URIs need to be replaced with % at line 42.',
'http://example.com/cr oss.png points to a resource that is not available at line 42.',
'Asterisk (*) incorrectly used in the CACHE section at line 43. If a line ' +
'in the NETWORK section contains only a single asterisk character, then ' +
'any URI not listed in the manifest will be treated as if the URI was ' +
'listed in the NETWORK section. Otherwise such URIs will be treated as ' +
'unavailable. Other uses of the * character are prohibited',
'The SETTINGS section may only contain a single value, "prefer-online" or "fast" at line 47.',
'FALLBACK section line 50 (/section1/ /offline1.html) prevents caching of ' +
'line 30 (/section1/blockedbyfallback.html) in the CACHE section.',
'/offline1.html points to a resource that is not available at line 50.',
'FALLBACK section line 51 (/section2/ offline2.html) prevents caching of ' +
'line 32 (/section2/blockedbyfallback.html) in the CACHE section.',
'offline2.html points to a resource that is not available at line 51.',
'Only two URIs separated by spaces are allowed in the FALLBACK section at line 52.',
'Asterisk (*) incorrectly used in the FALLBACK section at line 53. URIs ' +
'in the FALLBACK section simply need to match a prefix of the request URI.',
'offline3.html points to a resource that is not available at line 53.',
'Invalid section name (BLAH) at line 55.',
'Only two URIs separated by spaces are allowed in the FALLBACK section at line 55.'
];
let options = yield helpers.openTab(TEST_URI);
info("window open");
@ -25,7 +89,8 @@ function spawnTest() {
// Pages containing an appcache the notification bar gives options to allow
// or deny permission for the app to save data offline. Let's click Allow.
let notificationID = "offline-app-requested-sub1.test1.example.com";
let notification = PopupNotifications.getNotification(notificationID, gBrowser.selectedBrowser);
let notification =
PopupNotifications.getNotification(notificationID, gBrowser.selectedBrowser);
if (notification) {
info("Authorizing offline storage.");
@ -45,52 +110,7 @@ function spawnTest() {
args: {}
},
exec: {
output: [
/Manifest has a character encoding of ISO-8859-1\. Manifests must have the utf-8 character encoding\./,
/The first line of the manifest must be "CACHE MANIFEST" at line 1\./,
/"CACHE MANIFEST" is only valid on the first line but was found at line 3\./,
/images\/sound-icon\.png points to a resource that is not available at line 9\./,
/images\/background\.png points to a resource that is not available at line 10\./,
/NETWORK section line 13 \(\/checking\.cgi\) prevents caching of line 13 \(\/checking\.cgi\) in the NETWORK section\./,
/\/checking\.cgi points to a resource that is not available at line 13\./,
/Asterisk \(\*\) incorrectly used in the NETWORK section at line 14\. If a line in the NETWORK section contains only a single asterisk character, then any URI not listed in the manifest will be treated as if the URI was listed in the NETWORK section\. Otherwise such URIs will be treated as unavailable\. Other uses of the \* character are prohibited/,
/\.\.\/rel\.html points to a resource that is not available at line 17\./,
/\.\.\/\.\.\/rel\.html points to a resource that is not available at line 18\./,
/\.\.\/\.\.\/\.\.\/rel\.html points to a resource that is not available at line 19\./,
/\.\.\/\.\.\/\.\.\/\.\.\/rel\.html points to a resource that is not available at line 20\./,
/\.\.\/\.\.\/\.\.\/\.\.\/\.\.\/rel\.html points to a resource that is not available at line 21\./,
/\/\.\.\/ is not a valid URI prefix at line 22\./,
/\/test\.css points to a resource that is not available at line 23\./,
/\/test\.js points to a resource that is not available at line 24\./,
/test\.png points to a resource that is not available at line 25\./,
/\/main\/features\.js points to a resource that is not available at line 27\./,
/\/main\/settings\/index\.css points to a resource that is not available at line 28\./,
/http:\/\/example\.com\/scene\.jpg points to a resource that is not available at line 29\./,
/\/section1\/blockedbyfallback\.html points to a resource that is not available at line 30\./,
/http:\/\/example\.com\/images\/world\.jpg points to a resource that is not available at line 31\./,
/\/section2\/blockedbyfallback\.html points to a resource that is not available at line 32\./,
/\/main\/home points to a resource that is not available at line 34\./,
/main\/app\.js points to a resource that is not available at line 35\./,
/\/settings\/home points to a resource that is not available at line 37\./,
/\/settings\/app\.js points to a resource that is not available at line 38\./,
/The file http:\/\/sub1\.test1\.example\.com\/browser\/browser\/devtools\/commandline\/test\/browser_cmd_appcache_invalid_page3\.html was modified after http:\/\/sub1\.test1\.example\.com\/browser\/browser\/devtools\/commandline\/test\/browser_cmd_appcache_invalid_appcache\.appcache\. Unless the text in the manifest file is changed the cached version will be used instead at line 39\./,
/browser_cmd_appcache_invalid_page3\.html has cache-control set to no-store\. This will prevent the application cache from storing the file at line 39\./,
/http:\/\/example\.com\/logo\.png points to a resource that is not available at line 40\./,
/http:\/\/example\.com\/check\.png points to a resource that is not available at line 41\./,
/Spaces in URIs need to be replaced with % at line 42\./,
/http:\/\/example\.com\/cr oss\.png points to a resource that is not available at line 42\./,
/Asterisk \(\*\) incorrectly used in the CACHE section at line 43\. If a line in the NETWORK section contains only a single asterisk character, then any URI not listed in the manifest will be treated as if the URI was listed in the NETWORK section\. Otherwise such URIs will be treated as unavailable\. Other uses of the \* character are prohibited/,
/The SETTINGS section may only contain a single value, "prefer-online" or "fast" at line 47\./,
/FALLBACK section line 50 \(\/section1\/ \/offline1\.html\) prevents caching of line 30 \(\/section1\/blockedbyfallback\.html\) in the CACHE section\./,
/\/offline1\.html points to a resource that is not available at line 50\./,
/FALLBACK section line 51 \(\/section2\/ offline2\.html\) prevents caching of line 32 \(\/section2\/blockedbyfallback\.html\) in the CACHE section\./,
/offline2\.html points to a resource that is not available at line 51\./,
/Only two URIs separated by spaces are allowed in the FALLBACK section at line 52\./,
/Asterisk \(\*\) incorrectly used in the FALLBACK section at line 53\. URIs in the FALLBACK section simply need to match a prefix of the request URI\./,
/offline3\.html points to a resource that is not available at line 53\./,
/Invalid section name \(BLAH\) at line 55\./,
/Only two URIs separated by spaces are allowed in the FALLBACK section at line 55\./
]
output: lines.map(getRegexForString)
},
},
]);

Просмотреть файл

@ -27,6 +27,20 @@ function whenDelayedStartupFinished(aWindow, aCallback) {
}, "browser-delayed-startup-finished", false);
}
/**
* Creates a regular expression that matches a string. This greatly simplifies
* matching and debugging long strings.
*
* @param {String} text
* Text to convert
* @return {RegExp}
* Regular expression matching text
*/
function getRegexForString(str) {
str = str.replace(/(\.|\\|\/|\(|\)|\[|\]|\*|\+|\?|\$|\^|\|)/g, "\\$1");
return new RegExp(str);
}
/**
* Force GC on shutdown, because it seems that GCLI can outrun the garbage
* collector in some situations, which causes test failures in later tests

Просмотреть файл

@ -5,106 +5,71 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
let doc;
let h1;
let div;
let rotated;
let inspector;
let contentViewer;
function createDocument() {
let div = doc.createElement("div");
h1 = doc.createElement("h1");
let p1 = doc.createElement("p");
let p2 = doc.createElement("p");
let div2 = doc.createElement("div");
let p3 = doc.createElement("p");
doc.title = "Inspector Highlighter Meatballs";
h1.textContent = "Inspector Tree Selection Test";
p1.textContent = "This is some example text";
p2.textContent = "Lorem ipsum dolor sit amet, consectetur adipisicing " +
"elit, sed do eiusmod tempor incididunt ut labore et dolore magna " +
"aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco " +
"laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure " +
"dolor in reprehenderit in voluptate velit esse cillum dolore eu " +
"fugiat nulla pariatur. Excepteur sint occaecat cupidatat non " +
"proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
p3.textContent = "Lorem ipsum dolor sit amet, consectetur adipisicing " +
"elit, sed do eiusmod tempor incididunt ut labore et dolore magna " +
"aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco " +
"laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure " +
"dolor in reprehenderit in voluptate velit esse cillum dolore eu " +
"fugiat nulla pariatur. Excepteur sint occaecat cupidatat non " +
"proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
let div3 = doc.createElement("div");
div3.id = "checkOutThisWickedSpread";
div3.setAttribute("style", "position: absolute; top: 20px; right: 20px; height: 20px; width: 20px; background-color: yellow; border: 1px dashed black;");
let p4 = doc.createElement("p");
p4.setAttribute("style", "font-weight: 200; font-size: 8px; text-align: center;");
p4.textContent = "Smörgåsbord!";
div.appendChild(h1);
div.appendChild(p1);
div.appendChild(p2);
div2.appendChild(p3);
div3.appendChild(p4);
div = doc.createElement("div");
div.setAttribute("style",
"padding:5px; border:7px solid red; margin: 9px; " +
"position:absolute; top:30px; left:150px;");
let textNode = doc.createTextNode("Gort! Klaatu barada nikto!");
rotated = doc.createElement("div");
rotated.setAttribute("style",
"padding:5px; border:7px solid red; margin: 9px; " +
"transform:rotate(45deg); " +
"position:absolute; top:30px; left:80px;");
div.appendChild(textNode);
doc.body.appendChild(div);
doc.body.appendChild(div2);
doc.body.appendChild(div3);
doc.body.appendChild(rotated);
openInspector(aInspector => {
inspector = aInspector;
inspector.selection.setNode(div, null);
inspector.once("inspector-updated", () => {
inspector.toolbox.highlighterUtils.startPicker().then(testMouseOverH1Highlights);
});
inspector.once("inspector-updated", testMouseOverDivHighlights);
});
}
function testMouseOverH1Highlights() {
function testMouseOverDivHighlights() {
ok(isHighlighting(), "Highlighter is shown");
is(getHighlitNode(), div, "Highlighter's outline correspond to the non-rotated div");
testNonTransformedBoxModelDimensionsNoZoom();
}
function testNonTransformedBoxModelDimensionsNoZoom() {
info("Highlighted the non-rotated div");
isNodeCorrectlyHighlighted(div, "non-zoomed");
inspector.toolbox.once("highlighter-ready", testNonTransformedBoxModelDimensionsZoomed);
contentViewer = gBrowser.selectedBrowser.docShell.contentViewer
.QueryInterface(Ci.nsIMarkupDocumentViewer);
contentViewer.fullZoom = 2;
}
function testNonTransformedBoxModelDimensionsZoomed() {
info("Highlighted the zoomed, non-rotated div");
isNodeCorrectlyHighlighted(div, "zoomed");
inspector.toolbox.once("highlighter-ready", testMouseOverRotatedHighlights);
contentViewer.fullZoom = 1;
}
function testMouseOverRotatedHighlights() {
inspector.toolbox.once("highlighter-ready", () => {
ok(isHighlighting(), "Highlighter is shown");
is(getHighlitNode(), h1, "Highlighter's outline correspond to the selected node");
testBoxModelDimensions();
info("Highlighted the rotated div");
isNodeCorrectlyHighlighted(rotated, "rotated");
executeSoon(finishUp);
});
EventUtils.synthesizeMouse(h1, 2, 2, {type: "mousemove"}, content);
}
function testBoxModelDimensions() {
let h1Dims = h1.getBoundingClientRect();
let h1Width = Math.ceil(h1Dims.width);
let h1Height = Math.ceil(h1Dims.height);
let outlineDims = getSimpleBorderRect();
let outlineWidth = Math.ceil(outlineDims.width);
let outlineHeight = Math.ceil(outlineDims.height);
// Disabled due to bug 716245
is(outlineWidth, h1Width, "outline width matches dimensions of element (no zoom)");
is(outlineHeight, h1Height, "outline height matches dimensions of element (no zoom)");
// zoom the page by a factor of 2
let contentViewer = gBrowser.selectedBrowser.docShell.contentViewer
.QueryInterface(Ci.nsIMarkupDocumentViewer);
contentViewer.fullZoom = 2;
// simulate the zoomed dimensions of the div element
let h1Dims = h1.getBoundingClientRect();
// There seems to be some very minor differences in the floats, so let's
// floor the values
let h1Width = Math.floor(h1Dims.width * contentViewer.fullZoom);
let h1Height = Math.floor(h1Dims.height * contentViewer.fullZoom);
let outlineDims = getSimpleBorderRect();
let outlineWidth = Math.floor(outlineDims.width);
let outlineHeight = Math.floor(outlineDims.height);
is(outlineWidth, h1Width, "outline width matches dimensions of element (zoomed)");
is(outlineHeight, h1Height, "outline height matches dimensions of element (zoomed)");
executeSoon(finishUp);
inspector.selection.setNode(rotated);
}
function finishUp() {
inspector.toolbox.highlighterUtils.stopPicker().then(() => {
doc = h1 = inspector = null;
doc = div = rotated = inspector = contentViewer = null;
let target = TargetFactory.forTab(gBrowser.selectedTab);
gDevTools.closeToolbox(target);
gBrowser.removeCurrentTab();

Просмотреть файл

@ -17,19 +17,58 @@ function test() {
waitForFocus(setupInfobarTest, content);
}, true);
let style = "body{width:100%;height: 100%} div {position: absolute;height: 100px;width: 500px}#bottom {bottom: 0px}#vertical {height: 100%}#farbottom{bottom: -200px}";
let html = "<style>" + style + "</style><div id=vertical></div><div id=top class='class1 class2'></div><div id=bottom></div><div id=farbottom></div>"
let style = "body{width:100%;height: 100%} div {position: absolute;" +
"height: 100px;width: 500px}#bottom {bottom: 0px}#vertical {"+
"height: 100%}#farbottom{bottom: -200px}";
let html = "<style>" + style + "</style><div id=vertical></div>" +
"<div id=top class='class1 class2'></div><div id=bottom></div>" +
"<div id=farbottom></div>"
content.location = "data:text/html," + encodeURIComponent(html);
content.location = "data:text/html;charset=utf-8," + encodeURIComponent(html);
function setupInfobarTest() {
nodes = [
{node: doc.querySelector("#top"), position: "bottom", tag: "DIV", id: "#top", classes: ".class1.class2"},
{node: doc.querySelector("#vertical"), position: "overlap", tag: "DIV", id: "#vertical", classes: ""},
{node: doc.querySelector("#bottom"), position: "top", tag: "DIV", id: "#bottom", classes: ""},
{node: doc.querySelector("body"), position: "overlap", tag: "BODY", id: "", classes: ""},
{node: doc.querySelector("#farbottom"), position: "top", tag: "DIV", id: "#farbottom", classes: ""},
]
{
node: doc.querySelector("#top"),
position: "bottom",
tag: "DIV",
id: "#top",
classes: ".class1.class2",
dims: "500 x 100"
},
{
node: doc.querySelector("#vertical"),
position: "overlap",
tag: "DIV",
id: "#vertical",
classes: ""
// No dims as they will vary between computers
},
{
node: doc.querySelector("#bottom"),
position: "top",
tag: "DIV",
id: "#bottom",
classes: "",
dims: "500 x 100"
},
{
node: doc.querySelector("body"),
position: "overlap",
tag: "BODY",
id: "",
classes: ""
// No dims as they will vary between computers
},
{
node: doc.querySelector("#farbottom"),
position: "top",
tag: "DIV",
id: "#farbottom",
classes: "",
dims: "500 x 100"
},
];
for (let i = 0; i < nodes.length; i++) {
ok(nodes[i].node, "node " + i + " found");
@ -74,16 +113,24 @@ function test() {
let stack = browser.parentNode;
let container = stack.querySelector(".highlighter-nodeinfobar-positioner");
is(container.getAttribute("position"), nodes[cursor].position, "node " + cursor + ": position matches.");
is(container.getAttribute("position"),
nodes[cursor].position, "node " + cursor + ": position matches.");
let tagNameLabel = stack.querySelector(".highlighter-nodeinfobar-tagname");
is(tagNameLabel.textContent, nodes[cursor].tag, "node " + cursor + ": tagName matches.");
is(tagNameLabel.textContent, nodes[cursor].tag,
"node " + cursor + ": tagName matches.");
let idLabel = stack.querySelector(".highlighter-nodeinfobar-id");
is(idLabel.textContent, nodes[cursor].id, "node " + cursor + ": id matches.");
let classesBox = stack.querySelector(".highlighter-nodeinfobar-classes");
is(classesBox.textContent, nodes[cursor].classes, "node " + cursor + ": classes match.");
is(classesBox.textContent, nodes[cursor].classes,
"node " + cursor + ": classes match.");
if (nodes[cursor].dims) {
let dimBox = stack.querySelector(".highlighter-nodeinfobar-dimensions");
is(dimBox.textContent, nodes[cursor].dims, "node " + cursor + ": dims match.");
}
}
function finishUp() {

Просмотреть файл

@ -400,7 +400,7 @@ function focusSearchBoxUsingShortcut(panelWin, callback) {
altKey: modifiersAttr.match("alt"),
metaKey: modifiersAttr.match("meta"),
accelKey: modifiersAttr.match("accel")
}
};
let searchBox = panelWin.document.getElementById("inspector-searchbox");
searchBox.addEventListener("focus", function onFocus() {
@ -425,6 +425,60 @@ function getComputedPropertyValue(aName)
}
}
function isNodeCorrectlyHighlighted(node, prefix="") {
let boxModel = getBoxModelStatus();
let helper = new LayoutHelpers(window.content);
prefix += (prefix ? " " : "") + node.nodeName;
prefix += (node.id ? "#" + node.id : "");
prefix += (node.classList.length ? "." + [...node.classList].join(".") : "");
prefix += " ";
let quads = helper.getAdjustedQuads(node, "content");
let {p1:cp1, p2:cp2, p3:cp3, p4:cp4} = boxModel.content.points;
is(cp1.x, quads.p1.x, prefix + "content point 1 x co-ordinate is correct");
is(cp1.y, quads.p1.y, prefix + "content point 1 y co-ordinate is correct");
is(cp2.x, quads.p2.x, prefix + "content point 2 x co-ordinate is correct");
is(cp2.y, quads.p2.y, prefix + "content point 2 y co-ordinate is correct");
is(cp3.x, quads.p3.x, prefix + "content point 3 x co-ordinate is correct");
is(cp3.y, quads.p3.y, prefix + "content point 3 y co-ordinate is correct");
is(cp4.x, quads.p4.x, prefix + "content point 4 x co-ordinate is correct");
is(cp4.y, quads.p4.y, prefix + "content point 4 y co-ordinate is correct");
quads = helper.getAdjustedQuads(node, "padding");
let {p1:pp1, p2:pp2, p3:pp3, p4:pp4} = boxModel.padding.points;
is(pp1.x, quads.p1.x, prefix + "padding point 1 x co-ordinate is correct");
is(pp1.y, quads.p1.y, prefix + "padding point 1 y co-ordinate is correct");
is(pp2.x, quads.p2.x, prefix + "padding point 2 x co-ordinate is correct");
is(pp2.y, quads.p2.y, prefix + "padding point 2 y co-ordinate is correct");
is(pp3.x, quads.p3.x, prefix + "padding point 3 x co-ordinate is correct");
is(pp3.y, quads.p3.y, prefix + "padding point 3 y co-ordinate is correct");
is(pp4.x, quads.p4.x, prefix + "padding point 4 x co-ordinate is correct");
is(pp4.y, quads.p4.y, prefix + "padding point 4 y co-ordinate is correct");
quads = helper.getAdjustedQuads(node, "border");
let {p1:bp1, p2:bp2, p3:bp3, p4:bp4} = boxModel.border.points;
is(bp1.x, quads.p1.x, prefix + "border point 1 x co-ordinate is correct");
is(bp1.y, quads.p1.y, prefix + "border point 1 y co-ordinate is correct");
is(bp2.x, quads.p2.x, prefix + "border point 2 x co-ordinate is correct");
is(bp2.y, quads.p2.y, prefix + "border point 2 y co-ordinate is correct");
is(bp3.x, quads.p3.x, prefix + "border point 3 x co-ordinate is correct");
is(bp3.y, quads.p3.y, prefix + "border point 3 y co-ordinate is correct");
is(bp4.x, quads.p4.x, prefix + "border point 4 x co-ordinate is correct");
is(bp4.y, quads.p4.y, prefix + "border point 4 y co-ordinate is correct");
quads = helper.getAdjustedQuads(node, "margin");
let {p1:mp1, p2:mp2, p3:mp3, p4:mp4} = boxModel.margin.points;
is(mp1.x, quads.p1.x, prefix + "margin point 1 x co-ordinate is correct");
is(mp1.y, quads.p1.y, prefix + "margin point 1 y co-ordinate is correct");
is(mp2.x, quads.p2.x, prefix + "margin point 2 x co-ordinate is correct");
is(mp2.y, quads.p2.y, prefix + "margin point 2 y co-ordinate is correct");
is(mp3.x, quads.p3.x, prefix + "margin point 3 x co-ordinate is correct");
is(mp3.y, quads.p3.y, prefix + "margin point 3 y co-ordinate is correct");
is(mp4.x, quads.p4.x, prefix + "margin point 4 x co-ordinate is correct");
is(mp4.y, quads.p4.y, prefix + "margin point 4 y co-ordinate is correct");
}
function getContainerForRawNode(markupView, rawNode)
{
let front = markupView.walker.frontForRawNode(rawNode);

Просмотреть файл

@ -118,7 +118,8 @@ AppCacheUtils.prototype = {
for (let neturi of parsed.uris) {
if (neturi.section == "NETWORK") {
for (let parsedUri of parsed.uris) {
if (parsedUri.uri.startsWith(neturi.uri)) {
if (parsedUri.section !== "NETWORK" &&
parsedUri.uri.startsWith(neturi.uri)) {
this._addError(neturi.line, "networkBlocksURI", neturi.line,
neturi.original, parsedUri.line, parsedUri.original,
parsedUri.section);
@ -164,7 +165,7 @@ AppCacheUtils.prototype = {
this._addError(parsedUri.line, "cacheControlNoStore",
parsedUri.original, parsedUri.line);
}
} else {
} else if (parsedUri.original !== "*") {
this._addError(parsedUri.line, "notAvailable",
parsedUri.original, parsedUri.line);
}
@ -182,7 +183,6 @@ AppCacheUtils.prototype = {
let inputStream = Cc["@mozilla.org/scriptableinputstream;1"]
.createInstance(Ci.nsIScriptableInputStream);
let deferred = promise.defer();
let channelCharset = "";
let buffer = "";
let channel = Services.io.newChannel(uri, null, null);
@ -203,7 +203,7 @@ AppCacheUtils.prototype = {
},
onStopRequest: function onStartRequest(request, context, statusCode) {
if (statusCode == 0) {
if (statusCode === 0) {
request.QueryInterface(Ci.nsIHttpChannel);
let result = {
@ -279,7 +279,7 @@ AppCacheUtils.prototype = {
}
});
if (entries.length == 0) {
if (entries.length === 0) {
throw new Error(l10n.GetStringFromName("noResults"));
}
return entries;
@ -320,17 +320,23 @@ AppCacheUtils.prototype = {
_getManifestURI: function ACU__getManifestURI() {
let deferred = promise.defer();
let getURI = node => {
let getURI = () => {
let htmlNode = this.doc.querySelector("html[manifest]");
if (htmlNode) {
let pageUri = this.doc.location ? this.doc.location.href : this.uri;
let origin = pageUri.substr(0, pageUri.lastIndexOf("/") + 1);
return origin + htmlNode.getAttribute("manifest");
let manifestURI = htmlNode.getAttribute("manifest");
if (manifestURI.startsWith("/")) {
manifestURI = manifestURI.substr(1);
}
return origin + manifestURI;
}
};
if (this.doc) {
let uri = getURI(this.doc);
let uri = getURI();
return promise.resolve(uri);
} else {
this._getURIInfo(this.uri).then(uriInfo => {
@ -338,7 +344,7 @@ AppCacheUtils.prototype = {
let html = uriInfo.text;
let parser = _DOMParser;
this.doc = parser.parseFromString(html, "text/html");
let uri = getURI(this.doc);
let uri = getURI();
deferred.resolve(uri);
} else {
this.errors.push({
@ -394,10 +400,10 @@ ManifestParser.prototype = {
this.currSection = "CACHE";
for (let i = 0; i < lines.length; i++) {
let text = this.text = lines[i].replace(/^\s+|\s+$/g);
let text = this.text = lines[i].trim();
this.currentLine = i + 1;
if (i == 0 && text != "CACHE MANIFEST") {
if (i === 0 && text !== "CACHE MANIFEST") {
this._addError(1, "firstLineMustBeCacheManifest", 1);
}
@ -453,7 +459,7 @@ ManifestParser.prototype = {
if (/\s/.test(text)) {
this._addError(this.currentLine, "escapeSpaces", this.currentLine);
text = text.replace(/\s/g, "%20")
text = text.replace(/\s/g, "%20");
}
if (text[0] == "/") {
@ -506,7 +512,7 @@ ManifestParser.prototype = {
if (/\s/.test(namespace)) {
this._addError(this.currentLine, "escapeSpaces", this.currentLine);
namespace = namespace.replace(/\s/g, "%20")
namespace = namespace.replace(/\s/g, "%20");
}
if (namespace.substr(0, 4) == "/../") {

Просмотреть файл

@ -64,7 +64,7 @@ function StyleEditorUI(debuggee, target, panelDoc) {
this.selectedEditor = null;
this.savedLocations = {};
this._updateContextMenu = this._updateContextMenu.bind(this);
this._updateOptionsMenu = this._updateOptionsMenu.bind(this);
this._onStyleSheetCreated = this._onStyleSheetCreated.bind(this);
this._onNewDocument = this._onNewDocument.bind(this);
this._onMediaPrefChanged = this._onMediaPrefChanged.bind(this);
@ -142,36 +142,26 @@ StyleEditorUI.prototype = {
this._importFromFile(this._mockImportFile || null, this._window);
}.bind(this));
this._contextMenu = this._panelDoc.getElementById("sidebar-context");
this._contextMenu.addEventListener("popupshowing",
this._updateContextMenu);
this._optionsMenu = this._panelDoc.getElementById("style-editor-options-popup");
this._optionsMenu.addEventListener("popupshowing",
this._updateOptionsMenu);
this._sourcesItem = this._panelDoc.getElementById("context-origsources");
this._sourcesItem = this._panelDoc.getElementById("options-origsources");
this._sourcesItem.addEventListener("command",
this._toggleOrigSources);
this._mediaItem = this._panelDoc.getElementById("context-show-media");
this._mediaItem = this._panelDoc.getElementById("options-show-media");
this._mediaItem.addEventListener("command",
this._toggleMediaSidebar);
},
/**
* Update text of context menu option to reflect current preference
* settings
* Update options menu items to reflect current preference settings.
*/
_updateContextMenu: function() {
let sourceString = "showOriginalSources";
if (Services.prefs.getBoolPref(PREF_ORIG_SOURCES)) {
sourceString = "showCSSSources";
}
this._sourcesItem.setAttribute("label", _(sourceString + ".label"));
this._sourcesItem.setAttribute("accesskey", _(sourceString + ".accesskey"));
let mediaString = "showMediaSidebar"
if (Services.prefs.getBoolPref(PREF_MEDIA_SIDEBAR)) {
mediaString = "hideMediaSidebar";
}
this._mediaItem.setAttribute("label", _(mediaString + ".label"));
this._mediaItem.setAttribute("accesskey", _(mediaString + ".accesskey"));
_updateOptionsMenu: function() {
this._sourcesItem.setAttribute("checked",
Services.prefs.getBoolPref(PREF_ORIG_SOURCES));
this._mediaItem.setAttribute("checked",
Services.prefs.getBoolPref(PREF_MEDIA_SIDEBAR));
},
/**
@ -771,6 +761,9 @@ StyleEditorUI.prototype = {
destroy: function() {
this._clearStyleSheetEditors();
this._optionsMenu.removeEventListener("popupshowing",
this._updateOptionsMenu);
this._prefObserver.off(PREF_ORIG_SOURCES, this._onNewDocument);
this._prefObserver.off(PREF_MEDIA_SIDEBAR, this._onMediaPrefChanged);
this._prefObserver.destroy();

Просмотреть файл

@ -15,6 +15,10 @@ li.error > .stylesheet-info > .stylesheet-more > .stylesheet-error-message {
display: block;
}
.devtools-toolbar > spacer {
-moz-box-flex: 1;
}
.splitview-nav > li,
.stylesheet-info,
.stylesheet-more {

Просмотреть файл

@ -61,9 +61,16 @@
key="key_gotoLine"
command="cmd_gotoLine"/>
</xul:menupopup>
<xul:menupopup id="sidebar-context">
<xul:menuitem id="context-origsources"/>
<xul:menuitem id="context-show-media"/>
<xul:menupopup id="style-editor-options-popup"
position="before_start">
<xul:menuitem id="options-origsources"
type="checkbox"
label="&showOriginalSources.label;"
accesskey="&showOriginalSources.accesskey;"/>
<xul:menuitem id="options-show-media"
type="checkbox"
label="&showMediaSidebar.label;"
accesskey="&showMediaSidebar.accesskey;"/>
</xul:menupopup>
</xul:popupset>
@ -91,6 +98,11 @@
accesskey="&importButton.accesskey;"
tooltiptext="&importButton.tooltip;"
label="&importButton.label;"/>
<xul:spacer/>
<xul:toolbarbutton id="style-editor-options"
class="devtools-option-toolbarbutton"
tooltiptext="&optionsButton.tooltip;"
popup="style-editor-options-popup"/>
</xul:toolbar>
</xul:box>
<xul:box id="splitview-resizer-target" class="theme-sidebar splitview-nav-container"

Просмотреть файл

@ -24,6 +24,24 @@
<!ENTITY saveButton.tooltip "Save this style sheet to a file">
<!ENTITY saveButton.accesskey "S">
<!ENTITY optionsButton.tooltip "Style Editor options">
<!-- LOCALIZATION NOTE (showOriginalSources.label): This is the label on the context
menu item to toggle showing original sources in the editor. -->
<!ENTITY showOriginalSources.label "Show original sources">
<!-- LOCALIZATION NOTE (showOriginalSources.accesskey): This is the access key for
the menu item to toggle showing original sources in the editor. -->
<!ENTITY showOriginalSources.accesskey "o">
<!-- LOCALIZATION NOTE (showMediaSidebar.label): This is the label on the context
menu item to toggle showing @media rule shortcuts in a sidebar. -->
<!ENTITY showMediaSidebar.label "Show @media sidebar">
<!-- LOCALIZATION NOTE (showMediaSidebar.accesskey): This is the access key for
the menu item to toggle showing the @media sidebar. -->
<!ENTITY showMediaSidebar.accesskey "m">
<!-- LOCALICATION NOTE (mediaRules.label): This is shown above the list of @media rules
in each stylesheet editor sidebar. -->
<!ENTITY mediaRules.label "@media rules">

Просмотреть файл

@ -68,38 +68,6 @@ open.accesskey=l
# conjunction with accel (Command on Mac or Ctrl on other platforms) to Save
saveStyleSheet.commandkey=S
# LOCALIZATION NOTE (showOriginalSources.label): This is the label on the context
# menu item to toggle showing original sources in the editor.
showOriginalSources.label=Show original sources
# LOCALIZATION NOTE (showOriginalSources.accesskey): This is the access key for
# the menu item to toggle showing original sources in the editor.
showOriginalSources.accesskey=O
# LOCALIZATION NOTE (showCSSSources.label): This is the label on the context
# menu item to toggle back to showing only CSS sources in the editor.
showCSSSources.label=Show CSS sources
# LOCALIZATION NOTE (showCSSSources.accesskey): This is the access key for the
# menu item to toggle back to showing only CSS sources in the editor.
showCSSSources.accesskey=C
# LOCALIZATION NOTE (showMediaSidebar.label): This is the label on the context
# menu item to toggle showing @media rule shortcuts in a sidebar.
showMediaSidebar.label=Show @media sidebar
# LOCALIZATION NOTE (showMediaSidebar.accesskey): This is the access key for
# the menu item to toggle showing the @media sidebar.
showMediaSidebar.accesskey=M
# LOCALIZATION NOTE (hideMediaSidebar.label): This is the label on the context
# menu item to stop showing @media rule shortcuts in a sidebar.
hideMediaSidebar.label=Hide @media sidebar
# LOCALIZATION NOTE (hideMediaSidebar.accesskey): This is the access key for
# the menu item to stop showing the @media sidebar.
hideMediaSidebar.accesskey=H
# LOCALIZATION NOTE (ToolboxStyleEditor.label):
# This string is displayed in the title of the tab when the style editor is
# displayed inside the developer tools window and in the Developer Tools Menu.

Просмотреть файл

@ -285,6 +285,7 @@ browser.jar:
skin/classic/browser/devtools/responsiveui-screenshot.png (../shared/devtools/images/responsivemode/responsiveui-screenshot.png)
skin/classic/browser/devtools/responsiveui-screenshot@2x.png (../shared/devtools/images/responsivemode/responsiveui-screenshot@2x.png)
skin/classic/browser/devtools/toggle-tools.png (../shared/devtools/images/toggle-tools.png)
skin/classic/browser/devtools/toggle-tools@2x.png (../shared/devtools/images/toggle-tools@2x.png)
skin/classic/browser/devtools/dock-bottom@2x.png (../shared/devtools/images/dock-bottom@2x.png)
skin/classic/browser/devtools/dock-side@2x.png (../shared/devtools/images/dock-side@2x.png)
skin/classic/browser/devtools/floating-scrollbars.css (devtools/floating-scrollbars.css)

Просмотреть файл

@ -405,6 +405,7 @@ browser.jar:
skin/classic/browser/devtools/responsiveui-screenshot.png (../shared/devtools/images/responsivemode/responsiveui-screenshot.png)
skin/classic/browser/devtools/responsiveui-screenshot@2x.png (../shared/devtools/images/responsivemode/responsiveui-screenshot@2x.png)
skin/classic/browser/devtools/toggle-tools.png (../shared/devtools/images/toggle-tools.png)
skin/classic/browser/devtools/toggle-tools@2x.png (../shared/devtools/images/toggle-tools@2x.png)
skin/classic/browser/devtools/dock-bottom@2x.png (../shared/devtools/images/dock-bottom@2x.png)
skin/classic/browser/devtools/dock-side@2x.png (../shared/devtools/images/dock-side@2x.png)
* skin/classic/browser/devtools/inspector.css (../shared/devtools/inspector.css)

Просмотреть файл

@ -28,6 +28,12 @@
margin: auto 10px;
}
.developer-toolbar-button > .toolbarbutton-icon,
#developer-toolbar-closebutton > .toolbarbutton-icon {
width: 16px;
height: 16px;
}
#developer-toolbar-toolbox-button {
list-style-image: url("chrome://browser/skin/devtools/toggle-tools.png");
-moz-image-region: rect(0px, 16px, 16px, 0px);
@ -49,6 +55,25 @@
-moz-image-region: rect(0px, 64px, 16px, 48px);
}
@media (min-resolution: 2dppx) {
#developer-toolbar-toolbox-button {
list-style-image: url("chrome://browser/skin/devtools/toggle-tools@2x.png");
-moz-image-region: rect(0px, 32px, 32px, 0px);
}
#developer-toolbar-toolbox-button:hover {
-moz-image-region: rect(0px, 64px, 32px, 32px);
}
#developer-toolbar-toolbox-button:hover:active {
-moz-image-region: rect(0px, 96px, 32px, 64px);
}
#developer-toolbar-toolbox-button[checked=true] {
-moz-image-region: rect(0px, 128px, 32px, 96px);
}
}
#developer-toolbar-closebutton {
list-style-image: url("chrome://browser/skin/devtools/close.png");
-moz-appearance: none;
@ -59,6 +84,12 @@
opacity: 0.6;
}
@media (min-resolution: 2dppx) {
#developer-toolbar-closebutton {
list-style-image: url("chrome://browser/skin/devtools/close@2x.png");
}
}
#developer-toolbar-closebutton > .toolbarbutton-icon {
/* XXX Buttons have padding in widget/ that we don't want here but can't override with good CSS, so we must
use evil CSS to give the impression of smaller content */
@ -109,7 +140,6 @@ html|*#gcli-output-frame {
.gclitoolbar-input-node {
-moz-appearance: none;
color: hsl(210,30%,85%);
padding-left: 20px;
background-color: #242b33;
background-repeat: no-repeat;
background-position: 4px center;
@ -118,14 +148,35 @@ html|*#gcli-output-frame {
-1px 0 0 hsla(206,37%,4%,.2) inset;
line-height: 32px;
outline-style: none;
background-image: -moz-image-rect(url("chrome://browser/skin/devtools/commandline-icon.png"), 0, 16, 16, 0);
padding: 0;
}
.gclitoolbar-input-node[focused="true"] {
background-image: -moz-image-rect(url("chrome://browser/skin/devtools/commandline-icon.png"), 0, 32, 16, 16);
background-color: #232e38;
}
.gclitoolbar-input-node::before {
content: "";
display: inline-block;
-moz-box-ordinal-group: 0;
width: 16px;
height: 16px;
margin: 0 2px;
background-image: url("chrome://browser/skin/devtools/commandline-icon.png");
background-position: 0 center;
background-size: 32px 16px;
}
.gclitoolbar-input-node[focused="true"]::before {
background-position: -16px center;
}
@media (min-resolution: 2dppx) {
.gclitoolbar-input-node::before {
background-image: url("chrome://browser/skin/devtools/commandline-icon@2x.png");
}
}
.gclitoolbar-input-node > .textbox-input-box > html|*.textbox-input::-moz-selection {
background-color: hsl(210,30%,85%);
color: hsl(210,24%,16%);

Просмотреть файл

@ -74,6 +74,13 @@ html|*.highlighter-nodeinfobar-pseudo-classes {
color: hsl(200,74%,57%);
}
html|*.highlighter-nodeinfobar-dimensions {
color: hsl(210,30%,85%);
-moz-border-start: 1px solid #5a6169;
-moz-margin-start: 6px;
-moz-padding-start: 6px;
}
/* Highlighter - Node Infobar - box & arrow */
.highlighter-nodeinfobar-arrow {

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 1.7 KiB

После

Ширина:  |  Высота:  |  Размер: 883 B

Двоичные данные
browser/themes/shared/devtools/images/toggle-tools@2x.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.8 KiB

Просмотреть файл

@ -119,6 +119,11 @@
background-position: -24px 8px;
}
#style-editor-options {
width: 20px;
overflow: hidden;
}
/* Invert all toggle icons but the one in the active row for light theme */
.theme-light .splitview-nav > li:not(.splitview-active) .stylesheet-enabled {
filter: url(filters.svg#invert);

Просмотреть файл

@ -323,6 +323,7 @@ browser.jar:
skin/classic/browser/devtools/responsiveui-screenshot.png (../shared/devtools/images/responsivemode/responsiveui-screenshot.png)
skin/classic/browser/devtools/responsiveui-screenshot@2x.png (../shared/devtools/images/responsivemode/responsiveui-screenshot@2x.png)
skin/classic/browser/devtools/toggle-tools.png (../shared/devtools/images/toggle-tools.png)
skin/classic/browser/devtools/toggle-tools@2x.png (../shared/devtools/images/toggle-tools@2x.png)
skin/classic/browser/devtools/dock-bottom@2x.png (../shared/devtools/images/dock-bottom@2x.png)
skin/classic/browser/devtools/dock-side@2x.png (../shared/devtools/images/dock-side@2x.png)
skin/classic/browser/devtools/floating-scrollbars.css (devtools/floating-scrollbars.css)
@ -723,6 +724,7 @@ browser.jar:
skin/classic/aero/browser/devtools/responsiveui-screenshot.png (../shared/devtools/images/responsivemode/responsiveui-screenshot.png)
skin/classic/aero/browser/devtools/responsiveui-screenshot@2x.png (../shared/devtools/images/responsivemode/responsiveui-screenshot@2x.png)
skin/classic/aero/browser/devtools/toggle-tools.png (../shared/devtools/images/toggle-tools.png)
skin/classic/aero/browser/devtools/toggle-tools@2x.png (../shared/devtools/images/toggle-tools@2x.png)
skin/classic/aero/browser/devtools/dock-bottom@2x.png (../shared/devtools/images/dock-bottom@2x.png)
skin/classic/aero/browser/devtools/dock-side@2x.png (../shared/devtools/images/dock-side@2x.png)
skin/classic/aero/browser/devtools/floating-scrollbars.css (devtools/floating-scrollbars.css)

Просмотреть файл

@ -3938,6 +3938,7 @@ MOZ_ANDROID_HISTORY=
MOZ_WEBSMS_BACKEND=
MOZ_ANDROID_BEAM=
MOZ_ANDROID_SYNTHAPKS=
MOZ_LOCALE_SWITCHER=
ACCESSIBILITY=1
MOZ_TIME_MANAGER=
MOZ_PAY=
@ -4947,6 +4948,13 @@ if test -n "$MOZ_WEBSMS_BACKEND"; then
AC_DEFINE(MOZ_WEBSMS_BACKEND)
fi
dnl ========================================================
dnl = Enable runtime locale switching on Android
dnl ========================================================
if test -n "$MOZ_LOCALE_SWITCHER"; then
AC_DEFINE(MOZ_LOCALE_SWITCHER)
fi
dnl ========================================================
dnl = Enable NFC permission on Android
dnl ========================================================
@ -8567,6 +8575,7 @@ AC_SUBST(MOZ_ANDROID_HISTORY)
AC_SUBST(MOZ_WEBSMS_BACKEND)
AC_SUBST(MOZ_ANDROID_BEAM)
AC_SUBST(MOZ_ANDROID_SYNTHAPKS)
AC_SUBST(MOZ_LOCALE_SWITCHER)
AC_SUBST(MOZ_DISABLE_GECKOVIEW)
AC_SUBST(ENABLE_STRIP)
AC_SUBST(PKG_SKIP_STRIP)

Просмотреть файл

@ -121,6 +121,13 @@ public class AppConstants {
false;
#endif
public static final boolean MOZ_LOCALE_SWITCHER =
#ifdef MOZ_LOCALE_SWITCHER
true;
#else
false;
#endif
public static final boolean MOZ_UPDATER =
#ifdef MOZ_UPDATER
true;

Просмотреть файл

@ -75,6 +75,10 @@ public class BrowserLocaleManager implements LocaleManager {
}
}
public boolean isEnabled() {
return AppConstants.MOZ_LOCALE_SWITCHER;
}
/**
* Gecko uses locale codes like "es-ES", whereas a Java {@link Locale}
* stringifies as "es_ES".

Просмотреть файл

@ -18,6 +18,11 @@ import android.content.res.Resources;
*/
public interface LocaleManager {
void initialize(Context context);
/**
* @return true if locale switching is enabled.
*/
boolean isEnabled();
Locale getCurrentLocale(Context context);
String getAndApplyPersistedLocale(Context context);
void correctLocale(Context context, Resources resources, Configuration newConfig);

Просмотреть файл

@ -188,13 +188,18 @@ public class Tabs implements GeckoEventListener {
}
}
private Tab addTab(int id, String url, boolean external, int parentId, String title, boolean isPrivate) {
private Tab addTab(int id, String url, boolean external, int parentId, String title, boolean isPrivate, int tabIndex) {
final Tab tab = isPrivate ? new PrivateTab(mAppContext, id, url, external, parentId, title) :
new Tab(mAppContext, id, url, external, parentId, title);
synchronized (this) {
lazyRegisterBookmarkObserver();
mTabs.put(id, tab);
mOrder.add(tab);
if (tabIndex > -1) {
mOrder.add(tabIndex, tab);
} else {
mOrder.add(tab);
}
}
// Suppress the ADDED event to prevent animation of tabs created via session restore.
@ -427,7 +432,8 @@ public class Tabs implements GeckoEventListener {
tab = addTab(id, url, message.getBoolean("external"),
message.getInt("parentId"),
message.getString("title"),
message.getBoolean("isPrivate"));
message.getBoolean("isPrivate"),
message.getInt("tabIndex"));
// If we added the tab as a stub, we should have already
// selected it, so ignore this flag for stubbed tabs.
@ -799,7 +805,10 @@ public class Tabs implements GeckoEventListener {
// long as it's a valid URI.
String tabUrl = (url != null && Uri.parse(url).getScheme() != null) ? url : null;
added = addTab(tabId, tabUrl, external, parentId, url, isPrivate);
// Add the new tab to the end of the tab order.
final int tabIndex = -1;
added = addTab(tabId, tabUrl, external, parentId, url, isPrivate, tabIndex);
added.setDesktopMode(desktopMode);
}
} catch (Exception e) {

Просмотреть файл

@ -6,13 +6,11 @@ package org.mozilla.gecko.fxa.activities;
import java.util.Locale;
import org.mozilla.gecko.AppConstants;
import org.mozilla.gecko.R;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.background.fxa.FxAccountAgeLockoutHelper;
import org.mozilla.gecko.fxa.FirefoxAccounts;
import org.mozilla.gecko.fxa.FxAccountConstants;
import org.mozilla.gecko.sync.Utils;
import org.mozilla.gecko.sync.setup.activities.ActivityUtils;
import org.mozilla.gecko.sync.setup.activities.LocaleAware;

Просмотреть файл

@ -18,6 +18,7 @@ import org.mozilla.gecko.fxa.login.Married;
import org.mozilla.gecko.fxa.login.State;
import org.mozilla.gecko.fxa.sync.FxAccountSyncStatusHelper;
import org.mozilla.gecko.fxa.tasks.FxAccountCodeResender;
import org.mozilla.gecko.sync.SharedPreferencesClientsDataDelegate;
import org.mozilla.gecko.sync.SyncConfiguration;
import android.accounts.Account;
@ -28,10 +29,13 @@ import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.preference.CheckBoxPreference;
import android.preference.EditTextPreference;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceChangeListener;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceCategory;
import android.preference.PreferenceScreen;
import android.text.TextUtils;
/**
* A fragment that displays the status of an AndroidFxAccount.
@ -39,7 +43,9 @@ import android.preference.PreferenceScreen;
* The owning activity is responsible for providing an AndroidFxAccount at
* appropriate times.
*/
public class FxAccountStatusFragment extends PreferenceFragment implements OnPreferenceClickListener {
public class FxAccountStatusFragment
extends PreferenceFragment
implements OnPreferenceClickListener, OnPreferenceChangeListener {
private static final String LOG_TAG = FxAccountStatusFragment.class.getSimpleName();
// When a checkbox is toggled, wait 5 seconds (for other checkbox actions)
@ -65,7 +71,12 @@ public class FxAccountStatusFragment extends PreferenceFragment implements OnPre
protected CheckBoxPreference tabsPreference;
protected CheckBoxPreference passwordsPreference;
protected EditTextPreference deviceNamePreference;
protected volatile AndroidFxAccount fxAccount;
// The contract is: when fxAccount is non-null, then clientsDataDelegate is
// non-null. If violated then an IllegalStateException is thrown.
protected volatile SharedPreferencesClientsDataDelegate clientsDataDelegate;
// Used to post delayed sync requests.
protected Handler handler;
@ -88,6 +99,10 @@ public class FxAccountStatusFragment extends PreferenceFragment implements OnPre
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferences();
}
protected void addPreferences() {
addPreferencesFromResource(R.xml.fxaccount_status_prefscreen);
emailPreference = ensureFindPreference("email");
@ -119,6 +134,9 @@ public class FxAccountStatusFragment extends PreferenceFragment implements OnPre
historyPreference.setOnPreferenceClickListener(this);
tabsPreference.setOnPreferenceClickListener(this);
passwordsPreference.setOnPreferenceClickListener(this);
deviceNamePreference = (EditTextPreference) ensureFindPreference("device_name");
deviceNamePreference.setOnPreferenceChangeListener(this);
}
/**
@ -177,6 +195,8 @@ public class FxAccountStatusFragment extends PreferenceFragment implements OnPre
historyPreference.setEnabled(enabled);
tabsPreference.setEnabled(enabled);
passwordsPreference.setEnabled(enabled);
// Since we can't sync, we can't update our remote client record.
deviceNamePreference.setEnabled(enabled);
}
/**
@ -294,6 +314,14 @@ public class FxAccountStatusFragment extends PreferenceFragment implements OnPre
throw new IllegalArgumentException("fxAccount must not be null");
}
this.fxAccount = fxAccount;
try {
this.clientsDataDelegate = new SharedPreferencesClientsDataDelegate(fxAccount.getSyncPrefs());
} catch (Exception e) {
Logger.error(LOG_TAG, "Got exception fetching Sync prefs associated to Firefox Account; aborting.", e);
// Something is terribly wrong; best to get a stack trace rather than
// continue with a null clients delegate.
throw new IllegalStateException(e);
}
handler = new Handler(); // Attached to current (assumed to be UI) thread.
@ -319,6 +347,17 @@ public class FxAccountStatusFragment extends PreferenceFragment implements OnPre
FxAccountSyncStatusHelper.getInstance().stopObserving(syncStatusDelegate);
}
protected void hardRefresh() {
// This is the only way to guarantee that the EditText dialogs created by
// EditTextPreferences are re-created. This works around the issue described
// at http://androiddev.orkitra.com/?p=112079.
final PreferenceScreen statusScreen = (PreferenceScreen) ensureFindPreference("status_screen");
statusScreen.removeAll();
addPreferences();
refresh();
}
protected void refresh() {
// refresh is called from our onResume, which can happen before the owning
// Activity tells us about an account (via our public
@ -372,6 +411,10 @@ public class FxAccountStatusFragment extends PreferenceFragment implements OnPre
// No matter our state, we should update the checkboxes.
updateSelectedEngines();
}
final String clientName = clientsDataDelegate.getClientName();
deviceNamePreference.setSummary(clientName);
deviceNamePreference.setText(clientName);
}
/**
@ -571,4 +614,22 @@ public class FxAccountStatusFragment extends PreferenceFragment implements OnPre
button.setOnPreferenceClickListener(listener);
}
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (preference == deviceNamePreference) {
String newClientName = (String) newValue;
if (TextUtils.isEmpty(newClientName)) {
newClientName = clientsDataDelegate.getDefaultClientName();
}
final long now = System.currentTimeMillis();
clientsDataDelegate.setClientName(newClientName, now);
requestDelayedSync(); // Try to update our remote client record.
hardRefresh(); // Updates the value displayed to the user, among other things.
return true;
}
// For everything else, accept the change.
return true;
}
}

Просмотреть файл

@ -357,7 +357,11 @@ public class FxAccountSyncAdapter extends AbstractThreadedSyncAdapter {
FxAccountGlobalSession globalSession = null;
try {
ClientsDataDelegate clientsDataDelegate = new SharedPreferencesClientsDataDelegate(sharedPrefs);
final ClientsDataDelegate clientsDataDelegate = new SharedPreferencesClientsDataDelegate(sharedPrefs);
if (FxAccountConstants.LOG_PERSONAL_INFORMATION) {
FxAccountConstants.pii(LOG_TAG, "Client device name is: '" + clientsDataDelegate.getClientName() + "'.");
FxAccountConstants.pii(LOG_TAG, "Client device data last modified: " + clientsDataDelegate.getLastModifiedTimestamp());
}
// We compute skew over time using SkewHandler. This yields an unchanging
// skew adjustment that the HawkAuthHeaderProvider uses to adjust its

Просмотреть файл

@ -175,6 +175,7 @@
<!ENTITY fxaccount_status_header2 'Firefox Account'>
<!ENTITY fxaccount_status_signed_in_as 'Signed in as'>
<!ENTITY fxaccount_status_device_name 'Device name'>
<!ENTITY fxaccount_status_sync '&syncBrand.shortName.label;'>
<!ENTITY fxaccount_status_sync_enabled '&syncBrand.shortName.label;: enabled'>
<!ENTITY fxaccount_status_needs_verification2 'Your account needs to be verified. Tap to resend verification email.'>

Просмотреть файл

@ -131,6 +131,7 @@ OnSharedPreferenceChangeListener
* Track the last locale so we know whether to redisplay.
*/
private Locale lastLocale = Locale.getDefault();
private boolean localeSwitchingIsEnabled;
private void updateActionBarTitle(int title) {
if (Build.VERSION.SDK_INT >= 14) {
@ -268,6 +269,10 @@ OnSharedPreferenceChangeListener
// Apply the current user-selected locale, if necessary.
checkLocale();
// Track this so we can decide whether to show locale options.
// See also the workaround below for Bug 1015209.
localeSwitchingIsEnabled = BrowserLocaleManager.getInstance().isEnabled();
// For Android v11+ where we use Fragments (v11+ only due to bug 866352),
// check that PreferenceActivity.EXTRA_SHOW_FRAGMENT has been set
// (or set it) before super.onCreate() is called so Android can display
@ -283,10 +288,17 @@ OnSharedPreferenceChangeListener
updateTitle(getString(R.string.pref_header_customize));
}
// So that Android doesn't put the fragment title (or nothing at
// all) in the action bar.
if (onIsMultiPane()) {
// So that Android doesn't put the fragment title (or nothing at
// all) in the action bar.
updateActionBarTitle(R.string.settings_title);
if (Build.VERSION.SDK_INT < 13) {
// Affected by Bug 1015209 -- no detach/attach.
// If we try rejigging fragments, we'll crash, so don't
// enable locale switching at all.
localeSwitchingIsEnabled = false;
}
}
}
@ -397,6 +409,18 @@ OnSharedPreferenceChangeListener
public void onBuildHeaders(List<Header> target) {
if (onIsMultiPane()) {
loadHeadersFromResource(R.xml.preference_headers, target);
// If locale switching is disabled, remove the section
// entirely. This logic will need to be extended when
// content language selection (Bug 881510) is implemented.
if (!localeSwitchingIsEnabled) {
for (Header header : target) {
if (header.id == R.id.pref_header_language) {
target.remove(header);
break;
}
}
}
}
}
@ -564,9 +588,20 @@ OnSharedPreferenceChangeListener
private void setupPreferences(PreferenceGroup preferences, ArrayList<String> prefs) {
for (int i = 0; i < preferences.getPreferenceCount(); i++) {
Preference pref = preferences.getPreference(i);
// Eliminate locale switching if necessary.
// This logic will need to be extended when
// content language selection (Bug 881510) is implemented.
if (!localeSwitchingIsEnabled &&
"preferences_locale".equals(pref.getExtras().getString("resource", null))) {
preferences.removePreference(pref);
i--;
continue;
}
String key = pref.getKey();
if (pref instanceof PreferenceGroup) {
// If no datareporting is enabled, remove UI.
// If datareporting is disabled, remove UI.
if (PREFS_DATA_REPORTING_PREFERENCES.equals(key)) {
if (!AppConstants.MOZ_DATA_REPORTING) {
preferences.removePreference(pref);

Просмотреть файл

@ -28,7 +28,8 @@
</header>
<header android:fragment="org.mozilla.gecko.preferences.GeckoPreferenceFragment"
android:title="@string/pref_header_language">
android:title="@string/pref_header_language"
android:id="@+id/pref_header_language">
<extra android:name="resource"
android:value="preferences_locale" />
</header>

Просмотреть файл

@ -66,6 +66,12 @@
android:key="passwords"
android:persistent="false"
android:title="@string/fxaccount_status_passwords" />
<EditTextPreference
android:singleLine="true"
android:key="device_name"
android:persistent="false"
android:title="@string/fxaccount_status_device_name" />
</PreferenceCategory>
<PreferenceCategory
android:key="legal_category"

Просмотреть файл

@ -30,12 +30,33 @@ public class SharedPreferencesClientsDataDelegate implements ClientsDataDelegate
return accountGUID;
}
/**
* Set client name.
*
* @param clientName to change to.
*/
@Override
public synchronized void setClientName(String clientName, long now) {
sharedPreferences
.edit()
.putString(SyncConfiguration.PREF_CLIENT_NAME, clientName)
.putLong(SyncConfiguration.PREF_CLIENT_DATA_TIMESTAMP, now)
.commit();
}
@Override
public String getDefaultClientName() {
// Bug 1019719: localize this string!
return GlobalConstants.MOZ_APP_DISPLAYNAME + " on " + android.os.Build.MODEL;
}
@Override
public synchronized String getClientName() {
String clientName = sharedPreferences.getString(SyncConfiguration.PREF_CLIENT_NAME, null);
if (clientName == null) {
clientName = GlobalConstants.MOZ_APP_DISPLAYNAME + " on " + android.os.Build.MODEL;
sharedPreferences.edit().putString(SyncConfiguration.PREF_CLIENT_NAME, clientName).commit();
clientName = getDefaultClientName();
long now = System.currentTimeMillis();
setClientName(clientName, now);
}
return clientName;
}
@ -54,4 +75,9 @@ public class SharedPreferencesClientsDataDelegate implements ClientsDataDelegate
public synchronized int getClientsCount() {
return (int) sharedPreferences.getLong(SyncConfiguration.PREF_NUM_CLIENTS, 0);
}
@Override
public long getLastModifiedTimestamp() {
return sharedPreferences.getLong(SyncConfiguration.PREF_CLIENT_DATA_TIMESTAMP, 0);
}
}

Просмотреть файл

@ -258,6 +258,7 @@ public class SyncConfiguration {
public static final String PREF_ACCOUNT_GUID = "account.guid";
public static final String PREF_CLIENT_NAME = "account.clientName";
public static final String PREF_NUM_CLIENTS = "account.numClients";
public static final String PREF_CLIENT_DATA_TIMESTAMP = "account.clientDataTimestamp";
private static final String API_VERSION = "1.5";

Просмотреть файл

@ -6,8 +6,22 @@ package org.mozilla.gecko.sync.delegates;
public interface ClientsDataDelegate {
public String getAccountGUID();
public String getDefaultClientName();
public void setClientName(String clientName, long now);
public String getClientName();
public void setClientsCount(int clientsCount);
public int getClientsCount();
public boolean isLocalGUID(String guid);
/**
* The last time the client's data was modified in a way that should be
* reflected remotely.
* <p>
* Changing the client's name should be reflected remotely, while changing the
* clients count should not (since that data is only used to inform local
* policy.)
*
* @return timestamp in milliseconds.
*/
public long getLastModifiedTimestamp();
}

Просмотреть файл

@ -9,6 +9,7 @@ import java.util.ArrayList;
import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.background.db.Tab;
import org.mozilla.gecko.db.BrowserContract;
import org.mozilla.gecko.sync.delegates.ClientsDataDelegate;
import org.mozilla.gecko.sync.repositories.InactiveSessionException;
import org.mozilla.gecko.sync.repositories.NoContentProviderException;
import org.mozilla.gecko.sync.repositories.NoStoreDelegateException;
@ -30,12 +31,10 @@ import android.net.Uri;
import android.os.RemoteException;
public class FennecTabsRepository extends Repository {
protected final String localClientName;
protected final String localClientGuid;
protected final ClientsDataDelegate clientsDataDelegate;
public FennecTabsRepository(final String localClientName, final String localClientGuid) {
this.localClientName = localClientName;
this.localClientGuid = localClientGuid;
public FennecTabsRepository(ClientsDataDelegate clientsDataDelegate) {
this.clientsDataDelegate = clientsDataDelegate;
}
/**
@ -144,14 +143,17 @@ public class FennecTabsRepository extends Repository {
public void run() {
// We fetch all local tabs (since the record must contain them all)
// but only process the record if the timestamp is sufficiently
// recent.
// recent, or if the client data has been modified.
try {
final Cursor cursor = tabsHelper.safeQuery(tabsProvider, ".fetchSince()", null,
localClientSelection, localClientSelectionArgs, positionAscending);
try {
final String localClientGuid = clientsDataDelegate.getAccountGUID();
final String localClientName = clientsDataDelegate.getClientName();
final TabsRecord tabsRecord = FennecTabsRepository.tabsRecordFromCursor(cursor, localClientGuid, localClientName);
if (tabsRecord.lastModified >= timestamp) {
if (tabsRecord.lastModified >= timestamp ||
clientsDataDelegate.getLastModifiedTimestamp() >= timestamp) {
delegate.onFetchedRecord(tabsRecord);
}
} finally {

Просмотреть файл

@ -25,12 +25,9 @@ import org.mozilla.gecko.sync.SyncConfiguration;
import org.mozilla.gecko.sync.SyncConstants;
import org.mozilla.gecko.sync.repositories.NullCursorException;
import org.mozilla.gecko.sync.repositories.android.ClientsDatabaseAccessor;
import org.mozilla.gecko.sync.repositories.android.FennecTabsRepository;
import org.mozilla.gecko.sync.repositories.android.FennecTabsRepository.FennecTabsRepositorySession;
import org.mozilla.gecko.sync.repositories.domain.ClientRecord;
import org.mozilla.gecko.sync.setup.SyncAccounts;
import org.mozilla.gecko.sync.setup.activities.LocaleAware.LocaleAwareActivity;
import org.mozilla.gecko.sync.stage.SyncClientsEngineStage;
import org.mozilla.gecko.sync.syncadapter.SyncAdapter;
import android.accounts.Account;

Просмотреть файл

@ -4,7 +4,6 @@
package org.mozilla.gecko.sync.stage;
import org.mozilla.gecko.sync.delegates.ClientsDataDelegate;
import org.mozilla.gecko.sync.repositories.RecordFactory;
import org.mozilla.gecko.sync.repositories.Repository;
import org.mozilla.gecko.sync.repositories.android.FennecTabsRepository;
@ -31,8 +30,7 @@ public class FennecTabsServerSyncStage extends ServerSyncStage {
@Override
protected Repository getLocalRepository() {
final ClientsDataDelegate clientsDelegate = session.getClientsDelegate();
return new FennecTabsRepository(clientsDelegate.getClientName(), clientsDelegate.getAccountGUID());
return new FennecTabsRepository(session.getClientsDelegate());
}
@Override

Просмотреть файл

@ -397,6 +397,11 @@ public class SyncClientsEngineStage extends AbstractSessionManagingSyncStage {
return true;
}
if (session.getClientsDelegate().getLastModifiedTimestamp() > lastUpload) {
// Something's changed locally since we last uploaded.
return true;
}
// Note the opportunity for clock drift problems here.
// TODO: if we track download times, we can use the timestamp of most
// recent download response instead of the current time.

Просмотреть файл

@ -921,7 +921,12 @@ var BrowserApp = {
aParams = aParams || {};
let newTab = new Tab(aURI, aParams);
this._tabs.push(newTab);
if (typeof aParams.tabIndex == "number") {
this._tabs.splice(aParams.tabIndex, 0, newTab);
} else {
this._tabs.push(newTab);
}
let selected = "selected" in aParams ? aParams.selected : true;
if (selected)
@ -972,17 +977,23 @@ var BrowserApp = {
if (aTab == this.selectedTab)
this.selectedTab = null;
let tabIndex = this._tabs.indexOf(aTab);
let evt = document.createEvent("UIEvents");
evt.initUIEvent("TabClose", true, false, window, null);
evt.initUIEvent("TabClose", true, false, window, tabIndex);
aTab.browser.dispatchEvent(evt);
// Get a title for the undo close toast. Fall back to the URL if there is no title.
let title = aTab.browser.contentDocument.title || aTab.browser.contentDocument.URL;
aTab.destroy();
this._tabs.splice(this._tabs.indexOf(aTab), 1);
this._tabs.splice(tabIndex, 1);
if (aShowUndoToast) {
// Get a title for the undo close toast. Fall back to the URL if there is no title.
let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
let closedTabData = ss.getClosedTabs(window)[0];
let historyEntry = closedTabData.entries[closedTabData.index - 1];
let title = historyEntry.title || historyEntry.url;
let message = Strings.browser.formatStringFromName("undoCloseToast.message", [title], 1);
NativeWindow.toast.show(message, "short", {
button: {
@ -990,7 +1001,6 @@ var BrowserApp = {
label: Strings.browser.GetStringFromName("undoCloseToast.action2"),
callback: function() {
UITelemetry.addEvent("undo.1", "toast", null, "closetab");
let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
ss.undoCloseTab(window, 0);
}
}
@ -3050,6 +3060,7 @@ Tab.prototype = {
tabID: this.id,
uri: uri,
parentId: ("parentId" in aParams) ? aParams.parentId : -1,
tabIndex: ("tabIndex" in aParams) ? aParams.tabIndex : -1,
external: ("external" in aParams) ? aParams.external : false,
selected: ("selected" in aParams) ? aParams.selected : true,
title: title,

Просмотреть файл

@ -14,7 +14,7 @@ interface nsIDOMNode;
* tabs contained in them.
*/
[scriptable, uuid(fe116b56-0226-4562-b52a-a623dad07ead)]
[scriptable, uuid(91eca9cf-6741-4c8f-a3a0-2e957240894d)]
interface nsISessionStore : nsISupports
{
/**
@ -32,9 +32,9 @@ interface nsISessionStore : nsISupports
* Get closed tab data
*
* @param aWindow is the browser window for which to get closed tab data
* @returns a JSON string representing the list of closed tabs.
* @returns a JS array of closed tabs.
*/
AString getClosedTabData(in nsIDOMWindow aWindow);
jsval getClosedTabs(in nsIDOMWindow aWindow);
/**
* @param aWindow is the browser window to reopen a closed tab in.

Просмотреть файл

@ -46,6 +46,10 @@ SessionStore.prototype = {
_maxTabsUndo: 1,
_pendingWrite: 0,
// The index where the most recently closed tab was in the tabs array
// when it was closed.
_lastClosedTabIndex: -1,
init: function ss_init() {
// Get file references
this._sessionFile = Services.dirsvc.get("ProfD", Ci.nsILocalFile);
@ -98,6 +102,8 @@ SessionStore.prototype = {
for (let [ssid, win] in Iterator(this._windows))
win.closedTabs = [];
this._lastClosedTabIndex = -1;
if (this._loadState == STATE_RUNNING) {
// Save the purged state immediately
this.saveState();
@ -164,7 +170,7 @@ SessionStore.prototype = {
}
case "TabClose": {
let browser = aEvent.target;
this.onTabClose(window, browser);
this.onTabClose(window, browser, aEvent.detail);
this.onTabRemove(window, browser);
break;
}
@ -269,7 +275,7 @@ SessionStore.prototype = {
this.saveStateDelayed();
},
onTabClose: function ss_onTabClose(aWindow, aBrowser) {
onTabClose: function ss_onTabClose(aWindow, aBrowser, aTabIndex) {
if (this._maxTabsUndo == 0)
return;
@ -283,6 +289,8 @@ SessionStore.prototype = {
let length = this._windows[aWindow.__SSID].closedTabs.length;
if (length > this._maxTabsUndo)
this._windows[aWindow.__SSID].closedTabs.splice(this._maxTabsUndo, length - this._maxTabsUndo);
this._lastClosedTabIndex = aTabIndex;
}
},
@ -818,11 +826,11 @@ SessionStore.prototype = {
return this._windows[aWindow.__SSID].closedTabs.length;
},
getClosedTabData: function ss_getClosedTabData(aWindow) {
getClosedTabs: function ss_getClosedTabs(aWindow) {
if (!aWindow.__SSID)
throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
return JSON.stringify(this._windows[aWindow.__SSID].closedTabs);
return this._windows[aWindow.__SSID].closedTabs;
},
undoCloseTab: function ss_undoCloseTab(aWindow, aIndex) {
@ -845,11 +853,14 @@ SessionStore.prototype = {
let params = {
selected: true,
isPrivate: closedTab.isPrivate,
desktopMode: closedTab.desktopMode
desktopMode: closedTab.desktopMode,
tabIndex: this._lastClosedTabIndex
};
let tab = aWindow.BrowserApp.addTab(closedTab.entries[closedTab.index - 1].url, params);
this._restoreHistory(closedTab, tab.browser.sessionHistory);
this._lastClosedTabIndex = -1;
// Put back the extra data
tab.browser.__SS_extdata = closedTab.extData;
@ -869,6 +880,11 @@ SessionStore.prototype = {
// remove closed tab from the array
closedTabs.splice(aIndex, 1);
// Forget the last closed tab index if we're forgetting the last closed tab.
if (aIndex == 0) {
this._lastClosedTabIndex = -1;
}
},
getTabValue: function ss_getTabValue(aTab, aKey) {

Просмотреть файл

@ -64,6 +64,9 @@ MOZ_SERVICES_FXACCOUNTS=1
# Enable Wifi-AP/cell tower data reporting
MOZ_DATA_REPORTING=1
# Enable runtime locale switching.
MOZ_LOCALE_SWITCHER=1
# Enable the "synthetic APKs" implementation of Open Web Apps.
MOZ_ANDROID_SYNTHAPKS=1

Просмотреть файл

@ -169,6 +169,7 @@
<string name="fxaccount_status_activity_label">&syncBrand.shortName.label;</string>
<string name="fxaccount_status_header">&fxaccount_status_header2;</string>
<string name="fxaccount_status_signed_in_as">&fxaccount_status_signed_in_as;</string>
<string name="fxaccount_status_device_name">&fxaccount_status_device_name;</string>
<string name="fxaccount_status_sync">&fxaccount_status_sync;</string>
<string name="fxaccount_status_sync_enabled">&fxaccount_status_sync_enabled;</string>
<string name="fxaccount_status_needs_verification">&fxaccount_status_needs_verification2;</string>

Просмотреть файл

@ -7,6 +7,7 @@ import org.json.simple.JSONArray;
import org.mozilla.gecko.background.helpers.AndroidSyncTestCase;
import org.mozilla.gecko.background.sync.helpers.ExpectFetchDelegate;
import org.mozilla.gecko.background.sync.helpers.SessionTestHelper;
import org.mozilla.gecko.background.testhelpers.MockClientsDataDelegate;
import org.mozilla.gecko.db.BrowserContract;
import org.mozilla.gecko.sync.repositories.NoContentProviderException;
import org.mozilla.gecko.sync.repositories.RepositorySession;
@ -24,8 +25,9 @@ import android.database.Cursor;
import android.os.RemoteException;
public class TestFennecTabsRepositorySession extends AndroidSyncTestCase {
public static final String TEST_CLIENT_GUID = "test guid"; // Real GUIDs never contain spaces.
public static final String TEST_CLIENT_NAME = "test client name";
public static final MockClientsDataDelegate clientsDataDelegate = new MockClientsDataDelegate();
public static final String TEST_CLIENT_GUID = clientsDataDelegate.getAccountGUID();
public static final String TEST_CLIENT_NAME = clientsDataDelegate.getClientName();
// Override these to test against data that is not live.
public static final String TEST_TABS_CLIENT_GUID_IS_LOCAL_SELECTION = BrowserContract.Tabs.CLIENT_GUID + " IS ?";
@ -72,7 +74,7 @@ public class TestFennecTabsRepositorySession extends AndroidSyncTestCase {
* Override this chain in order to avoid our test code having to create two
* sessions all the time.
*/
return new FennecTabsRepository(TEST_CLIENT_NAME, TEST_CLIENT_GUID) {
return new FennecTabsRepository(clientsDataDelegate) {
@Override
public void createSession(RepositorySessionCreationDelegate delegate,
Context context) {
@ -197,8 +199,19 @@ public class TestFennecTabsRepositorySession extends AndroidSyncTestCase {
// Not all tabs are modified after this, but the record should contain them all.
performWait(fetchSinceRunnable(session, 1000, new Record[] { tabsRecord }));
// No tabs are modified after this, so we shouldn't get a record at all.
performWait(fetchSinceRunnable(session, 4000, new Record[] { }));
// No tabs are modified after this, but our client name has changed in the interim.
performWait(fetchSinceRunnable(session, 4000, new Record[] { tabsRecord }));
// No tabs are modified after this, and our client name hasn't changed, so
// we shouldn't get a record at all. Note: this runs after our static
// initializer that sets the client data timestamp.
final long now = System.currentTimeMillis();
performWait(fetchSinceRunnable(session, now, new Record[] { }));
// No tabs are modified after this, but our client name has changed, so
// again we get a record.
clientsDataDelegate.setClientName("new client name", System.currentTimeMillis());
performWait(fetchSinceRunnable(session, now, new Record[] { tabsRecord }));
session.abort();
}

Просмотреть файл

@ -10,6 +10,7 @@ public class MockClientsDataDelegate implements ClientsDataDelegate {
private String accountGUID;
private String clientName;
private int clientsCount;
private long clientDataTimestamp = 0;
@Override
public synchronized String getAccountGUID() {
@ -19,10 +20,21 @@ public class MockClientsDataDelegate implements ClientsDataDelegate {
return accountGUID;
}
@Override
public synchronized String getDefaultClientName() {
return "Default client";
}
@Override
public synchronized void setClientName(String clientName, long now) {
this.clientName = clientName;
this.clientDataTimestamp = now;
}
@Override
public synchronized String getClientName() {
if (clientName == null) {
clientName = "Default Name";
setClientName(getDefaultClientName(), System.currentTimeMillis());
}
return clientName;
}
@ -38,7 +50,12 @@ public class MockClientsDataDelegate implements ClientsDataDelegate {
}
@Override
public boolean isLocalGUID(String guid) {
public synchronized boolean isLocalGUID(String guid) {
return getAccountGUID().equals(guid);
}
}
@Override
public synchronized long getLastModifiedTimestamp() {
return clientDataTimestamp;
}
}

Просмотреть файл

@ -4143,6 +4143,60 @@
"n_values": 10,
"description": "Track click count on about:newtab tiles per index (0-8). For non-default row or column configurations all clicks into the '9' bucket."
},
"NEWTAB_PAGE_DIRECTORY_LINK0_SHOWN": {
"expires_in_version": "35",
"kind": "enumerated",
"n_values": 10,
"description": "Track impression count of directory link #0 per index (0-8). Grid configurations showing 9+ tiles fall into the '9' bucket."
},
"NEWTAB_PAGE_DIRECTORY_LINK1_SHOWN": {
"expires_in_version": "35",
"kind": "enumerated",
"n_values": 10,
"description": "Track impression count of directory link #1 per index (0-8). Grid configurations showing 9+ tiles fall into the '9' bucket."
},
"NEWTAB_PAGE_DIRECTORY_LINK2_SHOWN": {
"expires_in_version": "35",
"kind": "enumerated",
"n_values": 10,
"description": "Track impression count of directory link #2 per index (0-8). Grid configurations showing 9+ tiles fall into the '9' bucket."
},
"NEWTAB_PAGE_DIRECTORY_LINK3_SHOWN": {
"expires_in_version": "35",
"kind": "enumerated",
"n_values": 10,
"description": "Track impression count of directory link #3 per index (0-8). Grid configurations showing 9+ tiles fall into the '9' bucket."
},
"NEWTAB_PAGE_DIRECTORY_LINK4_SHOWN": {
"expires_in_version": "35",
"kind": "enumerated",
"n_values": 10,
"description": "Track impression count of directory link #4 per index (0-8). Grid configurations showing 9+ tiles fall into the '9' bucket."
},
"NEWTAB_PAGE_DIRECTORY_LINK5_SHOWN": {
"expires_in_version": "35",
"kind": "enumerated",
"n_values": 10,
"description": "Track impression count of directory link #5 per index (0-8). Grid configurations showing 9+ tiles fall into the '9' bucket."
},
"NEWTAB_PAGE_DIRECTORY_LINK6_SHOWN": {
"expires_in_version": "35",
"kind": "enumerated",
"n_values": 10,
"description": "Track impression count of directory link #6 per index (0-8). Grid configurations showing 9+ tiles fall into the '9' bucket."
},
"NEWTAB_PAGE_DIRECTORY_LINK7_SHOWN": {
"expires_in_version": "35",
"kind": "enumerated",
"n_values": 10,
"description": "Track impression count of directory link #7 per index (0-8). Grid configurations showing 9+ tiles fall into the '9' bucket."
},
"NEWTAB_PAGE_DIRECTORY_LINK8_SHOWN": {
"expires_in_version": "35",
"kind": "enumerated",
"n_values": 10,
"description": "Track impression count of directory link #8 per index (0-8). Grid configurations showing 9+ tiles fall into the '9' bucket."
},
"NEWTAB_PAGE_DIRECTORY_AFFILIATE_SHOWN": {
"expires_in_version": "35",
"kind": "enumerated",

Просмотреть файл

@ -412,98 +412,4 @@ LayoutHelpers.prototype = {
return [xOffset * scale, yOffset * scale];
},
/********************************************************************
* GetBoxQuads POLYFILL START TODO: Remove this when bug 917755 is fixed.
********************************************************************/
_getBoxQuadsFromRect: function(rect, node) {
let scale = this.calculateScale(node);
let [xOffset, yOffset] = this._getNodeOffsets(node);
let out = {
p1: {
x: rect.left * scale + xOffset,
y: rect.top * scale + yOffset
},
p2: {
x: (rect.left + rect.width) * scale + xOffset,
y: rect.top * scale + yOffset
},
p3: {
x: (rect.left + rect.width) * scale + xOffset,
y: (rect.top + rect.height) * scale + yOffset
},
p4: {
x: rect.left * scale + xOffset,
y: (rect.top + rect.height) * scale + yOffset
}
};
out.bounds = {
bottom: out.p4.y,
height: out.p4.y - out.p1.y,
left: out.p1.x,
right: out.p2.x,
top: out.p1.y,
width: out.p2.x - out.p1.x,
x: out.p1.x,
y: out.p1.y
};
return out;
},
_parseNb: function(distance) {
let nb = parseFloat(distance, 10);
return isNaN(nb) ? 0 : nb;
},
getAdjustedQuadsPolyfill: function(node, region) {
// Get the border-box rect
// Note that this is relative to the node's viewport, so before we can use
// it, will need to go back up the frames like getRect
let borderRect = node.getBoundingClientRect();
// If the boxType is border, no need to go any further, we're done
if (region === "border") {
return this._getBoxQuadsFromRect(borderRect, node);
}
// Else, need to get margin/padding/border distances
let style = node.ownerDocument.defaultView.getComputedStyle(node);
let camel = s => s.substring(0, 1).toUpperCase() + s.substring(1);
let distances = {border:{}, padding:{}, margin: {}};
for (let side of ["top", "right", "bottom", "left"]) {
distances.border[side] = this._parseNb(style["border" + camel(side) + "Width"]);
distances.padding[side] = this._parseNb(style["padding" + camel(side)]);
distances.margin[side] = this._parseNb(style["margin" + camel(side)]);
}
// From the border-box rect, calculate the content-box, padding-box and
// margin-box rects
function offsetRect(rect, offsetType, dir=1) {
return {
top: rect.top + (dir * distances[offsetType].top),
left: rect.left + (dir * distances[offsetType].left),
width: rect.width - (dir * (distances[offsetType].left + distances[offsetType].right)),
height: rect.height - (dir * (distances[offsetType].top + distances[offsetType].bottom))
};
}
if (region === "margin") {
return this._getBoxQuadsFromRect(offsetRect(borderRect, "margin", -1), node);
} else if (region === "padding") {
return this._getBoxQuadsFromRect(offsetRect(borderRect, "border"), node);
} else if (region === "content") {
let paddingRect = offsetRect(borderRect, "border");
return this._getBoxQuadsFromRect(offsetRect(paddingRect, "padding"), node);
}
},
/********************************************************************
* GetBoxQuads POLYFILL END
********************************************************************/
};

Просмотреть файл

@ -398,6 +398,9 @@ BoxModelHighlighter.prototype = {
let pseudoClassesBox = this.chromeDoc.createElementNS(XHTML_NS, "span");
pseudoClassesBox.className = "highlighter-nodeinfobar-pseudo-classes";
let dimensionBox = this.chromeDoc.createElementNS(XHTML_NS, "span");
dimensionBox.className = "highlighter-nodeinfobar-dimensions";
// Add some content to force a better boundingClientRect
pseudoClassesBox.textContent = "&nbsp;";
@ -411,6 +414,7 @@ BoxModelHighlighter.prototype = {
texthbox.appendChild(idLabel);
texthbox.appendChild(classesBox);
texthbox.appendChild(pseudoClassesBox);
texthbox.appendChild(dimensionBox);
nodeInfobar.appendChild(texthbox);
@ -427,6 +431,7 @@ BoxModelHighlighter.prototype = {
idLabel: idLabel,
classesBox: classesBox,
pseudoClassesBox: pseudoClassesBox,
dimensionBox: dimensionBox,
positioner: infobarPositioner,
barHeight: barHeight,
};
@ -582,9 +587,7 @@ BoxModelHighlighter.prototype = {
options.region = options.region || "content";
// TODO: Remove this polyfill
this.rect =
this.layoutHelpers.getAdjustedQuadsPolyfill(this.currentNode, "margin");
this.rect = this.layoutHelpers.getAdjustedQuads(this.currentNode, "margin");
if (!this.rect) {
return null;
@ -592,9 +595,8 @@ BoxModelHighlighter.prototype = {
if (this.rect.bounds.width > 0 && this.rect.bounds.height > 0) {
for (let boxType in this._boxModelNodes) {
// TODO: Remove this polyfill
let {p1, p2, p3, p4} = boxType === "margin" ? this.rect :
this.layoutHelpers.getAdjustedQuadsPolyfill(this.currentNode, boxType);
this.layoutHelpers.getAdjustedQuads(this.currentNode, boxType);
let boxNode = this._boxModelNodes[boxType];
boxNode.setAttribute("points",
@ -705,29 +707,36 @@ BoxModelHighlighter.prototype = {
* Update node information (tagName#id.class)
*/
_updateInfobar: function() {
if (this.currentNode) {
// Tag name
this.nodeInfo.tagNameLabel.textContent = this.currentNode.tagName;
// ID
this.nodeInfo.idLabel.textContent = this.currentNode.id ? "#" + this.currentNode.id : "";
// Classes
let classes = this.nodeInfo.classesBox;
classes.textContent = this.currentNode.classList.length ?
"." + Array.join(this.currentNode.classList, ".") : "";
// Pseudo-classes
let pseudos = PSEUDO_CLASSES.filter(pseudo => {
return DOMUtils.hasPseudoClassLock(this.currentNode, pseudo);
}, this);
let pseudoBox = this.nodeInfo.pseudoClassesBox;
pseudoBox.textContent = pseudos.join("");
this._moveInfobar();
if (!this.currentNode) {
return;
}
// Tag name
this.nodeInfo.tagNameLabel.textContent = this.currentNode.tagName;
// ID
this.nodeInfo.idLabel.textContent = this.currentNode.id ? "#" + this.currentNode.id : "";
// Classes
let classes = this.nodeInfo.classesBox;
classes.textContent = this.currentNode.classList.length ?
"." + Array.join(this.currentNode.classList, ".") : "";
// Pseudo-classes
let pseudos = PSEUDO_CLASSES.filter(pseudo => {
return DOMUtils.hasPseudoClassLock(this.currentNode, pseudo);
}, this);
let pseudoBox = this.nodeInfo.pseudoClassesBox;
pseudoBox.textContent = pseudos.join("");
// Dimensions
let dimensionBox = this.nodeInfo.dimensionBox;
let rect = this.currentNode.getBoundingClientRect();
dimensionBox.textContent = Math.ceil(rect.width) + " x " +
Math.ceil(rect.height);
this._moveInfobar();
},
/**

Просмотреть файл

@ -268,6 +268,7 @@ let DirectoryLinksProvider = {
this._readDirectoryLinksFile().then(rawLinks => {
// all directory links have a frecency of DIRECTORY_FRECENCY
aCallback(rawLinks.map((link, position) => {
link.directoryIndex = position;
link.frecency = DIRECTORY_FRECENCY;
link.lastVisitDate = rawLinks.length - position;
return link;

Просмотреть файл

@ -259,7 +259,7 @@ add_task(function test_linksURL_locale() {
links = yield fetchData();
do_check_eq(links.length, 1);
expected_data = [{url: "http://example.com", title: "US", frecency: DIRECTORY_FRECENCY, lastVisitDate: 1}];
expected_data = [{url: "http://example.com", title: "US", frecency: DIRECTORY_FRECENCY, lastVisitDate: 1, directoryIndex: 0}];
isIdentical(links, expected_data);
yield promiseDirectoryDownloadOnPrefChange("general.useragent.locale", "zh-CN");
@ -267,8 +267,8 @@ add_task(function test_linksURL_locale() {
links = yield fetchData();
do_check_eq(links.length, 2)
expected_data = [
{url: "http://example.net", title: "CN", frecency: DIRECTORY_FRECENCY, lastVisitDate: 2},
{url: "http://example.net/2", title: "CN2", frecency: DIRECTORY_FRECENCY, lastVisitDate: 1}
{url: "http://example.net", title: "CN", frecency: DIRECTORY_FRECENCY, lastVisitDate: 2, directoryIndex: 0},
{url: "http://example.net/2", title: "CN2", frecency: DIRECTORY_FRECENCY, lastVisitDate: 1, directoryIndex: 1}
];
isIdentical(links, expected_data);
@ -280,7 +280,7 @@ add_task(function test_DirectoryLinksProvider__prefObserver_url() {
let links = yield fetchData();
do_check_eq(links.length, 1);
let expectedData = [{url: "http://example.com", title: "LocalSource", frecency: DIRECTORY_FRECENCY, lastVisitDate: 1}];
let expectedData = [{url: "http://example.com", title: "LocalSource", frecency: DIRECTORY_FRECENCY, lastVisitDate: 1, directoryIndex: 0}];
isIdentical(links, expectedData);
// tests these 2 things: