Merge mozilla-central to mozilla-inbound

This commit is contained in:
Carsten "Tomcat" Book 2015-09-04 16:14:00 +02:00
commit 916718880d
149 changed files with 2653 additions and 617 deletions

View File

@ -15,11 +15,11 @@
<project name="platform_build" path="build" remote="b2g" revision="e935894ef5f27e2f04b9e929a45a958e6288a223">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="d7385b79e68d4ad662cacf810506e9ee53345d23"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="85ae6808d298a4010aaab341c66699f1b87eec9c"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="51ebaf824cc634665c5efcae95b8301ad1758c5e"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="f5d65f5b17d9766d7925aefd0486a1e526ae9bf0"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>

View File

@ -15,11 +15,11 @@
<project name="platform_build" path="build" remote="b2g" revision="e935894ef5f27e2f04b9e929a45a958e6288a223">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="d7385b79e68d4ad662cacf810506e9ee53345d23"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="85ae6808d298a4010aaab341c66699f1b87eec9c"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="51ebaf824cc634665c5efcae95b8301ad1758c5e"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="f5d65f5b17d9766d7925aefd0486a1e526ae9bf0"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>

View File

@ -19,12 +19,12 @@
<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="d7385b79e68d4ad662cacf810506e9ee53345d23"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="85ae6808d298a4010aaab341c66699f1b87eec9c"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="2d58f4b9206b50b8fda0d5036da6f0c62608db7c"/>
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="d70e4bfdcb65e7514de0f9315b74aea1c811678d"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="51ebaf824cc634665c5efcae95b8301ad1758c5e"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="f5d65f5b17d9766d7925aefd0486a1e526ae9bf0"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
<!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>

View File

@ -17,9 +17,9 @@
</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="d7385b79e68d4ad662cacf810506e9ee53345d23"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="85ae6808d298a4010aaab341c66699f1b87eec9c"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="51ebaf824cc634665c5efcae95b8301ad1758c5e"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="f5d65f5b17d9766d7925aefd0486a1e526ae9bf0"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="e1a50a20b3383e3b0959e5b32ef429425fd6be5b"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>

View File

@ -15,11 +15,11 @@
<project name="platform_build" path="build" remote="b2g" revision="e935894ef5f27e2f04b9e929a45a958e6288a223">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="d7385b79e68d4ad662cacf810506e9ee53345d23"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="85ae6808d298a4010aaab341c66699f1b87eec9c"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="51ebaf824cc634665c5efcae95b8301ad1758c5e"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="f5d65f5b17d9766d7925aefd0486a1e526ae9bf0"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>

View File

@ -15,11 +15,11 @@
<project name="platform_build" path="build" remote="b2g" revision="05a36844c1046a1eb07d5b1325f85ed741f961ea">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="d7385b79e68d4ad662cacf810506e9ee53345d23"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="85ae6808d298a4010aaab341c66699f1b87eec9c"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="51ebaf824cc634665c5efcae95b8301ad1758c5e"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="f5d65f5b17d9766d7925aefd0486a1e526ae9bf0"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>

View File

@ -19,12 +19,12 @@
<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="d7385b79e68d4ad662cacf810506e9ee53345d23"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="85ae6808d298a4010aaab341c66699f1b87eec9c"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="2d58f4b9206b50b8fda0d5036da6f0c62608db7c"/>
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="d70e4bfdcb65e7514de0f9315b74aea1c811678d"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="51ebaf824cc634665c5efcae95b8301ad1758c5e"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="f5d65f5b17d9766d7925aefd0486a1e526ae9bf0"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
<!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>

View File

@ -15,11 +15,11 @@
<project name="platform_build" path="build" remote="b2g" revision="e935894ef5f27e2f04b9e929a45a958e6288a223">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="d7385b79e68d4ad662cacf810506e9ee53345d23"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="85ae6808d298a4010aaab341c66699f1b87eec9c"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="51ebaf824cc634665c5efcae95b8301ad1758c5e"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="f5d65f5b17d9766d7925aefd0486a1e526ae9bf0"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>

View File

@ -1,9 +1,9 @@
{
"git": {
"git_revision": "d7385b79e68d4ad662cacf810506e9ee53345d23",
"git_revision": "85ae6808d298a4010aaab341c66699f1b87eec9c",
"remote": "https://git.mozilla.org/releases/gaia.git",
"branch": ""
},
"revision": "c379332cd161e9675eedc580725f101a18be38f6",
"revision": "40beee0c2eb5e129dc5a30f0fc83f73be0eb33da",
"repo_path": "integration/gaia-central"
}

View File

@ -17,9 +17,9 @@
</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="d7385b79e68d4ad662cacf810506e9ee53345d23"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="85ae6808d298a4010aaab341c66699f1b87eec9c"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="51ebaf824cc634665c5efcae95b8301ad1758c5e"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="f5d65f5b17d9766d7925aefd0486a1e526ae9bf0"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="e1a50a20b3383e3b0959e5b32ef429425fd6be5b"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>

View File

@ -15,11 +15,11 @@
<project name="platform_build" path="build" remote="b2g" revision="05a36844c1046a1eb07d5b1325f85ed741f961ea">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="d7385b79e68d4ad662cacf810506e9ee53345d23"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="85ae6808d298a4010aaab341c66699f1b87eec9c"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9e2923fd6cab93cf88b4b9ada82225e44fe6635"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="51ebaf824cc634665c5efcae95b8301ad1758c5e"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="f5d65f5b17d9766d7925aefd0486a1e526ae9bf0"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>

View File

