зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to mozilla-inbound
This commit is contained in:
Коммит
8a61d8f6b2
|
@ -19,8 +19,8 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="f31f95ec18882decc43c0e2f02df581580a32dc8"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="46cd188fdda2397d2b8f3303a184dcd52952e2b2"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5eb9b152764b4a4cf00af94ec02a808cdbb90a20"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="bf9aaf39dd5a6491925a022db167c460f8207d34"/>
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f31f95ec18882decc43c0e2f02df581580a32dc8"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="46cd188fdda2397d2b8f3303a184dcd52952e2b2"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5eb9b152764b4a4cf00af94ec02a808cdbb90a20"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="aebf432f334ec0b48eb358569b9dfbfbead48017"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
|
|
|
@ -15,9 +15,9 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="276ce45e78b09c4a4ee643646f691d22804754c1">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f31f95ec18882decc43c0e2f02df581580a32dc8"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="46cd188fdda2397d2b8f3303a184dcd52952e2b2"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5eb9b152764b4a4cf00af94ec02a808cdbb90a20"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
|
|
|
@ -19,8 +19,8 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="f31f95ec18882decc43c0e2f02df581580a32dc8"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="46cd188fdda2397d2b8f3303a184dcd52952e2b2"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5eb9b152764b4a4cf00af94ec02a808cdbb90a20"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="bf9aaf39dd5a6491925a022db167c460f8207d34"/>
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
</project>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f31f95ec18882decc43c0e2f02df581580a32dc8"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="46cd188fdda2397d2b8f3303a184dcd52952e2b2"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5eb9b152764b4a4cf00af94ec02a808cdbb90a20"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="aebf432f334ec0b48eb358569b9dfbfbead48017"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
|
|
|
@ -4,6 +4,6 @@
|
|||
"remote": "",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "4a5a2ee1fd7ef549f4289bec42046a116b3251c0",
|
||||
"revision": "623b1441cb54a3105b8212589aaf2aa77d537aef",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="f31f95ec18882decc43c0e2f02df581580a32dc8"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="46cd188fdda2397d2b8f3303a184dcd52952e2b2"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5eb9b152764b4a4cf00af94ec02a808cdbb90a20"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="f31f95ec18882decc43c0e2f02df581580a32dc8"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="46cd188fdda2397d2b8f3303a184dcd52952e2b2"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5eb9b152764b4a4cf00af94ec02a808cdbb90a20"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f31f95ec18882decc43c0e2f02df581580a32dc8"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="46cd188fdda2397d2b8f3303a184dcd52952e2b2"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5eb9b152764b4a4cf00af94ec02a808cdbb90a20"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="aebf432f334ec0b48eb358569b9dfbfbead48017"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="f31f95ec18882decc43c0e2f02df581580a32dc8"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="46cd188fdda2397d2b8f3303a184dcd52952e2b2"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5eb9b152764b4a4cf00af94ec02a808cdbb90a20"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
|
||||
|
|
|
@ -376,7 +376,7 @@
|
|||
label="&spellCheckToggle.label;"
|
||||
type="checkbox"
|
||||
accesskey="&spellCheckToggle.accesskey;"
|
||||
oncommand="InlineSpellCheckerUI.toggleEnabled();"/>
|
||||
oncommand="InlineSpellCheckerUI.toggleEnabled(window);"/>
|
||||
<menuitem id="spell-add-dictionaries-main"
|
||||
label="&spellAddDictionaries.label;"
|
||||
accesskey="&spellAddDictionaries.accesskey;"
|
||||
|
|
|
@ -26,7 +26,8 @@ support-files =
|
|||
[browser_graphs-09.js]
|
||||
[browser_graphs-10a.js]
|
||||
[browser_graphs-10b.js]
|
||||
[browser_graphs-11.js]
|
||||
[browser_graphs-11a.js]
|
||||
[browser_graphs-11b.js]
|
||||
[browser_graphs-12.js]
|
||||
[browser_graphs-13.js]
|
||||
[browser_graphs-14.js]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Tests that graph widgets can properly add data and regions.
|
||||
// Tests that graph widgets can properly add data, regions and highlights.
|
||||
|
||||
const TEST_DATA = [{ delta: 112, value: 48 }, { delta: 213, value: 59 }, { delta: 313, value: 60 }, { delta: 413, value: 59 }, { delta: 530, value: 59 }, { delta: 646, value: 58 }, { delta: 747, value: 60 }, { delta: 863, value: 48 }, { delta: 980, value: 37 }, { delta: 1097, value: 30 }, { delta: 1213, value: 29 }, { delta: 1330, value: 23 }, { delta: 1430, value: 10 }, { delta: 1534, value: 17 }, { delta: 1645, value: 20 }, { delta: 1746, value: 22 }, { delta: 1846, value: 39 }, { delta: 1963, value: 26 }, { delta: 2080, value: 27 }, { delta: 2197, value: 35 }, { delta: 2312, value: 47 }, { delta: 2412, value: 53 }, { delta: 2514, value: 60 }, { delta: 2630, value: 37 }, { delta: 2730, value: 36 }, { delta: 2830, value: 37 }, { delta: 2946, value: 36 }, { delta: 3046, value: 40 }, { delta: 3163, value: 47 }, { delta: 3280, value: 41 }, { delta: 3380, value: 35 }, { delta: 3480, value: 27 }, { delta: 3580, value: 39 }, { delta: 3680, value: 42 }, { delta: 3780, value: 49 }, { delta: 3880, value: 55 }, { delta: 3980, value: 60 }, { delta: 4080, value: 60 }, { delta: 4180, value: 60 }];
|
||||
const TEST_REGIONS = [{ start: 320, end: 460 }, { start: 780, end: 860 }];
|
||||
|
@ -22,13 +22,14 @@ function* performTest() {
|
|||
let graph = new LineGraphWidget(doc.body, "fps");
|
||||
yield graph.once("ready");
|
||||
|
||||
testGraph(graph);
|
||||
testDataAndRegions(graph);
|
||||
testHighlights(graph);
|
||||
|
||||
graph.destroy();
|
||||
host.destroy();
|
||||
}
|
||||
|
||||
function testGraph(graph) {
|
||||
function testDataAndRegions(graph) {
|
||||
let thrown1;
|
||||
try {
|
||||
graph.setRegions(TEST_REGIONS);
|
||||
|
@ -52,7 +53,7 @@ function testGraph(graph) {
|
|||
ok(graph.hasRegions(), "The graph should now have the regions set.");
|
||||
|
||||
is(graph.dataScaleX,
|
||||
graph.width / (4180 - 112), // last & first tick in TEST_DATA
|
||||
graph.width / 4180, // last & first tick in TEST_DATA
|
||||
"The data scale on the X axis is correct.");
|
||||
|
||||
is(graph.dataScaleY,
|
||||
|
@ -69,3 +70,17 @@ function testGraph(graph) {
|
|||
"The region's end value was properly normalized.");
|
||||
}
|
||||
}
|
||||
|
||||
function testHighlights(graph) {
|
||||
graph.setMask(TEST_REGIONS);
|
||||
ok(graph.hasMask(),
|
||||
"The graph should now have the highlights set.");
|
||||
|
||||
graph.setMask([]);
|
||||
ok(graph.hasMask(),
|
||||
"The graph shouldn't have anything highlighted.");
|
||||
|
||||
graph.setMask(null);
|
||||
ok(!graph.hasMask(),
|
||||
"The graph should have everything highlighted.");
|
||||
}
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Tests that bar graph's legend items handle mouseover/mouseout.
|
||||
|
||||
let {BarGraphWidget} = Cu.import("resource:///modules/devtools/Graphs.jsm", {});
|
||||
let {DOMHelpers} = Cu.import("resource:///modules/devtools/DOMHelpers.jsm", {});
|
||||
let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
|
||||
let {Hosts} = devtools.require("devtools/framework/toolbox-hosts");
|
||||
|
||||
const CATEGORIES = [
|
||||
{ color: "#46afe3", label: "Foo" },
|
||||
{ color: "#eb5368", label: "Bar" },
|
||||
{ color: "#70bf53", label: "Baz" }
|
||||
];
|
||||
|
||||
let test = Task.async(function*() {
|
||||
yield promiseTab("about:blank");
|
||||
yield performTest();
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
});
|
||||
|
||||
function* performTest() {
|
||||
let [host, win, doc] = yield createHost();
|
||||
doc.body.setAttribute("style", "position: fixed; width: 100%; height: 100%; margin: 0;");
|
||||
|
||||
let graph = new BarGraphWidget(doc.body, 1);
|
||||
graph.fixedWidth = 200;
|
||||
graph.fixedHeight = 100;
|
||||
|
||||
yield graph.once("ready");
|
||||
yield testGraph(graph);
|
||||
|
||||
graph.destroy();
|
||||
host.destroy();
|
||||
}
|
||||
|
||||
function* testGraph(graph) {
|
||||
graph.format = CATEGORIES;
|
||||
graph.dataOffsetX = 1000;
|
||||
graph.setData([{
|
||||
delta: 1100, values: [0, 2, 3]
|
||||
}, {
|
||||
delta: 1200, values: [1, 0, 2]
|
||||
}, {
|
||||
delta: 1300, values: [2, 1, 0]
|
||||
}, {
|
||||
delta: 1400, values: [0, 3, 1]
|
||||
}, {
|
||||
delta: 1500, values: [3, 0, 2]
|
||||
}, {
|
||||
delta: 1600, values: [3, 2, 0]
|
||||
}]);
|
||||
|
||||
is(graph._blocksBoundingRects.toSource(), "[{type:1, start:0, end:33.33333333333333, top:70, bottom:100}, {type:2, start:0, end:33.33333333333333, top:24, bottom:69}, {type:0, start:34.33333333333333, end:66.66666666666666, top:85, bottom:100}, {type:2, start:34.33333333333333, end:66.66666666666666, top:54, bottom:84}, {type:0, start:67.66666666666666, end:100, top:70, bottom:100}, {type:1, start:67.66666666666666, end:100, top:54, bottom:69}, {type:1, start:101, end:133.33333333333331, top:55, bottom:100}, {type:2, start:101, end:133.33333333333331, top:39, bottom:54}, {type:0, start:134.33333333333331, end:166.66666666666666, top:55, bottom:100}, {type:2, start:134.33333333333331, end:166.66666666666666, top:24, bottom:54}, {type:0, start:167.66666666666666, end:200, top:55, bottom:100}, {type:1, start:167.66666666666666, end:200, top:24, bottom:54}]",
|
||||
"The correct blocks bounding rects were calculated for the bar graph.");
|
||||
|
||||
let legendItems = graph._document.querySelectorAll(".bar-graph-widget-legend-item");
|
||||
is(legendItems.length, 3,
|
||||
"Three legend items should exist in the entire graph.");
|
||||
|
||||
yield testLegend(graph, 0, {
|
||||
highlights: "[{type:0, start:34.33333333333333, end:66.66666666666666, top:85, bottom:100}, {type:0, start:67.66666666666666, end:100, top:70, bottom:100}, {type:0, start:134.33333333333331, end:166.66666666666666, top:55, bottom:100}, {type:0, start:167.66666666666666, end:200, top:55, bottom:100}]",
|
||||
selection: "({start:34.33333333333333, end:200})",
|
||||
leftmost: "({type:0, start:34.33333333333333, end:66.66666666666666, top:85, bottom:100})",
|
||||
rightmost: "({type:0, start:167.66666666666666, end:200, top:55, bottom:100})"
|
||||
});
|
||||
yield testLegend(graph, 1, {
|
||||
highlights: "[{type:1, start:0, end:33.33333333333333, top:70, bottom:100}, {type:1, start:67.66666666666666, end:100, top:54, bottom:69}, {type:1, start:101, end:133.33333333333331, top:55, bottom:100}, {type:1, start:167.66666666666666, end:200, top:24, bottom:54}]",
|
||||
selection: "({start:0, end:200})",
|
||||
leftmost: "({type:1, start:0, end:33.33333333333333, top:70, bottom:100})",
|
||||
rightmost: "({type:1, start:167.66666666666666, end:200, top:24, bottom:54})"
|
||||
});
|
||||
yield testLegend(graph, 2, {
|
||||
highlights: "[{type:2, start:0, end:33.33333333333333, top:24, bottom:69}, {type:2, start:34.33333333333333, end:66.66666666666666, top:54, bottom:84}, {type:2, start:101, end:133.33333333333331, top:39, bottom:54}, {type:2, start:134.33333333333331, end:166.66666666666666, top:24, bottom:54}]",
|
||||
selection: "({start:0, end:166.66666666666666})",
|
||||
leftmost: "({type:2, start:0, end:33.33333333333333, top:24, bottom:69})",
|
||||
rightmost: "({type:2, start:134.33333333333331, end:166.66666666666666, top:24, bottom:54})"
|
||||
});
|
||||
}
|
||||
|
||||
function* testLegend(graph, index, { highlights, selection, leftmost, rightmost }) {
|
||||
// Hover.
|
||||
|
||||
let legendItems = graph._document.querySelectorAll(".bar-graph-widget-legend-item");
|
||||
let colorBlock = legendItems[index].querySelector("[view=color]");
|
||||
|
||||
let debounced = graph.once("legend-hover");
|
||||
graph._onLegendMouseOver({ target: colorBlock });
|
||||
ok(!graph.hasMask(), "The graph shouldn't get highlights immediately.");
|
||||
|
||||
let [type, rects] = yield debounced;
|
||||
ok(graph.hasMask(), "The graph should now have highlights.");
|
||||
|
||||
is(type, index,
|
||||
"The legend item was correctly hovered.");
|
||||
is(rects.toSource(), highlights,
|
||||
"The legend item highlighted the correct regions.");
|
||||
|
||||
// Unhover.
|
||||
|
||||
let unhovered = graph.once("legend-unhover");
|
||||
graph._onLegendMouseOut();
|
||||
ok(!graph.hasMask(), "The graph shouldn't have highlights anymore.");
|
||||
|
||||
yield unhovered;
|
||||
ok(true, "The 'legend-mouseout' event was emitted.");
|
||||
|
||||
// Select.
|
||||
|
||||
let selected = graph.once("legend-selection");
|
||||
graph._onLegendMouseDown(mockEvent(colorBlock));
|
||||
ok(graph.hasSelection(), "The graph should now have a selection.");
|
||||
is(graph.getSelection().toSource(), selection, "The graph has a correct selection.");
|
||||
|
||||
let [left, right] = yield selected;
|
||||
is(left.toSource(), leftmost, "The correct leftmost data block was found.");
|
||||
is(right.toSource(), rightmost, "The correct rightmost data block was found.");
|
||||
|
||||
// Deselect.
|
||||
|
||||
graph.dropSelection();
|
||||
}
|
||||
|
||||
function mockEvent(node) {
|
||||
return {
|
||||
target: node,
|
||||
preventDefault: () => {},
|
||||
stopPropagation: () => {}
|
||||
};
|
||||
}
|
|
@ -76,6 +76,11 @@ const BAR_GRAPH_SELECTION_STRIPES_COLOR = "rgba(255,255,255,0.1)";
|
|||
const BAR_GRAPH_REGION_BACKGROUND_COLOR = "transparent";
|
||||
const BAR_GRAPH_REGION_STRIPES_COLOR = "rgba(237,38,85,0.2)";
|
||||
|
||||
const BAR_GRAPH_HIGHLIGHTS_MASK_BACKGROUND = "rgba(255,255,255,0.75)";
|
||||
const BAR_GRAPH_HIGHLIGHTS_MASK_STRIPES = "rgba(255,255,255,0.5)";
|
||||
|
||||
const BAR_GRAPH_LEGEND_MOUSEOVER_DEBOUNCE = 50; // ms
|
||||
|
||||
/**
|
||||
* Small data primitives for all graphs.
|
||||
*/
|
||||
|
@ -232,11 +237,17 @@ AbstractCanvasGraph.prototype = {
|
|||
this._iframe.remove();
|
||||
|
||||
this._data = null;
|
||||
this._mask = null;
|
||||
this._maskArgs = null;
|
||||
this._regions = null;
|
||||
|
||||
this._cachedBackgroundImage = null;
|
||||
this._cachedGraphImage = null;
|
||||
this._cachedMaskImage = null;
|
||||
this._renderTargets.clear();
|
||||
gCachedStripePattern.clear();
|
||||
|
||||
this.emit("destroyed");
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -275,6 +286,15 @@ AbstractCanvasGraph.prototype = {
|
|||
throw "This method needs to be implemented by inheriting classes.";
|
||||
},
|
||||
|
||||
/**
|
||||
* Optionally builds and caches a mask image for this graph, composited
|
||||
* over the data image created via `buildGraphImage`. Inheriting classes
|
||||
* may override this method.
|
||||
*/
|
||||
buildMaskImage: function() {
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* When setting the data source, the coordinates and values may be
|
||||
* stretched or squeezed on the X/Y axis, to fit into the available space.
|
||||
|
@ -308,6 +328,19 @@ AbstractCanvasGraph.prototype = {
|
|||
this.setData(data);
|
||||
}),
|
||||
|
||||
/**
|
||||
* Adds a mask to this graph.
|
||||
*
|
||||
* @param any mask, options
|
||||
* See `buildMaskImage` in inheriting classes for the required args.
|
||||
*/
|
||||
setMask: function(mask, ...options) {
|
||||
this._mask = mask;
|
||||
this._maskArgs = [mask, ...options];
|
||||
this._cachedMaskImage = this.buildMaskImage.apply(this, this._maskArgs);
|
||||
this._shouldRedraw = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds regions to this graph.
|
||||
*
|
||||
|
@ -340,6 +373,14 @@ AbstractCanvasGraph.prototype = {
|
|||
return !!this._data;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets whether or not this graph has any mask applied.
|
||||
* @return boolean
|
||||
*/
|
||||
hasMask: function() {
|
||||
return !!this._mask;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets whether or not this graph has any regions.
|
||||
* @return boolean
|
||||
|
@ -582,6 +623,9 @@ AbstractCanvasGraph.prototype = {
|
|||
this._cachedBackgroundImage = this.buildBackgroundImage();
|
||||
this._cachedGraphImage = this.buildGraphImage();
|
||||
}
|
||||
if (this.hasMask()) {
|
||||
this._cachedMaskImage = this.buildMaskImage.apply(this, this._maskArgs);
|
||||
}
|
||||
if (this.hasRegions()) {
|
||||
this._bakeRegions(this._regions, this._cachedGraphImage);
|
||||
}
|
||||
|
@ -648,12 +692,22 @@ AbstractCanvasGraph.prototype = {
|
|||
let ctx = this._ctx;
|
||||
ctx.clearRect(0, 0, this._width, this._height);
|
||||
|
||||
if (this._cachedBackgroundImage) {
|
||||
ctx.drawImage(this._cachedBackgroundImage, 0, 0, this._width, this._height);
|
||||
}
|
||||
if (this._cachedGraphImage) {
|
||||
ctx.drawImage(this._cachedGraphImage, 0, 0, this._width, this._height);
|
||||
}
|
||||
if (this._cachedMaskImage) {
|
||||
ctx.globalCompositeOperation = "destination-out";
|
||||
ctx.drawImage(this._cachedMaskImage, 0, 0, this._width, this._height);
|
||||
}
|
||||
if (this._cachedBackgroundImage) {
|
||||
ctx.globalCompositeOperation = "destination-over";
|
||||
ctx.drawImage(this._cachedBackgroundImage, 0, 0, this._width, this._height);
|
||||
}
|
||||
|
||||
// Revert to the original global composition operation.
|
||||
if (this._cachedMaskImage || this._cachedBackgroundImage) {
|
||||
ctx.globalCompositeOperation = "source-over";
|
||||
}
|
||||
|
||||
if (this.hasCursor()) {
|
||||
this._drawCliphead();
|
||||
|
@ -1112,6 +1166,11 @@ LineGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
|
|||
regionBackgroundColor: LINE_GRAPH_REGION_BACKGROUND_COLOR,
|
||||
regionStripesColor: LINE_GRAPH_REGION_STRIPES_COLOR,
|
||||
|
||||
/**
|
||||
* Optionally offsets the `delta` in the data source by this scalar.
|
||||
*/
|
||||
dataOffsetX: 0,
|
||||
|
||||
/**
|
||||
* Points that are too close too each other in the graph will not be rendered.
|
||||
* This scalar specifies the required minimum squared distance between points.
|
||||
|
@ -1119,7 +1178,7 @@ LineGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
|
|||
minDistanceBetweenPoints: LINE_GRAPH_MIN_SQUARED_DISTANCE_BETWEEN_POINTS,
|
||||
|
||||
/**
|
||||
* Renders the graph on a canvas.
|
||||
* Renders the graph's data source.
|
||||
* @see AbstractCanvasGraph.prototype.buildGraphImage
|
||||
*/
|
||||
buildGraphImage: function() {
|
||||
|
@ -1140,7 +1199,7 @@ LineGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
|
|||
sumValues += value;
|
||||
}
|
||||
|
||||
let dataScaleX = this.dataScaleX = width / (lastTick - firstTick);
|
||||
let dataScaleX = this.dataScaleX = width / (lastTick - this.dataOffsetX);
|
||||
let dataScaleY = this.dataScaleY = height / maxValue * LINE_GRAPH_DAMPEN_VALUES;
|
||||
|
||||
/**
|
||||
|
@ -1166,7 +1225,7 @@ LineGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
|
|||
let prevY = 0;
|
||||
|
||||
for (let { delta, value } of this._data) {
|
||||
let currX = (delta - firstTick) * dataScaleX;
|
||||
let currX = (delta - this.dataOffsetX) * dataScaleX;
|
||||
let currY = height - value * dataScaleY;
|
||||
|
||||
if (delta == firstTick) {
|
||||
|
@ -1351,9 +1410,24 @@ LineGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
|
|||
this.BarGraphWidget = function(parent, ...args) {
|
||||
AbstractCanvasGraph.apply(this, [parent, "bar-graph", ...args]);
|
||||
|
||||
// Populated with [node, event, listener] entries which need to be removed
|
||||
// when this graph is being destroyed.
|
||||
this.outstandingEventListeners = [];
|
||||
|
||||
this.once("ready", () => {
|
||||
this._onLegendMouseOver = this._onLegendMouseOver.bind(this);
|
||||
this._onLegendMouseOut = this._onLegendMouseOut.bind(this);
|
||||
this._onLegendMouseDown = this._onLegendMouseDown.bind(this);
|
||||
this._onLegendMouseUp = this._onLegendMouseUp.bind(this);
|
||||
this._createLegend();
|
||||
});
|
||||
|
||||
this.once("destroyed", () => {
|
||||
for (let [node, event, listener] of this.outstandingEventListeners) {
|
||||
node.removeEventListener(event, listener);
|
||||
}
|
||||
this.outstandingEventListeners = null;
|
||||
});
|
||||
}
|
||||
|
||||
BarGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
|
||||
|
@ -1370,6 +1444,11 @@ BarGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
|
|||
*/
|
||||
format: null,
|
||||
|
||||
/**
|
||||
* Optionally offsets the `delta` in the data source by this scalar.
|
||||
*/
|
||||
dataOffsetX: 0,
|
||||
|
||||
/**
|
||||
* Bars that are too close too each other in the graph will be combined.
|
||||
* This scalar specifies the required minimum width of each bar.
|
||||
|
@ -1401,7 +1480,7 @@ BarGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
|
|||
},
|
||||
|
||||
/**
|
||||
* Renders the graph on a canvas.
|
||||
* Renders the graph's data source.
|
||||
* @see AbstractCanvasGraph.prototype.buildGraphImage
|
||||
*/
|
||||
buildGraphImage: function() {
|
||||
|
@ -1414,17 +1493,15 @@ BarGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
|
|||
|
||||
let totalTypes = this.format.length;
|
||||
let totalTicks = this._data.length;
|
||||
let firstTick = this._data[0].delta;
|
||||
let lastTick = this._data[totalTicks - 1].delta;
|
||||
|
||||
let minBarsWidth = this.minBarsWidth * this._pixelRatio;
|
||||
let minBlocksHeight = this.minBlocksHeight * this._pixelRatio;
|
||||
|
||||
let dataScaleX = this.dataScaleX = width / (lastTick - firstTick);
|
||||
let dataScaleX = this.dataScaleX = width / (lastTick - this.dataOffsetX);
|
||||
let dataScaleY = this.dataScaleY = height / this._calcMaxHeight({
|
||||
data: this._data,
|
||||
dataScaleX: dataScaleX,
|
||||
dataOffsetX: firstTick,
|
||||
minBarsWidth: minBarsWidth
|
||||
}) * BAR_GRAPH_DAMPEN_VALUES;
|
||||
|
||||
|
@ -1434,6 +1511,7 @@ BarGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
|
|||
// the same color in a single pass. See the @constructor for more
|
||||
// information about the data source, and how a "bar" contains "blocks".
|
||||
|
||||
this._blocksBoundingRects = [];
|
||||
let prevHeight = [];
|
||||
let scaledMarginEnd = BAR_GRAPH_BARS_MARGIN_END * this._pixelRatio;
|
||||
let unscaledMarginTop = BAR_GRAPH_BARS_MARGIN_TOP;
|
||||
|
@ -1442,17 +1520,17 @@ BarGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
|
|||
ctx.fillStyle = this.format[type].color || "#000";
|
||||
ctx.beginPath();
|
||||
|
||||
let prevLeft = 0;
|
||||
let prevRight = 0;
|
||||
let skippedCount = 0;
|
||||
let skippedHeight = 0;
|
||||
|
||||
for (let tick = 0; tick < totalTicks; tick++) {
|
||||
let delta = this._data[tick].delta;
|
||||
let value = this._data[tick].values[type] || 0;
|
||||
let blockLeft = (delta - firstTick) * dataScaleX;
|
||||
let blockRight = (delta - this.dataOffsetX) * dataScaleX;
|
||||
let blockHeight = value * dataScaleY;
|
||||
|
||||
let blockWidth = blockLeft - prevLeft;
|
||||
let blockWidth = blockRight - prevRight;
|
||||
if (blockWidth < minBarsWidth) {
|
||||
skippedCount++;
|
||||
skippedHeight += blockHeight;
|
||||
|
@ -1462,10 +1540,19 @@ BarGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
|
|||
let averageHeight = (blockHeight + skippedHeight) / (skippedCount + 1);
|
||||
if (averageHeight >= minBlocksHeight) {
|
||||
let bottom = height - ~~prevHeight[tick];
|
||||
ctx.moveTo(prevLeft, bottom);
|
||||
ctx.lineTo(prevLeft, bottom - averageHeight);
|
||||
ctx.lineTo(blockLeft, bottom - averageHeight);
|
||||
ctx.lineTo(blockLeft, bottom);
|
||||
ctx.moveTo(prevRight, bottom);
|
||||
ctx.lineTo(prevRight, bottom - averageHeight);
|
||||
ctx.lineTo(blockRight, bottom - averageHeight);
|
||||
ctx.lineTo(blockRight, bottom);
|
||||
|
||||
// Remember this block's type and location.
|
||||
this._blocksBoundingRects.push({
|
||||
type: type,
|
||||
start: prevRight,
|
||||
end: blockRight,
|
||||
top: bottom - averageHeight,
|
||||
bottom: bottom
|
||||
});
|
||||
|
||||
if (prevHeight[tick] === undefined) {
|
||||
prevHeight[tick] = averageHeight + unscaledMarginTop;
|
||||
|
@ -1474,7 +1561,7 @@ BarGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
|
|||
}
|
||||
}
|
||||
|
||||
prevLeft += blockWidth + scaledMarginEnd;
|
||||
prevRight += blockWidth + scaledMarginEnd;
|
||||
skippedHeight = 0;
|
||||
skippedCount = 0;
|
||||
}
|
||||
|
@ -1482,6 +1569,11 @@ BarGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
|
|||
ctx.fill();
|
||||
}
|
||||
|
||||
// The blocks bounding rects isn't guaranteed to be sorted ascending by
|
||||
// block location on the X axis. This should be the case, for better
|
||||
// cache cohesion and a faster `buildMaskImage`.
|
||||
this._blocksBoundingRects.sort((a, b) => a.start > b.start ? 1 : -1);
|
||||
|
||||
// Update the legend.
|
||||
|
||||
while (this._legendNode.hasChildNodes()) {
|
||||
|
@ -1494,6 +1586,74 @@ BarGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
|
|||
return canvas;
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders the graph's mask.
|
||||
* Fades in only the parts of the graph that are inside the specified areas.
|
||||
*
|
||||
* @param array highlights
|
||||
* A list of { start, end } values. Optionally, each object
|
||||
* in the list may also specify { top, bottom } pixel values if the
|
||||
* highlighting shouldn't span across the full height of the graph.
|
||||
* @param boolean inPixels
|
||||
* Set this to true if the { start, end } values in the highlights
|
||||
* list are pixel values, and not values from the data source.
|
||||
* @param function unpack [optional]
|
||||
* @see AbstractCanvasGraph.prototype.getMappedSelection
|
||||
*/
|
||||
buildMaskImage: function(highlights, inPixels = false, unpack = e => e.delta) {
|
||||
// A null `highlights` array is used to clear the mask. An empty array
|
||||
// will mask the entire graph.
|
||||
if (!highlights) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get a render target for the highlights. It will be overlaid on top of
|
||||
// the existing graph, masking the areas that aren't highlighted.
|
||||
|
||||
let { canvas, ctx } = this._getNamedCanvas("graph-highlights");
|
||||
let width = this._width;
|
||||
let height = this._height;
|
||||
|
||||
// Draw the background mask.
|
||||
|
||||
let pattern = AbstractCanvasGraph.getStripePattern({
|
||||
ownerDocument: this._document,
|
||||
backgroundColor: BAR_GRAPH_HIGHLIGHTS_MASK_BACKGROUND,
|
||||
stripesColor: BAR_GRAPH_HIGHLIGHTS_MASK_STRIPES
|
||||
});
|
||||
ctx.fillStyle = pattern;
|
||||
ctx.fillRect(0, 0, width, height);
|
||||
|
||||
// Clear highlighted areas.
|
||||
|
||||
let totalTicks = this._data.length;
|
||||
let firstTick = unpack(this._data[0]);
|
||||
let lastTick = unpack(this._data[totalTicks - 1]);
|
||||
|
||||
for (let { start, end, top, bottom } of highlights) {
|
||||
if (!inPixels) {
|
||||
start = map(start, firstTick, lastTick, 0, width);
|
||||
end = map(end, firstTick, lastTick, 0, width);
|
||||
}
|
||||
let firstSnap = findFirst(this._blocksBoundingRects, e => e.start >= start);
|
||||
let lastSnap = findLast(this._blocksBoundingRects, e => e.start >= start && e.end <= end);
|
||||
|
||||
let x1 = firstSnap ? firstSnap.start : start;
|
||||
let x2 = lastSnap ? lastSnap.end : firstSnap ? firstSnap.end : end;
|
||||
let y1 = top || 0;
|
||||
let y2 = bottom || height;
|
||||
ctx.clearRect(x1, y1, x2 - x1, y2 - y1);
|
||||
}
|
||||
|
||||
return canvas;
|
||||
},
|
||||
|
||||
/**
|
||||
* A list storing the bounding rectangle for each drawn block in the graph.
|
||||
* Created whenever `buildGraphImage` is invoked.
|
||||
*/
|
||||
_blocksBoundingRects: null,
|
||||
|
||||
/**
|
||||
* Calculates the height of the tallest bar that would eventially be rendered
|
||||
* in this graph.
|
||||
|
@ -1504,18 +1664,18 @@ BarGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
|
|||
* @return number
|
||||
* The tallest bar height in this graph.
|
||||
*/
|
||||
_calcMaxHeight: function({ data, dataScaleX, dataOffsetX, minBarsWidth }) {
|
||||
_calcMaxHeight: function({ data, dataScaleX, minBarsWidth }) {
|
||||
let maxHeight = 0;
|
||||
let prevLeft = 0;
|
||||
let prevRight = 0;
|
||||
let skippedCount = 0;
|
||||
let skippedHeight = 0;
|
||||
let scaledMarginEnd = BAR_GRAPH_BARS_MARGIN_END * this._pixelRatio;
|
||||
|
||||
for (let { delta, values } of data) {
|
||||
let barLeft = (delta - dataOffsetX) * dataScaleX;
|
||||
let barRight = (delta - this.dataOffsetX) * dataScaleX;
|
||||
let barHeight = values.reduce((a, b) => a + b, 0);
|
||||
|
||||
let barWidth = barLeft - prevLeft;
|
||||
let barWidth = barRight - prevRight;
|
||||
if (barWidth < minBarsWidth) {
|
||||
skippedCount++;
|
||||
skippedHeight += barHeight;
|
||||
|
@ -1525,7 +1685,7 @@ BarGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
|
|||
let averageHeight = (barHeight + skippedHeight) / (skippedCount + 1);
|
||||
maxHeight = Math.max(averageHeight, maxHeight);
|
||||
|
||||
prevLeft += barWidth + scaledMarginEnd;
|
||||
prevRight += barWidth + scaledMarginEnd;
|
||||
skippedHeight = 0;
|
||||
skippedCount = 0;
|
||||
}
|
||||
|
@ -1551,7 +1711,17 @@ BarGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
|
|||
|
||||
let colorNode = this._document.createElementNS(HTML_NS, "span");
|
||||
colorNode.setAttribute("view", "color");
|
||||
colorNode.setAttribute("data-index", this._legendNode.childNodes.length);
|
||||
colorNode.style.backgroundColor = color;
|
||||
colorNode.addEventListener("mouseover", this._onLegendMouseOver);
|
||||
colorNode.addEventListener("mouseout", this._onLegendMouseOut);
|
||||
colorNode.addEventListener("mousedown", this._onLegendMouseDown);
|
||||
colorNode.addEventListener("mouseup", this._onLegendMouseUp);
|
||||
|
||||
this.outstandingEventListeners.push([colorNode, "mouseover", this._onLegendMouseOver]);
|
||||
this.outstandingEventListeners.push([colorNode, "mouseout", this._onLegendMouseOut]);
|
||||
this.outstandingEventListeners.push([colorNode, "mousedown", this._onLegendMouseDown]);
|
||||
this.outstandingEventListeners.push([colorNode, "mouseup", this._onLegendMouseUp]);
|
||||
|
||||
let labelNode = this._document.createElementNS(HTML_NS, "span");
|
||||
labelNode.setAttribute("view", "label");
|
||||
|
@ -1560,6 +1730,65 @@ BarGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
|
|||
itemNode.appendChild(colorNode);
|
||||
itemNode.appendChild(labelNode);
|
||||
this._legendNode.appendChild(itemNode);
|
||||
},
|
||||
|
||||
/**
|
||||
* Invoked whenever a color node in the legend is hovered.
|
||||
*/
|
||||
_onLegendMouseOver: function(e) {
|
||||
setNamedTimeout("bar-graph-debounce", BAR_GRAPH_LEGEND_MOUSEOVER_DEBOUNCE, () => {
|
||||
let type = e.target.dataset.index;
|
||||
let rects = this._blocksBoundingRects.filter(e => e.type == type);
|
||||
|
||||
this._originalHighlights = this._mask;
|
||||
this._hasCustomHighlights = true;
|
||||
this.setMask(rects, true);
|
||||
|
||||
this.emit("legend-hover", [type, rects]);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Invoked whenever a color node in the legend is unhovered.
|
||||
*/
|
||||
_onLegendMouseOut: function() {
|
||||
clearNamedTimeout("bar-graph-debounce");
|
||||
|
||||
if (this._hasCustomHighlights) {
|
||||
this.setMask(this._originalHighlights);
|
||||
this._hasCustomHighlights = false;
|
||||
this._originalHighlights = null;
|
||||
}
|
||||
|
||||
this.emit("legend-unhover");
|
||||
},
|
||||
|
||||
/**
|
||||
* Invoked whenever a color node in the legend is pressed.
|
||||
*/
|
||||
_onLegendMouseDown: function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
let type = e.target.dataset.index;
|
||||
let rects = this._blocksBoundingRects.filter(e => e.type == type);
|
||||
let leftmost = rects[0];
|
||||
let rightmost = rects[rects.length - 1];
|
||||
if (!leftmost || !rightmost) {
|
||||
this.dropSelection();
|
||||
} else {
|
||||
this.setSelection({ start: leftmost.start, end: rightmost.end });
|
||||
}
|
||||
|
||||
this.emit("legend-selection", [leftmost, rightmost]);
|
||||
},
|
||||
|
||||
/**
|
||||
* Invoked whenever a color node in the legend is released.
|
||||
*/
|
||||
_onLegendMouseUp: function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1694,7 +1923,35 @@ this.CanvasGraphUtils = {
|
|||
|
||||
/**
|
||||
* Maps a value from one range to another.
|
||||
* @param number value, istart, istop, ostart, ostop
|
||||
* @return number
|
||||
*/
|
||||
function map(value, istart, istop, ostart, ostop) {
|
||||
return ostart + (ostop - ostart) * ((value - istart) / (istop - istart));
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the first element in an array that validates a predicate.
|
||||
* @param array
|
||||
* @param function predicate
|
||||
* @return number
|
||||
*/
|
||||
function findFirst(array, predicate) {
|
||||
for (let i = 0, len = array.length; i < len; i++) {
|
||||
let element = array[i];
|
||||
if (predicate(element)) return element;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the last element in an array that validates a predicate.
|
||||
* @param array
|
||||
* @param function predicate
|
||||
* @return number
|
||||
*/
|
||||
function findLast(array, predicate) {
|
||||
for (let i = array.length - 1; i >= 0; i--) {
|
||||
let element = array[i];
|
||||
if (predicate(element)) return element;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -926,10 +926,6 @@
|
|||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.graph-widget-canvas ~ * {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Line graph widget */
|
||||
|
||||
.line-graph-widget-canvas {
|
||||
|
@ -944,6 +940,7 @@
|
|||
top: 0;
|
||||
left: 0;
|
||||
border-right: 1px solid rgba(255,255,255,0.25);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.line-graph-widget-gutter-line {
|
||||
|
@ -976,6 +973,7 @@
|
|||
transform: translateY(-50%);
|
||||
font-size: 80%;
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.line-graph-widget-tooltip::before {
|
||||
|
@ -1053,6 +1051,7 @@
|
|||
left: 8px;
|
||||
color: #292e33;
|
||||
font-size: 80%;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.bar-graph-widget-legend-item {
|
||||
|
@ -1072,6 +1071,8 @@
|
|||
border: 1px solid #fff;
|
||||
border-radius: 1px;
|
||||
-moz-margin-end: 4px;
|
||||
pointer-events: all;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.bar-graph-widget-legend-item > [view="label"] {
|
||||
|
|
|
@ -106,6 +106,8 @@
|
|||
#endif
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
|
||||
#include "mozilla/dom/FeatureList.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
|
@ -1534,6 +1536,17 @@ Navigator::GetFeature(const nsAString& aName)
|
|||
}
|
||||
}
|
||||
|
||||
NS_NAMED_LITERAL_STRING(apiWindowPrefix, "api.window.");
|
||||
if (StringBeginsWith(aName, apiWindowPrefix)) {
|
||||
const nsAString& featureName = Substring(aName, apiWindowPrefix.Length(), aName.Length()-apiWindowPrefix.Length());
|
||||
if (IsFeatureDetectible(featureName)) {
|
||||
p->MaybeResolve(true);
|
||||
} else {
|
||||
p->MaybeResolve(JS::UndefinedHandleValue);
|
||||
}
|
||||
return p.forget();
|
||||
}
|
||||
|
||||
// resolve with <undefined> because the feature name is not supported
|
||||
p->MaybeResolve(JS::UndefinedHandleValue);
|
||||
|
||||
|
|
|
@ -13667,6 +13667,36 @@ class GlobalGenRoots():
|
|||
# Done.
|
||||
return curr
|
||||
|
||||
@staticmethod
|
||||
def FeatureList(config):
|
||||
things = set()
|
||||
for d in config.getDescriptors():
|
||||
if not d.interface.isExternal() and d.featureDetectibleThings is not None:
|
||||
things.update(d.featureDetectibleThings)
|
||||
things = CGList((CGGeneric(declare='"%s",' % t) for t in sorted(things)), joiner="\n")
|
||||
things.append(CGGeneric(declare="nullptr"))
|
||||
things = CGWrapper(CGIndenter(things), pre="static const char* const FeatureList[] = {\n",
|
||||
post="\n};\n")
|
||||
|
||||
helper = CGWrapper(CGIndenter(things), pre="bool IsFeatureDetectible(const nsAString& aFeature) {\n",
|
||||
post=dedent("""
|
||||
const char* const* feature = FeatureList;
|
||||
while (*feature) {
|
||||
if (aFeature.EqualsASCII(*feature)) {
|
||||
return true;
|
||||
}
|
||||
++feature;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
"""))
|
||||
|
||||
curr = CGNamespace.build(['mozilla', 'dom'], helper)
|
||||
curr = CGHeaders([], [], [], [], ["nsString.h"], [], 'FeatureList', curr)
|
||||
curr = CGIncludeGuard('FeatureList', curr)
|
||||
|
||||
return curr
|
||||
|
||||
# Code generator for simple events
|
||||
class CGEventGetter(CGNativeMember):
|
||||
|
|
|
@ -445,6 +445,18 @@ class Descriptor(DescriptorProvider):
|
|||
if permissionsIndex is not None:
|
||||
self.checkPermissionsIndicesForMembers[m.identifier.name] = permissionsIndex
|
||||
|
||||
self.featureDetectibleThings = set()
|
||||
if self.interface.getExtendedAttribute("FeatureDetectible") is not None:
|
||||
if self.interface.getNavigatorProperty():
|
||||
self.featureDetectibleThings.add("Navigator.%s" % self.interface.getNavigatorProperty())
|
||||
else:
|
||||
assert(self.interface.ctor() is not None)
|
||||
self.featureDetectibleThings.add(self.interface.identifier.name)
|
||||
|
||||
for m in self.interface.members:
|
||||
if m.getExtendedAttribute("FeatureDetectible") is not None:
|
||||
self.featureDetectibleThings.add("%s.%s" % (self.interface.identifier.name, m.identifier.name))
|
||||
|
||||
# Build the prototype chain.
|
||||
self.prototypeChain = []
|
||||
parent = interface
|
||||
|
|
|
@ -127,6 +127,7 @@ class WebIDLCodegenManager(LoggingMixin):
|
|||
|
||||
# Global parser derived declaration files.
|
||||
GLOBAL_DECLARE_FILES = {
|
||||
'FeatureList.h',
|
||||
'GeneratedAtomList.h',
|
||||
'PrototypeList.h',
|
||||
'RegisterBindings.h',
|
||||
|
|
|
@ -568,6 +568,16 @@ class IDLInterface(IDLObjectWithScope):
|
|||
[self.location])
|
||||
assert not parent or isinstance(parent, IDLInterface)
|
||||
|
||||
if self.getExtendedAttribute("FeatureDetectible"):
|
||||
if self.getExtendedAttribute("NoInterfaceObject"):
|
||||
raise WebIDLError("[FeatureDetectible] not allowed on interface "
|
||||
" with [NoInterfaceObject]",
|
||||
[self.location])
|
||||
if self.getExtendedAttribute("Pref"):
|
||||
raise WebIDLError("[FeatureDetectible] must not be specified "
|
||||
"in combination with [Pref]",
|
||||
[self.location])
|
||||
|
||||
self.parent = parent
|
||||
|
||||
assert iter(self.members)
|
||||
|
@ -1041,7 +1051,8 @@ class IDLInterface(IDLObjectWithScope):
|
|||
identifier == "OverrideBuiltins" or
|
||||
identifier == "ChromeOnly" or
|
||||
identifier == "Unforgeable" or
|
||||
identifier == "LegacyEventInit"):
|
||||
identifier == "LegacyEventInit" or
|
||||
identifier == "FeatureDetectible"):
|
||||
# Known extended attributes that do not take values
|
||||
if not attr.noArguments():
|
||||
raise WebIDLError("[%s] must take no arguments" % identifier,
|
||||
|
@ -2890,6 +2901,18 @@ class IDLAttribute(IDLInterfaceMember):
|
|||
raise WebIDLError("An attribute with [SameObject] must have an "
|
||||
"interface type as its type", [self.location])
|
||||
|
||||
if self.getExtendedAttribute("FeatureDetectible"):
|
||||
if not (self.getExtendedAttribute("Func") or
|
||||
self.getExtendedAttribute("AvailableIn") or
|
||||
self.getExtendedAttribute("CheckPermissions")):
|
||||
raise WebIDLError("[%s] is only allowed in combination with [Func], "
|
||||
"[AvailableIn] or [CheckPermissions]" % identifier,
|
||||
[attr.location, self.location])
|
||||
if self.getExtendedAttribute("Pref"):
|
||||
raise WebIDLError("[FeatureDetectible] must not be specified "
|
||||
"in combination with [Pref]",
|
||||
[self.location])
|
||||
|
||||
def validate(self):
|
||||
if ((self.getExtendedAttribute("Cached") or
|
||||
self.getExtendedAttribute("StoreInSlot")) and
|
||||
|
@ -3008,6 +3031,13 @@ class IDLAttribute(IDLInterfaceMember):
|
|||
raise WebIDLError("[LenientThis] is not allowed in combination "
|
||||
"with [%s]" % identifier,
|
||||
[attr.location, self.location])
|
||||
elif identifier == "FeatureDetectible":
|
||||
if not (self.getExtendedAttribute("Func") or
|
||||
self.getExtendedAttribute("AvailableIn") or
|
||||
self.getExtendedAttribute("CheckPermissions")):
|
||||
raise WebIDLError("[%s] is only allowed in combination with [Func], "
|
||||
"[AvailableIn] or [CheckPermissions]" % identifier,
|
||||
[attr.location, self.location])
|
||||
elif (identifier == "Pref" or
|
||||
identifier == "SetterThrows" or
|
||||
identifier == "Pure" or
|
||||
|
@ -3423,6 +3453,18 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
|
|||
self._overloads]
|
||||
|
||||
def finish(self, scope):
|
||||
if self.getExtendedAttribute("FeatureDetectible"):
|
||||
if not (self.getExtendedAttribute("Func") or
|
||||
self.getExtendedAttribute("AvailableIn") or
|
||||
self.getExtendedAttribute("CheckPermissions")):
|
||||
raise WebIDLError("[FeatureDetectible] is only allowed in combination "
|
||||
"with [Func], [AvailableIn] or [CheckPermissions]",
|
||||
[self.location])
|
||||
if self.getExtendedAttribute("Pref"):
|
||||
raise WebIDLError("[FeatureDetectible] must not be specified "
|
||||
"in combination with [Pref]",
|
||||
[self.location])
|
||||
|
||||
overloadWithPromiseReturnType = None
|
||||
overloadWithoutPromiseReturnType = None
|
||||
for overload in self._overloads:
|
||||
|
@ -3593,7 +3635,8 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
|
|||
[attr.location, self.location])
|
||||
elif (identifier == "Pure" or
|
||||
identifier == "CrossOriginCallable" or
|
||||
identifier == "WebGLHandlesContextLoss"):
|
||||
identifier == "WebGLHandlesContextLoss" or
|
||||
identifier == "FeatureDetectible"):
|
||||
# Known no-argument attributes.
|
||||
if not attr.noArguments():
|
||||
raise WebIDLError("[%s] must take no arguments" % identifier,
|
||||
|
|
|
@ -152,6 +152,7 @@
|
|||
#include "mozilla/dom/telephony/PTelephonyChild.h"
|
||||
#include "mozilla/dom/time/DateCacheCleaner.h"
|
||||
#include "mozilla/net/NeckoMessageUtils.h"
|
||||
#include "mozilla/RemoteSpellCheckEngineChild.h"
|
||||
|
||||
using namespace base;
|
||||
using namespace mozilla;
|
||||
|
@ -1054,6 +1055,20 @@ ContentChild::AllocPBlobChild(const BlobConstructorParams& aParams)
|
|||
return nsIContentChild::AllocPBlobChild(aParams);
|
||||
}
|
||||
|
||||
mozilla::PRemoteSpellcheckEngineChild *
|
||||
ContentChild::AllocPRemoteSpellcheckEngineChild()
|
||||
{
|
||||
NS_NOTREACHED("Default Constructor for PRemoteSpellcheckEngineChilf should never be called");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
ContentChild::DeallocPRemoteSpellcheckEngineChild(PRemoteSpellcheckEngineChild *child)
|
||||
{
|
||||
delete child;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ContentChild::DeallocPBlobChild(PBlobChild* aActor)
|
||||
{
|
||||
|
|
|
@ -26,6 +26,7 @@ struct ResourceMapping;
|
|||
struct OverrideMapping;
|
||||
|
||||
namespace mozilla {
|
||||
class RemoteSpellcheckEngineChild;
|
||||
|
||||
namespace ipc {
|
||||
class OptionalURIParams;
|
||||
|
@ -243,6 +244,8 @@ public:
|
|||
|
||||
virtual mozilla::jsipc::PJavaScriptChild* AllocPJavaScriptChild() MOZ_OVERRIDE;
|
||||
virtual bool DeallocPJavaScriptChild(mozilla::jsipc::PJavaScriptChild*) MOZ_OVERRIDE;
|
||||
virtual PRemoteSpellcheckEngineChild* AllocPRemoteSpellcheckEngineChild() MOZ_OVERRIDE;
|
||||
virtual bool DeallocPRemoteSpellcheckEngineChild(PRemoteSpellcheckEngineChild*) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool RecvSetOffline(const bool& offline) MOZ_OVERRIDE;
|
||||
|
||||
|
|
|
@ -158,6 +158,8 @@ using namespace mozilla::system;
|
|||
|
||||
#include "JavaScriptParent.h"
|
||||
|
||||
#include "mozilla/RemoteSpellCheckEngineParent.h"
|
||||
|
||||
#ifdef MOZ_B2G_FM
|
||||
#include "mozilla/dom/FMRadioParent.h"
|
||||
#endif
|
||||
|
@ -2695,6 +2697,20 @@ ContentParent::DeallocPBlobParent(PBlobParent* aActor)
|
|||
return true;
|
||||
}
|
||||
|
||||
mozilla::PRemoteSpellcheckEngineParent *
|
||||
ContentParent::AllocPRemoteSpellcheckEngineParent()
|
||||
{
|
||||
mozilla::RemoteSpellcheckEngineParent *parent = new mozilla::RemoteSpellcheckEngineParent();
|
||||
return parent;
|
||||
}
|
||||
|
||||
bool
|
||||
ContentParent::DeallocPRemoteSpellcheckEngineParent(PRemoteSpellcheckEngineParent *parent)
|
||||
{
|
||||
delete parent;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
ContentParent::KillHard()
|
||||
{
|
||||
|
|
|
@ -36,6 +36,7 @@ class nsIMemoryReporter;
|
|||
class ParentIdleListener;
|
||||
|
||||
namespace mozilla {
|
||||
class PRemoteSpellcheckEngineParent;
|
||||
|
||||
namespace ipc {
|
||||
class OptionalURIParams;
|
||||
|
@ -252,6 +253,7 @@ public:
|
|||
RecvPJavaScriptConstructor(PJavaScriptParent* aActor) MOZ_OVERRIDE {
|
||||
return PContentParent::RecvPJavaScriptConstructor(aActor);
|
||||
}
|
||||
virtual PRemoteSpellcheckEngineParent* AllocPRemoteSpellcheckEngineParent() MOZ_OVERRIDE;
|
||||
|
||||
virtual bool RecvRecordingDeviceEvents(const nsString& aRecordingStatus,
|
||||
const nsString& aPageURL,
|
||||
|
@ -395,6 +397,7 @@ private:
|
|||
|
||||
virtual bool DeallocPJavaScriptParent(mozilla::jsipc::PJavaScriptParent*) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool DeallocPRemoteSpellcheckEngineParent(PRemoteSpellcheckEngineParent*) MOZ_OVERRIDE;
|
||||
virtual PBrowserParent* AllocPBrowserParent(const IPCTabContext& aContext,
|
||||
const uint32_t& aChromeFlags,
|
||||
const uint64_t& aId,
|
||||
|
|
|
@ -31,6 +31,7 @@ include protocol PStorage;
|
|||
include protocol PTelephony;
|
||||
include protocol PTestShell;
|
||||
include protocol PJavaScript;
|
||||
include protocol PRemoteSpellcheckEngine;
|
||||
include DOMTypes;
|
||||
include JavaScriptTypes;
|
||||
include InputStreamParams;
|
||||
|
@ -311,6 +312,7 @@ intr protocol PContent
|
|||
manages PTelephony;
|
||||
manages PTestShell;
|
||||
manages PJavaScript;
|
||||
manages PRemoteSpellcheckEngine;
|
||||
|
||||
both:
|
||||
// Depending on exactly how the new browser is being created, it might be
|
||||
|
@ -464,6 +466,7 @@ parent:
|
|||
|
||||
async PJavaScript();
|
||||
|
||||
sync PRemoteSpellcheckEngine();
|
||||
PDeviceStorageRequest(DeviceStorageParams params);
|
||||
|
||||
PFileSystemRequest(FileSystemParams params);
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#include "nsString.h" // for nsAutoString, nsString, etc
|
||||
#include "nsStringFwd.h" // for nsAFlatString
|
||||
#include "nsStyleUtil.h" // for nsStyleUtil
|
||||
#include "nsXULAppAPI.h" // for XRE_GetProcessType
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
|
@ -705,8 +706,16 @@ nsEditorSpellCheck::UpdateCurrentDictionary(nsIEditorSpellCheckCallback* aCallba
|
|||
NS_ENSURE_STATE(doc);
|
||||
doc->GetContentLanguage(fetcher->mRootDocContentLang);
|
||||
|
||||
rv = fetcher->Fetch(mEditor);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (XRE_GetProcessType() == GeckoProcessType_Content) {
|
||||
// Content prefs don't work in E10S (Bug 1027898) pretend that we
|
||||
// didn't have any & trigger the asynchrous completion.
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NS_NewRunnableMethodWithArg<uint16_t>(fetcher, &DictionaryFetcher::HandleCompletion, 0);
|
||||
NS_DispatchToMainThread(runnable);
|
||||
} else {
|
||||
rv = fetcher->Fetch(mEditor);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
/* 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/. */
|
||||
|
||||
include protocol PContent;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
sync protocol PRemoteSpellcheckEngine {
|
||||
manager PContent;
|
||||
|
||||
parent:
|
||||
__delete__();
|
||||
|
||||
sync CheckForMisspelling(nsString aWord) returns (bool isMisspelled);
|
||||
|
||||
sync SetDictionary(nsString aDictionary) returns (bool success);
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,21 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "RemoteSpellCheckEngineChild.h"
|
||||
|
||||
namespace mozilla {
|
||||
RemoteSpellcheckEngineChild::RemoteSpellcheckEngineChild(mozSpellChecker *aOwner)
|
||||
:mOwner(aOwner)
|
||||
{
|
||||
}
|
||||
|
||||
RemoteSpellcheckEngineChild::~RemoteSpellcheckEngineChild()
|
||||
{
|
||||
// null out the owner's SpellcheckEngineChild to prevent state corruption
|
||||
// during shutdown
|
||||
mOwner->DeleteRemoteEngine();
|
||||
|
||||
}
|
||||
|
||||
} //namespace mozilla
|
|
@ -0,0 +1,26 @@
|
|||
/* 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/. */
|
||||
|
||||
#ifndef RemoteSpellcheckEngineChild_h_
|
||||
#define RemoteSpellcheckEngineChild_h_
|
||||
|
||||
#include "mozilla/PRemoteSpellcheckEngineChild.h"
|
||||
#include "mozSpellChecker.h"
|
||||
|
||||
class mozSpellChecker;
|
||||
|
||||
namespace mozilla {
|
||||
class RemoteSpellcheckEngineChild : public mozilla::PRemoteSpellcheckEngineChild
|
||||
{
|
||||
public:
|
||||
RemoteSpellcheckEngineChild(mozSpellChecker *aOwner);
|
||||
~RemoteSpellcheckEngineChild();
|
||||
|
||||
private:
|
||||
mozSpellChecker *mOwner;
|
||||
};
|
||||
|
||||
} //namespace mozilla
|
||||
|
||||
#endif // RemoteSpellcheckEngineChild_h_
|
|
@ -0,0 +1,49 @@
|
|||
/* 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/. */
|
||||
|
||||
#include "RemoteSpellCheckEngineParent.h"
|
||||
#include "mozISpellCheckingEngine.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
||||
#define DEFAULT_SPELL_CHECKER "@mozilla.org/spellchecker/engine;1"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
RemoteSpellcheckEngineParent::RemoteSpellcheckEngineParent()
|
||||
{
|
||||
mEngine = do_GetService(DEFAULT_SPELL_CHECKER);
|
||||
}
|
||||
|
||||
RemoteSpellcheckEngineParent::~RemoteSpellcheckEngineParent()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
RemoteSpellcheckEngineParent::RecvSetDictionary(
|
||||
const nsString& aDictionary,
|
||||
bool* success)
|
||||
{
|
||||
nsresult rv = mEngine->SetDictionary(aDictionary.get());
|
||||
*success = NS_SUCCEEDED(rv);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
RemoteSpellcheckEngineParent::RecvCheckForMisspelling(
|
||||
const nsString& aWord,
|
||||
bool* isMisspelled)
|
||||
{
|
||||
bool isCorrect = false;
|
||||
mEngine->Check(aWord.get(), &isCorrect);
|
||||
*isMisspelled = !isCorrect;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
RemoteSpellcheckEngineParent::ActorDestroy(ActorDestroyReason aWhy)
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/* 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/. */
|
||||
#ifndef RemoteSpellcheckEngineParent_h_
|
||||
#define RemoteSpellcheckEngineParent_h_
|
||||
|
||||
#include "mozISpellCheckingEngine.h"
|
||||
#include "mozilla/PRemoteSpellcheckEngineParent.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class RemoteSpellcheckEngineParent : public mozilla::PRemoteSpellcheckEngineParent {
|
||||
|
||||
public:
|
||||
RemoteSpellcheckEngineParent();
|
||||
|
||||
~RemoteSpellcheckEngineParent();
|
||||
|
||||
virtual void ActorDestroy(ActorDestroyReason aWhy);
|
||||
|
||||
bool RecvSetDictionary(const nsString& aDictionary, bool* success);
|
||||
|
||||
bool RecvCheckForMisspelling( const nsString& aWord, bool* isMisspelled);
|
||||
|
||||
private:
|
||||
nsCOMPtr<mozISpellCheckingEngine> mEngine;
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
|
@ -4,9 +4,11 @@
|
|||
# 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/.
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
SOURCES += [
|
||||
'mozHunspell.cpp',
|
||||
'mozHunspellDirProvider.cpp',
|
||||
'RemoteSpellCheckEngineChild.cpp',
|
||||
'RemoteSpellCheckEngineParent.cpp',
|
||||
]
|
||||
|
||||
if not CONFIG['MOZ_NATIVE_HUNSPELL']:
|
||||
|
@ -40,3 +42,14 @@ LOCAL_INCLUDES += [
|
|||
# Suppress warnings in third-party code.
|
||||
if CONFIG['CLANG_CXX']:
|
||||
CXXFLAGS += ['-Wno-unused-private-field']
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
IPDL_SOURCES = [
|
||||
'PRemoteSpellcheckEngine.ipdl',
|
||||
]
|
||||
|
||||
EXPORTS.mozilla += [
|
||||
'RemoteSpellCheckEngineChild.h',
|
||||
'RemoteSpellCheckEngineParent.h',
|
||||
]
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
# 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/.
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
SOURCES += [
|
||||
'mozEnglishWordUtils.cpp',
|
||||
'mozGenericWordUtils.cpp',
|
||||
'mozInlineSpellChecker.cpp',
|
||||
|
@ -24,5 +25,8 @@ LOCAL_INCLUDES += [
|
|||
'/content/base/src',
|
||||
'/editor/libeditor/base',
|
||||
]
|
||||
EXPORTS.mozilla += [
|
||||
'mozSpellChecker.h',
|
||||
]
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
|
|
@ -10,6 +10,13 @@
|
|||
#include "nsICategoryManager.h"
|
||||
#include "nsISupportsPrimitives.h"
|
||||
#include "nsISimpleEnumerator.h"
|
||||
#include "mozilla/PRemoteSpellcheckEngineChild.h"
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
|
||||
using mozilla::dom::ContentChild;
|
||||
using mozilla::PRemoteSpellcheckEngineChild;
|
||||
using mozilla::RemoteSpellcheckEngineChild;
|
||||
|
||||
#define DEFAULT_SPELL_CHECKER "@mozilla.org/spellchecker/engine;1"
|
||||
|
||||
|
@ -38,6 +45,10 @@ mozSpellChecker::~mozSpellChecker()
|
|||
}
|
||||
mSpellCheckingEngine = nullptr;
|
||||
mPersonalDictionary = nullptr;
|
||||
|
||||
if(XRE_GetProcessType() == GeckoProcessType_Content) {
|
||||
mEngine->Send__delete__(mEngine);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -46,6 +57,12 @@ mozSpellChecker::Init()
|
|||
mPersonalDictionary = do_GetService("@mozilla.org/spellchecker/personaldictionary;1");
|
||||
|
||||
mSpellCheckingEngine = nullptr;
|
||||
if (XRE_GetProcessType() == GeckoProcessType_Content) {
|
||||
mozilla::dom::ContentChild* contentChild = mozilla::dom::ContentChild::GetSingleton();
|
||||
MOZ_ASSERT(contentChild);
|
||||
mEngine = new RemoteSpellcheckEngineChild(this);
|
||||
contentChild->SendPRemoteSpellcheckEngineConstructor(mEngine);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -108,9 +125,16 @@ mozSpellChecker::CheckWord(const nsAString &aWord, bool *aIsMisspelled, nsTArray
|
|||
{
|
||||
nsresult result;
|
||||
bool correct;
|
||||
if(!mSpellCheckingEngine)
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
|
||||
if (XRE_GetProcessType() == GeckoProcessType_Content) {
|
||||
nsString wordwrapped = nsString(aWord);
|
||||
bool rv = mEngine->SendCheckForMisspelling(wordwrapped, aIsMisspelled);
|
||||
return rv ? NS_OK : NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
if(!mSpellCheckingEngine) {
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
}
|
||||
*aIsMisspelled = false;
|
||||
result = mSpellCheckingEngine->Check(PromiseFlatString(aWord).get(), &correct);
|
||||
NS_ENSURE_SUCCESS(result, result);
|
||||
|
@ -332,6 +356,13 @@ mozSpellChecker::GetCurrentDictionary(nsAString &aDictionary)
|
|||
NS_IMETHODIMP
|
||||
mozSpellChecker::SetCurrentDictionary(const nsAString &aDictionary)
|
||||
{
|
||||
if (XRE_GetProcessType() == GeckoProcessType_Content) {
|
||||
nsString wrappedDict = nsString(aDictionary);
|
||||
bool isSuccess;
|
||||
mEngine->SendSetDictionary(wrappedDict, &isSuccess);
|
||||
return isSuccess ? NS_OK : NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
// Calls to mozISpellCheckingEngine::SetDictionary might destroy us
|
||||
nsRefPtr<mozSpellChecker> kungFuDeathGrip = this;
|
||||
|
||||
|
@ -528,3 +559,7 @@ mozSpellChecker::GetEngineList(nsCOMArray<mozISpellCheckingEngine>* aSpellChecki
|
|||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void mozSpellChecker::DeleteRemoteEngine() {
|
||||
mEngine = nullptr;
|
||||
}
|
|
@ -17,6 +17,12 @@
|
|||
#include "nsTArray.h"
|
||||
#include "mozISpellI18NUtil.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "RemoteSpellCheckEngineChild.h"
|
||||
|
||||
namespace mozilla {
|
||||
class PRemoteSpellcheckEngineChild;
|
||||
class RemoteSpellcheckEngineChild;
|
||||
}
|
||||
|
||||
class mozSpellChecker : public nsISpellChecker
|
||||
{
|
||||
|
@ -43,6 +49,7 @@ public:
|
|||
NS_IMETHOD GetCurrentDictionary(nsAString &aDictionary);
|
||||
NS_IMETHOD SetCurrentDictionary(const nsAString &aDictionary);
|
||||
NS_IMETHOD CheckCurrentDictionary();
|
||||
void DeleteRemoteEngine();
|
||||
|
||||
protected:
|
||||
virtual ~mozSpellChecker();
|
||||
|
@ -59,5 +66,7 @@ protected:
|
|||
nsresult GetCurrentBlockIndex(nsITextServicesDocument *aDoc, int32_t *outBlockIndex);
|
||||
|
||||
nsresult GetEngineList(nsCOMArray<mozISpellCheckingEngine> *aDictionaryList);
|
||||
|
||||
mozilla::PRemoteSpellcheckEngineChild *mEngine;
|
||||
};
|
||||
#endif // mozSpellChecker_h__
|
||||
|
|
|
@ -1310,6 +1310,12 @@ public class BrowserApp extends GeckoApp
|
|||
GeckoPreferences.setResourceToOpen(settingsIntent, resource);
|
||||
startActivityForResult(settingsIntent, ACTIVITY_REQUEST_PREFERENCES);
|
||||
|
||||
// Don't use a transition to settings if we're on a device where that
|
||||
// would look bad.
|
||||
if (HardwareUtils.IS_KINDLE_DEVICE) {
|
||||
overridePendingTransition(0, 0);
|
||||
}
|
||||
|
||||
} else if ("Telemetry:Gather".equals(event)) {
|
||||
Telemetry.HistogramAdd("PLACES_PAGES_COUNT",
|
||||
BrowserDB.getCount(getContentResolver(), "history"));
|
||||
|
@ -1604,6 +1610,9 @@ public class BrowserApp extends GeckoApp
|
|||
int flags = Tabs.LOADURL_NONE;
|
||||
if (newTab) {
|
||||
flags |= Tabs.LOADURL_NEW_TAB;
|
||||
if (Tabs.getInstance().getSelectedTab().isPrivate()) {
|
||||
flags |= Tabs.LOADURL_PRIVATE;
|
||||
}
|
||||
}
|
||||
|
||||
Tabs.getInstance().loadUrl(url, searchEngine, -1, flags);
|
||||
|
|
|
@ -34,8 +34,6 @@ import android.database.MatrixCursor.RowBuilder;
|
|||
import android.os.Bundle;
|
||||
import android.support.v4.app.LoaderManager.LoaderCallbacks;
|
||||
import android.support.v4.content.Loader;
|
||||
import android.support.v4.widget.CursorAdapter;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
@ -252,9 +250,7 @@ public class RecentTabsPanel extends HomeFragment
|
|||
mClosedTabs = closedTabs;
|
||||
|
||||
// Reload the cursor to show recently closed tabs.
|
||||
if (mClosedTabs.length > 0 && canLoad()) {
|
||||
getLoaderManager().restartLoader(LOADER_ID_RECENT_TABS, null, mCursorLoaderCallbacks);
|
||||
}
|
||||
getLoaderManager().restartLoader(LOADER_ID_RECENT_TABS, null, mCursorLoaderCallbacks);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -310,8 +306,8 @@ public class RecentTabsPanel extends HomeFragment
|
|||
for (int i = 0; i < length; i++) {
|
||||
final String url = closedTabs[i].url;
|
||||
|
||||
// Don't show recent tabs for about:home.
|
||||
if (!AboutPages.isAboutHome(url)) {
|
||||
// Don't show recent tabs for about:home or about:privatebrowsing.
|
||||
if (!AboutPages.isTitlelessAboutPage(url)) {
|
||||
// If this is the first closed tab we're adding, add a header for the section.
|
||||
if (visibleClosedTabs == 0) {
|
||||
addRow(c, null, context.getString(R.string.home_closed_tabs_title), RecentTabs.TYPE_HEADER);
|
||||
|
|
|
@ -33,6 +33,7 @@ import org.mozilla.gecko.background.healthreport.HealthReportConstants;
|
|||
import org.mozilla.gecko.db.BrowserContract.SuggestedSites;
|
||||
import org.mozilla.gecko.home.HomePanelPicker;
|
||||
import org.mozilla.gecko.util.GeckoEventListener;
|
||||
import org.mozilla.gecko.util.HardwareUtils;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
import org.mozilla.gecko.widget.FloatingHintEditText;
|
||||
|
||||
|
@ -86,6 +87,11 @@ OnSharedPreferenceChangeListener
|
|||
{
|
||||
private static final String LOGTAG = "GeckoPreferences";
|
||||
|
||||
// We have a white background, which makes transitions on
|
||||
// some devices look bad. Don't use transitions on those
|
||||
// devices.
|
||||
private static final boolean NO_TRANSITIONS = HardwareUtils.IS_KINDLE_DEVICE;
|
||||
|
||||
private static final String NON_PREF_PREFIX = "android.not_a_preference.";
|
||||
public static final String INTENT_EXTRA_RESOURCES = "resource";
|
||||
public static String PREFS_HEALTHREPORT_UPLOAD_ENABLED = NON_PREF_PREFIX + "healthreport.uploadEnabled";
|
||||
|
@ -133,6 +139,19 @@ OnSharedPreferenceChangeListener
|
|||
private Locale lastLocale = Locale.getDefault();
|
||||
private boolean localeSwitchingIsEnabled;
|
||||
|
||||
private void startActivityForResultChoosingTransition(final Intent intent, final int requestCode) {
|
||||
startActivityForResult(intent, requestCode);
|
||||
if (NO_TRANSITIONS) {
|
||||
overridePendingTransition(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private void finishChoosingTransition() {
|
||||
finish();
|
||||
if (NO_TRANSITIONS) {
|
||||
overridePendingTransition(0, 0);
|
||||
}
|
||||
}
|
||||
private void updateActionBarTitle(int title) {
|
||||
if (Build.VERSION.SDK_INT >= 14) {
|
||||
final String newTitle = getString(title);
|
||||
|
@ -250,10 +269,10 @@ OnSharedPreferenceChangeListener
|
|||
// We also don't need to update the title.
|
||||
final Intent intent = (Intent) getIntent().clone();
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
|
||||
startActivityForResult(intent, REQUEST_CODE_PREF_SCREEN);
|
||||
startActivityForResultChoosingTransition(intent, REQUEST_CODE_PREF_SCREEN);
|
||||
|
||||
setResult(RESULT_CODE_LOCALE_DID_CHANGE);
|
||||
finish();
|
||||
finishChoosingTransition();
|
||||
}
|
||||
|
||||
private void checkLocale() {
|
||||
|
@ -440,6 +459,15 @@ OnSharedPreferenceChangeListener
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
super.onBackPressed();
|
||||
|
||||
if (NO_TRANSITIONS) {
|
||||
overridePendingTransition(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
@ -494,7 +522,7 @@ OnSharedPreferenceChangeListener
|
|||
// the settings screens.
|
||||
// We need to start nested PreferenceScreens withStartActivityForResult().
|
||||
// Android doesn't let us do that (see Preference.onClick), so we're overriding here.
|
||||
startActivityForResult(intent, REQUEST_CODE_PREF_SCREEN);
|
||||
startActivityForResultChoosingTransition(intent, REQUEST_CODE_PREF_SCREEN);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -505,9 +533,12 @@ OnSharedPreferenceChangeListener
|
|||
// Overriding because we want to use startActivityForResult for Fragment intents.
|
||||
Intent intent = onBuildStartFragmentIntent(fragmentName, args, titleRes, shortTitleRes);
|
||||
if (resultTo == null) {
|
||||
startActivityForResult(intent, REQUEST_CODE_PREF_SCREEN);
|
||||
startActivityForResultChoosingTransition(intent, REQUEST_CODE_PREF_SCREEN);
|
||||
} else {
|
||||
resultTo.startActivityForResult(intent, resultRequestCode);
|
||||
if (NO_TRANSITIONS) {
|
||||
overridePendingTransition(0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -525,7 +556,7 @@ OnSharedPreferenceChangeListener
|
|||
|
||||
// Pass this result up to the parent activity.
|
||||
setResult(RESULT_CODE_EXIT_SETTINGS);
|
||||
finish();
|
||||
finishChoosingTransition();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
@ -690,7 +721,7 @@ OnSharedPreferenceChangeListener
|
|||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
Intent dialogIntent = new Intent(GeckoPreferences.this, HomePanelPicker.class);
|
||||
startActivityForResult(dialogIntent, HomePanelPicker.REQUEST_CODE_ADD_PANEL);
|
||||
startActivityForResultChoosingTransition(dialogIntent, HomePanelPicker.REQUEST_CODE_ADD_PANEL);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
@ -740,7 +771,7 @@ OnSharedPreferenceChangeListener
|
|||
int itemId = item.getItemId();
|
||||
switch (itemId) {
|
||||
case android.R.id.home:
|
||||
finish();
|
||||
finishChoosingTransition();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1007,7 +1038,7 @@ OnSharedPreferenceChangeListener
|
|||
((ListPreference) preference).setSummary(newEntry);
|
||||
} else if (preference instanceof LinkPreference) {
|
||||
setResult(RESULT_CODE_EXIT_SETTINGS);
|
||||
finish();
|
||||
finishChoosingTransition();
|
||||
} else if (preference instanceof FontSizePreference) {
|
||||
final FontSizePreference fontSizePref = (FontSizePreference) preference;
|
||||
fontSizePref.setSummary(fontSizePref.getSavedFontSizeName());
|
||||
|
|
|
@ -8,6 +8,7 @@ package org.mozilla.gecko.preferences;
|
|||
import org.mozilla.gecko.fxa.activities.FxAccountGetStartedActivity;
|
||||
import org.mozilla.gecko.sync.setup.SyncAccounts;
|
||||
import org.mozilla.gecko.sync.setup.activities.SetupSyncActivity;
|
||||
import org.mozilla.gecko.util.HardwareUtils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
@ -39,6 +40,11 @@ class SyncPreference extends Preference {
|
|||
private void launchFxASetup() {
|
||||
Intent intent = new Intent(mContext, FxAccountGetStartedActivity.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
|
||||
if (HardwareUtils.IS_KINDLE_DEVICE) {
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
|
||||
}
|
||||
|
||||
mContext.startActivity(intent);
|
||||
}
|
||||
|
||||
|
|
|
@ -6,45 +6,50 @@
|
|||
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:gecko="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<org.mozilla.gecko.tabspanel.TabsTray android:id="@+id/private_tabs_tray"
|
||||
style="@style/TabsList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:choiceMode="singleChoice"
|
||||
gecko:tabs="tabs_private"/>
|
||||
<FrameLayout android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout android:id="@+id/private_tabs_empty"
|
||||
style="@style/TabsPanelFrame.PrivateTabs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<org.mozilla.gecko.tabspanel.TabsTray android:id="@+id/private_tabs_tray"
|
||||
style="@style/TabsList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:choiceMode="singleChoice"
|
||||
gecko:tabs="tabs_private"/>
|
||||
|
||||
<LinearLayout style="@style/TabsPanelSection.PrivateTabs.Header"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView style="@style/TabsPanelItem.TextAppearance.Header.PrivateTabs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/private_browsing_title"/>
|
||||
|
||||
<TextView style="@style/TabsPanelItem.TextAppearance"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/private_tabs_panel_empty_desc"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout style="@style/TabsPanelSection.PrivateTabs"
|
||||
<LinearLayout android:id="@+id/private_tabs_empty"
|
||||
style="@style/TabsPanelFrame.PrivateTabs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView android:id="@+id/private_tabs_learn_more"
|
||||
style="@style/TabsPanelItem.TextAppearance.Linkified.LearnMore"
|
||||
android:layout_width="match_parent"
|
||||
android:text="@string/private_tabs_panel_learn_more"/>
|
||||
<LinearLayout style="@style/TabsPanelSection.PrivateTabs.Header"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView style="@style/TabsPanelItem.TextAppearance.Header.PrivateTabs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/private_browsing_title"/>
|
||||
|
||||
<TextView style="@style/TabsPanelItem.TextAppearance"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/private_tabs_panel_empty_desc"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout style="@style/TabsPanelSection.PrivateTabs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView android:id="@+id/private_tabs_learn_more"
|
||||
style="@style/TabsPanelItem.TextAppearance.Linkified.LearnMore"
|
||||
android:layout_width="match_parent"
|
||||
android:text="@string/private_tabs_panel_learn_more"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
|
||||
</merge>
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
</style>
|
||||
|
||||
<style name="TabsPanelSection.PrivateTabs.Header">
|
||||
<item name="android:paddingTop">18dp</item>
|
||||
<item name="android:paddingTop">10dp</item>
|
||||
</style>
|
||||
|
||||
<style name="TabsPanelItem">
|
||||
|
|
|
@ -88,7 +88,7 @@
|
|||
<item name="android:paddingRight">6dp</item>
|
||||
<item name="android:layout_marginLeft">12dp</item>
|
||||
<item name="android:layout_marginRight">12dp</item>
|
||||
<item name="android:textSize">16dp</item>
|
||||
<item name="android:textSize">16sp</item>
|
||||
</style>
|
||||
|
||||
<style name="TabsPanelItem.TextAppearance.Header.FXAccounts">
|
||||
|
|
|
@ -474,8 +474,8 @@
|
|||
|
||||
<style name="TabsPanelSectionBase">
|
||||
<item name="android:orientation">vertical</item>
|
||||
<item name="android:layout_marginLeft">16dp</item>
|
||||
<item name="android:layout_marginRight">16dp</item>
|
||||
<item name="android:layout_marginLeft">40dp</item>
|
||||
<item name="android:layout_marginRight">40dp</item>
|
||||
</style>
|
||||
|
||||
<style name="TabsPanelSection" parent="TabsPanelSectionBase">
|
||||
|
@ -503,7 +503,7 @@
|
|||
<style name="TabsPanelItem.ButtonBase">
|
||||
<item name="android:background">@drawable/remote_tabs_setup_button_background</item>
|
||||
<item name="android:textColor">#FFFEFF</item>
|
||||
<item name="android:textSize">20sp</item>
|
||||
<item name="android:textSize">16sp</item>
|
||||
<item name="android:gravity">center</item>
|
||||
</style>
|
||||
|
||||
|
@ -512,17 +512,17 @@
|
|||
<item name="android:paddingBottom">18dp</item>
|
||||
<item name="android:paddingLeft">9dp</item>
|
||||
<item name="android:paddingRight">9dp</item>
|
||||
<item name="android:layout_marginLeft">24dp</item>
|
||||
<item name="android:layout_marginRight">24dp</item>
|
||||
</style>
|
||||
|
||||
<style name="TabsPanelItem.TextAppearance">
|
||||
<item name="android:textColor">#C0C9D0</item>
|
||||
<item name="android:textSize">16sp</item>
|
||||
<item name="android:textSize">14sp</item>
|
||||
<item name="android:lineSpacingMultiplier">1.35</item>
|
||||
</style>
|
||||
|
||||
<style name="TabsPanelItem.TextAppearance.Header">
|
||||
<item name="android:textSize">20sp</item>
|
||||
<item name="android:textSize">18sp</item>
|
||||
<item name="android:layout_marginBottom">16dp</item>
|
||||
</style>
|
||||
|
||||
<style name="TabsPanelItem.TextAppearance.Header.FXAccounts">
|
||||
|
|
|
@ -17,6 +17,7 @@ import android.util.AttributeSet;
|
|||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ScrollView;
|
||||
|
||||
/**
|
||||
* A container that wraps the private tabs {@link android.widget.AdapterView} and empty
|
||||
|
@ -24,7 +25,7 @@ import android.widget.FrameLayout;
|
|||
* this container as calling {@link android.widget.AdapterView#setVisibility} does not affect the
|
||||
* empty View's visibility.
|
||||
*/
|
||||
class PrivateTabsPanel extends FrameLayout implements CloseAllPanelView {
|
||||
class PrivateTabsPanel extends ScrollView implements CloseAllPanelView {
|
||||
private TabsPanel tabsPanel;
|
||||
private TabsTray tabsTray;
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ class RemoteTabsList extends ExpandableListView
|
|||
private ArrayList <ArrayList <HashMap <String, String>>> tabsList;
|
||||
|
||||
// A list of the clients that are currently expanded.
|
||||
private List<String> expandedClientList = new ArrayList<String>();
|
||||
private List<String> expandedClientList;
|
||||
|
||||
// The client that previously had an item selected is used to restore the scroll position.
|
||||
private String clientScrollPosition;
|
||||
|
@ -143,17 +143,26 @@ class RemoteTabsList extends ExpandableListView
|
|||
TAB_KEY,
|
||||
TAB_RESOURCE));
|
||||
|
||||
// Expand the client groups, and restore the previous scroll position.
|
||||
// Either set the initial UI state, or restore it.
|
||||
List<String> newExpandedClientList = new ArrayList<String>();
|
||||
for (int i = 0; i < clients.size(); i++) {
|
||||
final String clientGuid = clients.get(i).get("guid");
|
||||
if (expandedClientList.contains(clientGuid)) {
|
||||
|
||||
if (expandedClientList == null) {
|
||||
// On initial entry we expand all clients by default.
|
||||
newExpandedClientList.add(clientGuid);
|
||||
expandGroup(i);
|
||||
}
|
||||
} else {
|
||||
// On subsequent entries, we expand clients based on their previous UI state.
|
||||
if (expandedClientList.contains(clientGuid)) {
|
||||
newExpandedClientList.add(clientGuid);
|
||||
expandGroup(i);
|
||||
}
|
||||
|
||||
if (clientGuid.equals(clientScrollPosition)) {
|
||||
setSelectedGroup(i);
|
||||
// Additionally we reset the UI scroll position.
|
||||
if (clientGuid.equals(clientScrollPosition)) {
|
||||
setSelectedGroup(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
expandedClientList = newExpandedClientList;
|
||||
|
|
|
@ -30,6 +30,10 @@ public final class HardwareUtils {
|
|||
// Number of bytes of /proc/meminfo to read in one go.
|
||||
private static final int MEMINFO_BUFFER_SIZE_BYTES = 256;
|
||||
|
||||
private static final boolean IS_AMAZON_DEVICE = Build.MANUFACTURER.equalsIgnoreCase("Amazon");
|
||||
public static final boolean IS_KINDLE_DEVICE = IS_AMAZON_DEVICE &&
|
||||
(Build.MODEL.equals("Kindle Fire") ||
|
||||
Build.MODEL.startsWith("KF"));
|
||||
private static volatile int sTotalRAM = -1;
|
||||
|
||||
private static volatile boolean sInited;
|
||||
|
|
|
@ -1020,9 +1020,6 @@ var BrowserApp = {
|
|||
evt.initUIEvent("TabClose", true, false, window, tabIndex);
|
||||
aTab.browser.dispatchEvent(evt);
|
||||
|
||||
aTab.destroy();
|
||||
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);
|
||||
|
@ -1043,11 +1040,14 @@ var BrowserApp = {
|
|||
label: Strings.browser.GetStringFromName("undoCloseToast.action2"),
|
||||
callback: function() {
|
||||
UITelemetry.addEvent("undo.1", "toast", null, "closetab");
|
||||
ss.undoCloseTab(window, 0);
|
||||
ss.undoCloseTab(window, closedTabData);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
aTab.destroy();
|
||||
this._tabs.splice(tabIndex, 1);
|
||||
},
|
||||
|
||||
// Use this method to select a tab from JS. This method sends a message
|
||||
|
|
|
@ -14,7 +14,7 @@ interface nsIDOMNode;
|
|||
* tabs contained in them.
|
||||
*/
|
||||
|
||||
[scriptable, uuid(91eca9cf-6741-4c8f-a3a0-2e957240894d)]
|
||||
[scriptable, uuid(5497d9a1-c378-47a9-9488-4c47a644131a)]
|
||||
interface nsISessionStore : nsISupports
|
||||
{
|
||||
/**
|
||||
|
@ -38,10 +38,10 @@ interface nsISessionStore : nsISupports
|
|||
|
||||
/**
|
||||
* @param aWindow is the browser window to reopen a closed tab in.
|
||||
* @param aIndex is the index of the tab to be restored (FIFO ordered).
|
||||
* @param aCloseTabData is the data of the tab to be restored.
|
||||
* @returns a reference to the reopened tab.
|
||||
*/
|
||||
nsIDOMNode undoCloseTab(in nsIDOMWindow aWindow, in unsigned long aIndex);
|
||||
nsIDOMNode undoCloseTab(in nsIDOMWindow aWindow, in jsval aCloseTabData);
|
||||
|
||||
/**
|
||||
* @param aWindow is the browser window associated with the closed tab.
|
||||
|
|
|
@ -18,6 +18,7 @@ XPCOMUtils.defineLazyServiceGetter(this, "CrashReporter",
|
|||
XPCOMUtils.defineLazyModuleGetter(this, "Task", "resource://gre/modules/Task.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "sendMessageToJava", "resource://gre/modules/Messaging.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils", "resource://gre/modules/PrivateBrowsingUtils.jsm");
|
||||
|
||||
function dump(a) {
|
||||
Services.console.logStringMessage(a);
|
||||
|
@ -84,6 +85,7 @@ SessionStore.prototype = {
|
|||
observerService.addObserver(this, "application-background", true);
|
||||
observerService.addObserver(this, "ClosedTabs:StartNotifications", true);
|
||||
observerService.addObserver(this, "ClosedTabs:StopNotifications", true);
|
||||
observerService.addObserver(this, "last-pb-context-exited", true);
|
||||
break;
|
||||
case "final-ui-startup":
|
||||
observerService.removeObserver(this, "final-ui-startup");
|
||||
|
@ -169,6 +171,13 @@ SessionStore.prototype = {
|
|||
case "ClosedTabs:StopNotifications":
|
||||
this._notifyClosedTabs = false;
|
||||
break;
|
||||
case "last-pb-context-exited":
|
||||
// Clear private closed tab data when we leave private browsing.
|
||||
for (let [, window] in Iterator(this._windows)) {
|
||||
window.closedTabs = window.closedTabs.filter(tab => !tab.isPrivate);
|
||||
}
|
||||
this._lastClosedTabIndex = -1;
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -365,6 +374,13 @@ SessionStore.prototype = {
|
|||
|
||||
this.saveStateDelayed();
|
||||
this._updateCrashReportURL(aWindow);
|
||||
|
||||
// If the selected tab has changed while listening for closed tab
|
||||
// notifications, we may have switched between different private browsing
|
||||
// modes.
|
||||
if (this._notifyClosedTabs) {
|
||||
this._sendClosedTabsToJava(aWindow);
|
||||
}
|
||||
},
|
||||
|
||||
saveStateDelayed: function ss_saveStateDelayed() {
|
||||
|
@ -849,7 +865,7 @@ SessionStore.prototype = {
|
|||
return this._windows[aWindow.__SSID].closedTabs;
|
||||
},
|
||||
|
||||
undoCloseTab: function ss_undoCloseTab(aWindow, aIndex) {
|
||||
undoCloseTab: function ss_undoCloseTab(aWindow, aCloseTabData) {
|
||||
if (!aWindow.__SSID)
|
||||
throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
|
||||
|
||||
|
@ -857,28 +873,28 @@ SessionStore.prototype = {
|
|||
if (!closedTabs)
|
||||
return null;
|
||||
|
||||
// default to the most-recently closed tab
|
||||
aIndex = aIndex || 0;
|
||||
if (!(aIndex in closedTabs))
|
||||
throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
|
||||
|
||||
// fetch the data of closed tab, while removing it from the array
|
||||
let closedTab = closedTabs.splice(aIndex, 1).shift();
|
||||
// If the tab data is in the closedTabs array, remove it.
|
||||
closedTabs.find(function (tabData, i) {
|
||||
if (tabData == aCloseTabData) {
|
||||
closedTabs.splice(i, 1);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// create a new tab and bring to front
|
||||
let params = {
|
||||
selected: true,
|
||||
isPrivate: closedTab.isPrivate,
|
||||
desktopMode: closedTab.desktopMode,
|
||||
isPrivate: aCloseTabData.isPrivate,
|
||||
desktopMode: aCloseTabData.desktopMode,
|
||||
tabIndex: this._lastClosedTabIndex
|
||||
};
|
||||
let tab = aWindow.BrowserApp.addTab(closedTab.entries[closedTab.index - 1].url, params);
|
||||
this._restoreHistory(closedTab, tab.browser.sessionHistory);
|
||||
let tab = aWindow.BrowserApp.addTab(aCloseTabData.entries[aCloseTabData.index - 1].url, params);
|
||||
this._restoreHistory(aCloseTabData, tab.browser.sessionHistory);
|
||||
|
||||
this._lastClosedTabIndex = -1;
|
||||
|
||||
// Put back the extra data
|
||||
tab.browser.__SS_extdata = closedTab.extData;
|
||||
tab.browser.__SS_extdata = aCloseTabData.extData;
|
||||
|
||||
if (this._notifyClosedTabs) {
|
||||
this._sendClosedTabsToJava(aWindow);
|
||||
|
@ -915,9 +931,10 @@ SessionStore.prototype = {
|
|||
throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
|
||||
|
||||
let closedTabs = this._windows[aWindow.__SSID].closedTabs;
|
||||
let isPrivate = PrivateBrowsingUtils.isWindowPrivate(aWindow.BrowserApp.selectedBrowser.contentWindow);
|
||||
|
||||
let tabs = closedTabs
|
||||
.filter(tab => !tab.isPrivate)
|
||||
.filter(tab => tab.isPrivate == isPrivate)
|
||||
.map(function (tab) {
|
||||
// Get the url and title for the last entry in the session history.
|
||||
let lastEntry = tab.entries[tab.entries.length - 1];
|
||||
|
|
|
@ -541,7 +541,7 @@
|
|||
<xul:menuitem label="&selectAllCmd.label;" accesskey="&selectAllCmd.accesskey;" cmd="cmd_selectAll"/>
|
||||
<xul:menuseparator anonid="spell-check-separator"/>
|
||||
<xul:menuitem label="&spellCheckToggle.label;" type="checkbox" accesskey="&spellCheckToggle.accesskey;" anonid="spell-check-enabled"
|
||||
oncommand="this.parentNode.parentNode.spellCheckerUI.toggleEnabled();"/>
|
||||
oncommand="this.parentNode.parentNode.spellCheckerUI.toggleEnabled(window);"/>
|
||||
<xul:menu label="&spellDictionaries.label;" accesskey="&spellDictionaries.accesskey;" anonid="spell-dictionaries">
|
||||
<xul:menupopup anonid="spell-dictionaries-menu"
|
||||
onpopupshowing="event.stopPropagation();"
|
||||
|
|
|
@ -1107,9 +1107,7 @@ let Front = Class({
|
|||
|
||||
let deferred = this._requests.shift();
|
||||
if (packet.error) {
|
||||
let message = (packet.error == "unknownError" && packet.message) ?
|
||||
packet.message : packet.error;
|
||||
deferred.reject(message);
|
||||
deferred.reject(packet.error);
|
||||
} else {
|
||||
deferred.resolve(packet);
|
||||
}
|
||||
|
|
|
@ -191,7 +191,7 @@ InlineSpellChecker.prototype = {
|
|||
if (curlang == sortedList[i].id) {
|
||||
item.setAttribute("checked", "true");
|
||||
} else {
|
||||
var callback = function(me, val) { return function(evt) { me.selectDictionary(val); } };
|
||||
var callback = function(me, val) { return function(evt) { me.selectDictionary(val, me.menu.ownerDocument.defaultView); } };
|
||||
item.addEventListener("command", callback(this, i), true);
|
||||
}
|
||||
if (insertBefore)
|
||||
|
@ -272,8 +272,20 @@ InlineSpellChecker.prototype = {
|
|||
},
|
||||
|
||||
// callback for selecting a dictionary
|
||||
selectDictionary: function(index)
|
||||
selectDictionary: function(index, aWindow)
|
||||
{
|
||||
// Avoid a crash in multiprocess until Bug 1030451 lands
|
||||
// Remove aWindow parameter at that time
|
||||
const Ci = Components.interfaces;
|
||||
let chromeFlags = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).
|
||||
getInterface(Ci.nsIWebNavigation).
|
||||
QueryInterface(Ci.nsIDocShellTreeItem).treeOwner.
|
||||
QueryInterface(Ci.nsIInterfaceRequestor).
|
||||
getInterface(Ci.nsIXULWindow).chromeFlags;
|
||||
let chromeRemoteWindow = Ci.nsIWebBrowserChrome.CHROME_REMOTE_WINDOW;
|
||||
if (chromeFlags & chromeRemoteWindow) {
|
||||
return;
|
||||
}
|
||||
if (! this.mInlineSpellChecker || index < 0 || index >= this.mDictionaryNames.length)
|
||||
return;
|
||||
var spellchecker = this.mInlineSpellChecker.spellChecker;
|
||||
|
@ -293,8 +305,20 @@ InlineSpellChecker.prototype = {
|
|||
},
|
||||
|
||||
// callback for enabling or disabling spellchecking
|
||||
toggleEnabled: function()
|
||||
toggleEnabled: function(aWindow)
|
||||
{
|
||||
// Avoid a crash in multiprocess until Bug 1030451 lands
|
||||
// Remove aWindow parameter at that time
|
||||
const Ci = Components.interfaces;
|
||||
let chromeFlags = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).
|
||||
getInterface(Ci.nsIWebNavigation).
|
||||
QueryInterface(Ci.nsIDocShellTreeItem).treeOwner.
|
||||
QueryInterface(Ci.nsIInterfaceRequestor).
|
||||
getInterface(Ci.nsIXULWindow).chromeFlags;
|
||||
let chromeRemoteWindow = Ci.nsIWebBrowserChrome.CHROME_REMOTE_WINDOW;
|
||||
if (chromeFlags & chromeRemoteWindow) {
|
||||
return;
|
||||
}
|
||||
this.mEditor.setSpellcheckUserOverride(!this.mInlineSpellChecker.enableRealTimeSpell);
|
||||
},
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче