Merge mozilla-central to mozilla-inbound

This commit is contained in:
Carsten "Tomcat" Book 2015-05-21 15:14:47 +02:00
Родитель 62a256efa2 b4dc8a79b7
Коммит e40acbfc01
68 изменённых файлов: 4720 добавлений и 438 удалений

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

@ -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"