@ -248,7 +248,7 @@ let gSyncUI = {
},
handleToolbarButton: function SUI_handleStatusbarButton() {
if (this._needsSetup())
if (this._needsSetup() || this._loginFailed())
this.openSetup();
else
this.doSync();

View File

@ -6795,9 +6795,13 @@ var gIdentityHandler = {
delete this._permissionList;
return this._permissionList = document.getElementById("identity-popup-permission-list");
},
get _permissionSubviewList () {
delete this._permissionSubviewList;
return this._permissionSubviewList = document.getElementById("identity-popup-permission-subview-list");
get _permissionSubviewListPageFunctionality () {
delete this._permissionSubviewListPageFunctionality;
return this._permissionSubviewListPageFunctionality = document.getElementById("permission-subview-list-page-functionality");
},
get _permissionSubviewListSystemAccess () {
delete this._permissionSubviewListSystemAccess;
return this._permissionSubviewListSystemAccess = document.getElementById("permission-subview-list-system-access");
},
/**
@ -6819,7 +6823,8 @@ var gIdentityHandler = {
this._identityIcon = document.getElementById("page-proxy-favicon");
this._permissionsContainer = document.getElementById("identity-popup-permissions");
this._permissionList = document.getElementById("identity-popup-permission-list");
this._permissionSubviewList = document.getElementById("identity-popup-permission-subview-list");
this._permissionSubviewListPageFunctionality = document.getElementById("permission-subview-list-page-functionality");
this._permissionSubviewListSystemAccess = document.getElementById("permission-subview-list-system-access");
},
/**
@ -7290,26 +7295,33 @@ var gIdentityHandler = {
},
updateSitePermissions: function () {
while (this._permissionList.hasChildNodes())
this._permissionList.removeChild(this._permissionList.lastChild);
while (this._permissionSubviewList.hasChildNodes())
this._permissionSubviewList.removeChild(this._permissionSubviewList.lastChild);
// Clear all lists and then repopulate them.
this._permissionList.textContent = "";
this._permissionSubviewListPageFunctionality.textContent = "";
this._permissionSubviewListSystemAccess.textContent = "";
let uri = gBrowser.currentURI;
for (let permission of SitePermissions.listPermissions()) {
for (let permission of SitePermissions.listPageFunctionalityPermissions()) {
let state = SitePermissions.get(uri, permission);
let item = this._createPermissionItem(permission, state);
this._permissionSubviewListPageFunctionality.appendChild(item);
}
for (let permission of SitePermissions.listSystemAccessPermissions()) {
let state = SitePermissions.get(uri, permission);
let item = this._createPermissionItem(permission, state);
this._permissionSubviewListSystemAccess.appendChild(item);
}
for (let permission of SitePermissions.listPermissions()) {
// Add to the main view only if there is a known / non-default
// value for the permission for this site.
let state = SitePermissions.get(uri, permission);
if (state != SitePermissions.UNKNOWN) {
this._permissionList.appendChild(item.cloneNode(true));
let item = this._createPermissionItem(permission, state);
this._permissionList.appendChild(item);
}
// Add all permissions to the subview.
this._permissionSubviewList.appendChild(item);
}
this._permissionsContainer.hidden = !this._permissionList.hasChildNodes();

View File

@ -46,14 +46,14 @@ add_task(function* testSubviewListing() {
info("Opening control center and expanding permissions subview");
gIdentityHandler._identityBox.click();
let menulists = gIdentityHandler._permissionSubviewList.querySelectorAll("menulist");
let perms = SitePermissions.listPermissions();
info("Checking 'Page Functionality' permissions");
let pageFunctionalityMenulists = gIdentityHandler._permissionSubviewListPageFunctionality.querySelectorAll("menulist");
let pageFunctionalityPerms = SitePermissions.listPageFunctionalityPermissions();
is(pageFunctionalityMenulists.length, pageFunctionalityPerms.length, "One menulist for each permission");
is(menulists.length, perms.length, "One menulist for each permission");
for (let i = 0; i < menulists.length; i++) {
let menulist = menulists[i];
let perm = perms[i];
for (let i = 0; i < pageFunctionalityMenulists.length; i++) {
let menulist = pageFunctionalityMenulists[i];
let perm = pageFunctionalityPerms[i];
let expectedValue = SitePermissions.get(gBrowser.currentURI, perm);
if (expectedValue == SitePermissions.UNKNOWN) {
expectedValue = SitePermissions.getDefault(perm);
@ -62,5 +62,23 @@ add_task(function* testSubviewListing() {
is(menulist.id, "identity-popup-permission:" + perm, "Correct id for menulist: " + perm);
is(menulist.value, expectedValue, "Correct value on menulist: " + perm);
}
info("Checking 'System Access' permissions");
let systemAccessMenulists = gIdentityHandler._permissionSubviewListSystemAccess.querySelectorAll("menulist");
let systemAccessPerms = SitePermissions.listSystemAccessPermissions();
is(systemAccessMenulists.length, systemAccessPerms.length, "One menulist for each permission");
for (let i = 0; i < systemAccessMenulists.length; i++) {
let menulist = systemAccessMenulists[i];
let perm = systemAccessPerms[i];
let expectedValue = SitePermissions.get(gBrowser.currentURI, perm);
if (expectedValue == SitePermissions.UNKNOWN) {
expectedValue = SitePermissions.getDefault(perm);
}
is(menulist.id, "identity-popup-permission:" + perm, "Correct id for menulist: " + perm);
is(menulist.value, expectedValue, "Correct value on menulist: " + perm);
}
gIdentityHandler._identityPopup.hidden = true;
});

View File

@ -172,7 +172,16 @@
</vbox>
<vbox id="identity-popup-permissionsView-body">
<vbox id="identity-popup-permission-subview-list"/>
<vbox id="identity-popup-permission-subview-list">
<label class="identity-popup-subheadline"
value="&identity.permissionsPageFunctionality;"
crop="end"/>
<vbox id="permission-subview-list-page-functionality"></vbox>
<label class="identity-popup-subheadline"
value="&identity.permissionsSystemAccess;"
crop="end"/>
<vbox id="permission-subview-list-system-access"></vbox>
</vbox>
</vbox>
</panelview>
</panelmultiview>

View File

@ -542,11 +542,11 @@ html[dir="rtl"] .room-entry-call-btn {
html[dir="rtl"] .room-entry-context-actions > .dropdown-menu {
right: auto;
left: 45px;
left: 21px;
}
.room-entry-context-actions > .dropdown-menu {
right: 45px;
right: 21px;
bottom: auto;
left: auto;
}

View File

@ -175,6 +175,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "ExtensionManagement",
XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
"resource://gre/modules/AppConstants.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "WindowsUIUtils",
"@mozilla.org/windows-ui-utils;1", "nsIWindowsUIUtils");
const PREF_PLUGINS_NOTIFYUSER = "plugins.update.notifyUser";
const PREF_PLUGINS_UPDATEURL = "plugins.update.url";
@ -507,6 +510,12 @@ BrowserGlue.prototype = {
case "autocomplete-did-enter-text":
this._handleURLBarTelemetry(subject.QueryInterface(Ci.nsIAutoCompleteInput));
break;
case "tablet-mode-change":
if (data == "tablet-mode") {
Services.telemetry.getHistogramById("FX_TABLET_MODE_USED_DURING_SESSION")
.add(1);
}
break;
}
},
@ -613,6 +622,7 @@ BrowserGlue.prototype = {
os.addObserver(this, "flash-plugin-hang", false);
os.addObserver(this, "xpi-signature-changed", false);
os.addObserver(this, "autocomplete-did-enter-text", false);
os.addObserver(this, "tablet-mode-change", false);
ExtensionManagement.registerScript("chrome://browser/content/ext-utils.js");
ExtensionManagement.registerScript("chrome://browser/content/ext-browserAction.js");
@ -668,6 +678,7 @@ BrowserGlue.prototype = {
os.removeObserver(this, "flash-plugin-hang");
os.removeObserver(this, "xpi-signature-changed");
os.removeObserver(this, "autocomplete-did-enter-text");
os.removeObserver(this, "tablet-mode-change");
},
_onAppDefaults: function BG__onAppDefaults() {
@ -1041,6 +1052,13 @@ BrowserGlue.prototype = {
let scaling = aWindow.devicePixelRatio * 100;
Services.telemetry.getHistogramById(SCALING_PROBE_NAME).add(scaling);
}
#ifdef XP_WIN
if (WindowsUIUtils.inTabletMode) {
Services.telemetry.getHistogramById("FX_TABLET_MODE_USED_DURING_SESSION")
.add(1);
}
#endif
},
// the first browser window has finished initializing

View File

@ -461,21 +461,17 @@ this.PlacesUIUtils = {
showBookmarkDialog:
function PUIU_showBookmarkDialog(aInfo, aParentWindow) {
// Preserve size attributes differently based on the fact the dialog has
// a folder picker or not. If the picker is visible, the dialog should
// be resizable since it may not show enough content for the folders
// hierarchy.
// a folder picker or not, since it needs more horizontal space than the
// other controls.
let hasFolderPicker = !("hiddenRows" in aInfo) ||
aInfo.hiddenRows.indexOf("folderPicker") == -1;
// Use a different chrome url, since this allows to persist different sizes,
// based on resizability of the dialog.
// Use a different chrome url to persist different sizes.
let dialogURL = hasFolderPicker ?
"chrome://browser/content/places/bookmarkProperties2.xul" :
"chrome://browser/content/places/bookmarkProperties.xul";
let features =
"centerscreen,chrome,modal,resizable=" + (hasFolderPicker ? "yes" : "no");
aParentWindow.openDialog(dialogURL, "", features, aInfo);
let features = "centerscreen,chrome,modal,resizable=yes";
aParentWindow.openDialog(dialogURL, "", features, aInfo);
return ("performed" in aInfo && aInfo.performed);
},

View File

@ -72,6 +72,8 @@ const LIVEMARK_CONTAINER = 2;
const ACTION_EDIT = 0;
const ACTION_ADD = 1;
let elementsHeight = new Map();
var BookmarkPropertiesPanel = {
/** UI Text Strings */
@ -271,6 +273,43 @@ var BookmarkPropertiesPanel = {
var acceptButton = document.documentElement.getButton("accept");
acceptButton.label = this._getAcceptLabel();
// Do not use sizeToContent, otherwise, due to bug 90276, the dialog will
// grow at every opening.
// Since elements can be uncollapsed asynchronously, we must observe their
// mutations and resize the dialog using a cached element size.
this._height = window.outerHeight;
this._mutationObserver = new MutationObserver(mutations => {
for (let mutation of mutations) {
let target = mutation.target;
let id = target.id;
if (!/^editBMPanel_.*(Row|Checkbox)$/.test(id))
continue;
let collapsed = target.getAttribute("collapsed") === "true";
let wasCollapsed = mutation.oldValue === "true";
if (collapsed == wasCollapsed)
continue;
if (collapsed) {
this._height -= elementsHeight.get(id);
elementsHeight.delete(id);
} else {
elementsHeight.set(id, target.boxObject.height);
this._height += elementsHeight.get(id);
}
window.resizeTo(window.outerWidth, this._height);
}
});
this._mutationObserver.observe(document,
{ subtree: true,
attributeOldValue: true,
attributeFilter: ["collapsed"] });
// Some controls are flexible and we want to update their cached size when
// the dialog is resized.
window.addEventListener("resize", this);
this._beginBatch();
switch (this._action) {
@ -300,25 +339,6 @@ var BookmarkPropertiesPanel = {
break;
}
// Adjust the dialog size to the changes done by initPanel. This is necessary because
// initPanel, which shows and hides elements, may run after some async work was done
// here - i.e. after the DOM load event was processed.
window.sizeToContent();
// When collapsible elements change their collapsed attribute we must
// resize the dialog.
// sizeToContent is not usable due to bug 90276, so we'll use resizeTo
// instead and cache the element size. See WSucks in the legacy
// UI code (addBookmark2.js).
if (!this._element("tagsRow").collapsed) {
this._element("tagsSelectorRow")
.addEventListener("DOMAttrModified", this, false);
}
if (!this._element("folderRow").collapsed) {
this._element("folderTreeRow")
.addEventListener("DOMAttrModified", this, false);
}
if (!gEditItemOverlay.readOnly) {
// Listen on uri fields to enable accept button if input is valid
if (this._itemType == BOOKMARK_ITEM) {
@ -330,12 +350,9 @@ var BookmarkPropertiesPanel = {
}
}
}
window.sizeToContent();
}),
// nsIDOMEventListener
_elementsHeight: [],
handleEvent: function BPP_handleEvent(aEvent) {
var target = aEvent.target;
switch (aEvent.type) {
@ -347,24 +364,11 @@ var BookmarkPropertiesPanel = {
.getButton("accept").disabled = !this._inputIsValid();
}
break;
case "DOMAttrModified":
// this is called when collapsing a node, but also its direct children,
// we only need to resize when the original node changes.
if ((target.id == "editBMPanel_tagsSelectorRow" ||
target.id == "editBMPanel_folderTreeRow") &&
aEvent.attrName == "collapsed" &&
target == aEvent.originalTarget) {
var id = target.id;
var newHeight = window.outerHeight;
if (aEvent.newValue) // is collapsed
newHeight -= this._elementsHeight[id];
else {
this._elementsHeight[id] = target.boxObject.height;
newHeight += this._elementsHeight[id];
}
window.resizeTo(window.outerWidth, newHeight);
case "resize":
for (let [id, oldHeight] of elementsHeight) {
let newHeight = document.getElementById(id).boxObject.height;
this._height += - oldHeight + newHeight;
elementsHeight.set(id, newHeight);
}
break;
}
@ -418,12 +422,13 @@ var BookmarkPropertiesPanel = {
onDialogUnload() {
// gEditItemOverlay does not exist anymore here, so don't rely on it.
this._mutationObserver.disconnect();
delete this._mutationObserver;
window.removeEventListener("resize", this);
// Calling removeEventListener with arguments which do not identify any
// currently registered EventListener on the EventTarget has no effect.
this._element("tagsSelectorRow")
.removeEventListener("DOMAttrModified", this, false);
this._element("folderTreeRow")
.removeEventListener("DOMAttrModified", this, false);
this._element("locationField")
.removeEventListener("input", this, false);
},
@ -578,48 +583,44 @@ var BookmarkPropertiesPanel = {
childItemsTransactions);
},
/**
* Returns a transaction for creating a new live-bookmark item representing
* the various fields and opening arguments of the dialog.
*/
_getCreateNewLivemarkTransaction:
function BPP__getCreateNewLivemarkTransaction(aContainer, aIndex) {
return new PlacesCreateLivemarkTransaction(this._feedURI, this._siteURI,
this._title,
aContainer, aIndex);
},
_createNewItem: function BPP__getCreateItemTransaction() {
var [container, index] = this._getInsertionPointDetails();
var txn;
_createNewItem: Task.async(function* () {
let [container, index] = this._getInsertionPointDetails();
let txn;
switch (this._itemType) {
case BOOKMARK_FOLDER:
txn = this._getCreateNewFolderTransaction(container, index);
break;
case LIVEMARK_CONTAINER:
txn = this._getCreateNewLivemarkTransaction(container, index);
txn = new PlacesCreateLivemarkTransaction(this._feedURI, this._siteURI,
this._title, container, index);
break;
default: // BOOKMARK_ITEM
txn = this._getCreateNewBookmarkTransaction(container, index);
}
PlacesUtils.transactionManager.doTransaction(txn);
this._itemId = PlacesUtils.bookmarks.getIdForItemAt(container, index);
// This is a temporary hack until we use PlacesTransactions.jsm
if (txn._promise) {
yield txn._promise;
}
let folderGuid = yield PlacesUtils.promiseItemGuid(container);
let bm = yield PlacesUtils.bookmarks.fetch({
parentGuid: folderGuid,
index: PlacesUtils.bookmarks.DEFAULT_INDEX
});
this._itemId = yield PlacesUtils.promiseItemId(bm.guid);
return Object.freeze({
itemId: this._itemId,
get bookmarkGuid() {
throw new Error("Node-like bookmarkGuid getter called even though " +
"async transactions are disabled");
},
bookmarkGuid: bm.guid,
title: this._title,
uri: this._uri ? this._uri.spec : "",
type: this._itemType == BOOKMARK_ITEM ?
Ci.nsINavHistoryResultNode.RESULT_TYPE_URI :
Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER
});
},
}),
_promiseNewItem: Task.async(function* () {
if (!PlacesUIUtils.useAsyncTransactions)

View File

@ -34,6 +34,12 @@ let gEditItemOverlay = {
let isItem = itemId != -1;
let isFolderShortcut = isItem &&
node.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT;
let isTag = node && PlacesUtils.nodeIsTagQuery(node);
if (isTag) {
itemId = PlacesUtils.getConcreteItemId(node);
// For now we don't have access to the item guid synchronously for tags,
// so we'll need to fetch it later.
}
let isURI = node && PlacesUtils.nodeIsURI(node);
let uri = isURI ? NetUtil.newURI(node.uri) : null;
let title = node ? node.title : null;
@ -55,7 +61,7 @@ let gEditItemOverlay = {
isURI, uri, title,
isBookmark, isFolderShortcut, isParentReadOnly,
bulkTagging, uris,
visibleRows, postData };
visibleRows, postData, isTag };
},
get initialized() {
@ -89,12 +95,13 @@ let gEditItemOverlay = {
// This pane is read-only if:
// * the panel is not initialized
// * the node is a folder shortcut
// * the node is not bookmarked
// * the node is child of a read-only container and is not a bookmarked URI
// * the node is not bookmarked and not a tag container
// * the node is child of a read-only container and is not a bookmarked
// URI nor a tag container
return !this.initialized ||
this._paneInfo.isFolderShortcut ||
!this._paneInfo.isItem ||
(this._paneInfo.isParentReadOnly && !this._paneInfo.isBookmark);
(!this._paneInfo.isItem && !this._paneInfo.isTag) ||
(this._paneInfo.isParentReadOnly && !this._paneInfo.isBookmark && !this._paneInfo.isTag);
},
// the first field which was edited after this panel was initialized for
@ -518,7 +525,7 @@ let gEditItemOverlay = {
},
onNamePickerChange() {
if (this.readOnly || !this._paneInfo.isItem)
if (this.readOnly || !(this._paneInfo.isItem || this._paneInfo.isTag))
return;
// Here we update either the item title or its cached static title
@ -536,9 +543,13 @@ let gEditItemOverlay = {
PlacesUtils.transactionManager.doTransaction(txn);
return;
}
let guid = this._paneInfo.itemGuid;
PlacesTransactions.EditTitle({ guid, title: newTitle })
.transact().catch(Components.utils.reportError);
Task.spawn(function* () {
let guid = this._paneInfo.isTag
? (yield PlacesUtils.promiseItemGuid(this._paneInfo.itemId))
: this._paneInfo.itemGuid;
PlacesTransactions.EditTitle({ guid, title: newTitle })
.transact().catch(Components.utils.reportError);
}).catch(Cu.reportError);
}
},

View File

@ -24,7 +24,9 @@
<column flex="1" id="editBMPanel_editColumn" />
</columns>
<rows id="editBMPanel_rows">
<row align="center" id="editBMPanel_nameRow">
<row id="editBMPanel_nameRow"
align="center"
collapsed="true">
<label value="&editBookmarkOverlay.name.label;"
class="editBMPanel_rowLabel"
accesskey="&editBookmarkOverlay.name.accesskey;"
@ -33,7 +35,9 @@
onchange="gEditItemOverlay.onNamePickerChange();"/>
</row>
<row align="center" id="editBMPanel_locationRow">
<row id="editBMPanel_locationRow"
align="center"
collapsed="true">
<label value="&editBookmarkOverlay.location.label;"
class="editBMPanel_rowLabel"
accesskey="&editBookmarkOverlay.location.accesskey;"
@ -43,7 +47,9 @@
onchange="gEditItemOverlay.onLocationFieldChange();"/>
</row>
<row align="center" id="editBMPanel_folderRow">
<row id="editBMPanel_folderRow"
align="center"
collapsed="true">
<label value="&editBookmarkOverlay.folder.label;"
class="editBMPanel_rowLabel"
control="editBMPanel_folderMenuList"/>
@ -76,7 +82,9 @@
</hbox>
</row>
<row id="editBMPanel_folderTreeRow" collapsed="true" flex="1">
<row id="editBMPanel_folderTreeRow"
collapsed="true"
flex="1">
<spacer/>
<vbox flex="1">
<tree id="editBMPanel_folderTree"
@ -103,7 +111,9 @@
</vbox>
</row>
<row align="center" id="editBMPanel_tagsRow">
<row id="editBMPanel_tagsRow"
align="center"
collapsed="true">
<label value="&editBookmarkOverlay.tags.label;"
class="editBMPanel_rowLabel"
accesskey="&editBookmarkOverlay.tags.accesskey;"
@ -136,7 +146,9 @@
height="150"/>
</row>
<row align="center" id="editBMPanel_keywordRow">
<row id="editBMPanel_keywordRow"
align="center"
collapsed="true">
<observes element="additionalInfoBroadcaster" attribute="hidden"/>
<label value="&editBookmarkOverlay.keyword.label;"
class="editBMPanel_rowLabel"
@ -146,7 +158,8 @@
onchange="gEditItemOverlay.onKeywordFieldChange();"/>
</row>
<row id="editBMPanel_descriptionRow">
<row id="editBMPanel_descriptionRow"
collapsed="true">
<observes element="additionalInfoBroadcaster" attribute="hidden"/>
<label value="&editBookmarkOverlay.description.label;"
class="editBMPanel_rowLabel"
@ -161,6 +174,7 @@
</grid>
<checkbox id="editBMPanel_loadInSidebarCheckbox"
collapsed="true"
label="&editBookmarkOverlay.loadInSidebar.label;"
accesskey="&editBookmarkOverlay.loadInSidebar.accesskey;"
oncommand="gEditItemOverlay.onLoadInSidebarCheckboxCommand();">

View File

@ -25,6 +25,8 @@ support-files =
pageopeningwindow.html
[browser_bookmarkProperties_addFolderDefaultButton.js]
[browser_bookmarkProperties_addKeywordForThisSearch.js]
[browser_bookmarkProperties_addLivemark.js]
[browser_bookmarkProperties_editTagContainer.js]
[browser_bookmarkProperties_readOnlyRoot.js]
[browser_bookmarksProperties.js]
[browser_drag_bookmarks_on_toolbar.js]

View File

@ -0,0 +1,39 @@
"use strict"
add_task(function* () {
info("Add a live bookmark editing its data");
yield withSidebarTree("bookmarks", function* (tree) {
let itemId = PlacesUIUtils.leftPaneQueries["UnfiledBookmarks"];
tree.selectItems([itemId]);
yield withBookmarksDialog(
true,
function openDialog() {
PlacesCommandHook.addLiveBookmark("http://livemark.com/",
"livemark", "description");
},
function* test(dialogWin) {
let promiseTitleChangeNotification = promiseBookmarksNotification(
"onItemChanged", (itemId, prop, isAnno, val) => prop == "title" && val == "modified");
fillBookmarkTextField("editBMPanel_namePicker", "modified", dialogWin);
yield promiseTitleChangeNotification;
let bookmark = yield PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
index: PlacesUtils.bookmarks.DEFAULT_INDEX
});
is(bookmark.title, "modified", "folder name has been edited");
let livemark = yield PlacesUtils.livemarks.getLivemark({
guid: bookmark.guid
});
is(livemark.feedURI.spec, "http://livemark.com/", "livemark has the correct url");
is(livemark.title, "modified", "livemark has the correct title");
}
);
});
});

View File

@ -0,0 +1,71 @@
"use strict"
add_task(function* () {
info("Bug 479348 - Properties on a root should be read-only.");
let uri = NetUtil.newURI("http://example.com/");
let bm = yield PlacesUtils.bookmarks.insert({
url: uri.spec,
parentGuid: PlacesUtils.bookmarks.unfiledGuid
});
registerCleanupFunction(function* () {
yield PlacesUtils.bookmarks.remove(bm);
});
PlacesUtils.tagging.tagURI(uri, ["tag1"]);
let library = yield promiseLibrary();
let PlacesOrganizer = library.PlacesOrganizer;
registerCleanupFunction(function* () {
yield promiseLibraryClosed(library);
});
PlacesOrganizer.selectLeftPaneQuery("Tags");
let tree = PlacesOrganizer._places;
let tagsContainer = tree.selectedNode;
tagsContainer.containerOpen = true;
let fooTag = tagsContainer.getChild(0);
let tagNode = fooTag;
tree.selectNode(fooTag);
is(tagNode.title, 'tag1', "tagNode title is correct");
ok(tree.controller.isCommandEnabled("placesCmd_show:info"),
"'placesCmd_show:info' on current selected node is enabled");
yield withBookmarksDialog(
true,
function openDialog() {
tree.controller.doCommand("placesCmd_show:info");
},
function* test(dialogWin) {
// Check that the dialog is not read-only.
ok(!dialogWin.gEditItemOverlay.readOnly, "Dialog should not be read-only");
// Check that name picker is not read only
let namepicker = dialogWin.document.getElementById("editBMPanel_namePicker");
ok(!namepicker.readOnly, "Name field should not be read-only");
is(namepicker.value, "tag1", "Node title is correct");
let promiseTitleChangeNotification = promiseBookmarksNotification(
"onItemChanged", (itemId, prop, isAnno, val) => prop == "title" && val == "tag2");
fillBookmarkTextField("editBMPanel_namePicker", "tag2", dialogWin);
yield promiseTitleChangeNotification;
is(namepicker.value, "tag2", "Node title has been properly edited");
// Check the shortcut's title.
is(tree.selectedNode.title, "tag2", "The node has the correct title");
// Check the tags have been edited.
let tags = PlacesUtils.tagging.getTagsForURI(uri);
is(tags.length, 1, "Found the right number of tags");
ok(tags.includes("tag2"), "Found the expected tag");
}
);
// Check the tag change has been reverted.
let tags = PlacesUtils.tagging.getTagsForURI(uri);
is(tags.length, 1, "Found the right number of tags");
ok(tags.includes("tag1"), "Found the expected tag");
});

View File

@ -297,7 +297,7 @@ let withBookmarksDialog = Task.async(function* (autoCancel, openFn, taskFn) {
win.addEventListener("load", function load() {
win.removeEventListener("load", load);
ok(win.location.href.startsWith("chrome://browser/content/places/bookmarkProperties"),
"The bookmark properties dialog is ready");
"The bookmark properties dialog is open");
// This is needed for the overlay.
waitForFocus(() => {
resolve(win);
@ -318,7 +318,9 @@ let withBookmarksDialog = Task.async(function* (autoCancel, openFn, taskFn) {
let dialogWin = yield dialogPromise;
// Ensure overlay is loaded
ok(dialogWin.gEditItemOverlay.initialized, "EditItemOverlay is initialized");
info("waiting for the overlay to be loaded");
yield waitForCondition(() => dialogWin.gEditItemOverlay.initialized,
"EditItemOverlay should be initialized");
info("withBookmarksDialog: executing the task");
try {

View File

@ -41,6 +41,10 @@ body[globalTpEnabled] .showGlobalTpDisabled {
line-height: 2.5em;
}
#bar:-moz-dir(rtl) {
background-position: right 22px top 50%;
}
#main {
padding: 0 2em;
flex: 1;
@ -74,6 +78,10 @@ li {
background-position: 0 50%;
}
li:-moz-dir(rtl) {
background-position: 100% 50%;
}
#forgotten > li {
background-image: url("chrome://browser/skin/privatebrowsing/check.png");
}

View File

@ -976,6 +976,36 @@ InspectorPanel.prototype = {
});
},
/**
* Use in Console.
*
* Takes the currently selected node in the inspector and assigns it to a
* temp variable on the content window. Also opens the split console and
* autofills it with the temp variable.
*/
useInConsole: function() {
this._toolbox.openSplitConsole().then(() => {
let panel = this._toolbox.getPanel("webconsole");
let jsterm = panel.hud.jsterm;
let evalString = `let i = 0;
while (window.hasOwnProperty("temp" + i) && i < 1000) {
i++;
}
window["temp" + i] = $0;
"temp" + i;
`;
let options = {
selectedNodeActor: this.selection.nodeFront.actorID,
};
jsterm.requestEvaluation(evalString, options).then((res) => {
jsterm.setInputValue(res.result);
this.emit("console-var-ready");
});
});
},
/**
* Clear any pseudo-class locks applied to the current hierarchy.
*/

View File

@ -55,6 +55,9 @@
<menuitem id="node-menu-showdomproperties"
label="&inspectorShowDOMProperties.label;"
oncommand="inspector.showDOMProperties()"/>
<menuitem id="node-menu-useinconsole"
label="&inspectorUseInConsole.label;"
oncommand="inspector.useInConsole()"/>
<menuitem id="node-menu-expand"
label="&inspectorExpandNode.label;"
oncommand="inspector.expandNode()"/>

View File

@ -82,7 +82,8 @@ skip-if = e10s # GCLI isn't e10s compatible. See bug 1128988.
[browser_inspector_menu-01-sensitivity.js]
[browser_inspector_menu-02-copy-items.js]
[browser_inspector_menu-03-paste-items.js]
[browser_inspector_menu-04-other.js]
[browser_inspector_menu-04-use-in-console.js]
[browser_inspector_menu-05-other.js]
[browser_inspector_navigation.js]
[browser_inspector_pane-toggle-01.js]
[browser_inspector_pane-toggle-02.js]

View File

@ -16,36 +16,40 @@ const PASTE_MENU_ITEMS = [
"node-menu-pastelastchild",
];
const ACTIVE_ON_DOCTYPE_ITEMS = [
"node-menu-showdomproperties",
"node-menu-useinconsole"
];
const ALL_MENU_ITEMS = [
"node-menu-edithtml",
"node-menu-copyinner",
"node-menu-copyouter",
"node-menu-copyuniqueselector",
"node-menu-copyimagedatauri",
"node-menu-showdomproperties",
"node-menu-delete",
"node-menu-pseudo-hover",
"node-menu-pseudo-active",
"node-menu-pseudo-focus",
"node-menu-scrollnodeintoview",
"node-menu-screenshotnode"
].concat(PASTE_MENU_ITEMS);
].concat(PASTE_MENU_ITEMS, ACTIVE_ON_DOCTYPE_ITEMS);
const ITEMS_WITHOUT_SHOWDOMPROPS =
ALL_MENU_ITEMS.filter(item => item != "node-menu-showdomproperties");
const INACTIVE_ON_DOCTYPE_ITEMS =
ALL_MENU_ITEMS.filter(item => ACTIVE_ON_DOCTYPE_ITEMS.indexOf(item) === -1);
const TEST_CASES = [
{
desc: "doctype node with empty clipboard",
selector: null,
disabled: ITEMS_WITHOUT_SHOWDOMPROPS,
disabled: INACTIVE_ON_DOCTYPE_ITEMS,
},
{
desc: "doctype node with html on clipboard",
clipboardData: "<p>some text</p>",
clipboardDataType: "html",
selector: null,
disabled: ITEMS_WITHOUT_SHOWDOMPROPS,
disabled: INACTIVE_ON_DOCTYPE_ITEMS,
},
{
desc: "element node HTML on the clipboard",

View File

@ -0,0 +1,47 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Tests "Use in Console" menu item
const TEST_URL = TEST_URL_ROOT + "doc_inspector_menu.html";
registerCleanupFunction(() => {
Services.prefs.clearUserPref("devtools.toolbox.splitconsoleEnabled");
});
add_task(function* () {
let { inspector, toolbox } = yield openInspectorForURL(TEST_URL);
yield testUseInConsole();
function* testUseInConsole() {
info("Testing 'Use in Console' menu item.");
let useInConsoleNode = inspector.panelDoc.getElementById("node-menu-useinconsole");
yield selectNode("#console-var", inspector);
dispatchCommandEvent(useInConsoleNode);
yield inspector.once("console-var-ready");
let hud = toolbox.getPanel("webconsole").hud;
let jsterm = hud.jsterm;
let jstermInput = jsterm.hud.document.querySelector(".jsterm-input-node");
ok(jstermInput.value === "temp0", "first console variable is named temp0");
let result = yield jsterm.execute();
isnot(result.textContent.indexOf('<p id="console-var">'), -1, "variable temp0 references correct node");
yield selectNode("#console-var-multi", inspector);
dispatchCommandEvent(useInConsoleNode);
yield inspector.once("console-var-ready");
ok(jstermInput.value === "temp1", "second console variable is named temp1");
result = yield jsterm.execute();
isnot(result.textContent.indexOf('<p id="console-var-multi">'), -1, "variable temp1 references correct node");
jsterm.clearHistory();
}
});

View File

@ -71,12 +71,4 @@ add_task(function* () {
// Follow up bug to add this test - https://bugzilla.mozilla.org/show_bug.cgi?id=1154107
todo(false, "Verify that node is scrolled into the viewport.");
}
function dispatchCommandEvent(node) {
info("Dispatching command event on " + node);
let commandEvent = document.createEvent("XULCommandEvent");
commandEvent.initCommandEvent("command", true, true, window, 0, false, false,
false, false, null);
node.dispatchEvent(commandEvent);
}
});

View File

@ -20,6 +20,8 @@
<div id="hiddenElement" style="display: none;">
<p id="nestedHiddenElement">Visible element nested inside a non-visible element</p>
</div>
<p id="console-var">Paragraph for testing console variables</p>
<p id="console-var-multi">Paragraph for testing multiple console variables</p>
</div>
</body>
</html>

View File

@ -468,3 +468,15 @@ function redoChange(inspector) {
inspector.markup.undo.redo();
return mutated;
}
/**
* Dispatch a command event on a node (e.g. click on a contextual menu item).
* @param {DOMNode} node
*/
function dispatchCommandEvent(node) {
info("Dispatching command event on " + node);
let commandEvent = document.createEvent("XULCommandEvent");
commandEvent.initCommandEvent("command", true, true, window, 0, false, false,
false, false, null);
node.dispatchEvent(commandEvent);
}

View File

@ -0,0 +1,126 @@
/* 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/. */
"use strict";
/**
* This file contains the tree view, displaying all the samples and frames
* received from the proviler in a tree-like structure.
*/
const { Cc, Ci, Cu, Cr } = require("chrome");
const { L10N } = require("devtools/performance/global");
const { Heritage } = require("resource:///modules/devtools/ViewHelpers.jsm");
const { AbstractTreeItem } = require("resource:///modules/devtools/AbstractTreeItem.jsm");
const INDENTATION = exports.INDENTATION = 16; // px
const DEFAULT_AUTO_EXPAND_DEPTH = 2;
const COURSE_TYPES = ["objects", "scripts", "strings", "other"];
/**
* Every instance of a `CensusView` represents a row in the census tree. The same
* parent node is used for all rows.
*
* @param CensusView parent
* The CensusView considered the parent row. Should be null
* for root node.
* @param {CensusTreeNode} censusTreeNode
* Data from `takeCensus` transformed via `CensusTreeNode`.
* @see browser/toolkit/heapsnapshot/census-tree-node.js
* @param number level [optional]
* The indentation level in the call tree. The root node is at level 0.
* @param boolean hidden [optional]
* Whether this node should be hidden and not contribute to depth/level
* calculations. Defaults to false.
* @param number autoExpandDepth [optional]
* The depth to which the tree should automatically expand. Defaults to
* the caller's `autoExpandDepth` if a caller exists, otherwise defaults
* to DEFAULT_AUTO_EXPAND_DEPTH.
*/
function CensusView ({ caller, censusTreeNode, level, hidden, autoExpandDepth }) {
AbstractTreeItem.call(this, { parent: caller, level: level|0 - (hidden ? 1 : 0) });
this.autoExpandDepth = autoExpandDepth != null
? autoExpandDepth
: caller ? caller.autoExpandDepth
: DEFAULT_AUTO_EXPAND_DEPTH;
this.caller = caller;
this.censusTreeNode = censusTreeNode;
this.hidden = hidden;
};
CensusView.prototype = Heritage.extend(AbstractTreeItem.prototype, {
/**
* Creates the view for this tree node.
* @param nsIDOMNode document
* @param nsIDOMNode arrowNode
* @return nsIDOMNode
*/
_displaySelf: function (document, arrowNode) {
let data = this.censusTreeNode;
let cells = [];
// Only use an arrow if there are children
if (data.children && data.children.length) {
cells.push(arrowNode);
}
cells.push(this._createCell(document, data.name, "name"));
if (data.bytes != null) {
cells.push(this._createCell(document, data.bytes, "bytes"));
}
if (data.count != null) {
cells.push(this._createCell(document, data.count, "count"));
}
let targetNode = document.createElement("li");
targetNode.className = "heap-tree-item";
targetNode.style.MozMarginStart = `${this.level * INDENTATION}px`;
if (this.hidden) {
targetNode.style.display = "none";
}
for (let i = 0; i < cells.length; i++) {
targetNode.appendChild(cells[i]);
}
return targetNode;
},
/**
* Populates this node in the call tree with the corresponding "callees".
* These are defined in the `frame` data source for this call view.
* @param array:AbstractTreeItem children
*/
_populateSelf: function (children) {
let newLevel = this.level + 1;
let data = this.censusTreeNode;
if (data.children) {
for (let node of data.children) {
children.push(new CensusView({
caller: this,
level: newLevel,
censusTreeNode: node,
}));
}
}
},
/**
* Functions creating each cell in this call view.
* Invoked by `_displaySelf`.
*/
_createCell: function (doc, value, type) {
let cell = doc.createElement("span");
cell.className = "plain heap-tree-cell";
cell.setAttribute("type", type);
cell.innerHTML = value;
return cell;
},
});
exports.CensusView = CensusView;

View File

@ -0,0 +1,87 @@
/* 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/. */
"use strict";
/**
* Utilities for interfacing with census reports from dbg.memory.takeCensus().
*/
const COARSE_TYPES = new Set(["objects", "scripts", "strings", "other"]);
/**
* Takes a report from a census (`dbg.memory.takeCensus()`) and the breakdown
* used to generate the census and returns a structure used to render
* a tree to display the data.
*
* Returns a recursive "CensusTreeNode" object, looking like:
*
* CensusTreeNode = {
* // `children` if it exists, is sorted by `bytes`, if they are leaf nodes.
* children: ?[<CensusTreeNode...>],
* name: <?String>
* count: <?Number>
* bytes: <?Number>
* }
*
* @param {Object} breakdown
* @param {Object} report
* @param {?String} name
* @return {Object}
*/
function CensusTreeNode (breakdown, report, name) {
this.name = name;
this.bytes = void 0;
this.count = void 0;
this.children = void 0;
CensusTreeNodeBreakdowns[breakdown.by](this, breakdown, report);
if (this.children) {
this.children.sort(sortByBytes);
}
}
CensusTreeNode.prototype = null;
/**
* A series of functions to handle different breakdowns used by CensusTreeNode
*/
const CensusTreeNodeBreakdowns = Object.create(null);
CensusTreeNodeBreakdowns.count = function (node, breakdown, report) {
if (breakdown.bytes === true) {
node.bytes = report.bytes;
}
if (breakdown.count === true) {
node.count = report.count;
}
};
CensusTreeNodeBreakdowns.internalType = function (node, breakdown, report) {
node.children = [];
for (let key of Object.keys(report)) {
node.children.push(new CensusTreeNode(breakdown.then, report[key], key));
}
}
CensusTreeNodeBreakdowns.objectClass = function (node, breakdown, report) {
node.children = [];
for (let key of Object.keys(report)) {
let bd = key === "other" ? breakdown.other : breakdown.then;
node.children.push(new CensusTreeNode(bd, report[key], key));
}
}
CensusTreeNodeBreakdowns.coarseType = function (node, breakdown, report) {
node.children = [];
for (let type of Object.keys(breakdown).filter(type => COARSE_TYPES.has(type))) {
node.children.push(new CensusTreeNode(breakdown[type], report[type], type));
}
}
function sortByBytes (a, b) {
return (b.bytes || 0) - (a.bytes || 0);
}
exports.CensusTreeNode = CensusTreeNode;

View File

@ -0,0 +1,12 @@
# vim: set filetype=python:
# 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/.
EXTRA_JS_MODULES.devtools.memory += [
'modules/census-view.js',
'modules/census.js',
]
XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini']
MOCHITEST_CHROME_MANIFESTS += ['test/mochitest/chrome.ini']

View File

@ -0,0 +1,7 @@
[DEFAULT]
tags = devtools
skip-if = buildapp == 'b2g' || os == 'android'
support-files =
head.js
[test_census-view-01.html]

View File

@ -0,0 +1,12 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
const Cr = Components.results;
const CC = Components.Constructor;
const { require } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});

View File

@ -0,0 +1,100 @@
<!DOCTYPE HTML>
<html>
<!--
Bug 1067491 - Test taking a census over the RDP.
-->
<head>
<meta charset="utf-8">
<title>Census Tree 01</title>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<link href="chrome://browser/content/devtools/widgets.css" type="text/css" />
<link href="chrome://browser/skin/devtools/light-theme.css" type="text/css" />
<link href="chrome://browser/skin/devtools/common.css" type="text/css" />
<link href="chrome://browser/skin/devtools/widgets.css" type="text/css" />
<link href="chrome://browser/skin/devtools/memory.css" type="text/css" />
</head>
<body>
<ul id="container" style="width:100%;height:300px;"></ul>
<pre id="test">
<script src="head.js" type="application/javascript;version=1.8"></script>
<script>
window.onload = function() {
var { CensusTreeNode } = require("devtools/memory/census");
var { INDENTATION, CensusView } = require("devtools/memory/census-view");
SimpleTest.waitForExplicitFinish();
const countBreakdown = { by: "count", count: true, bytes: true };
const BREAKDOWN = {
by: "coarseType",
objects: { by: "objectClass", then: countBreakdown },
strings: countBreakdown,
other: { by: "internalType", then: countBreakdown },
};
const REPORT = {
"objects": {
"Function": { bytes: 10, count: 1 },
"Array": { bytes: 20, count: 2 },
},
"strings": { bytes: 10, count: 1 },
"other": {
"js::Shape": { bytes: 30, count: 3 },
"js::Shape2": { bytes: 40, count: 4 }
},
};
const EXPECTED_ROWS = [
{ level: 0, name: "strings", bytes: 10, count: 1, },
{ level: 0, name: "objects" },
{ level: 1, name: "Array", bytes: 20, count: 2, },
{ level: 1, name: "Function", bytes: 10, count: 1, },
{ level: 0, name: "other" },
{ level: 1, name: "js::Shape2", bytes: 40, count: 4, },
{ level: 1, name: "js::Shape", bytes: 30, count: 3, },
];
var censusTreeNode = new CensusTreeNode(BREAKDOWN, REPORT);
var view = new CensusView({
censusTreeNode: censusTreeNode,
hidden: true
});
view.attachTo(document.querySelector("#container"));
var ul = document.querySelector("#container");
var children = Array.from(ul.children).filter(n => n.style.display !== "none");
for (var i = 0; i < children.length; i++) {
var el = children[i];
var expected = EXPECTED_ROWS[i];
var nameEl = el.querySelector(".heap-tree-cell[type='name']");
var bytesEl = el.querySelector(".heap-tree-cell[type='bytes']");
var countEl = el.querySelector(".heap-tree-cell[type='count']");
is(nameEl.innerHTML, expected.name,
`correct name "${expected.name}" in heap tree`);
is(el.style.MozMarginStart, (INDENTATION * expected.level) + "px",
`correct indentation for ${expected.name}`);
if ("bytes" in expected) {
is(bytesEl.innerHTML, String(expected.bytes),
`correct bytes "${expected.bytes}" in heap tree`);
} else {
ok(!bytesEl, "no bytes correctly displayed for ${expected.name}");
}
if ("count" in expected) {
is(countEl.innerHTML, String(expected.count),
`correct count "${expected.count}" in heap tree`);
} else {
ok(!countEl, "no count correctly displayed for ${expected.name}");
}
}
SimpleTest.finish();
};
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,143 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
const Cr = Components.results;
const CC = Components.Constructor;
const { require } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
const { addDebuggerToGlobal } = Cu.import("resource://gre/modules/jsdebugger.jsm", {});
const { console } = Cu.import("resource://gre/modules/devtools/Console.jsm", {});
const { CensusTreeNode } = require("devtools/memory/census");
const Services = require("Services");
// Always log packets when running tests. runxpcshelltests.py will throw
// the output away anyway, unless you give it the --verbose flag.
Services.prefs.setBoolPref("devtools.debugger.log", true);
const SYSTEM_PRINCIPAL = Cc["@mozilla.org/systemprincipal;1"]
.createInstance(Ci.nsIPrincipal);
function dumpn(msg) {
dump("HEAPSNAPSHOT-TEST: " + msg + "\n");
}
function addTestingFunctionsToGlobal(global) {
global.eval(
`
const testingFunctions = Components.utils.getJSTestingFunctions();
for (let k in testingFunctions) {
this[k] = testingFunctions[k];
}
`
);
if (!global.print) {
global.print = do_print;
}
if (!global.newGlobal) {
global.newGlobal = newGlobal;
}
if (!global.Debugger) {
addDebuggerToGlobal(global);
}
}
addTestingFunctionsToGlobal(this);
/**
* Create a new global, with all the JS shell testing functions. Similar to the
* newGlobal function exposed to JS shells, and useful for porting JS shell
* tests to xpcshell tests.
*/
function newGlobal() {
const global = new Cu.Sandbox(SYSTEM_PRINCIPAL, { freshZone: true });
addTestingFunctionsToGlobal(global);
return global;
}
function assertThrowsValue(f, val, msg) {
var fullmsg;
try {
f();
} catch (exc) {
if ((exc === val) === (val === val) && (val !== 0 || 1 / exc === 1 / val))
return;
fullmsg = "Assertion failed: expected exception " + val + ", got " + exc;
}
if (fullmsg === undefined)
fullmsg = "Assertion failed: expected exception " + val + ", no exception thrown";
if (msg !== undefined)
fullmsg += " - " + msg;
throw new Error(fullmsg);
}
/**
* Returns the full path of the file with the specified name in a
* platform-independent and URL-like form.
*/
function getFilePath(aName, aAllowMissing=false, aUsePlatformPathSeparator=false)
{
let file = do_get_file(aName, aAllowMissing);
let path = Services.io.newFileURI(file).spec;
let filePrePath = "file://";
if ("nsILocalFileWin" in Ci &&
file instanceof Ci.nsILocalFileWin) {
filePrePath += "/";
}
path = path.slice(filePrePath.length);
if (aUsePlatformPathSeparator && path.match(/^\w:/)) {
path = path.replace(/\//g, "\\");
}
return path;
}
/**
* Save a heap snapshot to the file with the given name in the current
* directory, read it back as a HeapSnapshot instance, and then take a census of
* the heap snapshot's serialized heap graph with the provided census options.
*
* @param {Object|undefined} censusOptions
* Options that should be passed through to the takeCensus method. See
* js/src/doc/Debugger/Debugger.Memory.md for details.
*
* @param {Debugger|null} dbg
* If a Debugger object is given, only serialize the subgraph covered by
* the Debugger's debuggees. If null, serialize the whole heap graph.
*
* @param {String} fileName
* The file name to save the heap snapshot's core dump file to, within
* the current directory.
*
* @returns Census
*/
function saveHeapSnapshotAndTakeCensus(dbg=null, censusOptions=undefined,
// Add the Math.random() so that parallel
// tests are less likely to mess with
// each other.
fileName="core-dump-" + (Math.random()) + ".tmp") {
const filePath = getFilePath(fileName, true, true);
ok(filePath, "Should get a file path to save the core dump to.");
const snapshotOptions = dbg ? { debugger: dbg } : { runtime: true };
ChromeUtils.saveHeapSnapshot(filePath, snapshotOptions);
ok(true, "Should have saved a heap snapshot to " + filePath);
const snapshot = ChromeUtils.readHeapSnapshot(filePath);
ok(snapshot, "Should have read a heap snapshot back from " + filePath);
ok(snapshot instanceof HeapSnapshot, "snapshot should be an instance of HeapSnapshot");
equal(typeof snapshot.takeCensus, "function", "snapshot should have a takeCensus method");
return snapshot.takeCensus(censusOptions);
}
function compareCensusViewData (breakdown, report, expected, assertion) {
let data = new CensusTreeNode(breakdown, report);
equal(JSON.stringify(data), JSON.stringify(expected), assertion);
}

View File

@ -0,0 +1,37 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests CensusTreeNode with `internalType` breakdown.
*/
function run_test() {
compareCensusViewData(BREAKDOWN, REPORT, EXPECTED, `${JSON.stringify(BREAKDOWN)} has correct results.`);
}
const BREAKDOWN = {
by: "internalType",
then: { by: "count", count: true, bytes: true }
};
const REPORT = {
"JSObject": {
"bytes": 100,
"count": 10,
},
"js::Shape": {
"bytes": 500,
"count": 50,
},
"JSString": {
"bytes": 0,
"count": 0,
},
};
const EXPECTED = {
children: [
{ name: "js::Shape", bytes: 500, count: 50, },
{ name: "JSObject", bytes: 100, count: 10, },
{ name: "JSString", bytes: 0, count: 0, },
],
};

View File

@ -0,0 +1,45 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests CensusTreeNode with `coarseType` breakdown.
*/
function run_test() {
compareCensusViewData(BREAKDOWN, REPORT, EXPECTED, `${JSON.stringify(BREAKDOWN)} has correct results.`);
}
const countBreakdown = { by: "count", count: true, bytes: true };
const BREAKDOWN = {
by: "coarseType",
objects: { by: "objectClass", then: countBreakdown },
strings: countBreakdown,
other: { by: "internalType", then: countBreakdown },
};
const REPORT = {
"objects": {
"Function": { bytes: 10, count: 1 },
"Array": { bytes: 20, count: 2 },
},
"strings": { bytes: 10, count: 1 },
"other": {
"js::Shape": { bytes: 30, count: 3 },
"js::Shape2": { bytes: 40, count: 4 }
},
};
const EXPECTED = {
children: [
{ name: "strings", bytes: 10, count: 1, },
{ name: "objects", children: [
{ name: "Array", bytes: 20, count: 2, },
{ name: "Function", bytes: 10, count: 1, },
]},
{ name: "other", children: [
{ name: "js::Shape2", bytes: 40, count: 4, },
{ name: "js::Shape", bytes: 30, count: 3, },
]},
]
};

View File

@ -0,0 +1,38 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests CensusTreeNode with `objectClass` breakdown.
*/
function run_test() {
compareCensusViewData(BREAKDOWN, REPORT, EXPECTED, `${JSON.stringify(BREAKDOWN)} has correct results.`);
}
const countBreakdown = { by: "count", count: true, bytes: true };
const BREAKDOWN = {
by: "objectClass",
then: countBreakdown,
other: { by: "internalType", then: countBreakdown }
};
const REPORT = {
"Function": { bytes: 10, count: 10 },
"Array": { bytes: 100, count: 1 },
"other": {
"JIT::CODE::NOW!!!": { bytes: 20, count: 2 },
"JIT::CODE::LATER!!!": { bytes: 40, count: 4 }
}
};
const EXPECTED = {
children: [
{ name: "Array", bytes: 100, count: 1 },
{ name: "Function", bytes: 10, count: 10 },
{ name: "other", children: [
{ name: "JIT::CODE::LATER!!!", bytes: 40, count: 4 },
{ name: "JIT::CODE::NOW!!!", bytes: 20, count: 2 },
]}
]
};

View File

@ -0,0 +1,10 @@
[DEFAULT]
tags = devtools
head = head.js
tail =
firefox-appdir = browser
skip-if = toolkit == 'android' || toolkit == 'gonk'
[test_census-01.js]
[test_census-02.js]
[test_census-03.js]

View File

@ -16,6 +16,7 @@ DIRS += [
'inspector',
'layoutview',
'markupview',
'memory',
'netmonitor',
'performance',
'projecteditor',

View File

@ -730,6 +730,8 @@ you can use these alternative items. Otherwise, their values should be empty. -
<!ENTITY identity.moreInfoLinkText2 "More Information">
<!ENTITY identity.permissions "Permissions">
<!ENTITY identity.permissionsPageFunctionality "Page Functionality">
<!ENTITY identity.permissionsSystemAccess "System Access">
<!-- Name for the tabs toolbar as spoken by screen readers.
The word "toolbar" is appended automatically and should not be contained below! -->

View File

@ -105,6 +105,12 @@
opens the split Console and displays the properties in its side panel. -->
<!ENTITY inspectorShowDOMProperties.label "Show DOM Properties">
<!-- LOCALIZATION NOTE (inspectorUseInConsole.label): This is the label
shown in the inspector contextual-menu for the item that outputs a
variable for the current node to the console. When triggered,
this item opens the split Console. -->
<!ENTITY inspectorUseInConsole.label "Use in Console">
<!-- LOCALIZATION NOTE (inspectorExpandNode.label): This is the label
shown in the inspector contextual-menu for recursively expanding
mark-up elements -->

View File

@ -6,6 +6,8 @@ this.EXPORTED_SYMBOLS = [ "SitePermissions" ];
Components.utils.import("resource://gre/modules/Services.jsm");
const GROUP_PAGE_FUNCTIONALITY = "pagefunctionality";
const GROUP_SYSTEM_ACCESS = "systemaccess";
let gStringBundle =
Services.strings.createBundle("chrome://browser/locale/sitePermissions.properties");
@ -24,9 +26,34 @@ this.SitePermissions = {
return aURI.schemeIs("http") || aURI.schemeIs("https");
},
/* Returns an array of permission IDs that match a given
* group identifier
*/
_listPermissionsByGroup(group) {
let array = Object.keys(gPermissionObject).filter(p=> {
return gPermissionObject[p].group == group;
});
array.sort((a, b) => {
return this.getPermissionLabel(a).localeCompare(this.getPermissionLabel(b));
});
return array;
},
/* Returns an array of 'page functionality' permission IDs
*/
listPageFunctionalityPermissions() {
return this._listPermissionsByGroup(GROUP_PAGE_FUNCTIONALITY);
},
/* Returns an array of 'system access' permission IDs
*/
listSystemAccessPermissions() {
return this._listPermissionsByGroup(GROUP_SYSTEM_ACCESS);
},
/* Returns an array of all permission IDs.
*/
listPermissions: function () {
listPermissions () {
let array = Object.keys(gPermissionObject);
array.sort((a, b) => {
return this.getPermissionLabel(a).localeCompare(this.getPermissionLabel(b));
@ -136,12 +163,17 @@ let gPermissionObject = {
* Defaults to UNKNOWN, indicating that the user will be asked each time
* a page asks for that permissions.
*
* - group
* A string, either 'systemacces' or 'pagefunctionality'.
* Indicates what group this should be listed with in the UI
*
* - states
* Array of permission states to be exposed to the user.
* Defaults to ALLOW, BLOCK and the default state (see getDefault).
*/
"image": {
group: GROUP_PAGE_FUNCTIONALITY,
getDefault: function () {
return Services.prefs.getIntPref("permissions.default.image") == 2 ?
SitePermissions.BLOCK : SitePermissions.ALLOW;
@ -149,6 +181,7 @@ let gPermissionObject = {
},
"cookie": {
group: GROUP_PAGE_FUNCTIONALITY,
states: [ SitePermissions.ALLOW, SitePermissions.SESSION, SitePermissions.BLOCK ],
getDefault: function () {
if (Services.prefs.getIntPref("network.cookie.cookieBehavior") == 2)
@ -161,12 +194,19 @@ let gPermissionObject = {
}
},
"desktop-notification": {},
"desktop-notification": {
group: GROUP_PAGE_FUNCTIONALITY,
},
"camera": {},
"microphone": {},
"camera": {
group: GROUP_SYSTEM_ACCESS,
},
"microphone": {
group: GROUP_SYSTEM_ACCESS,
},
"popup": {
group: GROUP_PAGE_FUNCTIONALITY,
getDefault: function () {
return Services.prefs.getBoolPref("dom.disable_open_during_load") ?
SitePermissions.BLOCK : SitePermissions.ALLOW;
@ -174,6 +214,7 @@ let gPermissionObject = {
},
"install": {
group: GROUP_PAGE_FUNCTIONALITY,
getDefault: function () {
return Services.prefs.getBoolPref("xpinstall.whitelist.required") ?
SitePermissions.BLOCK : SitePermissions.ALLOW;
@ -181,16 +222,21 @@ let gPermissionObject = {
},
"geo": {
group: GROUP_SYSTEM_ACCESS,
exactHostMatch: true
},
"indexedDB": {},
"indexedDB": {
group: GROUP_SYSTEM_ACCESS,
},
"pointerLock": {
group: GROUP_SYSTEM_ACCESS,
exactHostMatch: true
},
"push": {
group: GROUP_SYSTEM_ACCESS,
exactHostMatch: true
}
};

View File

@ -0,0 +1,22 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
"use strict";
Components.utils.import("resource:///modules/SitePermissions.jsm");
add_task(function* testPermissionsListing() {
Assert.deepEqual(SitePermissions.listPermissions().sort(),
["camera","cookie","desktop-notification","geo","image",
"indexedDB","install","microphone","pointerLock","popup",
"push"],
"Correct list of all permissions");
Assert.deepEqual(SitePermissions.listPageFunctionalityPermissions().sort(),
["cookie","desktop-notification","image","install","popup"],
"Correct list of 'page functionality' permissions");
Assert.deepEqual(SitePermissions.listSystemAccessPermissions().sort(),
["camera","geo","indexedDB","microphone","pointerLock","push"],
"Correct list of 'page functionality' permissions");
});

View File

@ -6,3 +6,4 @@ skip-if = toolkit == 'android' || toolkit == 'gonk'
[test_DirectoryLinksProvider.js]
[test_NewTabURL.js]
[test_SitePermissions.js]

View File

@ -34,6 +34,8 @@
--identity-box-verified-background-color: #fff;
--panel-separator-color: ThreeDShadow;
--urlbar-separator-color: hsla(0,0%,16%,.2);
}
#menubar-items {
@ -1193,10 +1195,19 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-
/* Combined go/reload/stop button in location bar */
#urlbar > toolbarbutton {
#urlbar-go-button,
#urlbar-reload-button,
#urlbar-stop-button {
-moz-appearance: none;
padding: 0 2px;
list-style-image: url("chrome://browser/skin/reload-stop-go.png");
padding: 0 9px;
margin-inline-start: 2px;
border-inline-start: 1px solid var(--urlbar-separator-color);
border-image: linear-gradient(transparent 15%,
var(--urlbar-separator-color) 15%,
var(--urlbar-separator-color) 85%,
transparent 85%);
border-image-slice: 1;
}
#urlbar-reload-button {
@ -1244,10 +1255,18 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-
}
@media (min-resolution: 1.1dppx) {
#urlbar > toolbarbutton {
#urlbar-go-button,
#urlbar-reload-button,
#urlbar-stop-button {
list-style-image: url("chrome://browser/skin/reload-stop-go@2x.png");
}
#urlbar-go-button > .toolbarbutton-icon,
#urlbar-reload-button > .toolbarbutton-icon,
#urlbar-stop-button > .toolbarbutton-icon {
width: 14px;
}
#urlbar-go-button {
-moz-image-region: rect(0, 84px, 28px, 56px);
}
@ -1283,10 +1302,6 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-
#urlbar-stop-button:hover:active {
-moz-image-region: rect(56px, 56px, 84px, 28px);
}
#urlbar > toolbarbutton > .toolbarbutton-icon {
width: 14px;
}
}
/* Popup blocker button */

View File

@ -349,6 +349,7 @@ browser.jar:
skin/classic/browser/devtools/eyedropper.css (../shared/devtools/eyedropper.css)
* skin/classic/browser/devtools/netmonitor.css (../shared/devtools/netmonitor.css)
skin/classic/browser/devtools/performance.css (../shared/devtools/performance.css)
skin/classic/browser/devtools/memory.css (../shared/devtools/memory.css)
skin/classic/browser/devtools/promisedebugger.css (../shared/devtools/promisedebugger.css)
skin/classic/browser/devtools/timeline-filter.svg (../shared/devtools/images/timeline-filter.svg)
* skin/classic/browser/devtools/scratchpad.css (../shared/devtools/scratchpad.css)

View File

@ -43,6 +43,8 @@
--urlbar-dropmarker-active-2x-region: rect(0, 44px, 28px, 22px);
--panel-separator-color: hsla(210,4%,10%,.14);
--urlbar-separator-color: hsla(0,0%,16%,.2);
}
#urlbar:-moz-lwtheme:not([focused="true"]),
@ -1892,12 +1894,18 @@ richlistitem[type~="action"][actiontype="switchtab"][selected="true"] > .ac-url-
/* ----- COMBINED GO/RELOAD/STOP BUTTON IN LOCATION BAR ----- */
#urlbar > toolbarbutton {
#urlbar-go-button,
#urlbar-reload-button,
#urlbar-stop-button {
margin: 0;
-moz-padding-start: 2px;
-moz-padding-end: 1px;
background-origin: border-box;
list-style-image: url("chrome://browser/skin/reload-stop-go.png");
padding: 0 9px;
margin-inline-start: 2px;
border-inline-start: 1px solid var(--urlbar-separator-color);
border-image: linear-gradient(transparent 15%,
var(--urlbar-separator-color) 15%,
var(--urlbar-separator-color) 85%,
transparent 85%);
}
#urlbar-go-button {
@ -1932,33 +1940,19 @@ richlistitem[type~="action"][actiontype="switchtab"][selected="true"] > .ac-url-
-moz-image-region: rect(14px, 28px, 28px, 14px);
}
#bookmarks-menu-button[cui-areatype="toolbar"] > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon {
width: 18px;
height: 18px;
}
#bookmarks-menu-button[cui-areatype="toolbar"].bookmark-item > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon {
width: 16px;
height: 16px;
}
#BMB_bookmarksPopup[side="top"],
#BMB_bookmarksPopup[side="bottom"] {
margin-left: -26px;
margin-right: -26px;
}
#BMB_bookmarksPopup[side="left"],
#BMB_bookmarksPopup[side="right"] {
margin-top: -26px;
margin-bottom: -26px;
}
@media (min-resolution: 2dppx) {
#urlbar > toolbarbutton {
#urlbar-go-button,
#urlbar-reload-button,
#urlbar-stop-button {
list-style-image: url("chrome://browser/skin/reload-stop-go@2x.png");
}
#urlbar-go-button > .toolbarbutton-icon,
#urlbar-reload-button > .toolbarbutton-icon,
#urlbar-stop-button > .toolbarbutton-icon {
width: 14px;
}
#urlbar-go-button {
-moz-image-region: rect(0, 84px, 28px, 56px);
}
@ -1982,10 +1976,28 @@ richlistitem[type~="action"][actiontype="switchtab"][selected="true"] > .ac-url-
#urlbar-stop-button:hover:active {
-moz-image-region: rect(28px, 56px, 56px, 28px);
}
}
#urlbar > toolbarbutton > .toolbarbutton-icon {
width: 14px;
}
#bookmarks-menu-button[cui-areatype="toolbar"] > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon {
width: 18px;
height: 18px;
}
#bookmarks-menu-button[cui-areatype="toolbar"].bookmark-item > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon {
width: 16px;
height: 16px;
}
#BMB_bookmarksPopup[side="top"],
#BMB_bookmarksPopup[side="bottom"] {
margin-left: -26px;
margin-right: -26px;
}
#BMB_bookmarksPopup[side="left"],
#BMB_bookmarksPopup[side="right"] {
margin-top: -26px;
margin-bottom: -26px;
}
/* POPUP BLOCKER BUTTON */

