зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to mozilla-inbound
This commit is contained in:
Коммит
e40acbfc01
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="5a7f87b1505ba89b586372cbbbe9507d1016c40c"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="208ec11c33c9bd2fab7fcf8647bc162c9f3d484f"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="fffc68521ebb1501d6b015c6d1c4a17a04fdb2e2"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="5a7f87b1505ba89b586372cbbbe9507d1016c40c"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="208ec11c33c9bd2fab7fcf8647bc162c9f3d484f"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="fffc68521ebb1501d6b015c6d1c4a17a04fdb2e2"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<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="5a7f87b1505ba89b586372cbbbe9507d1016c40c"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="208ec11c33c9bd2fab7fcf8647bc162c9f3d484f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="fffc68521ebb1501d6b015c6d1c4a17a04fdb2e2"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="22664edc4c73e5fe8f5095ff1d5549db78a2bc10"/>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="5a7f87b1505ba89b586372cbbbe9507d1016c40c"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="208ec11c33c9bd2fab7fcf8647bc162c9f3d484f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="fffc68521ebb1501d6b015c6d1c4a17a04fdb2e2"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="deb1d684da56376a10d900e08d3f4139f3ace885"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="5a7f87b1505ba89b586372cbbbe9507d1016c40c"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="208ec11c33c9bd2fab7fcf8647bc162c9f3d484f"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="fffc68521ebb1501d6b015c6d1c4a17a04fdb2e2"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
@ -131,7 +131,7 @@
|
|||
<project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="f390788a00706c06e5248edfd8d27b365387e84a"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="fbd2becab3825c49e756db5149552f85049c66e2"/>
|
||||
<project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="f37bd545063039e30a92f2550ae78c0e6e4e2d08"/>
|
||||
<project name="platform_external_wpa_supplicant_8" path="external/wpa_supplicant_8" remote="b2g" revision="9a725b0f5aa715282f8854ac56e5b63efc9cd077"/>
|
||||
<project name="platform_external_wpa_supplicant_8" path="external/wpa_supplicant_8" remote="b2g" revision="0c6a6547cd1fd302fa2b0f6e375654df36bf0ec4"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="42f61f665e7a9857da8cd14b455e15bae98e6b44"/>
|
||||
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="d82e00fb6380b4f6cea7a96213913ee9eb441239"/>
|
||||
<project name="platform/development" path="development" revision="5968ff4e13e0d696ad8d972281fc27ae5a12829b"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="61e82f99bb8bc78d52b5717e9a2481ec7267fa33">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="5a7f87b1505ba89b586372cbbbe9507d1016c40c"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="208ec11c33c9bd2fab7fcf8647bc162c9f3d484f"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="fffc68521ebb1501d6b015c6d1c4a17a04fdb2e2"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
@ -144,7 +144,7 @@
|
|||
<project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="48132ec0b0dfe9fc29c7c3f0e799066be8999198"/>
|
||||
<!-- external/qemu for emulator-l need to be updated in bug-1121378 -->
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="7639c5c496ffd207bb627f2a59b2c5203ae6fefc"/>
|
||||
<project name="platform_external_wpa_supplicant_8" path="external/wpa_supplicant_8" remote="b2g" revision="326450198720862ade868801cc2f43597f3c1fd8"/>
|
||||
<project name="platform_external_wpa_supplicant_8" path="external/wpa_supplicant_8" remote="b2g" revision="cbda29a58abc4ea1f7f4611fe354ab67b606219d"/>
|
||||
<project name="platform/development" path="development" revision="0c51f6e0aa2ee57fcb75ec3b2ff6bf754cece63e"/>
|
||||
<project name="android-sdk" path="sdk" remote="b2g" revision="ff4190dc603f62a7caa48342aa268acf99863c5c"/>
|
||||
<!-- hardware-ril for emulator-l need to be updated in bug-1113054 -->
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<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="5a7f87b1505ba89b586372cbbbe9507d1016c40c"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="208ec11c33c9bd2fab7fcf8647bc162c9f3d484f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="fffc68521ebb1501d6b015c6d1c4a17a04fdb2e2"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="22664edc4c73e5fe8f5095ff1d5549db78a2bc10"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="5a7f87b1505ba89b586372cbbbe9507d1016c40c"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="208ec11c33c9bd2fab7fcf8647bc162c9f3d484f"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="fffc68521ebb1501d6b015c6d1c4a17a04fdb2e2"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"git": {
|
||||
"git_revision": "5a7f87b1505ba89b586372cbbbe9507d1016c40c",
|
||||
"git_revision": "208ec11c33c9bd2fab7fcf8647bc162c9f3d484f",
|
||||
"remote": "https://git.mozilla.org/releases/gaia.git",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "85b4fb5a2fd0040b3726a4f1e4218f2dbfd4edce",
|
||||
"revision": "2c0b8b5fdcd8bf1f06b25d58fe941a29dc26a2f3",
|
||||
"repo_path": "integration/gaia-central"
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="5a7f87b1505ba89b586372cbbbe9507d1016c40c"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="208ec11c33c9bd2fab7fcf8647bc162c9f3d484f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="fffc68521ebb1501d6b015c6d1c4a17a04fdb2e2"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="deb1d684da56376a10d900e08d3f4139f3ace885"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="61e82f99bb8bc78d52b5717e9a2481ec7267fa33">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="5a7f87b1505ba89b586372cbbbe9507d1016c40c"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="208ec11c33c9bd2fab7fcf8647bc162c9f3d484f"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="fffc68521ebb1501d6b015c6d1c4a17a04fdb2e2"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
|
|
@ -1446,7 +1446,9 @@ pref("devtools.performance.enabled", true);
|
|||
|
||||
// The default Performance UI settings
|
||||
pref("devtools.performance.memory.sample-probability", "0.05");
|
||||
pref("devtools.performance.memory.max-log-length", 2147483647); // Math.pow(2,31) - 1
|
||||
// Can't go higher than this without causing internal allocation overflows while
|
||||
// serializing the allocations data over the RDP.
|
||||
pref("devtools.performance.memory.max-log-length", 125000);
|
||||
pref("devtools.performance.timeline.hidden-markers", "[]");
|
||||
pref("devtools.performance.profiler.buffer-size", 10000000);
|
||||
pref("devtools.performance.profiler.sample-frequency-khz", 1);
|
||||
|
|
|
@ -563,6 +563,16 @@ var FullScreen = {
|
|||
document.documentElement.setAttribute("inFullscreen", true);
|
||||
}
|
||||
|
||||
ToolbarIconColor.inferFromText();
|
||||
|
||||
// For Lion fullscreen, all fullscreen controls are hidden, don't
|
||||
// bother to touch them. If we don't stop here, the following code
|
||||
// could cause the native fullscreen button be shown unexpectedly.
|
||||
// See bug 1165570.
|
||||
if (this.useLionFullScreen) {
|
||||
return;
|
||||
}
|
||||
|
||||
var fullscreenctls = document.getElementById("window-controls");
|
||||
var navbar = document.getElementById("nav-bar");
|
||||
var ctlsOnTabbar = window.toolbar.visible;
|
||||
|
@ -575,8 +585,6 @@ var FullScreen = {
|
|||
navbar.appendChild(fullscreenctls);
|
||||
}
|
||||
fullscreenctls.hidden = aShow;
|
||||
|
||||
ToolbarIconColor.inferFromText();
|
||||
}
|
||||
};
|
||||
XPCOMUtils.defineLazyGetter(FullScreen, "useLionFullScreen", function() {
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -116,6 +116,26 @@ Site.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
_newTabString: function(str, substrArr) {
|
||||
let regExp = /%[0-9]\$S/g;
|
||||
let matches;
|
||||
while (matches = regExp.exec(str)) {
|
||||
let match = matches[0];
|
||||
let index = match.charAt(1); // Get the digit in the regExp.
|
||||
str = str.replace(match, substrArr[index - 1]);
|
||||
}
|
||||
return str;
|
||||
},
|
||||
|
||||
_getSuggestedTileExplanation: function() {
|
||||
let targetedName = `<strong> ${this.link.targetedName} </strong>`;
|
||||
let targetedSite = `<strong> ${this.link.targetedSite} </strong>`;
|
||||
if (this.link.explanation) {
|
||||
return this._newTabString(this.link.explanation, [targetedName, targetedSite]);
|
||||
}
|
||||
return newTabString("suggested.button", [targetedName]);
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders the site's data (fills the HTML fragment).
|
||||
*/
|
||||
|
@ -140,9 +160,9 @@ Site.prototype = {
|
|||
}
|
||||
|
||||
this.node.setAttribute("suggested", true);
|
||||
let targetedSite = `<strong> ${this.link.targetedName} </strong>`;
|
||||
let explanation = this._getSuggestedTileExplanation();
|
||||
this._querySelector(".newtab-suggested").innerHTML =
|
||||
`<div class='newtab-suggested-bounds'> ${newTabString("suggested.button", [targetedSite])} </div>`;
|
||||
`<div class='newtab-suggested-bounds'> ${explanation} </div>`;
|
||||
}
|
||||
|
||||
if (this.isPinned())
|
||||
|
|
|
@ -3,6 +3,14 @@
|
|||
|
||||
const PRELOAD_PREF = "browser.newtab.preload";
|
||||
|
||||
let suggestedLink = {
|
||||
url: "http://example1.com/2",
|
||||
imageURI: "data:image/png;base64,helloWORLD3",
|
||||
title: "title2",
|
||||
type: "affiliate",
|
||||
frecent_sites: ["classroom.google.com", "codecademy.com", "elearning.ut.ac.id", "khanacademy.org", "learn.jquery.com", "teamtreehouse.com", "tutorialspoint.com", "udacity.com", "w3cschool.cc", "w3schools.com"]
|
||||
};
|
||||
|
||||
gDirectorySource = "data:application/json," + JSON.stringify({
|
||||
"enhanced": [{
|
||||
url: "http://example.com/",
|
||||
|
@ -16,21 +24,12 @@ gDirectorySource = "data:application/json," + JSON.stringify({
|
|||
title: "title1",
|
||||
type: "organic"
|
||||
}],
|
||||
"suggested": [{
|
||||
url: "http://example1.com/2",
|
||||
imageURI: "data:image/png;base64,helloWORLD3",
|
||||
title: "title2",
|
||||
type: "affiliate",
|
||||
frecent_sites: ["test.com"]
|
||||
}]
|
||||
"suggested": [suggestedLink]
|
||||
});
|
||||
|
||||
function runTests() {
|
||||
let origGetFrecentSitesName = DirectoryLinksProvider.getFrecentSitesName;
|
||||
DirectoryLinksProvider.getFrecentSitesName = () => "";
|
||||
let origEnhanced = NewTabUtils.allPages.enhanced;
|
||||
registerCleanupFunction(() => {
|
||||
DirectoryLinksProvider.getFrecentSitesName = origGetFrecentSitesName;
|
||||
Services.prefs.clearUserPref(PRELOAD_PREF);
|
||||
NewTabUtils.allPages.enhanced = origEnhanced;
|
||||
});
|
||||
|
@ -46,6 +45,7 @@ function runTests() {
|
|||
type: siteNode.getAttribute("type"),
|
||||
enhanced: siteNode.querySelector(".enhanced-content").style.backgroundImage,
|
||||
title: siteNode.querySelector(".newtab-title").textContent,
|
||||
suggested: siteNode.querySelector(".newtab-suggested").innerHTML
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -56,50 +56,56 @@ function runTests() {
|
|||
yield addNewTabPageTab();
|
||||
yield customizeNewTabPage("classic");
|
||||
yield customizeNewTabPage("enhanced"); // Toggle enhanced off
|
||||
let {type, enhanced, title} = getData(0);
|
||||
let {type, enhanced, title, suggested} = getData(0);
|
||||
isnot(type, "enhanced", "history link is not enhanced");
|
||||
is(enhanced, "", "history link has no enhanced image");
|
||||
is(title, "example.com");
|
||||
is(suggested, "", "There is no suggested explanation");
|
||||
|
||||
is(getData(1), null, "there is only one link and it's a history link");
|
||||
|
||||
// Test with enhanced = true
|
||||
yield addNewTabPageTab();
|
||||
yield customizeNewTabPage("enhanced"); // Toggle enhanced on
|
||||
({type, enhanced, title} = getData(0));
|
||||
({type, enhanced, title, suggested} = getData(0));
|
||||
is(type, "organic", "directory link is organic");
|
||||
isnot(enhanced, "", "directory link has enhanced image");
|
||||
is(title, "title1");
|
||||
is(suggested, "", "There is no suggested explanation");
|
||||
|
||||
({type, enhanced, title} = getData(1));
|
||||
({type, enhanced, title, suggested} = getData(1));
|
||||
is(type, "enhanced", "history link is enhanced");
|
||||
isnot(enhanced, "", "history link has enhanced image");
|
||||
is(title, "title");
|
||||
is(suggested, "", "There is no suggested explanation");
|
||||
|
||||
is(getData(2), null, "there are only 2 links, directory and enhanced history");
|
||||
|
||||
// Test with a pinned link
|
||||
setPinnedLinks("-1");
|
||||
yield addNewTabPageTab();
|
||||
({type, enhanced, title} = getData(0));
|
||||
({type, enhanced, title, suggested} = getData(0));
|
||||
is(type, "enhanced", "pinned history link is enhanced");
|
||||
isnot(enhanced, "", "pinned history link has enhanced image");
|
||||
is(title, "title");
|
||||
is(suggested, "", "There is no suggested explanation");
|
||||
|
||||
({type, enhanced, title} = getData(1));
|
||||
({type, enhanced, title, suggested} = getData(1));
|
||||
is(type, "organic", "directory link is organic");
|
||||
isnot(enhanced, "", "directory link has enhanced image");
|
||||
is(title, "title1");
|
||||
is(suggested, "", "There is no suggested explanation");
|
||||
|
||||
is(getData(2), null, "directory link pushed out by pinned history link");
|
||||
|
||||
// Test pinned link with enhanced = false
|
||||
yield addNewTabPageTab();
|
||||
yield customizeNewTabPage("enhanced"); // Toggle enhanced off
|
||||
({type, enhanced, title} = getData(0));
|
||||
({type, enhanced, title, suggested} = getData(0));
|
||||
isnot(type, "enhanced", "history link is not enhanced");
|
||||
is(enhanced, "", "history link has no enhanced image");
|
||||
is(title, "example.com");
|
||||
is(suggested, "", "There is no suggested explanation");
|
||||
|
||||
is(getData(1), null, "directory link still pushed out by pinned history link");
|
||||
|
||||
|
@ -117,10 +123,11 @@ function runTests() {
|
|||
|
||||
// Test with enhanced = false
|
||||
yield addNewTabPageTab();
|
||||
({type, enhanced, title} = getData(0));
|
||||
({type, enhanced, title, suggested} = getData(0));
|
||||
isnot(type, "enhanced", "history link is not enhanced");
|
||||
is(enhanced, "", "history link has no enhanced image");
|
||||
is(title, "example.com");
|
||||
is(suggested, "", "There is no suggested explanation");
|
||||
|
||||
isnot(getData(7), null, "there are 8 history links");
|
||||
is(getData(8), null, "there are 8 history links");
|
||||
|
@ -131,16 +138,52 @@ function runTests() {
|
|||
yield customizeNewTabPage("enhanced");
|
||||
|
||||
// Suggested link was not enhanced by directory link with same domain
|
||||
({type, enhanced, title} = getData(0));
|
||||
({type, enhanced, title, suggested} = getData(0));
|
||||
is(type, "affiliate", "suggested link is affiliate");
|
||||
is(enhanced, "", "suggested link has no enhanced image");
|
||||
is(title, "title2");
|
||||
ok(suggested.indexOf("Suggested for <strong> Web Education </strong> visitors") > -1, "Suggested for 'Web Education'");
|
||||
|
||||
// Enhanced history link shows up second
|
||||
({type, enhanced, title} = getData(1));
|
||||
({type, enhanced, title, suggested} = getData(1));
|
||||
is(type, "enhanced", "pinned history link is enhanced");
|
||||
isnot(enhanced, "", "pinned history link has enhanced image");
|
||||
is(title, "title");
|
||||
is(suggested, "", "There is no suggested explanation");
|
||||
|
||||
is(getData(9), null, "there is a suggested link followed by an enhanced history link and the remaining history links");
|
||||
|
||||
|
||||
|
||||
// Test override category/adgroup name.
|
||||
suggestedLink.adgroup_name = "Technology";
|
||||
Services.prefs.setCharPref(PREF_NEWTAB_DIRECTORYSOURCE,
|
||||
"data:application/json," + JSON.stringify({"suggested": [suggestedLink]}));
|
||||
|
||||
yield addNewTabPageTab();
|
||||
({type, enhanced, title, suggested} = getData(0));
|
||||
Cu.reportError("SUGGEST " + suggested);
|
||||
ok(suggested.indexOf("Suggested for <strong> Technology </strong> visitors") > -1, "Suggested for 'Technology'");
|
||||
|
||||
|
||||
// Test server provided explanation string.
|
||||
suggestedLink.explanation = "Suggested for %1$S enthusiasts who visit sites like %2$S";
|
||||
Services.prefs.setCharPref(PREF_NEWTAB_DIRECTORYSOURCE,
|
||||
"data:application/json," + encodeURIComponent(JSON.stringify({"suggested": [suggestedLink]})));
|
||||
|
||||
yield addNewTabPageTab();
|
||||
({type, enhanced, title, suggested} = getData(0));
|
||||
Cu.reportError("SUGGEST " + suggested);
|
||||
ok(suggested.indexOf("Suggested for <strong> Technology </strong> enthusiasts who visit sites like <strong> classroom.google.com </strong>") > -1, "Suggested for 'Technology' enthusiasts");
|
||||
|
||||
|
||||
// Test server provided explanation string without category override.
|
||||
delete suggestedLink.adgroup_name;
|
||||
Services.prefs.setCharPref(PREF_NEWTAB_DIRECTORYSOURCE,
|
||||
"data:application/json," + encodeURIComponent(JSON.stringify({"suggested": [suggestedLink]})));
|
||||
|
||||
yield addNewTabPageTab();
|
||||
({type, enhanced, title, suggested} = getData(0));
|
||||
Cu.reportError("SUGGEST " + suggested);
|
||||
ok(suggested.indexOf("Suggested for <strong> Web Education </strong> enthusiasts who visit sites like <strong> classroom.google.com </strong>") > -1, "Suggested for 'Web Education' enthusiasts");
|
||||
}
|
||||
|
|
|
@ -110,6 +110,7 @@ browser.jar:
|
|||
content/browser/newtab/newTab.xul (content/newtab/newTab.xul)
|
||||
* content/browser/newtab/newTab.js (content/newtab/newTab.js)
|
||||
content/browser/newtab/newTab.css (content/newtab/newTab.css)
|
||||
content/browser/newtab/newTab.inadjacent.json (content/newtab/newTab.inadjacent.json)
|
||||
* content/browser/pageinfo/pageInfo.xul (content/pageinfo/pageInfo.xul)
|
||||
content/browser/pageinfo/pageInfo.js (content/pageinfo/pageInfo.js)
|
||||
content/browser/pageinfo/pageInfo.css (content/pageinfo/pageInfo.css)
|
||||
|
|
|
@ -282,12 +282,9 @@ loop.shared.mixins = (function() {
|
|||
* elements and handling updates of the media containers.
|
||||
*/
|
||||
var MediaSetupMixin = {
|
||||
_videoDimensionsCache: {
|
||||
local: {},
|
||||
remote: {}
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.resetDimensionsCache();
|
||||
rootObject.addEventListener("orientationchange", this.updateVideoContainer);
|
||||
rootObject.addEventListener("resize", this.updateVideoContainer);
|
||||
},
|
||||
|
|
|
@ -487,6 +487,15 @@ describe("loop.shared.mixins", function() {
|
|||
expect(view._videoDimensionsCache.remote.camera.aspectRatio.height)
|
||||
.eql(0.32857142857142857);
|
||||
});
|
||||
|
||||
it("should not populate the cache on another component instance", function() {
|
||||
var view2 =
|
||||
TestUtils.renderIntoDocument(React.createElement(TestComp));
|
||||
|
||||
expect(view2._videoDimensionsCache.local).to.be.empty;
|
||||
expect(view2._videoDimensionsCache.remote).to.be.empty;
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const TESTCASE_URI = TEST_BASE + "color-block.html";
|
||||
const TESTCASE_URI = CHROME_URL_ROOT + "color-block.html";
|
||||
const DIV_COLOR = "#0000FF";
|
||||
|
||||
/**
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
// Tests that the eyedropper command works
|
||||
|
||||
const TESTCASE_URI = TEST_BASE + "color-block.html";
|
||||
const TESTCASE_URI = CHROME_URL_ROOT + "color-block.html";
|
||||
const DIV_COLOR = "#0000FF";
|
||||
|
||||
function test() {
|
||||
|
|
|
@ -1,42 +1,11 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const TEST_BASE = "chrome://mochitests/content/browser/browser/devtools/eyedropper/test/";
|
||||
const TEST_HOST = 'mochi.test:8888';
|
||||
// shared-head.js handles imports, constants, and utility functions
|
||||
Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/browser/devtools/framework/test/shared-head.js", this);
|
||||
Services.scriptloader.loadSubScript(TEST_DIR + "../../../commandline/test/helpers.js", this);
|
||||
|
||||
let { devtools } = Components.utils.import("resource://gre/modules/devtools/Loader.jsm", {});
|
||||
const { Eyedropper, EyedropperManager } = devtools.require("devtools/eyedropper/eyedropper");
|
||||
const { Promise: promise } = devtools.require("resource://gre/modules/Promise.jsm");
|
||||
|
||||
let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
|
||||
Services.scriptloader.loadSubScript(testDir + "../../../commandline/test/helpers.js", this);
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
function cleanup()
|
||||
{
|
||||
while (gBrowser.tabs.length > 1) {
|
||||
gBrowser.removeCurrentTab();
|
||||
}
|
||||
}
|
||||
|
||||
registerCleanupFunction(cleanup);
|
||||
|
||||
function addTab(uri) {
|
||||
let deferred = promise.defer();
|
||||
|
||||
let tab = gBrowser.addTab();
|
||||
|
||||
gBrowser.selectedTab = tab;
|
||||
gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
|
||||
deferred.resolve(tab);
|
||||
}, true);
|
||||
|
||||
content.location = uri;
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function waitForClipboard(setup, expected) {
|
||||
let deferred = promise.defer();
|
||||
|
|
|
@ -8,6 +8,7 @@ support-files =
|
|||
browser_toolbox_sidebar_tool.xul
|
||||
code_math.js
|
||||
head.js
|
||||
shared-head.js
|
||||
helper_disable_cache.js
|
||||
doc_theme.css
|
||||
doc_viewsource.html
|
||||
|
|
|
@ -1,145 +1,8 @@
|
|||
/* 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/. */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
let TargetFactory = gDevTools.TargetFactory;
|
||||
|
||||
const { console } = Cu.import("resource://gre/modules/devtools/Console.jsm", {});
|
||||
const { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
|
||||
const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
|
||||
const { ScratchpadManager } = Cu.import("resource:///modules/devtools/scratchpad-manager.jsm", {});
|
||||
|
||||
const URL_ROOT = "http://example.com/browser/browser/devtools/framework/test/";
|
||||
const CHROME_URL_ROOT = "chrome://mochitests/content/browser/browser/devtools/framework/test/";
|
||||
|
||||
let TargetFactory = devtools.TargetFactory;
|
||||
|
||||
// All test are asynchronous
|
||||
waitForExplicitFinish();
|
||||
|
||||
// Uncomment this pref to dump all devtools emitted events to the console.
|
||||
// Services.prefs.setBoolPref("devtools.dump.emit", true);
|
||||
|
||||
function getFrameScript() {
|
||||
let mm = gBrowser.selectedBrowser.messageManager;
|
||||
let frameURL = "chrome://browser/content/devtools/frame-script-utils.js";
|
||||
mm.loadFrameScript(frameURL, false);
|
||||
SimpleTest.registerCleanupFunction(() => {
|
||||
mm = null;
|
||||
});
|
||||
return mm;
|
||||
}
|
||||
|
||||
gDevTools.testing = true;
|
||||
SimpleTest.registerCleanupFunction(() => {
|
||||
gDevTools.testing = false;
|
||||
Services.prefs.clearUserPref("devtools.dump.emit");
|
||||
});
|
||||
|
||||
/**
|
||||
* Add a new test tab in the browser and load the given url.
|
||||
* @param {String} url The url to be loaded in the new tab
|
||||
* @return a promise that resolves to the tab object when the url is loaded
|
||||
*/
|
||||
function addTab(url) {
|
||||
info("Adding a new tab with URL: '" + url + "'");
|
||||
let def = promise.defer();
|
||||
|
||||
let tab = gBrowser.selectedTab = gBrowser.addTab();
|
||||
gBrowser.selectedBrowser.addEventListener("load", function onload() {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", onload, true);
|
||||
info("URL '" + url + "' loading complete");
|
||||
def.resolve(tab);
|
||||
}, true);
|
||||
content.location = url;
|
||||
|
||||
return def.promise;
|
||||
}
|
||||
|
||||
registerCleanupFunction(function tearDown() {
|
||||
while (gBrowser.tabs.length > 1) {
|
||||
gBrowser.removeCurrentTab();
|
||||
}
|
||||
});
|
||||
|
||||
function synthesizeKeyFromKeyTag(aKeyId, document) {
|
||||
let key = document.getElementById(aKeyId);
|
||||
isnot(key, null, "Successfully retrieved the <key> node");
|
||||
|
||||
let modifiersAttr = key.getAttribute("modifiers");
|
||||
|
||||
let name = null;
|
||||
|
||||
if (key.getAttribute("keycode"))
|
||||
name = key.getAttribute("keycode");
|
||||
else if (key.getAttribute("key"))
|
||||
name = key.getAttribute("key");
|
||||
|
||||
isnot(name, null, "Successfully retrieved keycode/key");
|
||||
|
||||
let modifiers = {
|
||||
shiftKey: modifiersAttr.match("shift"),
|
||||
ctrlKey: modifiersAttr.match("ctrl"),
|
||||
altKey: modifiersAttr.match("alt"),
|
||||
metaKey: modifiersAttr.match("meta"),
|
||||
accelKey: modifiersAttr.match("accel")
|
||||
};
|
||||
|
||||
EventUtils.synthesizeKey(name, modifiers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for eventName on target.
|
||||
* @param {Object} target An observable object that either supports on/off or
|
||||
* addEventListener/removeEventListener
|
||||
* @param {String} eventName
|
||||
* @param {Boolean} useCapture Optional, for addEventListener/removeEventListener
|
||||
* @return A promise that resolves when the event has been handled
|
||||
*/
|
||||
function once(target, eventName, useCapture=false) {
|
||||
info("Waiting for event: '" + eventName + "' on " + target + ".");
|
||||
|
||||
let deferred = promise.defer();
|
||||
|
||||
for (let [add, remove] of [
|
||||
["addEventListener", "removeEventListener"],
|
||||
["addListener", "removeListener"],
|
||||
["on", "off"]
|
||||
]) {
|
||||
if ((add in target) && (remove in target)) {
|
||||
target[add](eventName, function onEvent(...aArgs) {
|
||||
info("Got event: '" + eventName + "' on " + target + ".");
|
||||
target[remove](eventName, onEvent, useCapture);
|
||||
deferred.resolve.apply(deferred, aArgs);
|
||||
}, useCapture);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Some tests may need to import one or more of the test helper scripts.
|
||||
* A test helper script is simply a js file that contains common test code that
|
||||
* is either not common-enough to be in head.js, or that is located in a separate
|
||||
* directory.
|
||||
* The script will be loaded synchronously and in the test's scope.
|
||||
* @param {String} filePath The file path, relative to the current directory.
|
||||
* Examples:
|
||||
* - "helper_attributes_test_runner.js"
|
||||
* - "../../../commandline/test/helpers.js"
|
||||
*/
|
||||
function loadHelperScript(filePath) {
|
||||
let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
|
||||
Services.scriptloader.loadSubScript(testDir + "/" + filePath, this);
|
||||
}
|
||||
|
||||
function waitForTick() {
|
||||
let deferred = promise.defer();
|
||||
executeSoon(deferred.resolve);
|
||||
return deferred.promise;
|
||||
}
|
||||
// shared-head.js handles imports, constants, and utility functions
|
||||
Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/browser/devtools/framework/test/shared-head.js", this);
|
||||
|
||||
function toggleAllTools(state) {
|
||||
for (let [, tool] of gDevTools._tools) {
|
||||
|
@ -177,26 +40,6 @@ function getChromeActors(callback)
|
|||
});
|
||||
}
|
||||
|
||||
function loadToolbox (url) {
|
||||
let { promise: p, resolve } = promise.defer();
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||
|
||||
gBrowser.selectedBrowser.addEventListener("load", function onLoad(evt) {
|
||||
gBrowser.selectedBrowser.removeEventListener(evt.type, onLoad, true);
|
||||
gDevTools.showToolbox(target).then(resolve);
|
||||
}, true);
|
||||
|
||||
content.location = url;
|
||||
return p;
|
||||
}
|
||||
|
||||
function unloadToolbox (toolbox) {
|
||||
return toolbox.destroy().then(function() {
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
}
|
||||
|
||||
function getSourceActor(aSources, aURL) {
|
||||
let item = aSources.getItemForAttachment(a => a.source.url === aURL);
|
||||
return item && item.value;
|
||||
|
|
|
@ -0,0 +1,162 @@
|
|||
/* 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/. */
|
||||
|
||||
// This shared-head.js file is used for multiple directories in devtools.
|
||||
const {console} = Cu.import("resource://gre/modules/devtools/Console.jsm", {});
|
||||
const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
|
||||
const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
|
||||
const {ScratchpadManager} = Cu.import("resource:///modules/devtools/scratchpad-manager.jsm", {});
|
||||
const {TargetFactory} = devtools;
|
||||
|
||||
const TEST_DIR = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
|
||||
const CHROME_URL_ROOT = TEST_DIR + "/";
|
||||
const URL_ROOT = CHROME_URL_ROOT.replace("chrome://mochitests/content/", "http://example.com/");
|
||||
|
||||
// All test are asynchronous
|
||||
waitForExplicitFinish();
|
||||
|
||||
// Uncomment this pref to dump all devtools emitted events to the console.
|
||||
// Services.prefs.setBoolPref("devtools.dump.emit", true);
|
||||
|
||||
function getFrameScript() {
|
||||
let mm = gBrowser.selectedBrowser.messageManager;
|
||||
let frameURL = "chrome://browser/content/devtools/frame-script-utils.js";
|
||||
mm.loadFrameScript(frameURL, false);
|
||||
SimpleTest.registerCleanupFunction(() => {
|
||||
mm = null;
|
||||
});
|
||||
return mm;
|
||||
}
|
||||
|
||||
gDevTools.testing = true;
|
||||
SimpleTest.registerCleanupFunction(() => {
|
||||
gDevTools.testing = false;
|
||||
Services.prefs.clearUserPref("devtools.dump.emit");
|
||||
});
|
||||
|
||||
registerCleanupFunction(function cleanup() {
|
||||
while (gBrowser.tabs.length > 1) {
|
||||
gBrowser.removeCurrentTab();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Add a new test tab in the browser and load the given url.
|
||||
* @param {String} url The url to be loaded in the new tab
|
||||
* @return a promise that resolves to the tab object when the url is loaded
|
||||
*/
|
||||
function addTab(url) {
|
||||
info("Adding a new tab with URL: '" + url + "'");
|
||||
let def = promise.defer();
|
||||
|
||||
let tab = gBrowser.selectedTab = gBrowser.addTab();
|
||||
gBrowser.selectedBrowser.addEventListener("load", function onload() {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", onload, true);
|
||||
info("URL '" + url + "' loading complete");
|
||||
def.resolve(tab);
|
||||
}, true);
|
||||
content.location = url;
|
||||
|
||||
return def.promise;
|
||||
}
|
||||
|
||||
function synthesizeKeyFromKeyTag(aKeyId, document) {
|
||||
let key = document.getElementById(aKeyId);
|
||||
isnot(key, null, "Successfully retrieved the <key> node");
|
||||
|
||||
let modifiersAttr = key.getAttribute("modifiers");
|
||||
|
||||
let name = null;
|
||||
|
||||
if (key.getAttribute("keycode"))
|
||||
name = key.getAttribute("keycode");
|
||||
else if (key.getAttribute("key"))
|
||||
name = key.getAttribute("key");
|
||||
|
||||
isnot(name, null, "Successfully retrieved keycode/key");
|
||||
|
||||
let modifiers = {
|
||||
shiftKey: modifiersAttr.match("shift"),
|
||||
ctrlKey: modifiersAttr.match("ctrl"),
|
||||
altKey: modifiersAttr.match("alt"),
|
||||
metaKey: modifiersAttr.match("meta"),
|
||||
accelKey: modifiersAttr.match("accel")
|
||||
};
|
||||
|
||||
EventUtils.synthesizeKey(name, modifiers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for eventName on target.
|
||||
* @param {Object} target An observable object that either supports on/off or
|
||||
* addEventListener/removeEventListener
|
||||
* @param {String} eventName
|
||||
* @param {Boolean} useCapture Optional, for addEventListener/removeEventListener
|
||||
* @return A promise that resolves when the event has been handled
|
||||
*/
|
||||
function once(target, eventName, useCapture=false) {
|
||||
info("Waiting for event: '" + eventName + "' on " + target + ".");
|
||||
|
||||
let deferred = promise.defer();
|
||||
|
||||
for (let [add, remove] of [
|
||||
["addEventListener", "removeEventListener"],
|
||||
["addListener", "removeListener"],
|
||||
["on", "off"]
|
||||
]) {
|
||||
if ((add in target) && (remove in target)) {
|
||||
target[add](eventName, function onEvent(...aArgs) {
|
||||
info("Got event: '" + eventName + "' on " + target + ".");
|
||||
target[remove](eventName, onEvent, useCapture);
|
||||
deferred.resolve.apply(deferred, aArgs);
|
||||
}, useCapture);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Some tests may need to import one or more of the test helper scripts.
|
||||
* A test helper script is simply a js file that contains common test code that
|
||||
* is either not common-enough to be in head.js, or that is located in a separate
|
||||
* directory.
|
||||
* The script will be loaded synchronously and in the test's scope.
|
||||
* @param {String} filePath The file path, relative to the current directory.
|
||||
* Examples:
|
||||
* - "helper_attributes_test_runner.js"
|
||||
* - "../../../commandline/test/helpers.js"
|
||||
*/
|
||||
function loadHelperScript(filePath) {
|
||||
let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
|
||||
Services.scriptloader.loadSubScript(testDir + "/" + filePath, this);
|
||||
}
|
||||
|
||||
function waitForTick() {
|
||||
let deferred = promise.defer();
|
||||
executeSoon(deferred.resolve);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function loadToolbox (url) {
|
||||
let { promise: p, resolve } = promise.defer();
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||
|
||||
gBrowser.selectedBrowser.addEventListener("load", function onLoad(evt) {
|
||||
gBrowser.selectedBrowser.removeEventListener(evt.type, onLoad, true);
|
||||
gDevTools.showToolbox(target).then(resolve);
|
||||
}, true);
|
||||
|
||||
content.location = url;
|
||||
return p;
|
||||
}
|
||||
|
||||
function unloadToolbox (toolbox) {
|
||||
return toolbox.destroy().then(function() {
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
# Timeline Markers
|
||||
|
||||
## Common
|
||||
|
||||
* DOMHighResTimeStamp start
|
||||
* DOMHighResTimeStamp end
|
||||
* DOMString name
|
||||
* object? stack
|
||||
* object? endStack
|
||||
|
||||
## DOMEvent
|
||||
|
||||
Triggered when a DOM event occurs, like a click or a keypress.
|
||||
|
||||
* unsigned short eventPhase - a number indicating what phase this event is
|
||||
in (target, bubbling, capturing, maps to Ci.nsIDOMEvent constants)
|
||||
* DOMString type - the type of event, like "keypress" or "click"
|
||||
|
||||
## Reflow
|
||||
|
||||
Reflow markers (labeled as "Layout") indicate when a change has occurred to
|
||||
a DOM element's positioning that requires the frame tree (rendering
|
||||
representation of the DOM) to figure out the new position of a handful of
|
||||
elements. Fired via `PresShell::DoReflow`
|
||||
|
||||
## Paint
|
||||
|
||||
* sequence<{ long height, long width, long x, long y }> rectangles - An array
|
||||
of rectangle objects indicating where painting has occurred.
|
||||
|
||||
## Styles
|
||||
|
||||
Style markers (labeled as "Recalculating Styles") are triggered when Gecko
|
||||
needs to figure out the computational style of an element. Fired via
|
||||
`RestyleTracker::DoProcessRestyles` when there are elements to restyle.
|
||||
|
||||
* DOMString restyleHint - A string indicating what kind of restyling will need
|
||||
to be processed; for example "eRestyle_StyleAttribute" is relatively cheap,
|
||||
whereas "eRestyle_Subtree" is more expensive. The hint can be a string of
|
||||
any amount of the following, separated via " | ". All future restyleHints
|
||||
are from `RestyleManager::RestyleHintToString`.
|
||||
|
||||
* "eRestyle_Self"
|
||||
* "eRestyle_Subtree"
|
||||
* "eRestyle_LaterSiblings"
|
||||
* "eRestyle_CSSTransitions"
|
||||
* "eRestyle_CSSAnimations"
|
||||
* "eRestyle_SVGAttrAnimations"
|
||||
* "eRestyle_StyleAttribute"
|
||||
* "eRestyle_StyleAttribute_Animations"
|
||||
* "eRestyle_Force"
|
||||
* "eRestyle_ForceDescendants"
|
||||
|
||||
|
||||
## Javascript
|
||||
|
||||
`Javascript` markers are emitted indicating when JS execution begins and ends,
|
||||
with a reason that triggered it (causeName), like a requestAnimationFrame or
|
||||
a setTimeout.
|
||||
|
||||
* string causeName - The reason that JS was entered. There are many possible
|
||||
reasons, and the interesting ones to show web developers (triggered by content) are:
|
||||
|
||||
* "\<script\> element"
|
||||
* "EventListener.handleEvent"
|
||||
* "setInterval handler"
|
||||
* "setTimeout handler"
|
||||
* "FrameRequestCallback"
|
||||
* "EventHandlerNonNull"
|
||||
* "promise callback"
|
||||
* "promise initializer"
|
||||
* "Worker runnable"
|
||||
|
||||
There are also many more potential JS causes, some which are just internally
|
||||
used and won't emit a marker, but the below ones are only of interest to
|
||||
Gecko hackers, most likely
|
||||
|
||||
* "promise thenable"
|
||||
* "worker runnable"
|
||||
* "nsHTTPIndex set HTTPIndex property"
|
||||
* "XPCWrappedJS method call"
|
||||
* "nsHTTPIndex OnFTPControlLog"
|
||||
* "XPCWrappedJS QueryInterface"
|
||||
* "xpcshell argument processing”
|
||||
* "XPConnect sandbox evaluation "
|
||||
* "component loader report global"
|
||||
* "component loader load module"
|
||||
* "Cross-Process Object Wrapper call/construct"
|
||||
* "Cross-Process Object Wrapper ’set'"
|
||||
* "Cross-Process Object Wrapper 'get'"
|
||||
* "nsXULTemplateBuilder creation"
|
||||
* "TestShellCommand"
|
||||
* "precompiled XUL \<script\> element"
|
||||
* "XBL \<field\> initialization "
|
||||
* "NPAPI NPN_evaluate"
|
||||
* "NPAPI get"
|
||||
* "NPAPI set"
|
||||
* "NPAPI doInvoke"
|
||||
* "javascript: URI"
|
||||
* "geolocation.always_precise indexing"
|
||||
* "geolocation.app_settings enumeration"
|
||||
* "WebIDL dictionary creation"
|
||||
* "XBL \<constructor\>/\<destructor\> invocation"
|
||||
* "message manager script load"
|
||||
* "message handler script load"
|
||||
* "nsGlobalWindow report new global"
|
||||
|
||||
## GarbageCollection
|
||||
|
||||
Emitted after a full GC has occurred (which will emit past incremental events).
|
||||
|
||||
* DOMString causeName - The reason for a GC event to occur. A full list of
|
||||
GC reasons can be found [on MDN](https://developer.mozilla.org/en-US/docs/Tools/Debugger-API/Debugger.Memory#Debugger.Memory_Handler_Functions).
|
||||
* DOMString nonincremenetalReason - If the GC could not do an incremental
|
||||
GC (smaller, quick GC events), and we have to walk the entire heap and
|
||||
GC everything marked, then the reason listed here is why.
|
||||
|
||||
## ConsoleTime
|
||||
|
||||
A marker generated via `console.time()` and `console.timeEnd()`.
|
||||
|
||||
* DOMString causeName - the label passed into `console.time(label)` and
|
||||
`console.timeEnd(label)` if passed in.
|
||||
|
||||
## TimeStamp
|
||||
|
||||
A marker generated via `console.timeStamp(label)`.
|
||||
|
||||
* DOMString causeName - the label passed into `console.timeStamp(label)`
|
||||
if passed in.
|
||||
|
||||
## Parse HTML
|
||||
|
||||
## Parse XML
|
|
@ -335,14 +335,14 @@ RecordingModel.prototype = {
|
|||
if (!config.withMarkers) { break; }
|
||||
let [markers] = data;
|
||||
RecordingUtils.offsetMarkerTimes(markers, this._timelineStartTime);
|
||||
Array.prototype.push.apply(this._markers, markers);
|
||||
pushAll(this._markers, markers);
|
||||
break;
|
||||
}
|
||||
// Accumulate stack frames into an array.
|
||||
case "frames": {
|
||||
if (!config.withMarkers) { break; }
|
||||
let [, frames] = data;
|
||||
Array.prototype.push.apply(this._frames, frames);
|
||||
pushAll(this._frames, frames);
|
||||
break;
|
||||
}
|
||||
// Accumulate memory measurements into an array. Furthermore, the timestamp
|
||||
|
@ -372,10 +372,10 @@ RecordingModel.prototype = {
|
|||
let timeOffset = this._memoryStartTime * 1000;
|
||||
let timeScale = 1000;
|
||||
RecordingUtils.offsetAndScaleTimestamps(timestamps, timeOffset, timeScale);
|
||||
Array.prototype.push.apply(this._allocations.sites, sites);
|
||||
Array.prototype.push.apply(this._allocations.timestamps, timestamps);
|
||||
Array.prototype.push.apply(this._allocations.frames, frames);
|
||||
Array.prototype.push.apply(this._allocations.counts, counts);
|
||||
pushAll(this._allocations.sites, sites);
|
||||
pushAll(this._allocations.timestamps, timestamps);
|
||||
pushAll(this._allocations.frames, frames);
|
||||
pushAll(this._allocations.counts, counts);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -384,4 +384,23 @@ RecordingModel.prototype = {
|
|||
toString: () => "[object RecordingModel]"
|
||||
};
|
||||
|
||||
/**
|
||||
* Push all elements of src array into dest array. Marker data will come in small chunks
|
||||
* and add up over time, whereas allocation arrays can be > 500000 elements (and
|
||||
* Function.prototype.apply throws if applying more than 500000 elements, which
|
||||
* is what spawned this separate function), so iterate one element at a time.
|
||||
* @see bug 1166823
|
||||
* @see http://jsperf.com/concat-large-arrays
|
||||
* @see http://jsperf.com/concat-large-arrays/2
|
||||
*
|
||||
* @param {Array} dest
|
||||
* @param {Array} src
|
||||
*/
|
||||
function pushAll (dest, src) {
|
||||
let length = src.length;
|
||||
for (let i = 0; i < length; i++) {
|
||||
dest.push(src[i]);
|
||||
}
|
||||
}
|
||||
|
||||
exports.RecordingModel = RecordingModel;
|
||||
|
|
|
@ -454,6 +454,11 @@ let PerformanceController = {
|
|||
* @param {RecordingModel} model
|
||||
*/
|
||||
_onRecordingStateChange: function (state, model) {
|
||||
// If we get a state change for a recording that isn't being tracked in the front,
|
||||
// just ignore it. This can occur when stopping a profile via console that was cleared.
|
||||
if (state !== "recording-starting" && this.getRecordings().indexOf(model) === -1) {
|
||||
return;
|
||||
}
|
||||
switch (state) {
|
||||
// Fired when a RecordingModel was just created from the front
|
||||
case "recording-starting":
|
||||
|
|
|
@ -37,6 +37,7 @@ support-files =
|
|||
[browser_perf-console-record-06.js]
|
||||
[browser_perf-console-record-07.js]
|
||||
[browser_perf-console-record-08.js]
|
||||
[browser_perf-console-record-09.js]
|
||||
[browser_perf-data-massaging-01.js]
|
||||
[browser_perf-data-samples.js]
|
||||
[browser_perf-details-calltree-render.js]
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests that an error is not thrown when clearing out the recordings if there's
|
||||
* an in-progress console profile.
|
||||
*/
|
||||
|
||||
function spawnTest () {
|
||||
loadFrameScripts();
|
||||
let { target, toolbox, panel } = yield initPerformance(SIMPLE_URL);
|
||||
let win = panel.panelWin;
|
||||
let { gFront, PerformanceController } = win;
|
||||
|
||||
info("Starting console.profile()...");
|
||||
yield consoleProfile(win);
|
||||
yield PerformanceController.clearRecordings();
|
||||
|
||||
info("Ending console.profileEnd()...");
|
||||
consoleMethod("profileEnd");
|
||||
// Wait for the front to receive the stopped event
|
||||
yield once(gFront, "recording-stopped");
|
||||
|
||||
// Wait an extra tick or two since the above promise will be resolved
|
||||
// the same time as _onRecordingStateChange, which should not cause any throwing
|
||||
yield idleWait(100);
|
||||
ok(true, "Stopping an in-progress console profile after clearing recordings does not throw.");
|
||||
|
||||
yield teardown(panel);
|
||||
finish();
|
||||
}
|
|
@ -407,6 +407,12 @@ function OverviewViewOnStateChange (fn) {
|
|||
// from the event name, since there is a delay between starting
|
||||
// a recording and changing the selection.
|
||||
if (!currentRecording || !recording) {
|
||||
// If no recording (this can occur when having a console.profile recording, and
|
||||
// we do not stop it from the backend), and we are still rendering updates,
|
||||
// stop that.
|
||||
if (this.isRendering()) {
|
||||
this._stopPolling();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -90,7 +90,9 @@ Users can turn off the ping with in-new-tab-page controls.
|
|||
|
||||
As the new tab page is rendered, any images for tiles are downloaded if not
|
||||
already cached. The default servers hosting the images are Mozilla CDN that
|
||||
don't use cookies: https://tiles.cdn.mozilla.net/
|
||||
don't use cookies: https://tiles.cdn.mozilla.net/ and Firefox enforces that the
|
||||
images come from mozilla.net or data URIs when using the default directory
|
||||
source.
|
||||
|
||||
|
||||
Source JSON Format
|
||||
|
@ -130,8 +132,10 @@ Below is an example directory source file::
|
|||
],
|
||||
"suggested": [
|
||||
{
|
||||
"adgroup_name": "open-source browser",
|
||||
"bgColor": "#cae1f4",
|
||||
"directoryId": 702,
|
||||
"explanation": "Suggested for %1$S enthusiasts who visit sites like %2$S",
|
||||
"frecent_sites": [
|
||||
"addons.mozilla.org",
|
||||
"air.mozilla.org",
|
||||
|
@ -180,6 +184,11 @@ Suggested Link Object Extras
|
|||
|
||||
A suggested link has additional values:
|
||||
|
||||
- ``adgroup_name`` - string to override the hardcoded display name of the
|
||||
triggering set of sites in Firefox.
|
||||
- ``explanation`` - string to override the default explanation that appears
|
||||
below a Suggested Tile. %1$S is replaced by the triggering adgroup name and
|
||||
%2$S is replaced by the triggering site.
|
||||
- ``frecent_sites`` - array of strings of the sites that can trigger showing a
|
||||
Suggested Tile if the user has the site in one of the top 100 most-frecent
|
||||
pages. Only preapproved array of strings that are hardcoded into the
|
||||
|
|
|
@ -9,6 +9,7 @@ this.EXPORTED_SYMBOLS = ["DirectoryLinksProvider"];
|
|||
const Ci = Components.interfaces;
|
||||
const Cc = Components.classes;
|
||||
const Cu = Components.utils;
|
||||
const ParserUtils = Cc["@mozilla.org/parserutils;1"].getService(Ci.nsIParserUtils);
|
||||
|
||||
Cu.importGlobalProperties(["XMLHttpRequest"]);
|
||||
|
||||
|
@ -27,9 +28,22 @@ XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
|||
"resource://gre/modules/Promise.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
|
||||
"resource://gre/modules/UpdateChannel.jsm");
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "eTLD",
|
||||
"@mozilla.org/network/effective-tld-service;1",
|
||||
"nsIEffectiveTLDService");
|
||||
XPCOMUtils.defineLazyGetter(this, "gTextDecoder", () => {
|
||||
return new TextDecoder();
|
||||
});
|
||||
XPCOMUtils.defineLazyGetter(this, "gCryptoHash", function () {
|
||||
return Cc["@mozilla.org/security/hash;1"].createInstance(Ci.nsICryptoHash);
|
||||
});
|
||||
XPCOMUtils.defineLazyGetter(this, "gUnicodeConverter", function () {
|
||||
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
|
||||
.createInstance(Ci.nsIScriptableUnicodeConverter);
|
||||
converter.charset = 'utf8';
|
||||
return converter;
|
||||
});
|
||||
|
||||
|
||||
// The filename where directory links are stored locally
|
||||
const DIRECTORY_LINKS_FILE = "directoryLinks.json";
|
||||
|
@ -84,6 +98,9 @@ const ALLOWED_LINK_SCHEMES = new Set(["http", "https"]);
|
|||
// Only allow link image urls that are https or data
|
||||
const ALLOWED_IMAGE_SCHEMES = new Set(["https", "data"]);
|
||||
|
||||
// Only allow urls to Mozilla's CDN or empty (for data URIs)
|
||||
const ALLOWED_URL_BASE = new Set(["mozilla.net", ""]);
|
||||
|
||||
// The frecency of a directory link
|
||||
const DIRECTORY_FRECENCY = 1000;
|
||||
|
||||
|
@ -104,12 +121,18 @@ const DEFAULT_PRUNE_TIME_DELTA = 10*24*60*60*1000;
|
|||
// The min number of visible (not blocked) history tiles to have before showing suggested tiles
|
||||
const MIN_VISIBLE_HISTORY_TILES = 8;
|
||||
|
||||
// The max number of visible (not blocked) history tiles to test for inadjacency
|
||||
const MAX_VISIBLE_HISTORY_TILES = 15;
|
||||
|
||||
// Divide frecency by this amount for pings
|
||||
const PING_SCORE_DIVISOR = 10000;
|
||||
|
||||
// Allowed ping actions remotely stored as columns: case-insensitive [a-z0-9_]
|
||||
const PING_ACTIONS = ["block", "click", "pin", "sponsored", "sponsored_link", "unpin", "view"];
|
||||
|
||||
// Location of inadjacent sites json
|
||||
const INADJACENCY_SOURCE = "chrome://browser/content/newtab/newTab.inadjacent.json";
|
||||
|
||||
/**
|
||||
* Singleton that serves as the provider of directory links.
|
||||
* Directory links are a hard-coded set of links shown if a user's link
|
||||
|
@ -147,6 +170,23 @@ let DirectoryLinksProvider = {
|
|||
*/
|
||||
_topSitesWithSuggestedLinks: new Set(),
|
||||
|
||||
/**
|
||||
* lookup Set of inadjacent domains
|
||||
*/
|
||||
_inadjacentSites: new Set(),
|
||||
|
||||
/**
|
||||
* This flag is set if there is a suggested tile configured to avoid
|
||||
* inadjacent sites in new tab
|
||||
*/
|
||||
_avoidInadjacentSites: false,
|
||||
|
||||
/**
|
||||
* This flag is set if _avoidInadjacentSites is true and there is
|
||||
* an inadjacent site in the new tab
|
||||
*/
|
||||
_newTabHasInadjacentSite: false,
|
||||
|
||||
get _observedPrefs() Object.freeze({
|
||||
enhanced: PREF_NEWTAB_ENHANCED,
|
||||
linksURL: PREF_DIRECTORY_SOURCE,
|
||||
|
@ -158,6 +198,7 @@ let DirectoryLinksProvider = {
|
|||
if (!this.__linksURL) {
|
||||
try {
|
||||
this.__linksURL = Services.prefs.getCharPref(this._observedPrefs["linksURL"]);
|
||||
this.__linksURLModified = Services.prefs.prefHasUserValue(this._observedPrefs["linksURL"]);
|
||||
}
|
||||
catch (e) {
|
||||
Cu.reportError("Error fetching directory links url from prefs: " + e);
|
||||
|
@ -269,22 +310,26 @@ let DirectoryLinksProvider = {
|
|||
uri = uri.replace("%LOCALE%", this.locale);
|
||||
uri = uri.replace("%CHANNEL%", UpdateChannel.get());
|
||||
|
||||
return this._downloadJsonData(uri).then(json => {
|
||||
return OS.File.writeAtomic(this._directoryFilePath, json, {tmpPath: this._directoryFilePath + ".tmp"});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Downloads a links with json content
|
||||
* @param download uri
|
||||
* @return promise resolved to json string, "{}" returned if status != 200
|
||||
*/
|
||||
_downloadJsonData: function DirectoryLinksProvider__downloadJsonData(uri) {
|
||||
let deferred = Promise.defer();
|
||||
let xmlHttp = this._newXHR();
|
||||
|
||||
let self = this;
|
||||
xmlHttp.onload = function(aResponse) {
|
||||
let json = this.responseText;
|
||||
if (this.status && this.status != 200) {
|
||||
json = "{}";
|
||||
}
|
||||
OS.File.writeAtomic(self._directoryFilePath, json, {tmpPath: self._directoryFilePath + ".tmp"})
|
||||
.then(() => {
|
||||
deferred.resolve();
|
||||
},
|
||||
() => {
|
||||
deferred.reject("Error writing uri data in profD.");
|
||||
});
|
||||
deferred.resolve(json);
|
||||
};
|
||||
|
||||
xmlHttp.onerror = function(e) {
|
||||
|
@ -575,21 +620,30 @@ let DirectoryLinksProvider = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Check if a url's scheme is in a Set of allowed schemes
|
||||
* Check if a url's scheme is in a Set of allowed schemes and if the base
|
||||
* domain is allowed.
|
||||
* @param url to check
|
||||
* @param allowed Set of allowed schemes
|
||||
* @param checkBase boolean to check the base domain
|
||||
*/
|
||||
isURLAllowed: function DirectoryLinksProvider_isURLAllowed(url, allowed) {
|
||||
isURLAllowed(url, allowed, checkBase) {
|
||||
// Assume no url is an allowed url
|
||||
if (!url) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let scheme = "";
|
||||
let scheme = "", base = "";
|
||||
try {
|
||||
// A malformed url will not be allowed
|
||||
scheme = Services.io.newURI(url, null, null).scheme;
|
||||
let uri = Services.io.newURI(url, null, null);
|
||||
scheme = uri.scheme;
|
||||
|
||||
// URIs without base domains will be allowed
|
||||
base = Services.eTLD.getBaseDomain(uri);
|
||||
}
|
||||
catch(ex) {}
|
||||
return allowed.has(scheme);
|
||||
// Require a scheme match and the base only if desired
|
||||
return allowed.has(scheme) && (!checkBase || ALLOWED_URL_BASE.has(base));
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -602,12 +656,15 @@ let DirectoryLinksProvider = {
|
|||
this._enhancedLinks.clear();
|
||||
this._suggestedLinks.clear();
|
||||
this._clearCampaignTimeout();
|
||||
this._avoidInadjacentSites = false;
|
||||
|
||||
// Only check base domain for images when using the default pref
|
||||
let checkBase = !this.__linksURLModified;
|
||||
let validityFilter = function(link) {
|
||||
// Make sure the link url is allowed and images too if they exist
|
||||
return this.isURLAllowed(link.url, ALLOWED_LINK_SCHEMES) &&
|
||||
this.isURLAllowed(link.imageURI, ALLOWED_IMAGE_SCHEMES) &&
|
||||
this.isURLAllowed(link.enhancedImageURI, ALLOWED_IMAGE_SCHEMES);
|
||||
return this.isURLAllowed(link.url, ALLOWED_LINK_SCHEMES, false) &&
|
||||
this.isURLAllowed(link.imageURI, ALLOWED_IMAGE_SCHEMES, checkBase) &&
|
||||
this.isURLAllowed(link.enhancedImageURI, ALLOWED_IMAGE_SCHEMES, checkBase);
|
||||
}.bind(this);
|
||||
|
||||
rawLinks.suggested.filter(validityFilter).forEach((link, position) => {
|
||||
|
@ -617,8 +674,17 @@ let DirectoryLinksProvider = {
|
|||
return;
|
||||
}
|
||||
|
||||
link.targetedName = name;
|
||||
let sanitizeFlags = ParserUtils.SanitizerCidEmbedsOnly |
|
||||
ParserUtils.SanitizerDropForms |
|
||||
ParserUtils.SanitizerDropNonCSSPresentation;
|
||||
|
||||
link.explanation = link.explanation ? ParserUtils.convertToPlainText(link.explanation, sanitizeFlags, 0) : "";
|
||||
link.targetedName = ParserUtils.convertToPlainText(link.adgroup_name, sanitizeFlags, 0) || name;
|
||||
link.lastVisitDate = rawLinks.suggested.length - position;
|
||||
// check if link wants to avoid inadjacent sites
|
||||
if (link.check_inadjacency) {
|
||||
this._avoidInadjacentSites = true;
|
||||
}
|
||||
|
||||
// We cache suggested tiles here but do not push any of them in the links list yet.
|
||||
// The decision for which suggested tile to include will be made separately.
|
||||
|
@ -668,6 +734,8 @@ let DirectoryLinksProvider = {
|
|||
|
||||
// setup frequency cap file path
|
||||
this._frequencyCapFilePath = OS.Path.join(OS.Constants.Path.localProfileDir, FREQUENCY_CAP_FILE);
|
||||
// setup inadjacent sites URL
|
||||
this._inadjacentSitesUrl = INADJACENCY_SOURCE;
|
||||
|
||||
NewTabUtils.placesProvider.addObserver(this);
|
||||
NewTabUtils.links.addObserver(this);
|
||||
|
@ -683,6 +751,8 @@ let DirectoryLinksProvider = {
|
|||
yield this._readFrequencyCapFile();
|
||||
// fetch directory on startup without force
|
||||
yield this._fetchAndCacheLinksIfNecessary();
|
||||
// fecth inadjacent sites on startup
|
||||
yield this._loadInadjacentSites();
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
|
@ -715,6 +785,13 @@ let DirectoryLinksProvider = {
|
|||
this._topSitesWithSuggestedLinks.add(changedLinkSite);
|
||||
return true;
|
||||
}
|
||||
|
||||
// always run _updateSuggestedTile if aLink is inadjacent
|
||||
// and there are tiles configured to avoid it
|
||||
if (this._avoidInadjacentSites && this._isInadjacentLink(aLink)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
|
@ -758,11 +835,17 @@ let DirectoryLinksProvider = {
|
|||
|
||||
_getCurrentTopSiteCount: function() {
|
||||
let visibleTopSiteCount = 0;
|
||||
for (let link of NewTabUtils.links.getLinks().slice(0, MIN_VISIBLE_HISTORY_TILES)) {
|
||||
let newTabLinks = NewTabUtils.links.getLinks();
|
||||
for (let link of newTabLinks.slice(0, MIN_VISIBLE_HISTORY_TILES)) {
|
||||
// compute visibleTopSiteCount for suggested tiles
|
||||
if (link && (link.type == "history" || link.type == "enhanced")) {
|
||||
visibleTopSiteCount++;
|
||||
}
|
||||
}
|
||||
// since newTabLinks are available, set _newTabHasInadjacentSite here
|
||||
// note that _shouldUpdateSuggestedTile is called by _updateSuggestedTile
|
||||
this._newTabHasInadjacentSite = this._avoidInadjacentSites && this._checkForInadjacentSites(newTabLinks);
|
||||
|
||||
return visibleTopSiteCount;
|
||||
},
|
||||
|
||||
|
@ -851,6 +934,11 @@ let DirectoryLinksProvider = {
|
|||
return;
|
||||
}
|
||||
|
||||
// Skip link if it avoids inadjacent sites and newtab has one
|
||||
if (suggestedLink.check_inadjacency && this._newTabHasInadjacentSite) {
|
||||
return;
|
||||
}
|
||||
|
||||
possibleLinks.set(url, suggestedLink);
|
||||
|
||||
// Keep a map of URL to targeted sites. We later use this to show the user
|
||||
|
@ -892,6 +980,66 @@ let DirectoryLinksProvider = {
|
|||
return chosenSuggestedLink;
|
||||
},
|
||||
|
||||
/**
|
||||
* Loads inadjacent sites
|
||||
* @return a promise resolved when lookup Set for sites is built
|
||||
*/
|
||||
_loadInadjacentSites: function DirectoryLinksProvider_loadInadjacentSites() {
|
||||
return this._downloadJsonData(this._inadjacentSitesUrl).then(jsonString => {
|
||||
let jsonObject = {};
|
||||
try {
|
||||
jsonObject = JSON.parse(jsonString);
|
||||
}
|
||||
catch (e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
|
||||
this._inadjacentSites = new Set(jsonObject.domains);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Genegrates hash suitable for looking up inadjacent site
|
||||
* @param value to hsh
|
||||
* @return hased value, base64-ed
|
||||
*/
|
||||
_generateHash: function DirectoryLinksProvider_generateHash(value) {
|
||||
let byteArr = gUnicodeConverter.convertToByteArray(value);
|
||||
gCryptoHash.init(gCryptoHash.MD5);
|
||||
gCryptoHash.update(byteArr, byteArr.length);
|
||||
return gCryptoHash.finish(true);
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if link belongs to inadjacent domain
|
||||
* @param link to check
|
||||
* @return true for inadjacent domains, false otherwise
|
||||
*/
|
||||
_isInadjacentLink: function DirectoryLinksProvider_isInadjacentLink(link) {
|
||||
let baseDomain = link.baseDomain || NewTabUtils.extractSite(link.url || "");
|
||||
if (!baseDomain) {
|
||||
return false;
|
||||
}
|
||||
// check if hashed domain is inadjacent
|
||||
return this._inadjacentSites.has(this._generateHash(baseDomain));
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if new tab has inadjacent site
|
||||
* @param new tab links (or nothing, in which case NewTabUtils.links.getLinks() is called
|
||||
* @return true if new tab shows has inadjacent site
|
||||
*/
|
||||
_checkForInadjacentSites: function DirectoryLinksProvider_checkForInadjacentSites(newTabLink) {
|
||||
let links = newTabLink || NewTabUtils.links.getLinks();
|
||||
for (let link of links.slice(0, MAX_VISIBLE_HISTORY_TILES)) {
|
||||
// check links against inadjacent list - specifically include ALL link types
|
||||
if (this._isInadjacentLink(link)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Reads json file, parses its content, and returns resulting object
|
||||
* @param json file path
|
||||
|
|
|
@ -104,6 +104,16 @@ let suggestedTile4 = {
|
|||
"sponsoredtarget.com"
|
||||
]
|
||||
}
|
||||
let suggestedTile5 = {
|
||||
url: "http://eviltile.com",
|
||||
type: "affiliate",
|
||||
lastVisitDate: 5,
|
||||
explanation: "This is an evil tile <form><button formaction='javascript:alert(1)''>X</button></form> muhahaha",
|
||||
adgroup_name: "WE ARE EVIL <link rel='import' href='test.svg'/>",
|
||||
frecent_sites: [
|
||||
"eviltarget.com"
|
||||
]
|
||||
}
|
||||
let someOtherSite = {url: "http://someothersite.com", title: "Not_A_Suggested_Site"};
|
||||
|
||||
function getHttpHandler(path) {
|
||||
|
@ -199,6 +209,7 @@ function promiseCleanDirectoryLinksProvider() {
|
|||
yield promiseDirectoryDownloadOnPrefChange(kLocalePref, "en-US");
|
||||
yield promiseDirectoryDownloadOnPrefChange(kSourceUrlPref, kTestURL);
|
||||
yield DirectoryLinksProvider._clearFrequencyCap();
|
||||
yield DirectoryLinksProvider._loadInadjacentSites();
|
||||
DirectoryLinksProvider._lastDownloadMS = 0;
|
||||
DirectoryLinksProvider.reset();
|
||||
});
|
||||
|
@ -442,6 +453,7 @@ add_task(function test_suggestedLinksMap() {
|
|||
for (let link of expected_data[site]) {
|
||||
let linkCopy = JSON.parse(JSON.stringify(link));
|
||||
linkCopy.targetedName = "testing map";
|
||||
linkCopy.explanation = "";
|
||||
isIdentical(suggestedLinksItr.next().value, linkCopy);
|
||||
}
|
||||
})
|
||||
|
@ -1107,6 +1119,30 @@ add_task(function test_DirectoryLinksProvider_getAllowedImages() {
|
|||
do_check_eq(links[1].imageURI, data["directory"][5].imageURI);
|
||||
});
|
||||
|
||||
add_task(function test_DirectoryLinksProvider_getAllowedImages_base() {
|
||||
let data = {"directory": [
|
||||
{url: "http://example1.com", imageURI: "https://example.com"},
|
||||
{url: "http://example2.com", imageURI: "https://tiles.cdn.mozilla.net"},
|
||||
{url: "http://example3.com", imageURI: "https://tiles2.cdn.mozilla.net"},
|
||||
{url: "http://example4.com", enhancedImageURI: "https://mozilla.net"},
|
||||
{url: "http://example5.com", imageURI: "data:text/plain,hi"},
|
||||
]};
|
||||
let dataURI = 'data:application/json,' + JSON.stringify(data);
|
||||
yield promiseSetupDirectoryLinksProvider({linksURL: dataURI});
|
||||
|
||||
// Pretend we're using the default pref to trigger base matching
|
||||
DirectoryLinksProvider.__linksURLModified = false;
|
||||
|
||||
let links = yield fetchData();
|
||||
do_check_eq(links.length, 4);
|
||||
|
||||
// The only remaining images should be https with mozilla.net or data URI
|
||||
do_check_eq(links[0].url, data["directory"][1].url);
|
||||
do_check_eq(links[1].url, data["directory"][2].url);
|
||||
do_check_eq(links[2].url, data["directory"][3].url);
|
||||
do_check_eq(links[3].url, data["directory"][4].url);
|
||||
});
|
||||
|
||||
add_task(function test_DirectoryLinksProvider_getAllowedEnhancedImages() {
|
||||
let data = {"directory": [
|
||||
{url: "http://example.com", enhancedImageURI: "ftp://example.com"},
|
||||
|
@ -1688,3 +1724,203 @@ add_task(function test_DirectoryLinksProvider_ClickRemoval() {
|
|||
add_task(function test_DirectoryLinksProvider_anonymous() {
|
||||
do_check_true(DirectoryLinksProvider._newXHR().mozAnon);
|
||||
});
|
||||
|
||||
add_task(function test_sanitizeExplanation() {
|
||||
// Note: this is a basic test to ensure we applied sanitization to the link explanation.
|
||||
// Full testing for appropriate sanitization is done in parser/xml/test/unit/test_sanitizer.js.
|
||||
|
||||
let origGetFrecentSitesName = DirectoryLinksProvider.getFrecentSitesName;
|
||||
DirectoryLinksProvider.getFrecentSitesName = () => "testing map";
|
||||
|
||||
let data = {"suggested": [suggestedTile5]};
|
||||
let dataURI = 'data:application/json,' + encodeURIComponent(JSON.stringify(data));
|
||||
|
||||
yield promiseSetupDirectoryLinksProvider({linksURL: dataURI});
|
||||
let links = yield fetchData();
|
||||
|
||||
let suggestedSites = [...DirectoryLinksProvider._suggestedLinks.keys()];
|
||||
do_check_eq(suggestedSites.indexOf("eviltarget.com"), 0);
|
||||
do_check_eq(suggestedSites.length, 1);
|
||||
|
||||
let suggestedLink = [...DirectoryLinksProvider._suggestedLinks.get(suggestedSites[0]).values()][0];
|
||||
do_check_eq(suggestedLink.explanation, "This is an evil tile X muhahaha");
|
||||
do_check_eq(suggestedLink.targetedName, "WE ARE EVIL ");
|
||||
});
|
||||
|
||||
add_task(function test_inadjecentSites() {
|
||||
let suggestedTile = Object.assign({
|
||||
check_inadjacency: true
|
||||
}, suggestedTile1);
|
||||
|
||||
// Initial setup
|
||||
let topSites = ["1040.com", "site2.com", "hrblock.com", "site4.com", "freetaxusa.com", "site6.com"];
|
||||
let data = {"suggested": [suggestedTile], "directory": [someOtherSite]};
|
||||
let dataURI = 'data:application/json,' + JSON.stringify(data);
|
||||
|
||||
let testObserver = new TestFirstRun();
|
||||
DirectoryLinksProvider.addObserver(testObserver);
|
||||
|
||||
let origGetFrecentSitesName = DirectoryLinksProvider.getFrecentSitesName;
|
||||
DirectoryLinksProvider.getFrecentSitesName = () => "";
|
||||
|
||||
yield promiseSetupDirectoryLinksProvider({linksURL: dataURI});
|
||||
let links = yield fetchData();
|
||||
|
||||
let origIsTopPlacesSite = NewTabUtils.isTopPlacesSite;
|
||||
NewTabUtils.isTopPlacesSite = function(site) {
|
||||
return topSites.indexOf(site) >= 0;
|
||||
}
|
||||
|
||||
let origGetProviderLinks = NewTabUtils.getProviderLinks;
|
||||
NewTabUtils.getProviderLinks = function(provider) {
|
||||
return links;
|
||||
}
|
||||
|
||||
let origCurrentTopSiteCount = DirectoryLinksProvider._getCurrentTopSiteCount;
|
||||
DirectoryLinksProvider._getCurrentTopSiteCount = () => {
|
||||
origCurrentTopSiteCount.apply(DirectoryLinksProvider);
|
||||
return 8;
|
||||
};
|
||||
|
||||
// store oroginal inadjacent sites url
|
||||
let origInadjacentSitesUrl = DirectoryLinksProvider._inadjacentSitesUrl;
|
||||
|
||||
// loading inadjacent sites list function
|
||||
function setInadjacentSites(sites) {
|
||||
let badSiteB64 = [];
|
||||
sites.forEach(site => {
|
||||
badSiteB64.push(DirectoryLinksProvider._generateHash(site));
|
||||
});
|
||||
let theList = {"domains": badSiteB64};
|
||||
let dataURI = 'data:application/json,' + JSON.stringify(theList);
|
||||
DirectoryLinksProvider._inadjacentSitesUrl = dataURI;
|
||||
return DirectoryLinksProvider._loadInadjacentSites();
|
||||
};
|
||||
|
||||
// setup gLinks loader
|
||||
let gLinks = NewTabUtils.links;
|
||||
gLinks.addProvider(DirectoryLinksProvider);
|
||||
|
||||
function updateNewTabCache() {
|
||||
gLinks.populateCache();
|
||||
return new Promise(resolve => {
|
||||
NewTabUtils.allPages.register({
|
||||
observe: _ => _,
|
||||
update() {
|
||||
NewTabUtils.allPages.unregister(this);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// no suggested file
|
||||
do_check_eq(DirectoryLinksProvider._updateSuggestedTile(), undefined);
|
||||
// _avoidInadjacentSites should be set, since link.check_inadjacency is on
|
||||
do_check_true(DirectoryLinksProvider._avoidInadjacentSites);
|
||||
// make sure example.com is included in inadjacent sites list
|
||||
do_check_true(DirectoryLinksProvider._isInadjacentLink({baseDomain: "example.com"}));
|
||||
|
||||
function TestFirstRun() {
|
||||
this.promise = new Promise(resolve => {
|
||||
this.onLinkChanged = (directoryLinksProvider, link) => {
|
||||
do_check_eq(link.url, suggestedTile.url);
|
||||
do_check_eq(link.type, "affiliate");
|
||||
resolve();
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// Test first call to '_updateSuggestedTile()', called when fetching directory links.
|
||||
yield testObserver.promise;
|
||||
DirectoryLinksProvider.removeObserver(testObserver);
|
||||
|
||||
// update newtab cache
|
||||
yield updateNewTabCache();
|
||||
// this should have set
|
||||
do_check_true(DirectoryLinksProvider._avoidInadjacentSites);
|
||||
|
||||
// there should be siggested link
|
||||
let link = DirectoryLinksProvider._updateSuggestedTile();
|
||||
do_check_eq(link.url, "http://turbotax.com");
|
||||
// and it should have avoidInadjacentSites flag
|
||||
do_check_true(link.check_inadjacency);
|
||||
|
||||
// make someothersite.com inadjacent
|
||||
yield setInadjacentSites(["someothersite.com"]);
|
||||
|
||||
// there should be no suggested link
|
||||
link = DirectoryLinksProvider._updateSuggestedTile();
|
||||
do_check_false(link);
|
||||
do_check_true(DirectoryLinksProvider._newTabHasInadjacentSite);
|
||||
|
||||
// _handleLinkChanged must return true on inadjacent site
|
||||
do_check_true(DirectoryLinksProvider._handleLinkChanged({
|
||||
url: "http://someothersite.com",
|
||||
type: "history",
|
||||
}));
|
||||
// _handleLinkChanged must return false on ok site
|
||||
do_check_false(DirectoryLinksProvider._handleLinkChanged({
|
||||
url: "http://foobar.com",
|
||||
type: "history",
|
||||
}));
|
||||
|
||||
// change inadjacent list to sites not on newtab page
|
||||
yield setInadjacentSites(["foo.com", "bar.com"]);
|
||||
|
||||
link = DirectoryLinksProvider._updateSuggestedTile();
|
||||
// we should now have a link
|
||||
do_check_true(link);
|
||||
do_check_eq(link.url, "http://turbotax.com");
|
||||
|
||||
// make newtab offending again
|
||||
yield setInadjacentSites(["someothersite.com", "foo.com"]);
|
||||
// there should be no suggested link
|
||||
link = DirectoryLinksProvider._updateSuggestedTile();
|
||||
do_check_false(link);
|
||||
do_check_true(DirectoryLinksProvider._newTabHasInadjacentSite);
|
||||
|
||||
// remove avoidInadjacentSites flag from suggested tile and reload json
|
||||
delete suggestedTile.check_inadjacency;
|
||||
data = {"suggested": [suggestedTile], "directory": [someOtherSite]};
|
||||
dataURI = 'data:application/json,' + JSON.stringify(data);
|
||||
yield promiseDirectoryDownloadOnPrefChange(kSourceUrlPref, dataURI);
|
||||
yield fetchData();
|
||||
|
||||
// inadjacent checking should be disabled
|
||||
do_check_false(DirectoryLinksProvider._avoidInadjacentSites);
|
||||
link = DirectoryLinksProvider._updateSuggestedTile();
|
||||
do_check_true(link);
|
||||
do_check_eq(link.url, "http://turbotax.com");
|
||||
do_check_false(DirectoryLinksProvider._newTabHasInadjacentSite);
|
||||
|
||||
// _handleLinkChanged should return false now, even if newtab has bad site
|
||||
do_check_false(DirectoryLinksProvider._handleLinkChanged({
|
||||
url: "http://someothersite.com",
|
||||
type: "history",
|
||||
}));
|
||||
|
||||
// test _isInadjacentLink
|
||||
do_check_true(DirectoryLinksProvider._isInadjacentLink({baseDomain: "someothersite.com"}));
|
||||
do_check_false(DirectoryLinksProvider._isInadjacentLink({baseDomain: "bar.com"}));
|
||||
do_check_true(DirectoryLinksProvider._isInadjacentLink({url: "http://www.someothersite.com"}));
|
||||
do_check_false(DirectoryLinksProvider._isInadjacentLink({url: "http://www.bar.com"}));
|
||||
// try to crash _isInadjacentLink
|
||||
do_check_false(DirectoryLinksProvider._isInadjacentLink({baseDomain: ""}));
|
||||
do_check_false(DirectoryLinksProvider._isInadjacentLink({url: ""}));
|
||||
do_check_false(DirectoryLinksProvider._isInadjacentLink({url: "http://localhost:8081/"}));
|
||||
do_check_false(DirectoryLinksProvider._isInadjacentLink({url: "abracodabra"}));
|
||||
do_check_false(DirectoryLinksProvider._isInadjacentLink({}));
|
||||
|
||||
// test _checkForInadjacentSites
|
||||
do_check_true(DirectoryLinksProvider._checkForInadjacentSites());
|
||||
|
||||
// Cleanup
|
||||
gLinks.removeProvider(DirectoryLinksProvider);
|
||||
DirectoryLinksProvider._inadjacentSitesUrl = origInadjacentSitesUrl;
|
||||
DirectoryLinksProvider.getFrecentSitesName = origGetFrecentSitesName;
|
||||
NewTabUtils.isTopPlacesSite = origIsTopPlacesSite;
|
||||
NewTabUtils.getProviderLinks = origGetProviderLinks;
|
||||
DirectoryLinksProvider._getCurrentTopSiteCount = origCurrentTopSiteCount;
|
||||
yield promiseCleanDirectoryLinksProvider();
|
||||
});
|
||||
|
|
|
@ -112,6 +112,11 @@
|
|||
background: var(--theme-selection-background);
|
||||
}
|
||||
|
||||
.record-button[locked] {
|
||||
pointer-events: none;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
#performance-view .realtime-disabled-message,
|
||||
#performance-view .realtime-disabled-on-e10s-message {
|
||||
display: none;
|
||||
|
@ -158,10 +163,6 @@
|
|||
list-style-image: url(profiler-stopwatch-checked.svg);
|
||||
}
|
||||
|
||||
#main-record-button[locked] {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#main-record-button .button-icon {
|
||||
margin: 0;
|
||||
}
|
||||
|
|
|
@ -238,7 +238,12 @@ this.UserCustomizations = {
|
|||
}
|
||||
|
||||
try {
|
||||
Services.scriptloader.loadSubScript(aScript, sandbox, "UTF-8");
|
||||
let options = {
|
||||
target: sandbox,
|
||||
charset: "UTF-8",
|
||||
async: true
|
||||
}
|
||||
Services.scriptloader.loadSubScriptWithOptions(aScript, options);
|
||||
if (!this._loaded[manifestURL]) {
|
||||
this._loaded[manifestURL] = { css: [], scripts: [] };
|
||||
}
|
||||
|
|
|
@ -549,7 +549,8 @@ WebappsApplication.prototype = {
|
|||
},
|
||||
|
||||
get enabled() {
|
||||
return this._proxy.enabled;
|
||||
let value = this._proxy.enabled;
|
||||
return (value === undefined ? true : value);
|
||||
},
|
||||
|
||||
download: function() {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
document.addEventListener("DOMContentLoaded", function() {
|
||||
var head = document.getElementById("header");
|
||||
head.innerHTML = "Hello World!";
|
||||
}, false);
|
||||
}, false);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
document.addEventListener("DOMContentLoaded", function() {
|
||||
var head = document.getElementById("header2");
|
||||
head.innerHTML = "Customized content";
|
||||
}, false);
|
||||
}, false);
|
||||
|
|
|
@ -126,7 +126,7 @@ public:
|
|||
class ObexHeaderSet
|
||||
{
|
||||
public:
|
||||
ObexHeaderSet(uint8_t aOpcode) : mOpcode(aOpcode)
|
||||
ObexHeaderSet()
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -252,7 +252,6 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
uint8_t mOpcode;
|
||||
nsTArray<nsAutoPtr<ObexHeader> > mHeaders;
|
||||
};
|
||||
|
||||
|
|
|
@ -899,10 +899,19 @@ BluetoothOppManager::ServerDataHandler(UnixSocketBuffer* aMessage)
|
|||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
uint8_t opCode;
|
||||
/**
|
||||
* Ensure
|
||||
* - valid access to data[0], i.e., opCode
|
||||
* - received packet length smaller than max packet length
|
||||
*/
|
||||
int receivedLength = aMessage->GetSize();
|
||||
const uint8_t* data = aMessage->GetData();
|
||||
if (receivedLength < 1 || receivedLength > MAX_PACKET_LENGTH) {
|
||||
ReplyError(ObexResponseCode::BadRequest);
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t opCode;
|
||||
const uint8_t* data = aMessage->GetData();
|
||||
if (mPutPacketReceivedLength > 0) {
|
||||
opCode = mPutFinalFlag ? ObexRequestCode::PutFinal : ObexRequestCode::Put;
|
||||
} else {
|
||||
|
@ -918,12 +927,13 @@ BluetoothOppManager::ServerDataHandler(UnixSocketBuffer* aMessage)
|
|||
}
|
||||
}
|
||||
|
||||
ObexHeaderSet pktHeaders(opCode);
|
||||
ObexHeaderSet pktHeaders;
|
||||
if (opCode == ObexRequestCode::Connect) {
|
||||
// Section 3.3.1 "Connect", IrOBEX 1.2
|
||||
// [opcode:1][length:2][version:1][flags:1][MaxPktSizeWeCanReceive:2]
|
||||
// [Headers:var]
|
||||
if (!ParseHeaders(&data[7], receivedLength - 7, &pktHeaders)) {
|
||||
if (receivedLength < 7 ||
|
||||
!ParseHeaders(&data[7], receivedLength - 7, &pktHeaders)) {
|
||||
ReplyError(ObexResponseCode::BadRequest);
|
||||
return;
|
||||
}
|
||||
|
@ -933,7 +943,8 @@ BluetoothOppManager::ServerDataHandler(UnixSocketBuffer* aMessage)
|
|||
} else if (opCode == ObexRequestCode::Abort) {
|
||||
// Section 3.3.5 "Abort", IrOBEX 1.2
|
||||
// [opcode:1][length:2][Headers:var]
|
||||
if (!ParseHeaders(&data[3], receivedLength - 3, &pktHeaders)) {
|
||||
if (receivedLength < 3 ||
|
||||
!ParseHeaders(&data[3], receivedLength - 3, &pktHeaders)) {
|
||||
ReplyError(ObexResponseCode::BadRequest);
|
||||
return;
|
||||
}
|
||||
|
@ -943,7 +954,8 @@ BluetoothOppManager::ServerDataHandler(UnixSocketBuffer* aMessage)
|
|||
} else if (opCode == ObexRequestCode::Disconnect) {
|
||||
// Section 3.3.2 "Disconnect", IrOBEX 1.2
|
||||
// [opcode:1][length:2][Headers:var]
|
||||
if (!ParseHeaders(&data[3], receivedLength - 3, &pktHeaders)) {
|
||||
if (receivedLength < 3 ||
|
||||
!ParseHeaders(&data[3], receivedLength - 3, &pktHeaders)) {
|
||||
ReplyError(ObexResponseCode::BadRequest);
|
||||
return;
|
||||
}
|
||||
|
@ -1026,6 +1038,13 @@ BluetoothOppManager::ClientDataHandler(UnixSocketBuffer* aMessage)
|
|||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// Ensure valid access to data[0], i.e., opCode
|
||||
int receivedLength = aMessage->GetSize();
|
||||
if (receivedLength < 1) {
|
||||
BT_LOGR("Receive empty response packet");
|
||||
return;
|
||||
}
|
||||
|
||||
const uint8_t* data = aMessage->GetData();
|
||||
uint8_t opCode = data[0];
|
||||
|
||||
|
@ -1083,6 +1102,13 @@ BluetoothOppManager::ClientDataHandler(UnixSocketBuffer* aMessage)
|
|||
|
||||
AfterOppConnected();
|
||||
|
||||
// Ensure valid access to remote information
|
||||
if (receivedLength < 7) {
|
||||
BT_LOGR("The length of connect response packet is invalid");
|
||||
SendDisconnectRequest();
|
||||
return;
|
||||
}
|
||||
|
||||
// Keep remote information
|
||||
mRemoteObexVersion = data[3];
|
||||
mRemoteConnectionFlags = data[4];
|
||||
|
|
|
@ -183,17 +183,27 @@ BluetoothPbapManager::ReceiveSocketData(BluetoothSocket* aSocket,
|
|||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
const uint8_t* data = aMessage->GetData();
|
||||
/**
|
||||
* Ensure
|
||||
* - valid access to data[0], i.e., opCode
|
||||
* - received packet length smaller than max packet length
|
||||
*/
|
||||
int receivedLength = aMessage->GetSize();
|
||||
uint8_t opCode = data[0];
|
||||
if (receivedLength < 1 || receivedLength > MAX_PACKET_LENGTH) {
|
||||
ReplyError(ObexResponseCode::BadRequest);
|
||||
return;
|
||||
}
|
||||
|
||||
ObexHeaderSet pktHeaders(opCode);
|
||||
const uint8_t* data = aMessage->GetData();
|
||||
uint8_t opCode = data[0];
|
||||
ObexHeaderSet pktHeaders;
|
||||
switch (opCode) {
|
||||
case ObexRequestCode::Connect:
|
||||
// Section 3.3.1 "Connect", IrOBEX 1.2
|
||||
// [opcode:1][length:2][version:1][flags:1][MaxPktSizeWeCanReceive:2]
|
||||
// [Headers:var]
|
||||
if (!ParseHeaders(&data[7], receivedLength - 7, &pktHeaders)) {
|
||||
if (receivedLength < 7 ||
|
||||
!ParseHeaders(&data[7], receivedLength - 7, &pktHeaders)) {
|
||||
ReplyError(ObexResponseCode::BadRequest);
|
||||
return;
|
||||
}
|
||||
|
@ -213,7 +223,8 @@ BluetoothPbapManager::ReceiveSocketData(BluetoothSocket* aSocket,
|
|||
// Section 3.3.2 "Disconnect" and Section 3.3.5 "Abort", IrOBEX 1.2
|
||||
// The format of request packet of "Disconnect" and "Abort" are the same
|
||||
// [opcode:1][length:2][Headers:var]
|
||||
if (!ParseHeaders(&data[3], receivedLength - 3, &pktHeaders)) {
|
||||
if (receivedLength < 3 ||
|
||||
!ParseHeaders(&data[3], receivedLength - 3, &pktHeaders)) {
|
||||
ReplyError(ObexResponseCode::BadRequest);
|
||||
return;
|
||||
}
|
||||
|
@ -224,7 +235,8 @@ BluetoothPbapManager::ReceiveSocketData(BluetoothSocket* aSocket,
|
|||
case ObexRequestCode::SetPath: {
|
||||
// Section 3.3.6 "SetPath", IrOBEX 1.2
|
||||
// [opcode:1][length:2][flags:1][contants:1][Headers:var]
|
||||
if (!ParseHeaders(&data[5], receivedLength - 5, &pktHeaders)) {
|
||||
if (receivedLength < 5 ||
|
||||
!ParseHeaders(&data[5], receivedLength - 5, &pktHeaders)) {
|
||||
ReplyError(ObexResponseCode::BadRequest);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -878,10 +878,19 @@ BluetoothOppManager::ServerDataHandler(UnixSocketBuffer* aMessage)
|
|||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
uint8_t opCode;
|
||||
/**
|
||||
* Ensure
|
||||
* - valid access to data[0], i.e., opCode
|
||||
* - received packet length smaller than max packet length
|
||||
*/
|
||||
int receivedLength = aMessage->GetSize();
|
||||
const uint8_t* data = aMessage->GetData();
|
||||
if (receivedLength < 1 || receivedLength > MAX_PACKET_LENGTH) {
|
||||
ReplyError(ObexResponseCode::BadRequest);
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t opCode;
|
||||
const uint8_t* data = aMessage->GetData();
|
||||
if (mPutPacketReceivedLength > 0) {
|
||||
opCode = mPutFinalFlag ? ObexRequestCode::PutFinal : ObexRequestCode::Put;
|
||||
} else {
|
||||
|
@ -897,12 +906,13 @@ BluetoothOppManager::ServerDataHandler(UnixSocketBuffer* aMessage)
|
|||
}
|
||||
}
|
||||
|
||||
ObexHeaderSet pktHeaders(opCode);
|
||||
ObexHeaderSet pktHeaders;
|
||||
if (opCode == ObexRequestCode::Connect) {
|
||||
// Section 3.3.1 "Connect", IrOBEX 1.2
|
||||
// [opcode:1][length:2][version:1][flags:1][MaxPktSizeWeCanReceive:2]
|
||||
// [Headers:var]
|
||||
if (!ParseHeaders(&data[7], receivedLength - 7, &pktHeaders)) {
|
||||
if (receivedLength < 7 ||
|
||||
!ParseHeaders(&data[7], receivedLength - 7, &pktHeaders)) {
|
||||
ReplyError(ObexResponseCode::BadRequest);
|
||||
return;
|
||||
}
|
||||
|
@ -912,7 +922,8 @@ BluetoothOppManager::ServerDataHandler(UnixSocketBuffer* aMessage)
|
|||
} else if (opCode == ObexRequestCode::Abort) {
|
||||
// Section 3.3.5 "Abort", IrOBEX 1.2
|
||||
// [opcode:1][length:2][Headers:var]
|
||||
if (!ParseHeaders(&data[3], receivedLength - 3, &pktHeaders)) {
|
||||
if (receivedLength < 3 ||
|
||||
!ParseHeaders(&data[3], receivedLength - 3, &pktHeaders)) {
|
||||
ReplyError(ObexResponseCode::BadRequest);
|
||||
return;
|
||||
}
|
||||
|
@ -922,7 +933,8 @@ BluetoothOppManager::ServerDataHandler(UnixSocketBuffer* aMessage)
|
|||
} else if (opCode == ObexRequestCode::Disconnect) {
|
||||
// Section 3.3.2 "Disconnect", IrOBEX 1.2
|
||||
// [opcode:1][length:2][Headers:var]
|
||||
if (!ParseHeaders(&data[3], receivedLength - 3, &pktHeaders)) {
|
||||
if (receivedLength < 3 ||
|
||||
!ParseHeaders(&data[3], receivedLength - 3, &pktHeaders)) {
|
||||
ReplyError(ObexResponseCode::BadRequest);
|
||||
return;
|
||||
}
|
||||
|
@ -1005,6 +1017,13 @@ BluetoothOppManager::ClientDataHandler(UnixSocketBuffer* aMessage)
|
|||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// Ensure valid access to data[0], i.e., opCode
|
||||
int receivedLength = aMessage->GetSize();
|
||||
if (receivedLength < 1) {
|
||||
BT_LOGR("Receive empty response packet");
|
||||
return;
|
||||
}
|
||||
|
||||
const uint8_t* data = aMessage->GetData();
|
||||
uint8_t opCode = data[0];
|
||||
|
||||
|
@ -1062,6 +1081,13 @@ BluetoothOppManager::ClientDataHandler(UnixSocketBuffer* aMessage)
|
|||
|
||||
AfterOppConnected();
|
||||
|
||||
// Ensure valid access to remote information
|
||||
if (receivedLength < 7) {
|
||||
BT_LOGR("The length of connect response packet is invalid");
|
||||
SendDisconnectRequest();
|
||||
return;
|
||||
}
|
||||
|
||||
// Keep remote information
|
||||
mRemoteObexVersion = data[3];
|
||||
mRemoteConnectionFlags = data[4];
|
||||
|
|
|
@ -1461,7 +1461,7 @@ TelephonyService.prototype = {
|
|||
}
|
||||
|
||||
// Store unique value in the list.
|
||||
disconnectedCalls = [...Set(disconnectedCalls)];
|
||||
disconnectedCalls = [...new Set(disconnectedCalls)];
|
||||
|
||||
let callsForStateChanged = [];
|
||||
|
||||
|
|
|
@ -25,16 +25,21 @@
|
|||
#include "xpcprivate.h" // For xpc::OptionsBase
|
||||
#include "jswrapper.h"
|
||||
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/ToJSValue.h"
|
||||
#include "mozilla/HoldDropJSObjects.h"
|
||||
#include "mozilla/scache/StartupCache.h"
|
||||
#include "mozilla/scache/StartupCacheUtils.h"
|
||||
#include "mozilla/unused.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsStringGlue.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
|
||||
using namespace mozilla::scache;
|
||||
using namespace JS;
|
||||
using namespace xpc;
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
class MOZ_STACK_CLASS LoadSubScriptOptions : public OptionsBase {
|
||||
public:
|
||||
|
@ -44,17 +49,20 @@ public:
|
|||
, target(cx)
|
||||
, charset(NullString())
|
||||
, ignoreCache(false)
|
||||
, async(false)
|
||||
{ }
|
||||
|
||||
virtual bool Parse() {
|
||||
return ParseObject("target", &target) &&
|
||||
ParseString("charset", charset) &&
|
||||
ParseBoolean("ignoreCache", &ignoreCache);
|
||||
ParseBoolean("ignoreCache", &ignoreCache) &&
|
||||
ParseBoolean("async", &async);
|
||||
}
|
||||
|
||||
RootedObject target;
|
||||
nsString charset;
|
||||
bool ignoreCache;
|
||||
bool async;
|
||||
};
|
||||
|
||||
|
||||
|
@ -108,6 +116,324 @@ ReportError(JSContext* cx, const char* origMsg, nsIURI* uri)
|
|||
return ReportError(cx, msg.get());
|
||||
}
|
||||
|
||||
nsresult
|
||||
PrepareScript(nsIURI* uri,
|
||||
JSContext* cx,
|
||||
RootedObject& targetObj,
|
||||
const char* uriStr,
|
||||
const nsAString& charset,
|
||||
const char* buf,
|
||||
int64_t len,
|
||||
bool reuseGlobal,
|
||||
MutableHandleScript script,
|
||||
MutableHandleFunction function)
|
||||
{
|
||||
JS::CompileOptions options(cx);
|
||||
options.setFileAndLine(uriStr, 1);
|
||||
if (!charset.IsVoid()) {
|
||||
char16_t* scriptBuf = nullptr;
|
||||
size_t scriptLength = 0;
|
||||
|
||||
nsresult rv =
|
||||
nsScriptLoader::ConvertToUTF16(nullptr, reinterpret_cast<const uint8_t*>(buf), len,
|
||||
charset, nullptr, scriptBuf, scriptLength);
|
||||
|
||||
JS::SourceBufferHolder srcBuf(scriptBuf, scriptLength,
|
||||
JS::SourceBufferHolder::GiveOwnership);
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
return ReportError(cx, LOAD_ERROR_BADCHARSET, uri);
|
||||
}
|
||||
|
||||
if (!reuseGlobal) {
|
||||
options.setHasPollutedScope(!JS_IsGlobalObject(targetObj));
|
||||
JS::Compile(cx, options, srcBuf, script);
|
||||
} else {
|
||||
AutoObjectVector scopeChain(cx);
|
||||
if (!JS_IsGlobalObject(targetObj) &&
|
||||
!scopeChain.append(targetObj)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
// XXXbz do we really not care if the compile fails???
|
||||
JS::CompileFunction(cx, scopeChain, options, nullptr, 0, nullptr,
|
||||
srcBuf, function);
|
||||
}
|
||||
} else {
|
||||
// We only use lazy source when no special encoding is specified because
|
||||
// the lazy source loader doesn't know the encoding.
|
||||
if (!reuseGlobal) {
|
||||
options.setSourceIsLazy(true)
|
||||
.setHasPollutedScope(!JS_IsGlobalObject(targetObj));
|
||||
JS::Compile(cx, options, buf, len, script);
|
||||
} else {
|
||||
AutoObjectVector scopeChain(cx);
|
||||
if (!JS_IsGlobalObject(targetObj) &&
|
||||
!scopeChain.append(targetObj)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
// XXXbz do we really not care if the compile fails???
|
||||
JS::CompileFunction(cx, scopeChain, options, nullptr, 0, nullptr,
|
||||
buf, len, function);
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
EvalScript(JSContext* cx,
|
||||
RootedObject& target_obj,
|
||||
MutableHandleValue retval,
|
||||
nsIURI* uri,
|
||||
bool cache,
|
||||
RootedScript& script,
|
||||
RootedFunction& function)
|
||||
{
|
||||
if (function) {
|
||||
script = JS_GetFunctionScript(cx, function);
|
||||
}
|
||||
|
||||
bool ok = false;
|
||||
if (function) {
|
||||
ok = JS_CallFunction(cx, target_obj, function, JS::HandleValueArray::empty(),
|
||||
retval);
|
||||
} else {
|
||||
if (JS_IsGlobalObject(target_obj)) {
|
||||
ok = JS_ExecuteScript(cx, script, retval);
|
||||
} else {
|
||||
JS::AutoObjectVector scopeChain(cx);
|
||||
ok = scopeChain.append(target_obj) &&
|
||||
JS_ExecuteScript(cx, scopeChain, script, retval);
|
||||
}
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
JSAutoCompartment rac(cx, target_obj);
|
||||
if (!JS_WrapValue(cx, retval))
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
nsAutoCString cachePath;
|
||||
JSVersion version = JS_GetVersion(cx);
|
||||
cachePath.AppendPrintf("jssubloader/%d", version);
|
||||
PathifyURI(uri, cachePath);
|
||||
|
||||
nsCOMPtr<nsIScriptSecurityManager> secman =
|
||||
do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
|
||||
if (!secman)
|
||||
return NS_OK;
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
nsresult rv = secman->GetSystemPrincipal(getter_AddRefs(principal));
|
||||
if (NS_FAILED(rv) || !principal)
|
||||
return rv;
|
||||
|
||||
if (cache && ok && !!script) {
|
||||
WriteCachedScript(StartupCache::GetSingleton(),
|
||||
cachePath, cx, principal, script);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
class AsyncScriptLoader : public nsIStreamLoaderObserver
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_NSISTREAMLOADEROBSERVER
|
||||
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(AsyncScriptLoader)
|
||||
|
||||
AsyncScriptLoader(nsIChannel* aChannel, bool aReuseGlobal,
|
||||
JSObject* aTargetObj, const nsAString& aCharset,
|
||||
bool aCache, Promise* aPromise)
|
||||
: mChannel(aChannel)
|
||||
, mTargetObj(aTargetObj)
|
||||
, mPromise(aPromise)
|
||||
, mCharset(aCharset)
|
||||
, mReuseGlobal(aReuseGlobal)
|
||||
, mCache(aCache)
|
||||
{
|
||||
// Needed for the cycle collector to manage mTargetObj.
|
||||
mozilla::HoldJSObjects(this);
|
||||
}
|
||||
|
||||
private:
|
||||
virtual ~AsyncScriptLoader() {
|
||||
mozilla::DropJSObjects(this);
|
||||
}
|
||||
|
||||
nsRefPtr<nsIChannel> mChannel;
|
||||
Heap<JSObject*> mTargetObj;
|
||||
nsRefPtr<Promise> mPromise;
|
||||
nsString mCharset;
|
||||
bool mReuseGlobal;
|
||||
bool mCache;
|
||||
};
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(AsyncScriptLoader)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AsyncScriptLoader)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIStreamLoaderObserver)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AsyncScriptLoader)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromise)
|
||||
tmp->mTargetObj = nullptr;
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AsyncScriptLoader)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromise)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(AsyncScriptLoader)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mTargetObj)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(AsyncScriptLoader)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(AsyncScriptLoader)
|
||||
|
||||
class MOZ_STACK_CLASS AutoRejectPromise
|
||||
{
|
||||
public:
|
||||
AutoRejectPromise(JSContext* cx,
|
||||
Promise* aPromise,
|
||||
nsIGlobalObject* aGlobalObject)
|
||||
: mCx(cx)
|
||||
, mPromise(aPromise)
|
||||
, mGlobalObject(aGlobalObject) {}
|
||||
|
||||
~AutoRejectPromise() {
|
||||
if (mPromise) {
|
||||
JS::Rooted<JS::Value> undefined(mCx, JS::UndefinedValue());
|
||||
mPromise->MaybeReject(mCx, undefined);
|
||||
}
|
||||
}
|
||||
|
||||
void ResolvePromise(HandleValue aResolveValue) {
|
||||
mPromise->MaybeResolve(aResolveValue);
|
||||
mPromise = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
JSContext* mCx;
|
||||
nsRefPtr<Promise> mPromise;
|
||||
nsCOMPtr<nsIGlobalObject> mGlobalObject;
|
||||
};
|
||||
|
||||
NS_IMETHODIMP
|
||||
AsyncScriptLoader::OnStreamComplete(nsIStreamLoader* aLoader,
|
||||
nsISupports* aContext,
|
||||
nsresult aStatus,
|
||||
uint32_t aLength,
|
||||
const uint8_t* aBuf)
|
||||
{
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
mChannel->GetURI(getter_AddRefs(uri));
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> globalObject = xpc::NativeGlobal(mTargetObj);
|
||||
AutoEntryScript aes(globalObject, "async loadSubScript");
|
||||
JSContext* cx = aes.cx();
|
||||
AutoRejectPromise autoPromise(cx, mPromise, globalObject);
|
||||
|
||||
if (NS_FAILED(aStatus)) {
|
||||
ReportError(cx, "Unable to load script.", uri);
|
||||
}
|
||||
// Just notify that we are done with this load.
|
||||
NS_ENSURE_SUCCESS(aStatus, NS_OK);
|
||||
|
||||
if (aLength == 0) {
|
||||
return ReportError(cx, LOAD_ERROR_NOCONTENT, uri);
|
||||
}
|
||||
|
||||
if (aLength > INT32_MAX) {
|
||||
return ReportError(cx, LOAD_ERROR_CONTENTTOOBIG, uri);
|
||||
}
|
||||
|
||||
RootedFunction function(cx);
|
||||
RootedScript script(cx);
|
||||
nsAutoCString spec;
|
||||
uri->GetSpec(spec);
|
||||
|
||||
RootedObject target_obj(cx, mTargetObj);
|
||||
|
||||
nsresult rv = PrepareScript(uri, cx, target_obj, spec.get(),
|
||||
mCharset,
|
||||
reinterpret_cast<const char*>(aBuf), aLength,
|
||||
mReuseGlobal, &script, &function);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> retval(cx);
|
||||
rv = EvalScript(cx, target_obj, &retval, uri, mCache, script, function);
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
autoPromise.ResolvePromise(retval);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
mozJSSubScriptLoader::ReadScriptAsync(nsIURI* uri, JSObject* targetObjArg,
|
||||
const nsAString& charset,
|
||||
nsIIOService* serv, bool reuseGlobal,
|
||||
bool cache, MutableHandleValue retval)
|
||||
{
|
||||
RootedObject target_obj(nsContentUtils::RootingCx(), targetObjArg);
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> globalObject = xpc::NativeGlobal(target_obj);
|
||||
ErrorResult result;
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
if (NS_WARN_IF(!jsapi.Init(globalObject))) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
nsRefPtr<Promise> promise = Promise::Create(globalObject, result);
|
||||
if (result.Failed()) {
|
||||
promise = nullptr;
|
||||
}
|
||||
|
||||
DebugOnly<bool> asJS = ToJSValue(jsapi.cx(), promise, retval);
|
||||
MOZ_ASSERT(asJS, "Should not fail to convert the promise to a JS value");
|
||||
|
||||
// We create a channel and call SetContentType, to avoid expensive MIME type
|
||||
// lookups (bug 632490).
|
||||
nsCOMPtr<nsIChannel> channel;
|
||||
nsresult rv;
|
||||
rv = NS_NewChannel(getter_AddRefs(channel),
|
||||
uri,
|
||||
nsContentUtils::GetSystemPrincipal(),
|
||||
nsILoadInfo::SEC_NORMAL,
|
||||
nsIContentPolicy::TYPE_OTHER,
|
||||
nullptr, // aLoadGroup
|
||||
nullptr, // aCallbacks
|
||||
nsIRequest::LOAD_NORMAL,
|
||||
serv);
|
||||
|
||||
if (!NS_SUCCEEDED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
channel->SetContentType(NS_LITERAL_CSTRING("application/javascript"));
|
||||
|
||||
nsRefPtr<AsyncScriptLoader> loadObserver =
|
||||
new AsyncScriptLoader(channel,
|
||||
reuseGlobal,
|
||||
target_obj,
|
||||
charset,
|
||||
cache,
|
||||
promise);
|
||||
|
||||
nsCOMPtr<nsIStreamLoader> loader;
|
||||
rv = NS_NewStreamLoader(getter_AddRefs(loader), loadObserver);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIStreamListener> listener = loader.get();
|
||||
return channel->AsyncOpen(listener, nullptr);
|
||||
}
|
||||
|
||||
nsresult
|
||||
mozJSSubScriptLoader::ReadScript(nsIURI* uri, JSContext* cx, JSObject* targetObjArg,
|
||||
const nsAString& charset, const char* uriStr,
|
||||
|
@ -115,11 +441,11 @@ mozJSSubScriptLoader::ReadScript(nsIURI* uri, JSContext* cx, JSObject* targetObj
|
|||
bool reuseGlobal, JS::MutableHandleScript script,
|
||||
JS::MutableHandleFunction function)
|
||||
{
|
||||
RootedObject target_obj(cx, targetObjArg);
|
||||
|
||||
script.set(nullptr);
|
||||
function.set(nullptr);
|
||||
|
||||
RootedObject target_obj(cx, targetObjArg);
|
||||
|
||||
// We create a channel and call SetContentType, to avoid expensive MIME type
|
||||
// lookups (bug 632490).
|
||||
nsCOMPtr<nsIChannel> chan;
|
||||
|
@ -157,56 +483,13 @@ mozJSSubScriptLoader::ReadScript(nsIURI* uri, JSContext* cx, JSObject* targetObj
|
|||
|
||||
nsCString buf;
|
||||
rv = NS_ReadInputStreamToString(instream, buf, len);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
JS::CompileOptions options(cx);
|
||||
options.setFileAndLine(uriStr, 1);
|
||||
if (!charset.IsVoid()) {
|
||||
char16_t* scriptBuf = nullptr;
|
||||
size_t scriptLength = 0;
|
||||
|
||||
rv = nsScriptLoader::ConvertToUTF16(nullptr, reinterpret_cast<const uint8_t*>(buf.get()), len,
|
||||
charset, nullptr, scriptBuf, scriptLength);
|
||||
|
||||
JS::SourceBufferHolder srcBuf(scriptBuf, scriptLength,
|
||||
JS::SourceBufferHolder::GiveOwnership);
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
return ReportError(cx, LOAD_ERROR_BADCHARSET, uri);
|
||||
}
|
||||
|
||||
if (!reuseGlobal) {
|
||||
options.setHasPollutedScope(!JS_IsGlobalObject(target_obj));
|
||||
JS::Compile(cx, options, srcBuf, script);
|
||||
} else {
|
||||
AutoObjectVector scopeChain(cx);
|
||||
if (!JS_IsGlobalObject(target_obj) &&
|
||||
!scopeChain.append(target_obj)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
// XXXbz do we really not care if the compile fails???
|
||||
JS::CompileFunction(cx, scopeChain, options, nullptr, 0, nullptr,
|
||||
srcBuf, function);
|
||||
}
|
||||
} else {
|
||||
// We only use lazy source when no special encoding is specified because
|
||||
// the lazy source loader doesn't know the encoding.
|
||||
if (!reuseGlobal) {
|
||||
options.setSourceIsLazy(true)
|
||||
.setHasPollutedScope(!JS_IsGlobalObject(target_obj));
|
||||
JS::Compile(cx, options, buf.get(), len, script);
|
||||
} else {
|
||||
AutoObjectVector scopeChain(cx);
|
||||
if (!JS_IsGlobalObject(target_obj) &&
|
||||
!scopeChain.append(target_obj)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
// XXXbz do we really not care if the compile fails???
|
||||
JS::CompileFunction(cx, scopeChain, options, nullptr, 0, nullptr,
|
||||
buf.get(), len, function);
|
||||
}
|
||||
}
|
||||
rv = PrepareScript(uri, cx, target_obj, uriStr, charset,
|
||||
buf.get(), len,
|
||||
reuseGlobal,
|
||||
script, function);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -351,7 +634,6 @@ mozJSSubScriptLoader::DoLoadSubScriptWithOptions(const nsAString& url,
|
|||
uriStr = tmp;
|
||||
}
|
||||
|
||||
bool writeScript = false;
|
||||
JSVersion version = JS_GetVersion(cx);
|
||||
nsAutoCString cachePath;
|
||||
cachePath.AppendPrintf("jssubloader/%d", version);
|
||||
|
@ -361,45 +643,25 @@ mozJSSubScriptLoader::DoLoadSubScriptWithOptions(const nsAString& url,
|
|||
RootedScript script(cx);
|
||||
if (cache && !options.ignoreCache)
|
||||
rv = ReadCachedScript(cache, cachePath, cx, mSystemPrincipal, &script);
|
||||
|
||||
// If we are doing an async load, trigger it and bail out.
|
||||
if (!script && options.async) {
|
||||
return ReadScriptAsync(uri, targetObj, options.charset, serv,
|
||||
reusingGlobal, !!cache, retval);
|
||||
}
|
||||
|
||||
if (!script) {
|
||||
rv = ReadScript(uri, cx, targetObj, options.charset,
|
||||
static_cast<const char*>(uriStr.get()), serv,
|
||||
principal, reusingGlobal, &script, &function);
|
||||
writeScript = !!script;
|
||||
} else {
|
||||
cache = nullptr;
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv) || (!script && !function))
|
||||
return rv;
|
||||
|
||||
if (function) {
|
||||
script = JS_GetFunctionScript(cx, function);
|
||||
}
|
||||
|
||||
bool ok = false;
|
||||
if (function) {
|
||||
ok = JS_CallFunction(cx, targetObj, function, JS::HandleValueArray::empty(),
|
||||
retval);
|
||||
} else {
|
||||
if (JS_IsGlobalObject(targetObj)) {
|
||||
ok = JS_ExecuteScript(cx, script, retval);
|
||||
} else {
|
||||
JS::AutoObjectVector scopeChain(cx);
|
||||
ok = scopeChain.append(targetObj) &&
|
||||
JS_ExecuteScript(cx, scopeChain, script, retval);
|
||||
}
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
JSAutoCompartment rac(cx, result_obj);
|
||||
if (!JS_WrapValue(cx, retval))
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
if (cache && ok && writeScript) {
|
||||
WriteCachedScript(cache, cachePath, cx, mSystemPrincipal, script);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
return EvalScript(cx, targetObj, retval, uri, !!cache, script, function);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -39,6 +39,11 @@ private:
|
|||
bool reuseGlobal, JS::MutableHandleScript script,
|
||||
JS::MutableHandleFunction function);
|
||||
|
||||
nsresult ReadScriptAsync(nsIURI* uri, JSObject* target_obj,
|
||||
const nsAString& charset,
|
||||
nsIIOService* serv, bool reuseGlobal,
|
||||
bool cache, JS::MutableHandleValue retval);
|
||||
|
||||
nsresult DoLoadSubScriptWithOptions(const nsAString& url,
|
||||
LoadSubScriptOptions& options,
|
||||
JSContext* cx,
|
||||
|
|
|
@ -1,2 +1,5 @@
|
|||
var tags = [];
|
||||
function makeTags() {}
|
||||
|
||||
// This will be the return value of the script.
|
||||
42
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
var srvScope = {};
|
||||
|
||||
function success(result) {
|
||||
equal(result, 42, "Result of script is correct");
|
||||
ok('makeTags' in srvScope && srvScope.makeTags instanceof Function,
|
||||
"makeTags is a function.");
|
||||
do_test_finished();
|
||||
}
|
||||
|
||||
function error() {
|
||||
ok(false, "error loading the script asynchronously.");
|
||||
do_test_finished();
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
do_test_pending();
|
||||
|
||||
var file = do_get_file("bug451678_subscript.js");
|
||||
var ios = Cc["@mozilla.org/network/io-service;1"]
|
||||
.getService(Ci.nsIIOService);
|
||||
var uri = ios.newFileURI(file);
|
||||
var scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
|
||||
.getService(Ci.mozIJSSubScriptLoader);
|
||||
var p = scriptLoader.loadSubScriptWithOptions(uri.spec,
|
||||
{ target: srvScope,
|
||||
async: true });
|
||||
p.then(success, error);
|
||||
}
|
|
@ -55,6 +55,7 @@ support-files =
|
|||
[test_bug1081990.js]
|
||||
[test_bug1110546.js]
|
||||
[test_bug1131707.js]
|
||||
[test_bug1150106.js]
|
||||
[test_bug1150771.js]
|
||||
[test_bug1151385.js]
|
||||
[test_bug_442086.js]
|
||||
|
|
|
@ -289,10 +289,6 @@
|
|||
</activity>
|
||||
#endif
|
||||
|
||||
<activity android:name="org.mozilla.gecko.StartPane"
|
||||
android:theme="@style/GeckoStartPane"
|
||||
android:excludeFromRecents="true"/>
|
||||
|
||||
<activity android:name="org.mozilla.gecko.webapp.Dispatcher"
|
||||
android:noHistory="true" >
|
||||
<intent-filter>
|
||||
|
|
|
@ -49,6 +49,7 @@ import org.mozilla.gecko.preferences.ClearOnShutdownPref;
|
|||
import org.mozilla.gecko.preferences.GeckoPreferences;
|
||||
import org.mozilla.gecko.prompts.Prompt;
|
||||
import org.mozilla.gecko.prompts.PromptListItem;
|
||||
import org.mozilla.gecko.sync.repositories.android.FennecTabsRepository;
|
||||
import org.mozilla.gecko.sync.setup.SyncAccounts;
|
||||
import org.mozilla.gecko.tabqueue.TabQueueHelper;
|
||||
import org.mozilla.gecko.tabqueue.TabQueuePrompt;
|
||||
|
@ -784,6 +785,7 @@ public class BrowserApp extends GeckoApp
|
|||
"Menu:Remove",
|
||||
"Reader:Share",
|
||||
"Sanitize:ClearHistory",
|
||||
"Sanitize:ClearSyncedTabs",
|
||||
"Settings:Show",
|
||||
"Telemetry:Gather",
|
||||
"Updater:Launch");
|
||||
|
@ -1348,6 +1350,7 @@ public class BrowserApp extends GeckoApp
|
|||
"Menu:Remove",
|
||||
"Reader:Share",
|
||||
"Sanitize:ClearHistory",
|
||||
"Sanitize:ClearSyncedTabs",
|
||||
"Settings:Show",
|
||||
"Telemetry:Gather",
|
||||
"Updater:Launch");
|
||||
|
@ -1405,6 +1408,15 @@ public class BrowserApp extends GeckoApp
|
|||
});
|
||||
}
|
||||
|
||||
private void handleClearSyncedTabs() {
|
||||
ThreadUtils.postToBackgroundThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
FennecTabsRepository.deleteNonLocalClientsAndTabs(getContext());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void shareCurrentUrl() {
|
||||
Tab tab = Tabs.getInstance().getSelectedTab();
|
||||
if (tab == null) {
|
||||
|
@ -1685,6 +1697,9 @@ public class BrowserApp extends GeckoApp
|
|||
} else if ("Sanitize:ClearHistory".equals(event)) {
|
||||
handleClearHistory(message.optBoolean("clearSearchHistory", false));
|
||||
callback.sendSuccess(true);
|
||||
} else if ("Sanitize:ClearSyncedTabs".equals(event)) {
|
||||
handleClearSyncedTabs();
|
||||
callback.sendSuccess(true);
|
||||
} else if ("Settings:Show".equals(event)) {
|
||||
final String resource =
|
||||
message.optString(GeckoPreferences.INTENT_EXTRA_RESOURCES, null);
|
||||
|
|
|
@ -260,6 +260,7 @@ size. -->
|
|||
<!ENTITY pref_private_data_offlineApps "Offline website data">
|
||||
<!ENTITY pref_private_data_siteSettings2 "Site settings">
|
||||
<!ENTITY pref_private_data_downloadFiles2 "Downloads">
|
||||
<!ENTITY pref_private_data_syncedTabs "Synced tabs">
|
||||
|
||||
|
||||
<!ENTITY pref_about_firefox "About &brandShortName;">
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<resources xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<style name="GeckoStartPane" parent="Gecko.Dialog"/>
|
||||
|
||||
</resources>
|
|
@ -81,6 +81,7 @@
|
|||
<item>@string/pref_private_data_cache</item>
|
||||
<item>@string/pref_private_data_offlineApps</item>
|
||||
<item>@string/pref_private_data_siteSettings</item>
|
||||
<item>@string/pref_private_data_syncedTabs</item>
|
||||
</string-array>
|
||||
<string-array name="pref_private_data_defaults">
|
||||
<item>true</item>
|
||||
|
@ -92,6 +93,7 @@
|
|||
<item>true</item>
|
||||
<item>true</item>
|
||||
<item>true</item>
|
||||
<item>true</item>
|
||||
</string-array>
|
||||
<string-array name="pref_private_data_values">
|
||||
<item>private.data.history</item>
|
||||
|
@ -103,6 +105,7 @@
|
|||
<item>private.data.cache</item>
|
||||
<item>private.data.offlineApps</item>
|
||||
<item>private.data.siteSettings</item>
|
||||
<item>private.data.syncedTabs</item>
|
||||
</string-array>
|
||||
<string-array name="pref_private_data_keys">
|
||||
<item>private.data.history</item>
|
||||
|
@ -114,6 +117,7 @@
|
|||
<item>private.data.cache</item>
|
||||
<item>private.data.offlineApps</item>
|
||||
<item>private.data.siteSettings</item>
|
||||
<item>private.data.syncedTabs</item>
|
||||
</string-array>
|
||||
<string-array name="pref_clear_on_exit_defaults">
|
||||
<item>false</item>
|
||||
|
@ -125,6 +129,7 @@
|
|||
<item>false</item>
|
||||
<item>false</item>
|
||||
<item>false</item>
|
||||
<item>false</item>
|
||||
</string-array>
|
||||
<string-array name="pref_restore_entries">
|
||||
<item>@string/pref_restore_always</item>
|
||||
|
|
|
@ -104,8 +104,6 @@
|
|||
<item name="floatingHintEditTextStyle">@style/FloatingHintEditText</item>
|
||||
</style>
|
||||
|
||||
<style name="GeckoStartPane" parent="GeckoBase"/>
|
||||
|
||||
<!-- Make an activity appear like an overlay. -->
|
||||
<style name="OverlayActivity" parent="Gecko">
|
||||
<item name="android:windowBackground">@android:color/transparent</item>
|
||||
|
|
|
@ -229,6 +229,7 @@
|
|||
<string name="pref_private_data_offlineApps">&pref_private_data_offlineApps;</string>
|
||||
<string name="pref_private_data_siteSettings">&pref_private_data_siteSettings2;</string>
|
||||
<string name="pref_private_data_downloadFiles2">&pref_private_data_downloadFiles2;</string>
|
||||
<string name="pref_private_data_syncedTabs">&pref_private_data_syncedTabs;</string>
|
||||
<string name="pref_import_android">&pref_import_android;</string>
|
||||
<string name="pref_import_android_summary">&pref_import_android_summary;</string>
|
||||
<string name="pref_update_autodownload">&pref_update_autodownload2;</string>
|
||||
|
|
|
@ -15,6 +15,7 @@ Cu.import("resource://gre/modules/Messaging.jsm");
|
|||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://gre/modules/Downloads.jsm");
|
||||
Cu.import("resource://gre/modules/osfile.jsm");
|
||||
Cu.import("resource://gre/modules/Accounts.jsm");
|
||||
|
||||
function dump(a) {
|
||||
Services.console.logStringMessage(a);
|
||||
|
@ -266,7 +267,25 @@ Sanitizer.prototype = {
|
|||
{
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
syncedTabs: {
|
||||
clear: function ()
|
||||
{
|
||||
return Messaging.sendRequestForResult({ type: "Sanitize:ClearSyncedTabs" })
|
||||
.catch(e => Cu.reportError("Java-side synced tabs clearing failed: " + e));
|
||||
},
|
||||
|
||||
canClear: function(aCallback)
|
||||
{
|
||||
Accounts.anySyncAccountsExist().then(aCallback)
|
||||
.catch(function(err) {
|
||||
Cu.reportError("Java-side synced tabs clearing failed: " + err)
|
||||
aCallback(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -233,8 +233,9 @@ this.MobileIdentityManager = {
|
|||
}
|
||||
|
||||
return this._iccInfo;
|
||||
#endif
|
||||
#else
|
||||
return null;
|
||||
#endif
|
||||
},
|
||||
|
||||
get iccIds() {
|
||||
|
@ -253,8 +254,9 @@ this.MobileIdentityManager = {
|
|||
}
|
||||
|
||||
return this._iccIds;
|
||||
#endif
|
||||
#else
|
||||
return null;
|
||||
#endif
|
||||
},
|
||||
|
||||
get credStore() {
|
||||
|
|
|
@ -192,6 +192,7 @@ user_pref("browser.download.panel.shown", true);
|
|||
// Assume the about:newtab page's intro panels have been shown to not depend on
|
||||
// which test runs first and happens to open about:newtab
|
||||
user_pref("browser.newtabpage.introShown", true);
|
||||
user_pref("browser.newtabpage.updateIntroShown", true);
|
||||
|
||||
// Tell the PBackground infrastructure to run a test at startup.
|
||||
user_pref("pbackground.testing", true);
|
||||
|
|
|
@ -100,6 +100,34 @@ const PREFS_BLACKLIST = [
|
|||
/^print[.]macosx[.]pagesetup/,
|
||||
];
|
||||
|
||||
// Table of getters for various preference types.
|
||||
// It's important to use getComplexValue for strings: it returns Unicode (wchars), getCharPref returns UTF-8 encoded chars.
|
||||
const PREFS_GETTERS = {};
|
||||
|
||||
PREFS_GETTERS[Ci.nsIPrefBranch.PREF_STRING] = (prefs, name) => prefs.getComplexValue(name, Ci.nsISupportsString).data;
|
||||
PREFS_GETTERS[Ci.nsIPrefBranch.PREF_INT] = (prefs, name) => prefs.getIntPref(name);
|
||||
PREFS_GETTERS[Ci.nsIPrefBranch.PREF_BOOL] = (prefs, name) => prefs.getBoolPref(name);
|
||||
|
||||
// Return the preferences filtered by PREFS_BLACKLIST and PREFS_WHITELIST lists
|
||||
// and also by the custom 'filter'-ing function.
|
||||
function getPrefList(filter) {
|
||||
filter = filter || (name => true);
|
||||
function getPref(name) {
|
||||
let type = Services.prefs.getPrefType(name);
|
||||
if (!(type in PREFS_GETTERS))
|
||||
throw new Error("Unknown preference type " + type + " for " + name);
|
||||
return PREFS_GETTERS[type](Services.prefs, name);
|
||||
}
|
||||
|
||||
return PREFS_WHITELIST.reduce(function(prefs, branch) {
|
||||
Services.prefs.getChildList(branch).forEach(function(name) {
|
||||
if (filter(name) && !PREFS_BLACKLIST.some(re => re.test(name)))
|
||||
prefs[name] = getPref(name);
|
||||
});
|
||||
return prefs;
|
||||
}, {});
|
||||
}
|
||||
|
||||
this.Troubleshoot = {
|
||||
|
||||
/**
|
||||
|
@ -220,45 +248,11 @@ let dataProviders = {
|
|||
},
|
||||
|
||||
modifiedPreferences: function modifiedPreferences(done) {
|
||||
function getPref(name) {
|
||||
let table = {};
|
||||
table[Ci.nsIPrefBranch.PREF_STRING] = "getCharPref";
|
||||
table[Ci.nsIPrefBranch.PREF_INT] = "getIntPref";
|
||||
table[Ci.nsIPrefBranch.PREF_BOOL] = "getBoolPref";
|
||||
let type = Services.prefs.getPrefType(name);
|
||||
if (!(type in table))
|
||||
throw new Error("Unknown preference type " + type + " for " + name);
|
||||
return Services.prefs[table[type]](name);
|
||||
}
|
||||
done(PREFS_WHITELIST.reduce(function (prefs, branch) {
|
||||
Services.prefs.getChildList(branch).forEach(function (name) {
|
||||
if (Services.prefs.prefHasUserValue(name) &&
|
||||
!PREFS_BLACKLIST.some(function (re) re.test(name)))
|
||||
prefs[name] = getPref(name);
|
||||
});
|
||||
return prefs;
|
||||
}, {}));
|
||||
done(getPrefList(name => Services.prefs.prefHasUserValue(name)));
|
||||
},
|
||||
|
||||
lockedPreferences: function lockedPreferences(done) {
|
||||
function getPref(name) {
|
||||
let table = {};
|
||||
table[Ci.nsIPrefBranch.PREF_STRING] = "getCharPref";
|
||||
table[Ci.nsIPrefBranch.PREF_INT] = "getIntPref";
|
||||
table[Ci.nsIPrefBranch.PREF_BOOL] = "getBoolPref";
|
||||
let type = Services.prefs.getPrefType(name);
|
||||
if (!(type in table))
|
||||
throw new Error("Unknown preference type " + type + " for " + name);
|
||||
return Services.prefs[table[type]](name);
|
||||
}
|
||||
done(PREFS_WHITELIST.reduce(function (prefs, branch) {
|
||||
Services.prefs.getChildList(branch).forEach(function (name) {
|
||||
if (Services.prefs.prefIsLocked(name) &&
|
||||
!PREFS_BLACKLIST.some(function (re) re.test(name)))
|
||||
prefs[name] = getPref(name);
|
||||
});
|
||||
return prefs;
|
||||
}, {}));
|
||||
done(getPrefList(name => Services.prefs.prefIsLocked(name)));
|
||||
},
|
||||
|
||||
graphics: function graphics(done) {
|
||||
|
|
|
@ -68,6 +68,22 @@ let tests = [
|
|||
done();
|
||||
});
|
||||
},
|
||||
|
||||
function unicodePreferences(done) {
|
||||
let name = "font.name.sans-serif.x-western";
|
||||
let utf8Value = "\xc4\x8capk\xc5\xafv Krasopis"
|
||||
let unicodeValue = "\u010Capk\u016Fv Krasopis";
|
||||
|
||||
// set/getCharPref work with 8bit strings (utf8)
|
||||
Services.prefs.setCharPref(name, utf8Value);
|
||||
|
||||
Troubleshoot.snapshot(function (snapshot) {
|
||||
let p = snapshot.modifiedPreferences;
|
||||
is(p[name], unicodeValue, "The pref should have correct Unicode value.");
|
||||
Services.prefs.deleteBranch(name);
|
||||
done();
|
||||
});
|
||||
}
|
||||
];
|
||||
|
||||
// This is inspired by JSON Schema, or by the example on its Wikipedia page
|
||||
|
|
|
@ -18,8 +18,6 @@ support-files =
|
|||
[test_0015_check_incompat_basic_addons.xul]
|
||||
[test_0016_check_incompat_basic_license_addons.xul]
|
||||
[test_0017_check_staging_basic.xul]
|
||||
skip-if = os != 'win'
|
||||
reason = Bug 918029 and bug 1164560 - timeout caused by copying too many files.
|
||||
[test_0021_check_billboard.xul]
|
||||
[test_0022_check_billboard_license.xul]
|
||||
[test_0023_check_incompat_billboard.xul]
|
||||
|
@ -61,10 +59,10 @@ reason = Bug 918029 and bug 1164560 - timeout caused by copying too many files.
|
|||
[test_0095_restartNotification_remoteInvalidNumber.xul]
|
||||
[test_0096_restartNotification_stagedBackground.xul]
|
||||
[test_0097_restartNotification_stagedServiceBackground.xul]
|
||||
skip-if = os != 'win'
|
||||
reason = only Windows has the maintenance service.
|
||||
[test_0101_background_restartNotification.xul]
|
||||
[test_0102_background_restartNotification_staging.xul]
|
||||
skip-if = os == 'linux'
|
||||
reason = Bug 918029 - timeout caused by copying too many files.
|
||||
[test_0103_background_restartNotification_stagingService.xul]
|
||||
skip-if = os != 'win'
|
||||
reason = only Windows has the maintenance service.
|
||||
|
|
|
@ -330,6 +330,7 @@ function runTestDefaultWaitForWindowClosed() {
|
|||
|
||||
setupFiles();
|
||||
setupPrefs();
|
||||
gEnv.set("MOZ_TEST_SKIP_UPDATE_STAGE", "1");
|
||||
removeUpdateDirsAndFiles();
|
||||
reloadUpdateManagerData();
|
||||
setupAddons(runTest);
|
||||
|
@ -358,6 +359,7 @@ function finishTestDefault() {
|
|||
verifyTestsRan();
|
||||
|
||||
resetPrefs();
|
||||
gEnv.set("MOZ_TEST_SKIP_UPDATE_STAGE", "");
|
||||
resetFiles();
|
||||
removeUpdateDirsAndFiles();
|
||||
reloadUpdateManagerData();
|
||||
|
|
|
@ -130,6 +130,10 @@ XPCOMUtils.defineLazyGetter(this, "gPrefRoot", function test_gPR() {
|
|||
return Services.prefs.getBranch(null);
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gEnv",
|
||||
"@mozilla.org/process/environment;1",
|
||||
"nsIEnvironment");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gZipW", function test_gZipW() {
|
||||
return Cc["@mozilla.org/zipwriter;1"].
|
||||
createInstance(Ci.nsIZipWriter);
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
// These are generated at compile time based on the DER file for the channel
|
||||
// being used
|
||||
#ifdef MOZ_VERIFY_MAR_SIGNATURE
|
||||
#ifdef UPDATER_XPCSHELL_CERT
|
||||
#ifdef TEST_UPDATER
|
||||
#include "../xpcshellCert.h"
|
||||
#else
|
||||
#include "primaryCert.h"
|
||||
|
@ -85,7 +85,7 @@ ArchiveReader::VerifySignature()
|
|||
#ifndef MOZ_VERIFY_MAR_SIGNATURE
|
||||
return OK;
|
||||
#else
|
||||
#ifdef UPDATER_XPCSHELL_CERT
|
||||
#ifdef TEST_UPDATER
|
||||
int rv = VerifyLoadedCert(mArchive, xpcshellCertData);
|
||||
#else
|
||||
int rv = VerifyLoadedCert(mArchive, primaryCertData);
|
||||
|
|
|
@ -8,6 +8,6 @@ Program('updater-xpcshell')
|
|||
|
||||
updater_rel_path = '../'
|
||||
DIST_INSTALL = False
|
||||
DEFINES['UPDATER_XPCSHELL_CERT'] = True
|
||||
DEFINES['TEST_UPDATER'] = True
|
||||
include('../updater-common.build')
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
|
|
@ -2260,7 +2260,18 @@ UpdateThreadFunc(void *param)
|
|||
#endif
|
||||
|
||||
if (rv == OK && sStagedUpdate && !sIsOSUpdate) {
|
||||
#ifdef TEST_UPDATER
|
||||
// The MOZ_TEST_SKIP_UPDATE_STAGE environment variable prevents copying
|
||||
// the files in dist/bin in the test updater when staging an update since
|
||||
// this can cause tests to timeout.
|
||||
if (getenv("MOZ_TEST_SKIP_UPDATE_STAGE")) {
|
||||
rv = OK;
|
||||
} else {
|
||||
rv = CopyInstallDirToDestDir();
|
||||
}
|
||||
#else
|
||||
rv = CopyInstallDirToDestDir();
|
||||
#endif
|
||||
}
|
||||
|
||||
if (rv == OK) {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
// Microsoft Visual C++ generated resource script.
|
||||
//
|
||||
#ifdef UPDATER_XPCSHELL_CERT
|
||||
#ifdef TEST_UPDATER
|
||||
#include "../resource.h"
|
||||
#define MANIFEST_PATH "../updater.exe.manifest"
|
||||
#define COMCTL32_MANIFEST_PATH "../updater.exe.comctl32.manifest"
|
||||
|
|
Загрузка…
Ссылка в новой задаче