View File

@ -449,6 +449,7 @@ browser.jar:
skin/classic/browser/devtools/eyedropper.css (../shared/devtools/eyedropper.css)
* skin/classic/browser/devtools/netmonitor.css (../shared/devtools/netmonitor.css)
skin/classic/browser/devtools/performance.css (../shared/devtools/performance.css)
skin/classic/browser/devtools/memory.css (../shared/devtools/memory.css)
skin/classic/browser/devtools/promisedebugger.css (../shared/devtools/promisedebugger.css)
skin/classic/browser/devtools/timeline-filter.svg (../shared/devtools/images/timeline-filter.svg)
* skin/classic/browser/devtools/scratchpad.css (../shared/devtools/scratchpad.css)

View File

@ -170,6 +170,11 @@
font-size: 150%;
}
.identity-popup-subheadline {
margin: 0;
font-weight: bold;
}
.identity-popup-warning-gray {
-moz-padding-start: 24px;
background: url(chrome://browser/skin/controlcenter/warning-gray.svg) no-repeat 0 50%;
@ -297,6 +302,12 @@ description#identity-popup-content-verifier,
margin-top: 5px;
}
#permission-subview-list-page-functionality,
#permission-subview-list-system-access {
margin: 3px 0;
-moz-margin-start: 3px;
}
#identity-popup-permission-list menulist.identity-popup-permission,
#identity-popup-permission-subview-list menulist.identity-popup-permission {
max-width: 10em;

View File

@ -50,6 +50,7 @@
/* Url and search bars */
--url-and-searchbar-background-color: #171B1F;
--url-and-searchbar-color: #fff;
--urlbar-separator-color: #5F6670;
--urlbar-dropmarker-url: url("chrome://browser/skin/devedition/urlbar-history-dropmarker.svg");
--urlbar-dropmarker-region: rect(0px, 11px, 14px, 0px);
--urlbar-dropmarker-hover-region: rect(0, 22px, 14px, 11px);
@ -62,7 +63,6 @@
}
:root[devtoolstheme="dark"] #identity-box {
--identity-box-border-color: #5F6670;
--identity-box-chrome-color: #46afe3;
--identity-box-verified-background-color: transparent;
--identity-box-selected-background-color: rgba(231,230,230,.2);

View File

@ -0,0 +1,81 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/* 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/. */
/* CSS Variables specific to this panel that aren't defined by the themes */
.theme-dark {
--cell-border-color: rgba(255,255,255,0.15);
--cell-border-color-light: rgba(255,255,255,0.1);
--focus-cell-border-color: rgba(255,255,255,0.5);
--row-alt-background-color: rgba(29,79,115,0.15);
--row-hover-background-color: rgba(29,79,115,0.25);
}
.theme-light {
--cell-border-color: rgba(0,0,0,0.15);
--cell-border-color-light: rgba(0,0,0,0.1);
--focus-cell-border-color: rgba(0,0,0,0.3);
--row-alt-background-color: rgba(76,158,217,0.1);
--row-hover-background-color: rgba(76,158,217,0.2);
}
.heap-view {
position: relative;
}
.heap-view .theme-twisty {
text-align: end;
}
.heap-tree-item {
list-style-type: none;
/* display: none; */
}
.heap-tree-item[expanded] {
display: block;
}
.heap-tree-item:nth-child(2n) {
background-color: var(--row-alt-background-color);
}
.heap-tree-item:hover {
background-color: var(--row-hover-background-color);
}
.heap-tree-item:focus {
background-color: var(--theme-selection-background);
}
.heap-tree-item:focus description {
color: var(--theme-selection-color) !important;
}
.heap-tree-item:focus .call-tree-cell {
-moz-border-end-color: var(--focus-cell-border-color);
}
.heap-tree-cell[type="bytes"], .heap-tree-cell[type="count"] {
position: absolute;
text-align: right;
width: 40px;
}
.heap-tree-cell[type="name"] {
width: 150px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: block;
}
.heap-tree-cell[type="count"] {
left: 300px;
}
.heap-tree-cell[type="bytes"] {
left: 250px;
}

View File

@ -5,7 +5,6 @@
%endif
#identity-box {
--identity-box-border-color: hsla(0,0%,16%,.2);
--identity-box-selected-background-color: rgb(231,230,230);
--identity-box-verified-color: hsl(92,100%,30%);
%ifdef MOZ_OFFICIAL_BRANDING
@ -18,10 +17,10 @@
%endif
%endif
border-inline-end: 1px solid var(--identity-box-border-color);
border-inline-end: 1px solid var(--urlbar-separator-color);
border-image: linear-gradient(transparent 15%,
var(--identity-box-border-color) 15%,
var(--identity-box-border-color) 85%,
var(--urlbar-separator-color) 15%,
var(--urlbar-separator-color) 85%,
transparent 85%);
border-image-slice: 1;
font-size: .9em;

View File

@ -47,6 +47,8 @@
--urlbar-dropmarker-active-2x-region: rect(0, 66px, 28px, 44px);
--panel-separator-color: ThreeDLightShadow;
--urlbar-separator-color: hsla(0,0%,16%,.2);
}
#menubar-items {
@ -1649,12 +1651,20 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-
/* combined go/reload/stop button in location bar */
#urlbar > toolbarbutton {
#urlbar-go-button,
#urlbar-reload-button,
#urlbar-stop-button {
-moz-appearance: none;
padding: 0 2px;
background-origin: border-box;
border: none;
border-style: none;
list-style-image: url("chrome://browser/skin/reload-stop-go.png");
padding: 0 9px;
margin-inline-start: 2px;
border-inline-start: 1px solid var(--urlbar-separator-color);
border-image: linear-gradient(transparent 15%,
var(--urlbar-separator-color) 15%,
var(--urlbar-separator-color) 85%,
transparent 85%);
border-image-slice: 1;
}
#urlbar-reload-button {
@ -1702,10 +1712,18 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-
}
@media (min-resolution: 1.1dppx) {
#urlbar > toolbarbutton {
#urlbar-go-button,
#urlbar-reload-button,
#urlbar-stop-button {
list-style-image: url("chrome://browser/skin/reload-stop-go@2x.png");
}
#urlbar-go-button > .toolbarbutton-icon,
#urlbar-reload-button > .toolbarbutton-icon,
#urlbar-stop-button > .toolbarbutton-icon {
width: 14px;
}
#urlbar-go-button {
-moz-image-region: rect(0, 84px, 28px, 56px);
}
@ -1741,10 +1759,6 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-
#urlbar-stop-button:hover:active {
-moz-image-region: rect(56px, 56px, 84px, 28px);
}
#urlbar > toolbarbutton > .toolbarbutton-icon {
width: 14px;
}
}
/* popup blocker button */

View File

@ -472,6 +472,7 @@ browser.jar:
skin/classic/browser/devtools/debugger.css (../shared/devtools/debugger.css)
* skin/classic/browser/devtools/netmonitor.css (../shared/devtools/netmonitor.css)
skin/classic/browser/devtools/performance.css (../shared/devtools/performance.css)
skin/classic/browser/devtools/memory.css (../shared/devtools/memory.css)
skin/classic/browser/devtools/promisedebugger.css (../shared/devtools/promisedebugger.css)
skin/classic/browser/devtools/timeline-filter.svg (../shared/devtools/images/timeline-filter.svg)
* skin/classic/browser/devtools/scratchpad.css (../shared/devtools/scratchpad.css)

View File

@ -78,7 +78,7 @@ BluetoothPbapManager::HandleShutdown()
BluetoothPbapManager::BluetoothPbapManager() : mConnected(false)
, mRemoteMaxPacketLength(0)
, mRequirePhonebookSize(false)
, mPhonebookSizeRequired(false)
{
mDeviceAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
mCurrentPath.AssignLiteral("");
@ -273,7 +273,7 @@ BluetoothPbapManager::ReceiveSocketData(BluetoothSocket* aSocket,
// All OBEX request messages shall be sent as one OBEX packet containing
// all of the headers. I.e. OBEX GET with opcode 0x83 shall always be
// used. OBEX GET with opcode 0x03 shall never be used.
BT_WARNING("PBAP shall always uses OBEX GetFinal instead of Get.");
BT_LOGR("PBAP shall always uses OBEX GetFinal instead of Get.");
// no break. Treat 'Get' as 'GetFinal' for error tolerance.
case ObexRequestCode::GetFinal: {
@ -282,8 +282,8 @@ BluetoothPbapManager::ReceiveSocketData(BluetoothSocket* aSocket,
// final body information (in an End-of-Body header) arrives, along with
// the response code 0xA0 Success.
if (mVCardDataStream) {
if (!ReplyToGet(mVCardDataStream)) {
BT_WARNING("Failed to reply to PBAP GET request.");
if (!ReplyToGet()) {
BT_LOGR("Failed to reply to PBAP GET request.");
ReplyError(ObexResponseCode::InternalServerError);
}
return;
@ -541,7 +541,7 @@ BluetoothPbapManager::AppendBtNamedValueByTagId(
if (order < MOZ_ARRAY_LENGTH(sOrderStr)) {
BT_APPEND_NAMED_VALUE(aValues, "order", sOrderStr[order]);
} else {
BT_WARNING("%s: Unexpected value '%d' of 'Order'", __FUNCTION__, order);
BT_LOGR("Unexpected value %d of 'Order'", order);
}
break;
}
@ -572,8 +572,7 @@ BluetoothPbapManager::AppendBtNamedValueByTagId(
if (searchKey < MOZ_ARRAY_LENGTH(sSearchKeyStr)) {
BT_APPEND_NAMED_VALUE(aValues, "searchKey", sSearchKeyStr[searchKey]);
} else {
BT_WARNING("%s: Unexpected value '%d' of 'SearchProperty'",
__FUNCTION__, searchKey);
BT_LOGR("Unexpected value %d of 'SearchProperty'", searchKey);
}
break;
}
@ -590,7 +589,7 @@ BluetoothPbapManager::AppendBtNamedValueByTagId(
// Section 5 "Phone Book Access Profile Functions", PBAP 1.2
// Replying 'PhonebookSize' is mandatory if 'MaxListCount' parameter is
// present in the request with a value of 0, else it is excluded.
mRequirePhonebookSize = !maxListCount;
mPhonebookSizeRequired = !maxListCount;
BT_APPEND_NAMED_VALUE(aValues, "maxListCount", (uint32_t) maxListCount);
break;
@ -697,7 +696,7 @@ BluetoothPbapManager::AfterPbapDisconnected()
mConnected = false;
mRemoteMaxPacketLength = 0;
mRequirePhonebookSize = false;
mPhonebookSizeRequired = false;
if (mVCardDataStream) {
mVCardDataStream->Close();
@ -819,12 +818,12 @@ BluetoothPbapManager::ReplyToPullPhonebook(Blob* aBlob, uint16_t aPhonebookSize)
return false;
}
if (!GetInputStreamFromBlob(mVCardDataStream, aBlob)) {
if (!GetInputStreamFromBlob(aBlob)) {
ReplyError(ObexResponseCode::InternalServerError);
return false;
}
return ReplyToGet(mVCardDataStream, aPhonebookSize);
return ReplyToGet(aPhonebookSize);
}
bool
@ -845,12 +844,12 @@ BluetoothPbapManager::ReplyToPullvCardListing(Blob* aBlob,
return false;
}
if (!GetInputStreamFromBlob(mVCardDataStream, aBlob)) {
if (!GetInputStreamFromBlob(aBlob)) {
ReplyError(ObexResponseCode::InternalServerError);
return false;
}
return ReplyToGet(mVCardDataStream, aPhonebookSize);
return ReplyToGet(aPhonebookSize);
}
bool
@ -869,19 +868,18 @@ BluetoothPbapManager::ReplyToPullvCardEntry(Blob* aBlob)
return false;
}
if (!GetInputStreamFromBlob(mVCardDataStream, aBlob)) {
if (!GetInputStreamFromBlob(aBlob)) {
ReplyError(ObexResponseCode::InternalServerError);
return false;
}
return ReplyToGet(mVCardDataStream);
return ReplyToGet();
}
bool
BluetoothPbapManager::ReplyToGet(nsIInputStream* aStream,
uint16_t aPhonebookSize)
BluetoothPbapManager::ReplyToGet(uint16_t aPhonebookSize)
{
MOZ_ASSERT(aStream);
MOZ_ASSERT(mVCardDataStream);
MOZ_ASSERT(mRemoteMaxPacketLength >= kObexLeastMaxSize);
// This response will be composed by these four parts.
@ -897,7 +895,7 @@ BluetoothPbapManager::ReplyToGet(nsIInputStream* aStream,
unsigned int index = kObexRespHeaderSize;
// ---- Part 2, add [response code:1][length:2] to response ---- //
if (mRequirePhonebookSize) {
if (mPhonebookSizeRequired) {
// convert little endian to big endian
uint8_t phonebookSize[2];
phonebookSize[0] = (aPhonebookSize & 0xFF00) >> 8;
@ -917,7 +915,7 @@ BluetoothPbapManager::ReplyToGet(nsIInputStream* aStream,
mRemoteMaxPacketLength,
appParameters,
sizeof(appParameters));
mRequirePhonebookSize = false;
mPhonebookSizeRequired = false;
}
// ---- Part 3, add [headerId:1][length:2][Body:var] to response ---- //
@ -928,9 +926,10 @@ BluetoothPbapManager::ReplyToGet(nsIInputStream* aStream,
// Read vCard data from input stream
uint32_t numRead = 0;
nsAutoArrayPtr<char> buffer(new char[remainingPacketSize]);
nsresult rv = aStream->Read(buffer, remainingPacketSize, &numRead);
nsresult rv = mVCardDataStream->Read(buffer, remainingPacketSize, &numRead);
if (NS_FAILED(rv)) {
BT_WARNING("Failed to read from input stream.");
BT_LOGR("Failed to read from input stream. rv=0x%x",
static_cast<uint32_t>(rv));
return false;
}
@ -951,8 +950,8 @@ BluetoothPbapManager::ReplyToGet(nsIInputStream* aStream,
opcode = ObexResponseCode::Success;
index += AppendHeaderEndOfBody(&res[index]);
aStream->Close();
aStream = nullptr;
mVCardDataStream->Close();
mVCardDataStream = nullptr;
}
SendObexData(res, opcode, index);
@ -962,19 +961,20 @@ BluetoothPbapManager::ReplyToGet(nsIInputStream* aStream,
}
bool
BluetoothPbapManager::GetInputStreamFromBlob(nsIInputStream* aStream,
Blob* aBlob)
BluetoothPbapManager::GetInputStreamFromBlob(Blob* aBlob)
{
// PBAP can only handle one OBEX BODY transfer at the same time.
if (mVCardDataStream) {
BT_WARNING("Shouldn't handle multiple PBAP responses at the same time");
BT_LOGR("Shouldn't handle multiple PBAP responses simultaneously");
mVCardDataStream->Close();
mVCardDataStream = nullptr;
}
ErrorResult rv;
aBlob->GetInternalStream(getter_AddRefs(mVCardDataStream), rv);
if (NS_WARN_IF(rv.Failed())) {
if (rv.Failed()) {
BT_LOGR("Failed to get internal stream from blob. rv=0x%x",
rv.ErrorCodeAsInt());
return false;
}
@ -1043,11 +1043,12 @@ BluetoothPbapManager::OnSocketDisconnect(BluetoothSocket* aSocket)
void
BluetoothPbapManager::Disconnect(BluetoothProfileController* aController)
{
if (mSocket) {
mSocket->Close();
} else {
BT_WARNING("%s: No ongoing connection to disconnect", __FUNCTION__);
if (!mSocket) {
BT_LOGR("No ongoing connection to disconnect");
return;
}
mSocket->Close();
}
NS_IMPL_ISUPPORTS(BluetoothPbapManager, nsIObserver)

View File

@ -143,8 +143,7 @@ private:
void ReplyToSetPath();
void ReplyError(uint8_t aError);
void SendObexData(uint8_t* aData, uint8_t aOpcode, int aSize);
bool ReplyToGet(nsIInputStream* aStream, uint16_t aPhonebookSize = 0);
bool GetInputStreamFromBlob(nsIInputStream* aStream, Blob* aBlob);
bool ReplyToGet(uint16_t aPhonebookSize = 0);
uint8_t SetPhoneBookPath(uint8_t flags, const ObexHeaderSet& aHeader);
uint8_t PullPhonebook(const ObexHeaderSet& aHeader);
@ -158,6 +157,7 @@ private:
InfallibleTArray<uint32_t> PackPropertiesMask(uint8_t* aData, int aSize);
bool CompareHeaderTarget(const ObexHeaderSet& aHeader);
bool IsLegalPath(const nsAString& aPath);
bool GetInputStreamFromBlob(Blob* aBlob);
void AfterPbapConnected();
void AfterPbapDisconnected();
@ -188,15 +188,14 @@ private:
nsRefPtr<BluetoothSocket> mServerSocket;
/**
* The data stream of vCards which is used in current processing response.
* The vCard data stream for current processing response
*/
nsCOMPtr<nsIInputStream> mVCardDataStream;
/**
* A flag to indicate whether 'PhonebookSize' is mandatory for next OBEX
* response
* Whether 'PhonebookSize' is mandatory for next OBEX response
*/
bool mRequirePhonebookSize;
bool mPhonebookSizeRequired;
};
END_BLUETOOTH_NAMESPACE

View File

@ -185,8 +185,8 @@ HTTP(..) != 1170688.html 1170688-ref.html
== emoji-03.html emoji-03-ref.html
# the next two will fail on OS X 10.6 and on Windows prior to 8.1 because no color emoji font is present,
# and also on Linux/Android/B2G platforms until we have color emoji fonts there
fails-if(OSX==1006||/^Windows\x20NT\x20(5|6\.[0-2])/.test(http.oscpu)||gtkWidget||Android||B2G) != emoji-03.html emoji-03-notref.html
fails-if(OSX==1006||/^Windows\x20NT\x20(5|6\.[0-2])/.test(http.oscpu)||gtkWidget||Android||B2G) == emoji-04.html emoji-04-ref.html
fails-if(OSX==1006||/^Windows\x20NT\x20(5|6\.[0-2])/.test(http.oscpu)||gtkWidget||Android) != emoji-03.html emoji-03-notref.html
fails-if(OSX==1006||/^Windows\x20NT\x20(5|6\.[0-2])/.test(http.oscpu)||gtkWidget||Android) == emoji-04.html emoji-04-ref.html
!= emoji-05.html emoji-05-notref.html
# check that Graphite shaping (bug 631479) is working

View File

@ -97,6 +97,8 @@ public interface BrowserDB {
*/
public abstract Cursor getRecentHistory(ContentResolver cr, int limit);
public abstract Cursor getRecentHistoryBetweenTime(ContentResolver cr, int historyLimit, long start, long end);
public abstract void expireHistory(ContentResolver cr, ExpirePriority priority);
public abstract void removeHistoryEntry(ContentResolver cr, String url);

View File

@ -719,6 +719,21 @@ public class LocalBrowserDB implements BrowserDB {
History.DATE_LAST_VISITED + " DESC");
}
@Override
public Cursor getRecentHistoryBetweenTime(ContentResolver cr, int limit, long start, long end) {
return cr.query(combinedUriWithLimit(limit),
new String[] { Combined._ID,
Combined.BOOKMARK_ID,
Combined.HISTORY_ID,
Combined.URL,
Combined.TITLE,
Combined.DATE_LAST_VISITED,
Combined.VISITS },
History.DATE_LAST_VISITED + " >= " + start + " AND " + History.DATE_LAST_VISITED + " < " + end,
null,
History.DATE_LAST_VISITED + " DESC");
}
@Override
public void expireHistory(ContentResolver cr, ExpirePriority priority) {
Uri url = mHistoryExpireUriWithProfile;

View File

@ -224,6 +224,11 @@ public class StubBrowserDB implements BrowserDB {
return null;
}
@Override
public Cursor getRecentHistoryBetweenTime(ContentResolver cr, int limit, long time, long end) {
return null;
}
public void expireHistory(ContentResolver cr, BrowserContract.ExpirePriority priority) {
}

View File

@ -0,0 +1,143 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* 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/. */
package org.mozilla.gecko.home;
import android.content.Context;
import android.database.Cursor;
import android.util.SparseArray;
import android.view.View;
import android.widget.TextView;
import org.mozilla.gecko.R;
import org.mozilla.gecko.db.BrowserContract;
/**
* CursorAdapter for <code>HistoryPanel</code> to partition history items by <code>MostRecentSection</code> range headers.
*/
public class HistoryHeaderListCursorAdapter extends MultiTypeCursorAdapter implements HistoryPanel.HistoryUrlProvider {
private static final int ROW_HEADER = 0;
private static final int ROW_STANDARD = 1;
private static final int[] VIEW_TYPES = new int[] { ROW_STANDARD, ROW_HEADER };
private static final int[] LAYOUT_TYPES = new int[] { R.layout.home_item_row, R.layout.home_header_row };
// Maps headers in the list with their respective sections
private final SparseArray<HistoryPanel.MostRecentSection> mMostRecentSections;
public HistoryHeaderListCursorAdapter(Context context) {
super(context, null, VIEW_TYPES, LAYOUT_TYPES);
// Initialize map of history sections
mMostRecentSections = new SparseArray<>();
}
@Override
public Object getItem(int position) {
final int type = getItemViewType(position);
// Header items are not in the cursor
if (type == ROW_HEADER) {
return null;
}
return super.getItem(position - getMostRecentSectionsCountBefore(position));
}
@Override
public int getItemViewType(int position) {
if (mMostRecentSections.get(position) != null) {
return ROW_HEADER;
}
return ROW_STANDARD;
}
@Override
public boolean isEnabled(int position) {
return (getItemViewType(position) == ROW_STANDARD);
}
@Override
public int getCount() {
// Add the history section headers to the number of reported results.
return super.getCount() + mMostRecentSections.size();
}
@Override
public Cursor swapCursor(Cursor cursor) {
loadMostRecentSections(cursor);
Cursor oldCursor = super.swapCursor(cursor);
return oldCursor;
}
@Override
public void bindView(View view, Context context, int position) {
final int type = getItemViewType(position);
if (type == ROW_HEADER) {
final HistoryPanel.MostRecentSection section = mMostRecentSections.get(position);
final TextView row = (TextView) view;
row.setText(HistoryPanel.getMostRecentSectionTitle(section));
} else {
// Account for the most recent section headers
position -= getMostRecentSectionsCountBefore(position);
final Cursor c = getCursor(position);
final TwoLinePageRow row = (TwoLinePageRow) view;
row.updateFromCursor(c);
}
}
private int getMostRecentSectionsCountBefore(int position) {
// Account for the number headers before the given position
int sectionsBefore = 0;
final int historySectionsCount = mMostRecentSections.size();
for (int i = 0; i < historySectionsCount; i++) {
final int sectionPosition = mMostRecentSections.keyAt(i);
if (sectionPosition > position) {
break;
}
sectionsBefore++;
}
return sectionsBefore;
}
private void loadMostRecentSections(Cursor c) {
// Clear any history sections that may have been loaded before.
mMostRecentSections.clear();
if (c == null || !c.moveToFirst()) {
return;
}
HistoryPanel.MostRecentSection section = null;
do {
final int position = c.getPosition();
final long time = c.getLong(c.getColumnIndexOrThrow(BrowserContract.History.DATE_LAST_VISITED));
final HistoryPanel.MostRecentSection itemSection = HistoryPanel.getMostRecentSectionForTime(time);
if (section != itemSection) {
section = itemSection;
mMostRecentSections.append(position + mMostRecentSections.size(), section);
}
// Reached the last section, no need to continue
if (section == HistoryPanel.MostRecentSection.OLDER_THAN_SIX_MONTHS) {
break;
}
} while (c.moveToNext());
}
@Override
public String getURL(int position) {
position -= getMostRecentSectionsCountBefore(position);
final Cursor c = getCursor(position);
return c.getString(c.getColumnIndexOrThrow(BrowserContract.History.URL));
}
}

View File

@ -0,0 +1,53 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* 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/. */
package org.mozilla.gecko.home;
import android.content.Context;
import android.database.Cursor;
import android.support.v4.widget.CursorAdapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import org.mozilla.gecko.db.BrowserContract;
/**
* A Cursor adapter that is used to populate the history list items in split plane mode.
*/
public class HistoryItemAdapter extends CursorAdapter implements HistoryPanel.HistoryUrlProvider {
private final int resource;
public HistoryItemAdapter(Context context, Cursor c, int resource) {
super(context, c, false);
this.resource = resource;
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return LayoutInflater.from(context).inflate(resource, parent, false);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return super.getView(position, convertView, parent);
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
final TwoLinePageRow row = (TwoLinePageRow) view;
row.updateFromCursor(cursor);
}
@Override
public String getURL(int position) {
final Cursor cursor = getCursor();
if (cursor == null || !cursor.moveToPosition(position)) {
throw new IllegalStateException("Couldn't move cursor to position " + position);
}
return cursor.getString(cursor.getColumnIndexOrThrow(BrowserContract.History.URL));
}
}

View File

@ -5,8 +5,11 @@
package org.mozilla.gecko.home;
import java.util.Date;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.EnumSet;
import java.util.List;
import java.util.Locale;
import org.json.JSONException;
import org.json.JSONObject;
@ -15,25 +18,29 @@ import org.mozilla.gecko.EventDispatcher;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.GeckoEvent;
import org.mozilla.gecko.GeckoProfile;
import org.mozilla.gecko.GeckoScreenOrientation;
import org.mozilla.gecko.R;
import org.mozilla.gecko.RestrictedProfiles;
import org.mozilla.gecko.Telemetry;
import org.mozilla.gecko.TelemetryContract;
import org.mozilla.gecko.db.BrowserContract.Combined;
import org.mozilla.gecko.db.BrowserContract.History;
import org.mozilla.gecko.db.BrowserDB;
import org.mozilla.gecko.home.HomeContextMenuInfo.RemoveItemType;
import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
import org.mozilla.gecko.restrictions.Restriction;
import org.mozilla.gecko.util.ColorUtils;
import org.mozilla.gecko.util.HardwareUtils;
import android.app.AlertDialog;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.Configuration;
import android.database.Cursor;
import android.graphics.Typeface;
import android.os.Bundle;
import android.support.v4.content.Loader;
import android.support.v4.widget.CursorAdapter;
import android.text.SpannableStringBuilder;
import android.text.TextPaint;
import android.text.method.LinkMovementMethod;
@ -41,13 +48,13 @@ import android.text.style.ClickableSpan;
import android.text.style.StyleSpan;
import android.text.style.UnderlineSpan;
import android.util.Log;
import android.util.SparseArray;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
@ -58,6 +65,11 @@ public class HistoryPanel extends HomeFragment {
// Logging tag name
private static final String LOGTAG = "GeckoHistoryPanel";
// For the time sections in history
private static final long MS_PER_DAY = 86400000;
private static final long MS_PER_WEEK = MS_PER_DAY * 7;
private static final List<MostRecentSectionRange> recentSectionTimeOffsetList = new ArrayList<>(MostRecentSection.values().length);
// Cursor loader ID for history query
private static final int LOADER_ID_HISTORY = 0;
@ -65,11 +77,19 @@ public class HistoryPanel extends HomeFragment {
private final static String FORMAT_S1 = "%1$s";
private final static String FORMAT_S2 = "%2$s";
// Maintain selected range state.
// Only accessed from the UI thread.
private static MostRecentSection selected;
// Adapter for the list of recent history entries.
private HistoryAdapter mAdapter;
private CursorAdapter mAdapter;
// Adapter for the timeline of history entries.
private ArrayAdapter<MostRecentSection> mRangeAdapter;
// The view shown by the fragment.
private HomeListView mList;
private HomeListView mRangeList;
// The button view for clearing browsing history.
private View mClearHistoryButton;
@ -80,22 +100,62 @@ public class HistoryPanel extends HomeFragment {
// Callbacks used for the search and favicon cursor loaders
private CursorLoaderCallbacks mCursorLoaderCallbacks;
// The time ranges for each section
public enum MostRecentSection {
TODAY,
YESTERDAY,
WEEK,
THIS_MONTH,
MONTH_AGO,
TWO_MONTHS_AGO,
THREE_MONTHS_AGO,
FOUR_MONTHS_AGO,
FIVE_MONTHS_AGO,
MostRecentSection, OLDER_THAN_SIX_MONTHS
};
protected interface HistoryUrlProvider {
public String getURL(int position);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.home_history_panel, container, false);
if (HardwareUtils.isTablet() && GeckoScreenOrientation.getInstance().getAndroidOrientation() == Configuration.ORIENTATION_LANDSCAPE) {
return inflater.inflate(R.layout.home_history_split_pane_panel, container, false);
} else {
return inflater.inflate(R.layout.home_history_panel, container, false);
}
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mRangeList = (HomeListView) view.findViewById(R.id.range_list);
mList = (HomeListView) view.findViewById(R.id.list);
mList.setTag(HomePager.LIST_TAG_HISTORY);
if (mRangeList != null) {
mRangeList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapter, View view, int position, long id) {
final MostRecentSection rangeItem = (MostRecentSection) adapter.getItemAtPosition(position);
if (rangeItem != null) {
// Notify data has changed for both range and item adapter.
// This will update selected rangeItem item background and the tabs list.
// This will also update the selected range along with cursor start and end.
selected = rangeItem;
mRangeAdapter.notifyDataSetChanged();
getLoaderManager().getLoader(LOADER_ID_HISTORY).forceLoad();
}
}
});
}
mList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
position -= mAdapter.getMostRecentSectionsCountBefore(position);
final Cursor c = mAdapter.getCursor(position);
final String url = c.getString(c.getColumnIndexOrThrow(History.URL));
final String url = ((HistoryUrlProvider) mAdapter).getURL(position);
Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.LIST_ITEM);
@ -177,6 +237,7 @@ public class HistoryPanel extends HomeFragment {
@Override
public void onDestroyView() {
super.onDestroyView();
mRangeList = null;
mList = null;
mEmptyView = null;
mClearHistoryButton = null;
@ -186,37 +247,45 @@ public class HistoryPanel extends HomeFragment {
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Intialize adapter
mAdapter = new HistoryAdapter(getActivity());
mList.setAdapter(mAdapter);
// Reset selection.
selected = mRangeList == null ? MostRecentSection.THIS_MONTH : MostRecentSection.TODAY;
// Initialize adapter
if (mRangeList != null) {
mAdapter = new HistoryItemAdapter(getActivity(), null, R.layout.home_item_row);
mRangeAdapter = new HistoryRangeAdapter(getActivity(), R.layout.home_history_range_item);
mRangeList.setAdapter(mRangeAdapter);
mList.setAdapter(mAdapter);
} else {
mAdapter = new HistoryHeaderListCursorAdapter(getActivity());
mList.setAdapter(mAdapter);
}
// Create callbacks before the initial loader is started
mCursorLoaderCallbacks = new CursorLoaderCallbacks();
// Update the section string with current time as reference.
updateRecentSectionOffset(getActivity());
loadIfVisible();
}
@Override
protected void loadIfVisible() {
// Force reload fragment only in tablets.
if (canLoad() && HardwareUtils.isTablet()) {
load();
return;
}
super.loadIfVisible();
}
@Override
protected void load() {
getLoaderManager().initLoader(LOADER_ID_HISTORY, null, mCursorLoaderCallbacks);
}
private static class HistoryCursorLoader extends SimpleCursorLoader {
// Max number of history results
private static final int HISTORY_LIMIT = 100;
private final BrowserDB mDB;
public HistoryCursorLoader(Context context) {
super(context);
mDB = GeckoProfile.get(context).getDB();
}
@Override
public Cursor loadCursor() {
final ContentResolver cr = getContext().getContentResolver();
return mDB.getRecentHistory(cr, HISTORY_LIMIT);
}
}
private void updateUiFromCursor(Cursor c) {
if (c != null && c.getCount() > 0) {
if (RestrictedProfiles.isAllowed(getActivity(), Restriction.DISALLOW_CLEAR_HISTORY)) {
@ -314,176 +383,109 @@ public class HistoryPanel extends HomeFragment {
return ssb;
}
private static class HistoryAdapter extends MultiTypeCursorAdapter {
private static final int ROW_HEADER = 0;
private static final int ROW_STANDARD = 1;
private static void updateRecentSectionOffset(final Context context) {
final long now = System.currentTimeMillis();
final Calendar cal = Calendar.getInstance();
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 1);
private static final int[] VIEW_TYPES = new int[] { ROW_STANDARD, ROW_HEADER };
private static final int[] LAYOUT_TYPES = new int[] { R.layout.home_item_row, R.layout.home_header_row };
// Calculate the start, end time and display text for the MostRecentSection range.
recentSectionTimeOffsetList.add(MostRecentSection.TODAY.ordinal(),
new MostRecentSectionRange(cal.getTimeInMillis(), now, context.getString(R.string.history_today_section)));
recentSectionTimeOffsetList.add(MostRecentSection.YESTERDAY.ordinal(),
new MostRecentSectionRange(cal.getTimeInMillis() - MS_PER_DAY, cal.getTimeInMillis(), context.getString(R.string.history_yesterday_section)));
recentSectionTimeOffsetList.add(MostRecentSection.WEEK.ordinal(),
new MostRecentSectionRange(cal.getTimeInMillis() - MS_PER_WEEK, now, context.getString(R.string.history_week_section)));
// For the time sections in history
private static final long MS_PER_DAY = 86400000;
private static final long MS_PER_WEEK = MS_PER_DAY * 7;
// Update the calendar to start of next month.
cal.add(Calendar.MONTH, 1);
cal.set(Calendar.DAY_OF_MONTH, cal.getMinimum(Calendar.DAY_OF_MONTH));
// The time ranges for each section
private static enum MostRecentSection {
TODAY,
YESTERDAY,
WEEK,
OLDER
};
// Iterate over the remaining MostRecentSections, to find the start, end and display text.
for (int i = MostRecentSection.THIS_MONTH.ordinal(); i <= MostRecentSection.OLDER_THAN_SIX_MONTHS.ordinal(); i++) {
final long end = cal.getTimeInMillis();
cal.add(Calendar.MONTH, -1);
final long start = cal.getTimeInMillis();
final String displayName = (i != MostRecentSection.OLDER_THAN_SIX_MONTHS.ordinal())
? cal.getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.getDefault())
: context.getString(R.string.history_older_section);
recentSectionTimeOffsetList.add(i, new MostRecentSectionRange(start, end, displayName));
}
}
private final Context mContext;
private static class HistoryCursorLoader extends SimpleCursorLoader {
// Max number of history results
private static final int HISTORY_LIMIT = 100;
private final BrowserDB mDB;
// Maps headers in the list with their respective sections
private final SparseArray<MostRecentSection> mMostRecentSections;
public HistoryAdapter(Context context) {
super(context, null, VIEW_TYPES, LAYOUT_TYPES);
mContext = context;
// Initialize map of history sections
mMostRecentSections = new SparseArray<MostRecentSection>();
public HistoryCursorLoader(Context context) {
super(context);
mDB = GeckoProfile.get(context).getDB();
}
@Override
public Object getItem(int position) {
final int type = getItemViewType(position);
public Cursor loadCursor() {
final ContentResolver cr = getContext().getContentResolver();
updateRecentSectionOffset(getContext());
MostRecentSectionRange mostRecentSectionRange = recentSectionTimeOffsetList.get(selected.ordinal());
return mDB.getRecentHistoryBetweenTime(cr, HISTORY_LIMIT, mostRecentSectionRange.start, mostRecentSectionRange.end);
}
}
// Header items are not in the cursor
if (type == ROW_HEADER) {
return null;
protected static String getMostRecentSectionTitle(MostRecentSection section) {
return recentSectionTimeOffsetList.get(section.ordinal()).displayName;
}
protected static MostRecentSection getMostRecentSectionForTime(long time) {
for (int i = 0; i < MostRecentSection.OLDER_THAN_SIX_MONTHS.ordinal(); i++) {
if (time > recentSectionTimeOffsetList.get(i).start) {
return MostRecentSection.values()[i];
}
}
return super.getItem(position - getMostRecentSectionsCountBefore(position));
return MostRecentSection.OLDER_THAN_SIX_MONTHS;
}
private static class MostRecentSectionRange {
private final long start;
private final long end;
private final String displayName;
private MostRecentSectionRange(long start, long end, String displayName) {
this.start = start;
this.end = end;
this.displayName = displayName;
}
}
private static class HistoryRangeAdapter extends ArrayAdapter<MostRecentSection> {
private final Context context;
private final int resource;
public HistoryRangeAdapter(Context context, int resource) {
super(context, resource, MostRecentSection.values());
this.context = context;
this.resource = resource;
}
@Override
public int getItemViewType(int position) {
if (mMostRecentSections.get(position) != null) {
return ROW_HEADER;
}
return ROW_STANDARD;
}
@Override
public boolean isEnabled(int position) {
return (getItemViewType(position) == ROW_STANDARD);
}
@Override
public int getCount() {
// Add the history section headers to the number of reported results.
return super.getCount() + mMostRecentSections.size();
}
@Override
public Cursor swapCursor(Cursor cursor) {
loadMostRecentSections(cursor);
Cursor oldCursor = super.swapCursor(cursor);
return oldCursor;
}
@Override
public void bindView(View view, Context context, int position) {
final int type = getItemViewType(position);
if (type == ROW_HEADER) {
final MostRecentSection section = mMostRecentSections.get(position);
final TextView row = (TextView) view;
row.setText(getMostRecentSectionTitle(section));
public View getView(int position, View convertView, ViewGroup parent) {
final View view;
if (convertView != null) {
view = convertView;
} else {
// Account for the most recent section headers
position -= getMostRecentSectionsCountBefore(position);
final Cursor c = getCursor(position);
final TwoLinePageRow row = (TwoLinePageRow) view;
row.updateFromCursor(c);
final LayoutInflater inflater = LayoutInflater.from(context);
view = inflater.inflate(resource, parent, false);
view.setTag(view.findViewById(R.id.range_title));
}
}
private String getMostRecentSectionTitle(MostRecentSection section) {
switch (section) {
case TODAY:
return mContext.getString(R.string.history_today_section);
case YESTERDAY:
return mContext.getString(R.string.history_yesterday_section);
case WEEK:
return mContext.getString(R.string.history_week_section);
case OLDER:
return mContext.getString(R.string.history_older_section);
}
throw new IllegalStateException("Unrecognized history section");
}
private int getMostRecentSectionsCountBefore(int position) {
// Account for the number headers before the given position
int sectionsBefore = 0;
final int historySectionsCount = mMostRecentSections.size();
for (int i = 0; i < historySectionsCount; i++) {
final int sectionPosition = mMostRecentSections.keyAt(i);
if (sectionPosition > position) {
break;
}
sectionsBefore++;
}
return sectionsBefore;
}
private static MostRecentSection getMostRecentSectionForTime(long from, long time) {
long delta = from - time;
if (delta < 0) {
return MostRecentSection.TODAY;
}
if (delta < MS_PER_DAY) {
return MostRecentSection.YESTERDAY;
}
if (delta < MS_PER_WEEK) {
return MostRecentSection.WEEK;
}
return MostRecentSection.OLDER;
}
private void loadMostRecentSections(Cursor c) {
// Clear any history sections that may have been loaded before.
mMostRecentSections.clear();
if (c == null || !c.moveToFirst()) {
return;
}
final Date now = new Date();
now.setHours(0);
now.setMinutes(0);
now.setSeconds(0);
final long today = now.getTime();
MostRecentSection section = null;
do {
final int position = c.getPosition();
final long time = c.getLong(c.getColumnIndexOrThrow(History.DATE_LAST_VISITED));
final MostRecentSection itemSection = HistoryAdapter.getMostRecentSectionForTime(today, time);
if (section != itemSection) {
section = itemSection;
mMostRecentSections.append(position + mMostRecentSections.size(), section);
}
// Reached the last section, no need to continue
if (section == MostRecentSection.OLDER) {
break;
}
} while (c.moveToNext());
final MostRecentSection current = getItem(position);
final TextView textView = (TextView) view.getTag();
textView.setText(getMostRecentSectionTitle(current));
textView.setTextColor(ColorUtils.getColor(context, current == selected ? R.color.text_and_tabs_tray_grey : R.color.disabled_grey));
textView.setCompoundDrawablesWithIntrinsicBounds(0, 0, current == selected ? R.drawable.home_group_collapsed : 0, 0);
return view;
}
}

View File

@ -218,27 +218,22 @@ class SearchEngineRow extends AnimatedHeightLayout {
final ContentResolver cr = getContext().getContentResolver();
String[] columns = new String[] { SearchHistory.QUERY };
String sortOrderAndLimit = SearchHistory.DATE + " DESC";
String actualQuery = SearchHistory.QUERY + " LIKE ?";
String[] queryArgs = new String[] { '%' + searchTerm + '%' };
String sortOrderAndLimit = SearchHistory.DATE + " DESC LIMIT 4";
final Cursor c = cr.query(SearchHistory.CONTENT_URI, columns, actualQuery, queryArgs, sortOrderAndLimit);
final Cursor c = cr.query(SearchHistory.CONTENT_URI, columns, null, null, sortOrderAndLimit);
if (c == null) {
return;
}
try {
if (c.moveToFirst()) {
int counter = 0;
final int searchColumn = c.getColumnIndexOrThrow(SearchHistory.QUERY);
do {
final String savedSearch = c.getString(searchColumn);
if (counter == 4) {
break;
}
// Bug 1200371 will move the filtering/matching and limit into the sql query
if (savedSearch.startsWith(searchTerm)) {
bindSuggestionView(savedSearch, animate, recycledSuggestionCount, suggestionCounter, true);
++suggestionCounter;
++counter;
}
bindSuggestionView(savedSearch, animate, recycledSuggestionCount, suggestionCounter, true);
++suggestionCounter;
} while (c.moveToNext());
}
} finally {

View File

@ -55,8 +55,9 @@
<!ENTITY history_today_section "Today">
<!ENTITY history_yesterday_section "Yesterday">
<!ENTITY history_week_section2 "Last Week">
<!ENTITY history_older_section2 "Last Month">
<!ENTITY history_week_section3 "Last 7 days">
<!ENTITY history_this_month_section "This month">
<!ENTITY history_older_section3 "Older than 6 months">
<!ENTITY go "Go">
<!ENTITY search "Search">
@ -69,8 +70,6 @@
<!ENTITY edit_mode_cancel "Cancel">
<!ENTITY close_tab "Close Tab">
<!ENTITY tab_audio_playing "This tab is playing audio">
<!ENTITY tab_audio_muted "This tab has been muted">
<!ENTITY one_tab "1 tab">
<!-- Localization note (num_tabs2) : Number of tabs is always more than one.
We can't use android plural forms, sadly. See bug #753859. -->
@ -81,6 +80,14 @@
as possible because it's shown as a label in a toast. Ideally, this string
is upper-case, to match Google and Android's convention. -->
<!ENTITY switch_button_message "SWITCH">
<!-- Localization note (tab_title_prefix_is_playing_audio): This string is not
visible in the UI, but rather used as a text-to-speech content description
for sight-impaired a11y users. The content description is set on a tab
title in a list of open tabs when content in that tab is playing audio.
&formatS; will be replaced with the title of the tab, as received from the
web page. When audio is not playing in a tab, &formatS; will be used as
the content description. -->
<!ENTITY tab_title_prefix_is_playing_audio "Playing audio &formatS;">
<!ENTITY settings "Settings">
<!ENTITY settings_title "Settings">

View File

@ -313,6 +313,8 @@ gbjar.sources += [
'home/BrowserSearch.java',
'home/DynamicPanel.java',
'home/FramePanelLayout.java',
'home/HistoryHeaderListCursorAdapter.java',
'home/HistoryItemAdapter.java',
'home/HistoryPanel.java',
'home/HomeAdapter.java',
'home/HomeBanner.java',

View File

@ -0,0 +1,18 @@
<?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/. -->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:background="@color/about_page_header_grey"
android:layout_width="match_parent"
android:layout_height="@dimen/page_row_height">
<TextView android:id="@+id/range_title"
style="@style/Widget.TwoLinePageRow.Title"
android:paddingLeft="15dp"
android:paddingRight="15dp"
android:layout_gravity="center_vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</FrameLayout>

View File

@ -0,0 +1,53 @@
<?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/. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="horizontal">
<org.mozilla.gecko.home.HomeListView
android:id="@+id/range_list"
style="@style/Widget.HistoryListView"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="@dimen/split_plane_left_pane_weight"/>
<View
android:layout_width="1dp"
android:layout_height="match_parent"
android:background="@color/divider_light"/>
<ViewStub android:id="@id/home_empty_view_stub"
android:layout="@layout/home_empty_panel"
android:layout_height="match_parent"
android:layout_width="0dp"
android:layout_weight="@dimen/split_plane_right_pane_weight"/>
<org.mozilla.gecko.home.HomeListView
android:id="@+id/list"
style="@style/Widget.HistoryListView"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="@dimen/split_plane_right_pane_weight"/>
</LinearLayout>
<Button android:id="@+id/clear_history_button"
style="@style/Widget.Home.ActionButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/home_button_bar_bg"
android:text="@string/home_clear_history_button"
android:gravity="center"
android:visibility="gone"/>
</LinearLayout>

View File

@ -218,4 +218,6 @@
<item name="tab_strip_content_start" type="dimen">12dp</item>
<item name="split_plane_left_pane_weight" format="float" type="dimen">0.32</item>
<item name="split_plane_right_pane_weight" format="float" type="dimen">0.68</item>
</resources>

View File

@ -578,6 +578,11 @@
<item name="android:drawSelectorOnTop">true</item>
</style>
<style name="Widget.HistoryListView" parent="Widget.HomeListView">
<item name="android:childDivider">@color/divider_light</item>
<item name="android:drawSelectorOnTop">true</item>
</style>
<!-- TabsLayout Row -->
<style name="TabLayoutItemTextAppearance">
<item name="android:textColor">#FFFFFFFF</item>

View File

@ -87,8 +87,9 @@
<string name="history_today_section">&history_today_section;</string>
<string name="history_yesterday_section">&history_yesterday_section;</string>
<string name="history_week_section">&history_week_section2;</string>
<string name="history_older_section">&history_older_section2;</string>
<string name="history_week_section">&history_week_section3;</string>
<string name="history_this_month_section">&history_this_month_section;</string>
<string name="history_older_section">&history_older_section3;</string>
<string name="share">&share;</string>
<string name="share_title">&share_title;</string>
@ -311,11 +312,10 @@
<string name="stop">&stop;</string>
<string name="site_security">&site_security;</string>
<string name="close_tab">&close_tab;</string>
<string name="tab_audio_playing">&tab_audio_playing;</string>
<string name="tab_audio_muted">&tab_audio_muted;</string>
<string name="new_tab_opened">&new_tab_opened;</string>
<string name="new_private_tab_opened">&new_private_tab_opened;</string>
<string name="switch_button_message">&switch_button_message;</string>
<string name="tab_title_prefix_is_playing_audio">&tab_title_prefix_is_playing_audio;</string>
<string name="one_tab">&one_tab;</string>
<string name="num_tabs">&num_tabs2;</string>
<string name="addons">&addons;</string>

View File

@ -141,14 +141,19 @@ public class TabsLayoutItemView extends LinearLayout
if (mThumbnailWrapper != null) {
mThumbnailWrapper.setRecording(tab.isRecording());
}
mTitle.setText(tab.getDisplayTitle());
final String tabTitle = tab.getDisplayTitle();
mTitle.setText(tabTitle);
mCloseButton.setTag(this);
// TODO: Set content description to indicate audio is playing.
if (tab.isAudioPlaying()) {
mTitle.setCompoundDrawablesWithIntrinsicBounds(R.drawable.tab_audio_playing, 0, 0, 0);
final String tabTitleWithAudio =
getResources().getString(R.string.tab_title_prefix_is_playing_audio, tabTitle);
mTitle.setContentDescription(tabTitleWithAudio);
} else {
mTitle.setCompoundDrawables(null, null, null, null);
mTitle.setContentDescription(tabTitle);
}
}

View File

@ -98,6 +98,9 @@ body {
height: 100%;
}
#edit-login-header {
background-color: #F5F5F5;
}
.update-button {
flex: 1;

View File

@ -12,7 +12,17 @@ Cu.import("resource://services-sync/util.js");
Cu.import("resource://testing-common/services/sync/utils.js");
Cu.import("resource://gre/modules/FileUtils.jsm");
const FAKE_SERVER_URL = "http://dummy:9000/";
let fakeServer = new SyncServer();
fakeServer.start();
do_register_cleanup(function() {
return new Promise(resolve => {
fakeServer.stop(resolve);
});
});
let fakeServerUrl = "http://localhost:" + fakeServer.port;
const logsdir = FileUtils.getDir("ProfD", ["weave", "logs"], true);
const PROLONGED_ERROR_DURATION =
@ -236,8 +246,8 @@ add_identity_test(this, function test_shouldReportError() {
// Give ourselves a clusterURL so that the temporary 401 no-error situation
// doesn't come into play.
Service.serverURL = FAKE_SERVER_URL;
Service.clusterURL = FAKE_SERVER_URL;
Service.serverURL = fakeServerUrl;
Service.clusterURL = fakeServerUrl;
// Test dontIgnoreErrors, non-network, non-prolonged, login error reported
Status.resetSync();
@ -589,8 +599,8 @@ add_identity_test(this, function test_sync_syncAndReportErrors_prolonged_non_net
add_identity_test(this, function test_login_syncAndReportErrors_network_error() {
// Test network errors are reported when calling syncAndReportErrors.
yield configureIdentity({username: "broken.wipe"});
Service.serverURL = FAKE_SERVER_URL;
Service.clusterURL = FAKE_SERVER_URL;
Service.serverURL = fakeServerUrl;
Service.clusterURL = fakeServerUrl;
let deferred = Promise.defer();
Svc.Obs.add("weave:ui:login:error", function onSyncError() {
@ -629,8 +639,8 @@ add_identity_test(this, function test_login_syncAndReportErrors_prolonged_networ
// when calling syncAndReportErrors.
yield configureIdentity({username: "johndoe"});
Service.serverURL = FAKE_SERVER_URL;
Service.clusterURL = FAKE_SERVER_URL;
Service.serverURL = fakeServerUrl;
Service.clusterURL = fakeServerUrl;
let deferred = Promise.defer();
Svc.Obs.add("weave:ui:login:error", function onSyncError() {
@ -715,8 +725,8 @@ add_task(function test_sync_prolonged_non_network_error() {
add_identity_test(this, function test_login_prolonged_network_error() {
// Test prolonged, network errors are reported
yield configureIdentity({username: "johndoe"});
Service.serverURL = FAKE_SERVER_URL;
Service.clusterURL = FAKE_SERVER_URL;
Service.serverURL = fakeServerUrl;
Service.clusterURL = fakeServerUrl;
let deferred = Promise.defer();
Svc.Obs.add("weave:ui:login:error", function onSyncError() {
@ -801,8 +811,8 @@ add_task(function test_sync_non_network_error() {
add_identity_test(this, function test_login_network_error() {
yield configureIdentity({username: "johndoe"});
Service.serverURL = FAKE_SERVER_URL;
Service.clusterURL = FAKE_SERVER_URL;
Service.serverURL = fakeServerUrl;
Service.clusterURL = fakeServerUrl;
let deferred = Promise.defer();
// Test network errors are not reported.

View File

@ -175,6 +175,20 @@ def configure_dependent_task(task_path, parameters, taskid, templates, build_tre
return task
def set_interactive_task(task, interactive):
r"""Make the task interactive.
:param task: task definition.
:param interactive: True if the task should be interactive.
"""
if not interactive:
return
payload = task["task"]["payload"]
if "features" not in payload:
payload["features"] = {}
payload["features"]["interactive"] = True
@CommandProvider
class DecisionTask(object):
@Command('taskcluster-decision', category="ci",
@ -255,6 +269,12 @@ class Graph(object):
help='email address of who owns this graph')
@CommandArgument('--extend-graph',
action="store_true", dest="ci", help='Omit create graph arguments')
@CommandArgument('--interactive',
required=False,
default=False,
action="store_true",
dest="interactive",
help="Run the tasks with the interactive feature enabled")
def create_graph(self, **params):
from taskcluster_graph.commit_parser import parse_commit
from taskcluster_graph.slugid import slugid
@ -287,6 +307,8 @@ class Graph(object):
# once everything uses in-tree mozharness (bug 1187706), this can go away.
mozharness = load_mozharness_info()
cmdline_interactive = params.get('interactive', False)
# Template parameters used when expanding the graph
parameters = dict(gaia_info().items() + {
'index': 'index',
@ -337,9 +359,11 @@ class Graph(object):
}
for build in job_graph:
interactive = cmdline_interactive or build["interactive"]
build_parameters = dict(parameters)
build_parameters['build_slugid'] = slugid()
build_task = templates.load(build['task'], build_parameters)
set_interactive_task(build_task, interactive)
if params['revision_hash']:
decorate_task_treeherder_routes(build_task['task'],
@ -412,6 +436,7 @@ class Graph(object):
slugid(),
templates,
build_treeherder_config)
set_interactive_task(post_task, interactive)
graph['tasks'].append(post_task)
for test in build['dependents']:
@ -443,6 +468,7 @@ class Graph(object):
slugid(),
templates,
build_treeherder_config)
set_interactive_task(test_task, interactive)
if params['revision_hash']:
decorate_task_treeherder_routes(
@ -485,6 +511,12 @@ class CIBuild(object):
help='email address of who owns this graph')
@CommandArgument('build_task',
help='path to build task definition')
@CommandArgument('--interactive',
required=False,
default=False,
action="store_true",
dest="interactive",
help="Run the task with the interactive feature enabled")
def create_ci_build(self, **params):
from taskcluster_graph.templates import Templates
import taskcluster_graph.build_task
@ -518,6 +550,7 @@ class CIBuild(object):
try:
build_task = templates.load(params['build_task'], build_parameters)
set_interactive_task(build_task, params.get('interactive', False))
except IOError:
sys.stderr.write(
"Could not load build task file. Ensure path is a relative " \

View File

@ -222,6 +222,7 @@ def parse_commit(message, jobs):
parser.add_argument('-b', '--build', dest='build_types')
parser.add_argument('-p', '--platform', nargs='?', dest='platforms', const='all', default='all')
parser.add_argument('-u', '--unittests', nargs='?', dest='tests', const='all', default='all')
parser.add_argument('-i', '--interactive', dest='interactive', action='store_true', default=False)
args, unknown = parser.parse_known_args(parts[try_idx:])
# Then builds...
@ -278,6 +279,7 @@ def parse_commit(message, jobs):
'additional-parameters': additional_parameters,
'build_name': platform,
'build_type': build_type,
'interactive': args.interactive,
})
return result

View File

@ -2911,7 +2911,7 @@ PlacesCreateLivemarkTransaction.prototype = {
doTransaction: function CLTXN_doTransaction()
{
PlacesUtils.livemarks.addLivemark(
this._promise = PlacesUtils.livemarks.addLivemark(
{ title: this.item.title
, feedURI: this.item.feedURI
, parentId: this.item.parentId
@ -2930,7 +2930,7 @@ PlacesCreateLivemarkTransaction.prototype = {
{
// The getLivemark callback may fail, but it is used just to serialize,
// so it doesn't matter.
PlacesUtils.livemarks.getLivemark({ id: this.item.id })
this._promise = PlacesUtils.livemarks.getLivemark({ id: this.item.id })
.then(null, null).then( () => {
PlacesUtils.bookmarks.removeItem(this.item.id);
});

View File

@ -4234,6 +4234,11 @@
"extended_statistics_ok": true,
"description": "Session restore: Time spent blocking the main thread while restoring a window state (ms)"
},
"FX_TABLET_MODE_USED_DURING_SESSION": {
"expires_in_version": "46",
"kind": "count",
"description": "Windows 10+ only: The number of times tablet-mode is used during a session"
},
"FX_URLBAR_SELECTED_RESULT_INDEX": {
"expires_in_version": "45",
"kind": "enumerated",

View File

@ -0,0 +1,74 @@
/* 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/. */
"use strict";
const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
const { DevToolsWorker } = require("devtools/toolkit/shared/worker.js");
const WORKER_URL = "resource://gre/modules/devtools/heapsnapshot/HeapAnalysesWorker.js";
let workerCounter = 0;
/**
* A HeapAnalysesClient instance provides a developer-friendly interface for
* interacting with a HeapAnalysesWorker. This enables users to be ignorant of
* the message passing protocol used to communicate with the worker. The
* HeapAnalysesClient owns the worker, and terminating the worker is done by
* terminating the client (see the `destroy` method).
*/
const HeapAnalysesClient = module.exports = function () {
this._worker = new DevToolsWorker(WORKER_URL, {
name: `HeapAnalyses-${workerCounter++}`,
verbose: DevToolsUtils.dumpn.wantLogging
});
};
/**
* Destroy the worker, causing it to release its resources (such as heap
* snapshots it has deserialized and read into memory). The client is no longer
* usable after calling this method.
*/
HeapAnalysesClient.prototype.destroy = function () {
this._worker.destroy();
};
/**
* Tell the worker to read into memory the heap snapshot at the given file
* path. This is a prerequisite for asking the worker to perform various
* analyses on a heap snapshot.
*
* @param {String} snapshotFilePath
*
* @returns Promise
* The promise is fulfilled if the heap snapshot is successfully
* deserialized and read into memory. The promise is rejected if that
* does not happen, eg due to a bad file path or malformed heap
* snapshot file.
*/
HeapAnalysesClient.prototype.readHeapSnapshot = function (snapshotFilePath) {
return this._worker.performTask("readHeapSnapshot", { snapshotFilePath });
};
/**
* Ask the worker to perform a census analysis on the heap snapshot with the
* given path. The heap snapshot at the given path must have already been read
* into memory by the worker (see `readHeapSnapshot`).
*
* @param {String} snapshotFilePath
*
* @param {Object} censusOptions
* A structured-cloneable object specifying the requested census's
* breakdown. See the "takeCensus" section of
* `js/src/doc/Debugger/Debugger.Memory.md` for detailed documentation.
*
* @returns Promise<census report>
* The report generated by the given census breakdown.
*/
HeapAnalysesClient.prototype.takeCensus = function (snapshotFilePath,
censusOptions) {
return this._worker.performTask("takeCensus", {
snapshotFilePath,
censusOptions
});
};

View File

@ -0,0 +1,37 @@
/* 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/. */
/*global ThreadSafeChromeUtils*/
// This is a worker which reads offline heap snapshots into memory and performs
// heavyweight analyses on them without blocking the main thread. A
// HeapAnalysesWorker is owned and communicated with by a HeapAnalysesClient
// instance. See HeapAnalysesClient.js.
"use strict";
importScripts("resource://gre/modules/devtools/shared/worker-helper.js");
// The set of HeapSnapshot instances this worker has read into memory. Keyed by
// snapshot file path.
const snapshots = Object.create(null);
/**
* @see HeapAnalysesClient.prototype.readHeapSnapshot
*/
workerHelper.createTask(self, "readHeapSnapshot", ({ snapshotFilePath }) => {
snapshots[snapshotFilePath] =
ThreadSafeChromeUtils.readHeapSnapshot(snapshotFilePath);
return true;
});
/**
* @see HeapAnalysesClient.prototype.takeCensus
*/
workerHelper.createTask(self, "takeCensus", ({ snapshotFilePath, censusOptions }) => {
if (!snapshots[snapshotFilePath]) {
throw new Error(`No known heap snapshot for '${snapshotFilePath}'`);
}
return snapshots[snapshotFilePath].takeCensus(censusOptions);
});

View File

@ -0,0 +1,37 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
if CONFIG['ENABLE_TESTS']:
DIRS += ['tests/gtest']
XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
MOCHITEST_CHROME_MANIFESTS += ['tests/mochitest/chrome.ini']
EXPORTS.mozilla.devtools += [
'AutoMemMap.h',
'CoreDump.pb.h',
'DeserializedNode.h',
'HeapSnapshot.h',
'ZeroCopyNSIOutputStream.h',
]
SOURCES += [
'AutoMemMap.cpp',
'CoreDump.pb.cc',
'DeserializedNode.cpp',
'HeapSnapshot.cpp',
'ZeroCopyNSIOutputStream.cpp',
]
# Disable RTTI in google protocol buffer
DEFINES['GOOGLE_PROTOBUF_NO_RTTI'] = True
FINAL_LIBRARY = 'xul'
EXTRA_JS_MODULES.devtools.heapsnapshot += [
'HeapAnalysesClient.js',
'HeapAnalysesWorker.js',
]

Some files were not shown because too many files have changed in this diff Show More