mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge m-c to inbound on a CLOSED TREE.
This commit is contained in:
commit
f97c846614
@ -19,13 +19,13 @@
|
||||
<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="f1d4d4c6d0e6079b2f7228a7ae849d4608e2c076"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="a351fe62c11737c722ad33aaff438f6ccd00bd4a"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="353d422fcbd0b41b76e1262f0992a832420a7567"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d11f524d00cacf5ba0dfbf25e4aa2158b1c3a036"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="456499c44d1ef39b602ea02e9ed460b6aab85b44"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="cf1dcc0704c0c1845f8a0a0b44838f7e0c0362c9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="707630df1b4270eae3dd49b7344c645f32c1b5f4"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
|
||||
<!-- Stock Android things -->
|
||||
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
|
||||
<project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>
|
||||
|
@ -17,10 +17,10 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f1d4d4c6d0e6079b2f7228a7ae849d4608e2c076"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="a351fe62c11737c722ad33aaff438f6ccd00bd4a"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="353d422fcbd0b41b76e1262f0992a832420a7567"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="cf1dcc0704c0c1845f8a0a0b44838f7e0c0362c9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="707630df1b4270eae3dd49b7344c645f32c1b5f4"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
||||
<!-- Stock Android things -->
|
||||
|
@ -15,14 +15,14 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="a9e08b91e9cd1f0930f16cfc49ec72f63575d5fe">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f1d4d4c6d0e6079b2f7228a7ae849d4608e2c076"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="a351fe62c11737c722ad33aaff438f6ccd00bd4a"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="353d422fcbd0b41b76e1262f0992a832420a7567"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="cf1dcc0704c0c1845f8a0a0b44838f7e0c0362c9"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="707630df1b4270eae3dd49b7344c645f32c1b5f4"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
|
||||
<!-- Stock Android things -->
|
||||
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="f92a936f2aa97526d4593386754bdbf02db07a12"/>
|
||||
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="6e47ff2790f5656b5b074407829ceecf3e6188c4"/>
|
||||
|
@ -19,13 +19,13 @@
|
||||
<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="f1d4d4c6d0e6079b2f7228a7ae849d4608e2c076"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="a351fe62c11737c722ad33aaff438f6ccd00bd4a"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="353d422fcbd0b41b76e1262f0992a832420a7567"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d11f524d00cacf5ba0dfbf25e4aa2158b1c3a036"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="456499c44d1ef39b602ea02e9ed460b6aab85b44"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="cf1dcc0704c0c1845f8a0a0b44838f7e0c0362c9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="707630df1b4270eae3dd49b7344c645f32c1b5f4"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
|
||||
<!-- Stock Android things -->
|
||||
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
|
||||
<project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>
|
||||
|
@ -4,6 +4,6 @@
|
||||
"branch": "",
|
||||
"revision": ""
|
||||
},
|
||||
"revision": "6064a315797a6db79e5ea926a69835b3d72e723b",
|
||||
"revision": "c5bd933fe99317a7e99822f2d9345ae67a3043df",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
@ -17,12 +17,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="f1d4d4c6d0e6079b2f7228a7ae849d4608e2c076"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="a351fe62c11737c722ad33aaff438f6ccd00bd4a"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="353d422fcbd0b41b76e1262f0992a832420a7567"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="cf1dcc0704c0c1845f8a0a0b44838f7e0c0362c9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="707630df1b4270eae3dd49b7344c645f32c1b5f4"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
|
||||
<!-- Stock Android things -->
|
||||
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
|
||||
<project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="f1d4d4c6d0e6079b2f7228a7ae849d4608e2c076"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="a351fe62c11737c722ad33aaff438f6ccd00bd4a"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="353d422fcbd0b41b76e1262f0992a832420a7567"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
|
@ -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="f1d4d4c6d0e6079b2f7228a7ae849d4608e2c076"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="a351fe62c11737c722ad33aaff438f6ccd00bd4a"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="353d422fcbd0b41b76e1262f0992a832420a7567"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="cf1dcc0704c0c1845f8a0a0b44838f7e0c0362c9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="707630df1b4270eae3dd49b7344c645f32c1b5f4"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
|
||||
<!-- Stock Android things -->
|
||||
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
|
||||
<project name="platform/bionic" path="bionic" revision="cd5dfce80bc3f0139a56b58aca633202ccaee7f8"/>
|
||||
|
@ -17,12 +17,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="f1d4d4c6d0e6079b2f7228a7ae849d4608e2c076"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="a351fe62c11737c722ad33aaff438f6ccd00bd4a"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="353d422fcbd0b41b76e1262f0992a832420a7567"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="cf1dcc0704c0c1845f8a0a0b44838f7e0c0362c9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="707630df1b4270eae3dd49b7344c645f32c1b5f4"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
|
||||
<project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
|
||||
<!-- Stock Android things -->
|
||||
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
|
||||
|
@ -17,10 +17,10 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f1d4d4c6d0e6079b2f7228a7ae849d4608e2c076"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="a351fe62c11737c722ad33aaff438f6ccd00bd4a"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="353d422fcbd0b41b76e1262f0992a832420a7567"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="cf1dcc0704c0c1845f8a0a0b44838f7e0c0362c9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="707630df1b4270eae3dd49b7344c645f32c1b5f4"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
||||
<!-- Stock Android things -->
|
||||
|
@ -17,12 +17,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="f1d4d4c6d0e6079b2f7228a7ae849d4608e2c076"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="a351fe62c11737c722ad33aaff438f6ccd00bd4a"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="353d422fcbd0b41b76e1262f0992a832420a7567"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="cf1dcc0704c0c1845f8a0a0b44838f7e0c0362c9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="707630df1b4270eae3dd49b7344c645f32c1b5f4"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
|
||||
<project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
|
||||
<!-- Stock Android things -->
|
||||
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
|
||||
|
@ -1633,7 +1633,7 @@ BrowserGlue.prototype = {
|
||||
// be set to the version it has been added in, we will compare its value
|
||||
// to users' smartBookmarksVersion and add new smart bookmarks without
|
||||
// recreating old deleted ones.
|
||||
const SMART_BOOKMARKS_VERSION = 6;
|
||||
const SMART_BOOKMARKS_VERSION = 7;
|
||||
const SMART_BOOKMARKS_ANNO = "Places/SmartBookmark";
|
||||
const SMART_BOOKMARKS_PREF = "browser.places.smartBookmarksVersion";
|
||||
|
||||
@ -1665,7 +1665,7 @@ BrowserGlue.prototype = {
|
||||
Ci.nsINavHistoryQueryOptions.SORT_BY_VISITCOUNT_DESCENDING +
|
||||
"&maxResults=" + MAX_RESULTS),
|
||||
parent: PlacesUtils.toolbarFolderId,
|
||||
position: toolbarIndex++,
|
||||
get position() { return toolbarIndex++; },
|
||||
newInVersion: 1
|
||||
},
|
||||
RecentlyBookmarked: {
|
||||
@ -1680,7 +1680,7 @@ BrowserGlue.prototype = {
|
||||
"&maxResults=" + MAX_RESULTS +
|
||||
"&excludeQueries=1"),
|
||||
parent: PlacesUtils.bookmarksMenuFolderId,
|
||||
position: menuIndex++,
|
||||
get position() { return menuIndex++; },
|
||||
newInVersion: 1
|
||||
},
|
||||
RecentTags: {
|
||||
@ -1692,25 +1692,31 @@ BrowserGlue.prototype = {
|
||||
Ci.nsINavHistoryQueryOptions.SORT_BY_LASTMODIFIED_DESCENDING +
|
||||
"&maxResults=" + MAX_RESULTS),
|
||||
parent: PlacesUtils.bookmarksMenuFolderId,
|
||||
position: menuIndex++,
|
||||
get position() { return menuIndex++; },
|
||||
newInVersion: 1
|
||||
},
|
||||
};
|
||||
|
||||
if (Services.metro && Services.metro.supported) {
|
||||
smartBookmarks.Windows8Touch = {
|
||||
title: bundle.GetStringFromName("windows8TouchTitle"),
|
||||
uri: NetUtil.newURI("place:folder=" +
|
||||
PlacesUtils.annotations.getItemsWithAnnotation('metro/bookmarksRoot', {})[0] +
|
||||
"&queryType=" +
|
||||
Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS +
|
||||
"&sort=" +
|
||||
Ci.nsINavHistoryQueryOptions.SORT_BY_DATEADDED_DESCENDING +
|
||||
"&maxResults=" + MAX_RESULTS +
|
||||
"&excludeQueries=1"),
|
||||
title: PlacesUtils.getString("windows8TouchTitle"),
|
||||
get uri() {
|
||||
let metroBookmarksRoot = PlacesUtils.annotations.getItemsWithAnnotation('metro/bookmarksRoot', {});
|
||||
if (metroBookmarksRoot.length > 0) {
|
||||
return NetUtil.newURI("place:folder=" +
|
||||
metroBookmarksRoot[0] +
|
||||
"&queryType=" +
|
||||
Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS +
|
||||
"&sort=" +
|
||||
Ci.nsINavHistoryQueryOptions.SORT_BY_DATEADDED_DESCENDING +
|
||||
"&maxResults=" + MAX_RESULTS +
|
||||
"&excludeQueries=1")
|
||||
}
|
||||
return null;
|
||||
},
|
||||
parent: PlacesUtils.bookmarksMenuFolderId,
|
||||
position: menuIndex++,
|
||||
newInVersion: 6
|
||||
get position() { return menuIndex++; },
|
||||
newInVersion: 7
|
||||
};
|
||||
}
|
||||
|
||||
@ -1722,9 +1728,13 @@ BrowserGlue.prototype = {
|
||||
let queryId = PlacesUtils.annotations.getItemAnnotation(itemId, SMART_BOOKMARKS_ANNO);
|
||||
if (queryId in smartBookmarks) {
|
||||
let smartBookmark = smartBookmarks[queryId];
|
||||
if (!smartBookmark.uri) {
|
||||
PlacesUtils.bookmarks.removeItem(itemId);
|
||||
return;
|
||||
}
|
||||
smartBookmark.itemId = itemId;
|
||||
smartBookmark.parent = PlacesUtils.bookmarks.getFolderIdForItem(itemId);
|
||||
smartBookmark.position = PlacesUtils.bookmarks.getItemIndex(itemId);
|
||||
smartBookmark.updatedPosition = PlacesUtils.bookmarks.getItemIndex(itemId);
|
||||
}
|
||||
else {
|
||||
// We don't remove old Smart Bookmarks because user could still
|
||||
@ -1742,7 +1752,7 @@ BrowserGlue.prototype = {
|
||||
// bookmark if it has been removed.
|
||||
if (smartBookmarksCurrentVersion > 0 &&
|
||||
smartBookmark.newInVersion <= smartBookmarksCurrentVersion &&
|
||||
!smartBookmark.itemId)
|
||||
!smartBookmark.itemId || !smartBookmark.uri)
|
||||
continue;
|
||||
|
||||
// Remove old version of the smart bookmark if it exists, since it
|
||||
@ -1755,7 +1765,7 @@ BrowserGlue.prototype = {
|
||||
smartBookmark.itemId =
|
||||
PlacesUtils.bookmarks.insertBookmark(smartBookmark.parent,
|
||||
smartBookmark.uri,
|
||||
smartBookmark.position,
|
||||
smartBookmark.updatedPosition || smartBookmark.position,
|
||||
smartBookmark.title);
|
||||
PlacesUtils.annotations.setItemAnnotation(smartBookmark.itemId,
|
||||
SMART_BOOKMARKS_ANNO,
|
||||
|
@ -63,10 +63,9 @@ let (XULAppInfo = {
|
||||
}
|
||||
|
||||
// Smart bookmarks constants.
|
||||
let isMetroSupported = Services.metro && Services.metro.supported;
|
||||
const SMART_BOOKMARKS_VERSION = 6
|
||||
const SMART_BOOKMARKS_VERSION = 7;
|
||||
const SMART_BOOKMARKS_ON_TOOLBAR = 1;
|
||||
const SMART_BOOKMARKS_ON_MENU = isMetroSupported ? 4 : 3; // Takes in count the additional separator.
|
||||
const SMART_BOOKMARKS_ON_MENU = 3; // Takes into account the additional separator.
|
||||
|
||||
// Default bookmarks constants.
|
||||
const DEFAULT_BOOKMARKS_ON_TOOLBAR = 1;
|
||||
|
@ -0,0 +1,13 @@
|
||||
var { Cc, Ci } = require("chrome");
|
||||
var { once } = require("sdk/system/events");
|
||||
|
||||
var observerService = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
|
||||
var observer = {
|
||||
observe: function () {
|
||||
debugger;
|
||||
}
|
||||
};
|
||||
|
||||
once("sdk:loader:destroy", () => observerService.removeObserver(observer, "debuggerAttached"));
|
||||
|
||||
observerService.addObserver(observer, "debuggerAttached", false);
|
@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "browser_dbg_addon3",
|
||||
"title": "browser_dbg_addon3",
|
||||
"id": "jid1-ami3akps3baaeg",
|
||||
"description": "a basic add-on",
|
||||
"author": "",
|
||||
"license": "MPL 2.0",
|
||||
"version": "0.1"
|
||||
}
|
BIN
browser/devtools/debugger/test/addon3.xpi
Normal file
BIN
browser/devtools/debugger/test/addon3.xpi
Normal file
Binary file not shown.
@ -2,6 +2,7 @@
|
||||
support-files =
|
||||
addon1.xpi
|
||||
addon2.xpi
|
||||
addon3.xpi
|
||||
code_binary_search.coffee
|
||||
code_binary_search.js
|
||||
code_binary_search.map
|
||||
@ -80,6 +81,7 @@ support-files =
|
||||
testactors.js
|
||||
|
||||
[browser_dbg_aaa_run_first_leaktest.js]
|
||||
[browser_dbg_addonactor.js]
|
||||
[browser_dbg_auto-pretty-print-01.js]
|
||||
[browser_dbg_auto-pretty-print-02.js]
|
||||
[browser_dbg_bfcache.js]
|
||||
|
99
browser/devtools/debugger/test/browser_dbg_addonactor.js
Normal file
99
browser/devtools/debugger/test/browser_dbg_addonactor.js
Normal file
@ -0,0 +1,99 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Make sure we can attach to addon actors.
|
||||
|
||||
const ADDON3_URL = EXAMPLE_URL + "addon3.xpi";
|
||||
const ADDON_MODULE_URL = "resource://jid1-ami3akps3baaeg-at-jetpack/browser_dbg_addon3/lib/main.js";
|
||||
|
||||
var gAddon, gClient, gThreadClient;
|
||||
|
||||
function test() {
|
||||
if (!DebuggerServer.initialized) {
|
||||
DebuggerServer.init(() => true);
|
||||
DebuggerServer.addBrowserActors();
|
||||
}
|
||||
|
||||
let transport = DebuggerServer.connectPipe();
|
||||
gClient = new DebuggerClient(transport);
|
||||
gClient.connect((aType, aTraits) => {
|
||||
is(aType, "browser",
|
||||
"Root actor should identify itself as a browser.");
|
||||
|
||||
installAddon()
|
||||
.then(attachAddonActorForUrl.bind(null, gClient, ADDON3_URL))
|
||||
.then(attachAddonThread)
|
||||
.then(testDebugger)
|
||||
.then(testSources)
|
||||
.then(uninstallAddon)
|
||||
.then(closeConnection)
|
||||
.then(finish)
|
||||
.then(null, aError => {
|
||||
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function installAddon () {
|
||||
return addAddon(ADDON3_URL).then(aAddon => {
|
||||
gAddon = aAddon;
|
||||
});
|
||||
}
|
||||
|
||||
function attachAddonThread ([aGrip, aResponse]) {
|
||||
info("attached addon actor for URL");
|
||||
let deferred = promise.defer();
|
||||
|
||||
gClient.attachThread(aResponse.threadActor, (aResponse, aThreadClient) => {
|
||||
info("attached thread");
|
||||
gThreadClient = aThreadClient;
|
||||
gThreadClient.resume(deferred.resolve);
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function testDebugger() {
|
||||
info('Entering testDebugger');
|
||||
let deferred = promise.defer();
|
||||
|
||||
once(gClient, "paused").then(() => {
|
||||
ok(true, "Should be able to attach to addon actor");
|
||||
gThreadClient.resume(deferred.resolve)
|
||||
});
|
||||
|
||||
Services.obs.notifyObservers(null, "debuggerAttached", null);
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function testSources() {
|
||||
let deferred = promise.defer();
|
||||
|
||||
gThreadClient.getSources(aResponse => {
|
||||
// source URLs contain launch-specific temporary directory path,
|
||||
// hence the ".contains" call.
|
||||
const matches = aResponse.sources.filter(s =>
|
||||
s.url.contains(ADDON_MODULE_URL));
|
||||
is(matches.length, 1,
|
||||
"the main script of the addon is present in the source list");
|
||||
deferred.resolve();
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function uninstallAddon() {
|
||||
return removeAddon(gAddon);
|
||||
}
|
||||
|
||||
function closeConnection () {
|
||||
let deferred = promise.defer();
|
||||
gClient.close(deferred.resolve);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
gClient = null;
|
||||
gAddon = null;
|
||||
gThreadClient = null;
|
||||
});
|
@ -154,6 +154,7 @@ function getTabActorForUrl(aClient, aUrl) {
|
||||
}
|
||||
|
||||
function getAddonActorForUrl(aClient, aUrl) {
|
||||
info("Get addon actor for URL: " + aUrl);
|
||||
let deferred = promise.defer();
|
||||
|
||||
aClient.listAddons(aResponse => {
|
||||
@ -661,3 +662,14 @@ function filterTraces(aPanel, f) {
|
||||
return Array.filter(traces, f);
|
||||
}
|
||||
|
||||
function attachAddonActorForUrl(aClient, aUrl) {
|
||||
let deferred = promise.defer();
|
||||
|
||||
getAddonActorForUrl(aClient, aUrl).then(aGrip => {
|
||||
aClient.attachAddon(aGrip.actor, aResponse => {
|
||||
deferred.resolve([aGrip, aResponse]);
|
||||
});
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
@ -562,7 +562,11 @@ TabWebProgressListener.prototype = {
|
||||
*/
|
||||
destroy: function TWPL_destroy() {
|
||||
if (this.target.tab) {
|
||||
this.target.tab.linkedBrowser.removeProgressListener(this);
|
||||
try {
|
||||
this.target.tab.linkedBrowser.removeProgressListener(this);
|
||||
} catch (ex) {
|
||||
// This can throw when a tab crashes in e10s.
|
||||
}
|
||||
}
|
||||
this.target._webProgressListener = null;
|
||||
this.target._navRequest = null;
|
||||
|
@ -130,6 +130,99 @@ var Scratchpad = {
|
||||
return obj;
|
||||
},
|
||||
|
||||
/**
|
||||
* Add the event listeners for popupshowing events.
|
||||
*/
|
||||
_setupPopupShowingListeners: function SP_setupPopupShowing() {
|
||||
let elementIDs = ['sp-menu_editpopup', 'scratchpad-text-popup'];
|
||||
|
||||
for (let elementID of elementIDs) {
|
||||
let elem = document.getElementById(elementID);
|
||||
if (elem) {
|
||||
elem.addEventListener("popupshowing", function () {
|
||||
goUpdateGlobalEditMenuItems();
|
||||
let commands = ['cmd_undo', 'cmd_redo', 'cmd_delete', 'cmd_findAgain'];
|
||||
commands.forEach(goUpdateCommand);
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Add the event event listeners for command events.
|
||||
*/
|
||||
_setupCommandListeners: function SP_setupCommands() {
|
||||
let commands = {
|
||||
"cmd_gotoLine": () => {
|
||||
goDoCommand('cmd_gotoLine');
|
||||
},
|
||||
"sp-cmd-newWindow": () => {
|
||||
Scratchpad.openScratchpad();
|
||||
},
|
||||
"sp-cmd-openFile": () => {
|
||||
Scratchpad.openFile();
|
||||
},
|
||||
"sp-cmd-clearRecentFiles": () => {
|
||||
Scratchpad.clearRecentFiles();
|
||||
},
|
||||
"sp-cmd-save": () => {
|
||||
Scratchpad.saveFile();
|
||||
},
|
||||
"sp-cmd-saveas": () => {
|
||||
Scratchpad.saveFileAs();
|
||||
},
|
||||
"sp-cmd-revert": () => {
|
||||
Scratchpad.promptRevert();
|
||||
},
|
||||
"sp-cmd-close": () => {
|
||||
Scratchpad.close();
|
||||
},
|
||||
"sp-cmd-run": () => {
|
||||
Scratchpad.run();
|
||||
},
|
||||
"sp-cmd-inspect": () => {
|
||||
Scratchpad.inspect();
|
||||
},
|
||||
"sp-cmd-display": () => {
|
||||
Scratchpad.display();
|
||||
},
|
||||
"sp-cmd-pprint": () => {
|
||||
Scratchpad.prettyPrint();
|
||||
},
|
||||
"sp-cmd-contentContext": () => {
|
||||
Scratchpad.setContentContext();
|
||||
},
|
||||
"sp-cmd-browserContext": () => {
|
||||
Scratchpad.setBrowserContext();
|
||||
},
|
||||
"sp-cmd-reloadAndRun": () => {
|
||||
Scratchpad.reloadAndRun();
|
||||
},
|
||||
"sp-cmd-evalFunction": () => {
|
||||
Scratchpad.evalTopLevelFunction();
|
||||
},
|
||||
"sp-cmd-errorConsole": () => {
|
||||
Scratchpad.openErrorConsole();
|
||||
},
|
||||
"sp-cmd-webConsole": () => {
|
||||
Scratchpad.openWebConsole();
|
||||
},
|
||||
"sp-cmd-documentationLink": () => {
|
||||
Scratchpad.openDocumentationPage();
|
||||
},
|
||||
"sp-cmd-hideSidebar": () => {
|
||||
Scratchpad.sidebar.hide();
|
||||
}
|
||||
}
|
||||
|
||||
for (let command in commands) {
|
||||
let elem = document.getElementById(command);
|
||||
if (elem) {
|
||||
elem.addEventListener("command", commands[command]);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The script execution context. This tells Scratchpad in which context the
|
||||
* script shall execute.
|
||||
@ -1168,7 +1261,7 @@ var Scratchpad = {
|
||||
menuitem.setAttribute("disabled", true);
|
||||
}
|
||||
|
||||
menuitem.setAttribute("oncommand", "Scratchpad.openFile(" + i + ");");
|
||||
menuitem.addEventListener("command", Scratchpad.openFile.bind(Scratchpad, i));
|
||||
recentFilesPopup.appendChild(menuitem);
|
||||
}
|
||||
|
||||
@ -1513,6 +1606,8 @@ var Scratchpad = {
|
||||
PreferenceObserver.init();
|
||||
CloseObserver.init();
|
||||
}).then(null, (err) => console.log(err.message));
|
||||
this._setupCommandListeners();
|
||||
this._setupPopupShowingListeners();
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -33,41 +33,32 @@
|
||||
<script type="application/javascript" src="chrome://global/content/globalOverlay.js"/>
|
||||
<script type="application/javascript" src="chrome://browser/content/devtools/scratchpad.js"/>
|
||||
|
||||
|
||||
<script type="application/javascript">
|
||||
function goUpdateSourceEditorMenuItems() {
|
||||
goUpdateGlobalEditMenuItems();
|
||||
let commands = ['cmd_undo', 'cmd_redo', 'cmd_delete', 'cmd_findAgain'];
|
||||
commands.forEach(goUpdateCommand);
|
||||
}
|
||||
</script>
|
||||
|
||||
<commandset id="editMenuCommands"/>
|
||||
|
||||
<commandset id="sourceEditorCommands">
|
||||
<command id="cmd_gotoLine" oncommand="goDoCommand('cmd_gotoLine')"/>
|
||||
<command id="cmd_gotoLine" oncommand=";"/>
|
||||
</commandset>
|
||||
|
||||
<commandset id="sp-commandset">
|
||||
<command id="sp-cmd-newWindow" oncommand="Scratchpad.openScratchpad();"/>
|
||||
<command id="sp-cmd-openFile" oncommand="Scratchpad.openFile();"/>
|
||||
<command id="sp-cmd-clearRecentFiles" oncommand="Scratchpad.clearRecentFiles();"/>
|
||||
<command id="sp-cmd-save" oncommand="Scratchpad.saveFile();"/>
|
||||
<command id="sp-cmd-saveas" oncommand="Scratchpad.saveFileAs();"/>
|
||||
<command id="sp-cmd-revert" oncommand="Scratchpad.promptRevert();" disabled="true"/>
|
||||
<command id="sp-cmd-close" oncommand="Scratchpad.close();"/>
|
||||
<command id="sp-cmd-run" oncommand="Scratchpad.run();"/>
|
||||
<command id="sp-cmd-inspect" oncommand="Scratchpad.inspect();"/>
|
||||
<command id="sp-cmd-display" oncommand="Scratchpad.display();"/>
|
||||
<command id="sp-cmd-pprint" oncommand="Scratchpad.prettyPrint();"/>
|
||||
<command id="sp-cmd-contentContext" oncommand="Scratchpad.setContentContext();"/>
|
||||
<command id="sp-cmd-browserContext" oncommand="Scratchpad.setBrowserContext();" disabled="true"/>
|
||||
<command id="sp-cmd-reloadAndRun" oncommand="Scratchpad.reloadAndRun();"/>
|
||||
<command id="sp-cmd-evalFunction" oncommand="Scratchpad.evalTopLevelFunction();"/>
|
||||
<command id="sp-cmd-errorConsole" oncommand="Scratchpad.openErrorConsole();" disabled="true"/>
|
||||
<command id="sp-cmd-webConsole" oncommand="Scratchpad.openWebConsole();"/>
|
||||
<command id="sp-cmd-documentationLink" oncommand="Scratchpad.openDocumentationPage();"/>
|
||||
<command id="sp-cmd-hideSidebar" oncommand="Scratchpad.sidebar.hide();"/>
|
||||
<command id="sp-cmd-newWindow" oncommand=";"/>
|
||||
<command id="sp-cmd-openFile" oncommand=";"/>
|
||||
<command id="sp-cmd-clearRecentFiles" oncommand=";"/>
|
||||
<command id="sp-cmd-save" oncommand=";"/>
|
||||
<command id="sp-cmd-saveas" oncommand=";"/>
|
||||
<command id="sp-cmd-revert" oncommand=";" disabled="true"/>
|
||||
<command id="sp-cmd-close" oncommand=";"/>
|
||||
<command id="sp-cmd-run" oncommand=";"/>
|
||||
<command id="sp-cmd-inspect" oncommand=";"/>
|
||||
<command id="sp-cmd-display" oncommand=";"/>
|
||||
<command id="sp-cmd-pprint" oncommand=";"/>
|
||||
<command id="sp-cmd-contentContext" oncommand=";"/>
|
||||
<command id="sp-cmd-browserContext" oncommand=";" disabled="true"/>
|
||||
<command id="sp-cmd-reloadAndRun" oncommand=";"/>
|
||||
<command id="sp-cmd-evalFunction" oncommand=";"/>
|
||||
<command id="sp-cmd-errorConsole" oncommand=";" disabled="true"/>
|
||||
<command id="sp-cmd-webConsole" oncommand=";"/>
|
||||
<command id="sp-cmd-documentationLink" oncommand=";"/>
|
||||
<command id="sp-cmd-hideSidebar" oncommand=";"/>
|
||||
</commandset>
|
||||
|
||||
<keyset id="editMenuKeys"/>
|
||||
@ -172,8 +163,7 @@
|
||||
|
||||
<menu id="sp-edit-menu" label="&editMenu.label;"
|
||||
accesskey="&editMenu.accesskey;">
|
||||
<menupopup id="sp-menu_editpopup"
|
||||
onpopupshowing="goUpdateSourceEditorMenuItems()">
|
||||
<menupopup id="sp-menu_editpopup">
|
||||
<menuitem id="menu_undo"/>
|
||||
<menuitem id="menu_redo"/>
|
||||
<menuseparator/>
|
||||
@ -305,8 +295,7 @@
|
||||
|
||||
|
||||
<popupset id="scratchpad-popups">
|
||||
<menupopup id="scratchpad-text-popup"
|
||||
onpopupshowing="goUpdateSourceEditorMenuItems()">
|
||||
<menupopup id="scratchpad-text-popup">
|
||||
<menuitem id="cMenu_cut"/>
|
||||
<menuitem id="cMenu_copy"/>
|
||||
<menuitem id="cMenu_paste"/>
|
||||
|
@ -745,6 +745,40 @@ Tooltip.prototype = {
|
||||
def.reject();
|
||||
}
|
||||
|
||||
return def.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the content of the tooltip to display a font family preview.
|
||||
* This is based on Lea Verou's Dablet. See https://github.com/LeaVerou/dabblet
|
||||
* for more info.
|
||||
*
|
||||
* @param {String} font
|
||||
* The font family value.
|
||||
*/
|
||||
setFontFamilyContent: function(font) {
|
||||
let def = promise.defer();
|
||||
|
||||
if (font) {
|
||||
// Main container
|
||||
let vbox = this.doc.createElement("vbox");
|
||||
vbox.setAttribute("flex", "1");
|
||||
|
||||
// Display the font family previewer
|
||||
let previewer = this.doc.createElement("description");
|
||||
previewer.setAttribute("flex", "1");
|
||||
previewer.style.fontFamily = font;
|
||||
previewer.classList.add("devtools-tooltip-font-previewer-text");
|
||||
previewer.textContent = "(ABCabc123&@%)";
|
||||
vbox.appendChild(previewer);
|
||||
|
||||
this.content = vbox;
|
||||
|
||||
def.resolve();
|
||||
} else {
|
||||
def.reject();
|
||||
}
|
||||
|
||||
return def.promise;
|
||||
}
|
||||
};
|
||||
|
@ -531,14 +531,20 @@ CssHtmlTree.prototype = {
|
||||
}
|
||||
}
|
||||
|
||||
// Test for css transform
|
||||
if (target.classList.contains("property-value")) {
|
||||
let propValue = target;
|
||||
let propName = target.parentNode.querySelector(".property-name");
|
||||
|
||||
// Test for css transform
|
||||
if (propName.textContent === "transform") {
|
||||
return this.tooltip.setCssTransformContent(propValue.textContent,
|
||||
this.pageStyle, this.viewedElement);
|
||||
}
|
||||
|
||||
// Test for font family
|
||||
if (propName.textContent === "font-family") {
|
||||
return this.tooltip.setFontFamilyContent(propValue.textContent);
|
||||
}
|
||||
}
|
||||
|
||||
// If the target isn't one that should receive a tooltip, signal it by rejecting
|
||||
|
@ -1142,6 +1142,27 @@ CssRuleView.prototype = {
|
||||
}
|
||||
}
|
||||
|
||||
// Get the nodes containing the property name and property value,
|
||||
// and test for font family
|
||||
let propertyRoot = target.parentNode;
|
||||
let propertyNameNode = propertyRoot.querySelector(".ruleview-propertyname");
|
||||
|
||||
if (!propertyNameNode) {
|
||||
propertyRoot = propertyRoot.parentNode;
|
||||
propertyNameNode = propertyRoot.querySelector(".ruleview-propertyname");
|
||||
}
|
||||
|
||||
let propertyName;
|
||||
if (propertyNameNode) {
|
||||
propertyName = propertyNameNode.textContent;
|
||||
}
|
||||
|
||||
if (propertyName === "font-family" &&
|
||||
target.classList.contains("ruleview-propertyvalue")) {
|
||||
this.previewTooltip.setFontFamilyContent(target.textContent).then(def.resolve);
|
||||
hasTooltip = true;
|
||||
}
|
||||
|
||||
if (hasTooltip) {
|
||||
this.colorPicker.revert();
|
||||
this.colorPicker.hide();
|
||||
|
@ -56,6 +56,8 @@ support-files = browser_ruleview_pseudoelement.html
|
||||
[browser_bug765105_background_image_tooltip.js]
|
||||
[browser_bug889638_rule_view_color_picker.js]
|
||||
[browser_bug726427_csstransform_tooltip.js]
|
||||
[browser_bug702577_fontfamily_tooltip_shorthand.js]
|
||||
[browser_bug702577_fontfamily_tooltip_longhand.js]
|
||||
[browser_bug940500_rule_view_pick_gradient_color.js]
|
||||
[browser_ruleview_original_source_link.js]
|
||||
support-files =
|
||||
|
@ -0,0 +1,131 @@
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
let contentDoc;
|
||||
let inspector;
|
||||
let ruleView;
|
||||
let computedView;
|
||||
|
||||
const PAGE_CONTENT = [
|
||||
'<style type="text/css">',
|
||||
' #testElement {',
|
||||
' font-family: cursive;',
|
||||
' color: #333;',
|
||||
' padding-left: 70px;',
|
||||
' }',
|
||||
'</style>',
|
||||
'<div id="testElement">test element</div>'
|
||||
].join("\n");
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
gBrowser.selectedBrowser.addEventListener("load", function(evt) {
|
||||
gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee, true);
|
||||
contentDoc = content.document;
|
||||
waitForFocus(createDocument, content);
|
||||
}, true);
|
||||
|
||||
content.location = "data:text/html;charset=utf-8,font family longhand tooltip test";
|
||||
}
|
||||
|
||||
function createDocument() {
|
||||
contentDoc.body.innerHTML = PAGE_CONTENT;
|
||||
|
||||
openRuleView((aInspector, aRuleView) => {
|
||||
inspector = aInspector;
|
||||
ruleView = aRuleView;
|
||||
startTests();
|
||||
});
|
||||
}
|
||||
|
||||
function startTests() {
|
||||
inspector.selection.setNode(contentDoc.querySelector("#testElement"));
|
||||
inspector.once("inspector-updated", testRuleView);
|
||||
}
|
||||
|
||||
function endTests() {
|
||||
contentDoc = inspector = ruleView = computedView = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
|
||||
function assertTooltipShownOn(tooltip, element, cb) {
|
||||
// If there is indeed a show-on-hover on element, the xul panel will be shown
|
||||
tooltip.panel.addEventListener("popupshown", function shown() {
|
||||
tooltip.panel.removeEventListener("popupshown", shown, true);
|
||||
cb();
|
||||
}, true);
|
||||
tooltip._showOnHover(element);
|
||||
}
|
||||
|
||||
function testRuleView() {
|
||||
info("Testing font-family tooltips in the rule view");
|
||||
|
||||
let panel = ruleView.previewTooltip.panel;
|
||||
|
||||
// Check that the rule view has a tooltip and that a XUL panel has been created
|
||||
ok(ruleView.previewTooltip, "Tooltip instance exists");
|
||||
ok(panel, "XUL panel exists");
|
||||
|
||||
// Get the font family property inside the rule view
|
||||
let {valueSpan} = getRuleViewProperty("font-family");
|
||||
|
||||
// And verify that the tooltip gets shown on this property
|
||||
assertTooltipShownOn(ruleView.previewTooltip, valueSpan, () => {
|
||||
let description = panel.getElementsByTagName("description")[0];
|
||||
is(description.style.fontFamily, "cursive", "Tooltips contains correct font-family style");
|
||||
|
||||
ruleView.previewTooltip.hide();
|
||||
|
||||
testComputedView();
|
||||
});
|
||||
}
|
||||
|
||||
function testComputedView() {
|
||||
info("Testing font-family tooltips in the computed view");
|
||||
|
||||
inspector.sidebar.select("computedview");
|
||||
computedView = inspector.sidebar.getWindowForTab("computedview").computedview.view;
|
||||
let doc = computedView.styleDocument;
|
||||
|
||||
let panel = computedView.tooltip.panel;
|
||||
let {valueSpan} = getComputedViewProperty("font-family");
|
||||
|
||||
assertTooltipShownOn(computedView.tooltip, valueSpan, () => {
|
||||
let description = panel.getElementsByTagName("description")[0];
|
||||
is(description.style.fontFamily, "cursive", "Tooltips contains correct font-family style");
|
||||
|
||||
computedView.tooltip.hide();
|
||||
|
||||
endTests();
|
||||
});
|
||||
}
|
||||
|
||||
function getRuleViewProperty(name) {
|
||||
let prop = null;
|
||||
[].forEach.call(ruleView.doc.querySelectorAll(".ruleview-property"), property => {
|
||||
let nameSpan = property.querySelector(".ruleview-propertyname");
|
||||
let valueSpan = property.querySelector(".ruleview-propertyvalue");
|
||||
|
||||
if (nameSpan.textContent === name) {
|
||||
prop = {nameSpan: nameSpan, valueSpan: valueSpan};
|
||||
}
|
||||
});
|
||||
return prop;
|
||||
}
|
||||
|
||||
function getComputedViewProperty(name) {
|
||||
let prop = null;
|
||||
[].forEach.call(computedView.styleDocument.querySelectorAll(".property-view"), property => {
|
||||
let nameSpan = property.querySelector(".property-name");
|
||||
let valueSpan = property.querySelector(".property-value");
|
||||
|
||||
if (nameSpan.textContent === name) {
|
||||
prop = {nameSpan: nameSpan, valueSpan: valueSpan};
|
||||
}
|
||||
});
|
||||
return prop;
|
||||
}
|
@ -0,0 +1,133 @@
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
let contentDoc;
|
||||
let inspector;
|
||||
let ruleView;
|
||||
let computedView;
|
||||
|
||||
const PAGE_CONTENT = [
|
||||
'<style type="text/css">',
|
||||
' #testElement {',
|
||||
' font: italic bold .8em/1.2 Arial;',
|
||||
' }',
|
||||
'</style>',
|
||||
'<div id="testElement">test element</div>'
|
||||
].join("\n");
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
gBrowser.selectedBrowser.addEventListener("load", function(evt) {
|
||||
gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee, true);
|
||||
contentDoc = content.document;
|
||||
waitForFocus(createDocument, content);
|
||||
}, true);
|
||||
|
||||
content.location = "data:text/html;charset=utf-8,font family shorthand tooltip test";
|
||||
}
|
||||
|
||||
function createDocument() {
|
||||
contentDoc.body.innerHTML = PAGE_CONTENT;
|
||||
|
||||
openRuleView((aInspector, aRuleView) => {
|
||||
inspector = aInspector;
|
||||
ruleView = aRuleView;
|
||||
startTests();
|
||||
});
|
||||
}
|
||||
|
||||
function startTests() {
|
||||
inspector.selection.setNode(contentDoc.querySelector("#testElement"));
|
||||
inspector.once("inspector-updated", testRuleView);
|
||||
}
|
||||
|
||||
function endTests() {
|
||||
contentDoc = inspector = ruleView = computedView = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
|
||||
function assertTooltipShownOn(tooltip, element, cb) {
|
||||
// If there is indeed a show-on-hover on element, the xul panel will be shown
|
||||
tooltip.panel.addEventListener("popupshown", function shown() {
|
||||
tooltip.panel.removeEventListener("popupshown", shown, true);
|
||||
cb();
|
||||
}, true);
|
||||
tooltip._showOnHover(element);
|
||||
}
|
||||
|
||||
function testRuleView() {
|
||||
info("Testing font-family tooltips in the rule view");
|
||||
|
||||
let panel = ruleView.previewTooltip.panel;
|
||||
|
||||
// Check that the rule view has a tooltip and that a XUL panel has been created
|
||||
ok(ruleView.previewTooltip, "Tooltip instance exists");
|
||||
ok(panel, "XUL panel exists");
|
||||
|
||||
// Get the computed font family property inside the font rule view
|
||||
let propertyList = ruleView.element.querySelectorAll(".ruleview-propertylist");
|
||||
let fontExpander = propertyList[1].querySelectorAll(".ruleview-expander")[0];
|
||||
fontExpander.click();
|
||||
|
||||
let {valueSpan} = getRuleViewProperty("font-family");
|
||||
|
||||
// And verify that the tooltip gets shown on this property
|
||||
assertTooltipShownOn(ruleView.previewTooltip, valueSpan, () => {
|
||||
let description = panel.getElementsByTagName("description")[0];
|
||||
is(description.style.fontFamily, "Arial", "Tooltips contains correct font-family style");
|
||||
|
||||
ruleView.previewTooltip.hide();
|
||||
|
||||
testComputedView();
|
||||
});
|
||||
}
|
||||
|
||||
function testComputedView() {
|
||||
info("Testing font-family tooltips in the computed view");
|
||||
|
||||
inspector.sidebar.select("computedview");
|
||||
computedView = inspector.sidebar.getWindowForTab("computedview").computedview.view;
|
||||
let doc = computedView.styleDocument;
|
||||
|
||||
let panel = computedView.tooltip.panel;
|
||||
let {valueSpan} = getComputedViewProperty("font-family");
|
||||
|
||||
assertTooltipShownOn(computedView.tooltip, valueSpan, () => {
|
||||
let description = panel.getElementsByTagName("description")[0];
|
||||
is(description.style.fontFamily, "Arial", "Tooltips contains correct font-family style");
|
||||
|
||||
computedView.tooltip.hide();
|
||||
|
||||
endTests();
|
||||
});
|
||||
}
|
||||
|
||||
function getRuleViewProperty(name) {
|
||||
let prop = null;
|
||||
[].forEach.call(ruleView.doc.querySelectorAll(".ruleview-computedlist"), property => {
|
||||
let nameSpan = property.querySelector(".ruleview-propertyname");
|
||||
let valueSpan = property.querySelector(".ruleview-propertyvalue");
|
||||
|
||||
if (nameSpan.textContent === name) {
|
||||
prop = {nameSpan: nameSpan, valueSpan: valueSpan};
|
||||
}
|
||||
});
|
||||
return prop;
|
||||
}
|
||||
|
||||
function getComputedViewProperty(name) {
|
||||
let prop = null;
|
||||
[].forEach.call(computedView.styleDocument.querySelectorAll(".property-view"), property => {
|
||||
let nameSpan = property.querySelector(".property-name");
|
||||
let valueSpan = property.querySelector(".property-value");
|
||||
|
||||
if (nameSpan.textContent === name) {
|
||||
prop = {nameSpan: nameSpan, valueSpan: valueSpan};
|
||||
}
|
||||
});
|
||||
return prop;
|
||||
}
|
@ -70,10 +70,6 @@ detailsPane.itemsCountLabel=One item;#1 items
|
||||
mostVisitedTitle=Most Visited
|
||||
recentlyBookmarkedTitle=Recently Bookmarked
|
||||
recentTagsTitle=Recent Tags
|
||||
# LOCALIZATION NOTE (windows8TouchTitle): this is the name of the folder used
|
||||
# to store bookmarks created in Metro mode and share bookmarks between Metro
|
||||
# and Desktop.
|
||||
windows8TouchTitle=Windows 8 Touch
|
||||
|
||||
OrganizerQueryHistory=History
|
||||
OrganizerQueryDownloads=Downloads
|
||||
|
@ -8,6 +8,13 @@ const Cu = Components.utils;
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
|
||||
"resource://gre/modules/PlacesUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
|
||||
"resource://gre/modules/NetUtil.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Task",
|
||||
"resource://gre/modules/Task.jsm");
|
||||
|
||||
// Custom factory object to ensure that we're a singleton
|
||||
const BrowserStartupServiceFactory = {
|
||||
_instance: null,
|
||||
@ -62,7 +69,35 @@ BrowserStartup.prototype = {
|
||||
|
||||
Cu.import("resource://gre/modules/BookmarkJSONUtils.jsm");
|
||||
|
||||
BookmarkJSONUtils.importFromURL("chrome://browser/locale/bookmarks.json", false);
|
||||
Task.spawn(function() {
|
||||
yield BookmarkJSONUtils.importFromURL("chrome://browser/locale/bookmarks.json", false);
|
||||
|
||||
// Create the new smart bookmark.
|
||||
const MAX_RESULTS = 10;
|
||||
const SMART_BOOKMARKS_ANNO = "Places/SmartBookmark";
|
||||
|
||||
// Place the Metro folder at the end of the smart bookmarks list.
|
||||
let maxIndex = Math.max.apply(null,
|
||||
PlacesUtils.annotations.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO).map(id => {
|
||||
return PlacesUtils.bookmarks.getItemIndex(id);
|
||||
}));
|
||||
let smartBookmarkId =
|
||||
PlacesUtils.bookmarks.insertBookmark(PlacesUtils.bookmarksMenuFolderId,
|
||||
NetUtil.newURI("place:folder=" +
|
||||
PlacesUtils.annotations.getItemsWithAnnotation('metro/bookmarksRoot', {})[0] +
|
||||
"&queryType=" +
|
||||
Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS +
|
||||
"&sort=" +
|
||||
Ci.nsINavHistoryQueryOptions.SORT_BY_DATEADDED_DESCENDING +
|
||||
"&maxResults=" + MAX_RESULTS +
|
||||
"&excludeQueries=1"),
|
||||
maxIndex + 1,
|
||||
PlacesUtils.getString("windows8TouchTitle"));
|
||||
PlacesUtils.annotations.setItemAnnotation(smartBookmarkId,
|
||||
SMART_BOOKMARKS_ANNO,
|
||||
"Windows8Touch", 0,
|
||||
PlacesUtils.annotations.EXPIRE_NEVER);
|
||||
});
|
||||
},
|
||||
|
||||
_startupActions: function() {
|
||||
|
@ -174,6 +174,14 @@
|
||||
margin-bottom: -4px;
|
||||
}
|
||||
|
||||
/* Tooltip: Font Family Previewer Text */
|
||||
.devtools-tooltip-font-previewer-text {
|
||||
max-width: 400px;
|
||||
line-height: 1.5;
|
||||
font-size: 150%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Tooltip: Alert Icon */
|
||||
|
||||
.devtools-tooltip-alert-icon {
|
||||
|
@ -312,6 +312,10 @@ div.CodeMirror span.eval-text {
|
||||
border-bottom: 1px solid #434850;
|
||||
}
|
||||
|
||||
.theme-tooltip-panel .devtools-tooltip-font-previewer-text {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.theme-tooltip-panel .devtools-tooltip-simple-text:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
@ -321,6 +321,10 @@ div.CodeMirror span.eval-text {
|
||||
border-bottom: 1px solid #d9e1e8;
|
||||
}
|
||||
|
||||
.theme-tooltip-panel .devtools-tooltip-font-previewer-text {
|
||||
color: black;
|
||||
}
|
||||
|
||||
.theme-tooltip-panel .devtools-tooltip-simple-text:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
@ -6,7 +6,8 @@
|
||||
%include downloads.css
|
||||
%undef WINDOWS_AERO
|
||||
|
||||
@media (-moz-windows-default-theme) {
|
||||
@media (-moz-windows-default-theme) and (-moz-os-version: windows-vista),
|
||||
(-moz-windows-default-theme) and (-moz-os-version: windows-win7) {
|
||||
richlistitem[type="download"] {
|
||||
border: 1px solid transparent;
|
||||
border-bottom: 1px solid hsl(213,40%,90%);
|
||||
|
@ -20,10 +20,20 @@
|
||||
|
||||
#downloadsHistory {
|
||||
background: transparent;
|
||||
color: -moz-nativehyperlinktext;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
%ifdef WINDOWS_AERO
|
||||
@media (-moz-os-version: windows-vista),
|
||||
(-moz-os-version: windows-win7) {
|
||||
%endif
|
||||
#downloadsHistory {
|
||||
color: -moz-nativehyperlinktext;
|
||||
}
|
||||
%ifdef WINDOWS_AERO
|
||||
}
|
||||
%endif
|
||||
|
||||
#downloadsPanel[keyfocus] > #downloadsFooter > #downloadsHistory:focus {
|
||||
outline: 1px -moz-dialogtext dotted;
|
||||
outline-offset: -1px;
|
||||
@ -34,18 +44,47 @@
|
||||
margin: 1em;
|
||||
}
|
||||
|
||||
#downloadsPanel[hasdownloads] > #downloadsFooter {
|
||||
background-color: hsla(210,4%,10%,.04);
|
||||
box-shadow: 0 1px 0 hsla(210,4%,10%,.08) inset;
|
||||
transition-duration: 150ms;
|
||||
transition-property: background-color;
|
||||
}
|
||||
|
||||
#downloadsPanel[hasdownloads] > #downloadsFooter:hover {
|
||||
background-color: hsla(210,4%,10%,.05);
|
||||
}
|
||||
|
||||
#downloadsPanel[hasdownloads] > #downloadsFooter:hover:active {
|
||||
background-color: hsla(210,4%,10%,.1);
|
||||
box-shadow: 0 2px 0 0 hsla(210,4%,10%,.1) inset;
|
||||
}
|
||||
|
||||
%ifdef WINDOWS_AERO
|
||||
@media (-moz-os-version: windows-vista),
|
||||
(-moz-os-version: windows-win7) {
|
||||
%endif
|
||||
@media (-moz-windows-default-theme) {
|
||||
#downloadsPanel[hasdownloads] > #downloadsFooter {
|
||||
border-bottom-left-radius: 3px;
|
||||
border-bottom-right-radius: 3px;
|
||||
transition-duration: 0s;
|
||||
}
|
||||
|
||||
#downloadsPanel[hasdownloads] > #downloadsFooter,
|
||||
#downloadsPanel[hasdownloads] > #downloadsFooter:hover,
|
||||
#downloadsPanel[hasdownloads] > #downloadsFooter:hover:active {
|
||||
%ifdef WINDOWS_AERO
|
||||
background-color: #f1f5fb;
|
||||
%else
|
||||
background-color: hsla(216,45%,88%,.98);
|
||||
%endif
|
||||
box-shadow: 0px 1px 2px rgb(204,214,234) inset;
|
||||
border-bottom-left-radius: 3px;
|
||||
border-bottom-right-radius: 3px;
|
||||
}
|
||||
}
|
||||
%ifdef WINDOWS_AERO
|
||||
}
|
||||
%endif
|
||||
|
||||
/*** Downloads Summary and List items ***/
|
||||
|
||||
@ -166,15 +205,41 @@ richlistitem[type="download"]:first-child {
|
||||
|
||||
/*** Highlighted list items ***/
|
||||
|
||||
#downloadsPanel:not([keyfocus]) > #downloadsListBox > richlistitem[type="download"][state="1"][exists]:hover {
|
||||
background-color: hsla(210,4%,10%,.08);
|
||||
outline: 1px solid hsla(210,4%,10%,.1);
|
||||
outline-offset: -1px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#downloadsPanel:not([keyfocus]) > #downloadsListBox > richlistitem[type="download"][state="1"][exists]:hover:active {
|
||||
background-color: hsla(210,4%,10%,.15);
|
||||
outline: 1px solid hsla(210,4%,10%,.15);
|
||||
box-shadow: 0 1px 0 0 hsla(210,4%,10%,.05) inset;
|
||||
}
|
||||
|
||||
%ifdef WINDOWS_AERO
|
||||
@media (-moz-os-version: windows-vista),
|
||||
(-moz-os-version: windows-win7) {
|
||||
%endif
|
||||
#downloadsPanel:not([keyfocus]) > #downloadsListBox > richlistitem[type="download"][state="1"][exists]:hover {
|
||||
border-radius: 3px;
|
||||
outline: 0;
|
||||
border-top: 1px solid hsla(0,0%,100%,.2);
|
||||
border-bottom: 1px solid hsla(0,0%,0%,.2);
|
||||
background-color: Highlight;
|
||||
color: HighlightText;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#downloadsPanel:not([keyfocus]) > #downloadsListBox > richlistitem[type="download"][state="1"][exists]:hover:active {
|
||||
background-color: Highlight;
|
||||
outline: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
%ifdef WINDOWS_AERO
|
||||
}
|
||||
%endif
|
||||
|
||||
/*** Button icons ***/
|
||||
|
||||
.downloadButton.downloadCancel {
|
||||
|
@ -1,5 +1,5 @@
|
||||
[DEFAULT]
|
||||
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # b2g-desktop(bug 979446, frequent failures)
|
||||
skip-if = buildapp == 'b2g' # bug 979446, frequent failures
|
||||
support-files =
|
||||
serve_file.sjs
|
||||
|
||||
|
@ -2746,9 +2746,16 @@ Debugger::findAllGlobals(JSContext *cx, unsigned argc, Value *vp)
|
||||
return false;
|
||||
|
||||
for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) {
|
||||
if (c->options().invisibleToDebugger())
|
||||
continue;
|
||||
|
||||
c->zone()->scheduledForDestruction = false;
|
||||
|
||||
GlobalObject *global = c->maybeGlobal();
|
||||
|
||||
if (cx->runtime()->isSelfHostingGlobal(global))
|
||||
continue;
|
||||
|
||||
if (global) {
|
||||
/*
|
||||
* We pulled |global| out of nowhere, so it's possible that it was
|
||||
|
@ -104,7 +104,7 @@ public class HomeConfigInvalidator implements GeckoEventListener {
|
||||
try {
|
||||
if (event.equals(EVENT_HOMEPANELS_INSTALL)) {
|
||||
Log.d(LOGTAG, EVENT_HOMEPANELS_INSTALL);
|
||||
handlePanelInstall(createPanelConfigFromMessage(message));
|
||||
handlePanelInstall(createPanelConfigFromMessage(message), InvalidationMode.DELAYED);
|
||||
} else if (event.equals(EVENT_HOMEPANELS_UNINSTALL)) {
|
||||
Log.d(LOGTAG, EVENT_HOMEPANELS_UNINSTALL);
|
||||
final String panelId = message.getString(JSON_KEY_PANEL_ID);
|
||||
@ -126,20 +126,23 @@ public class HomeConfigInvalidator implements GeckoEventListener {
|
||||
/**
|
||||
* Adds a new PanelConfig to the HomeConfig.
|
||||
*
|
||||
* This posts the invalidation of HomeConfig immediately.
|
||||
*
|
||||
* @param panelConfig panel to add
|
||||
*/
|
||||
public void installPanel(PanelConfig panelConfig) {
|
||||
handlePanelInstall(panelConfig);
|
||||
Log.d(LOGTAG, "installPanel: " + panelConfig.getTitle());
|
||||
handlePanelInstall(panelConfig, InvalidationMode.IMMEDIATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs in the gecko thread.
|
||||
*/
|
||||
private void handlePanelInstall(PanelConfig panelConfig) {
|
||||
private void handlePanelInstall(PanelConfig panelConfig, InvalidationMode mode) {
|
||||
mPendingChanges.offer(new ConfigChange(ChangeType.INSTALL, panelConfig));
|
||||
Log.d(LOGTAG, "handlePanelInstall: " + mPendingChanges.size());
|
||||
|
||||
scheduleInvalidation(InvalidationMode.DELAYED);
|
||||
scheduleInvalidation(mode);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -224,6 +224,10 @@ public class HomePager extends ViewPager {
|
||||
if (mDecor != null) {
|
||||
mDecor.onPageSelected(item);
|
||||
}
|
||||
|
||||
if (mHomeBanner != null) {
|
||||
mHomeBanner.setActive(item == mDefaultPageIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -300,22 +304,30 @@ public class HomePager extends ViewPager {
|
||||
// in the pager.
|
||||
setAdapter(adapter);
|
||||
|
||||
// Use the default panel as defined in the HomePager's configuration
|
||||
// if the initial panel wasn't explicitly set by the load() caller,
|
||||
// or if the initial panel is not found in the adapter.
|
||||
final int itemPosition = (mInitialPanelId == null) ? -1 : adapter.getItemPosition(mInitialPanelId);
|
||||
if (itemPosition > -1) {
|
||||
setCurrentItem(itemPosition, false);
|
||||
mInitialPanelId = null;
|
||||
if (count == 0) {
|
||||
mDefaultPageIndex = -1;
|
||||
|
||||
// Hide the banner if there are no enabled panels.
|
||||
if (mHomeBanner != null) {
|
||||
mHomeBanner.setActive(false);
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < count; i++) {
|
||||
final PanelConfig panelConfig = enabledPanels.get(i);
|
||||
if (panelConfig.isDefault()) {
|
||||
if (enabledPanels.get(i).isDefault()) {
|
||||
mDefaultPageIndex = i;
|
||||
setCurrentItem(i, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Use the default panel if the initial panel wasn't explicitly set by the
|
||||
// load() caller, or if the initial panel is not found in the adapter.
|
||||
final int itemPosition = (mInitialPanelId == null) ? -1 : adapter.getItemPosition(mInitialPanelId);
|
||||
if (itemPosition > -1) {
|
||||
setCurrentItem(itemPosition, false);
|
||||
mInitialPanelId = null;
|
||||
} else {
|
||||
setCurrentItem(mDefaultPageIndex, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@ -135,11 +136,20 @@ public class HomePanelPicker extends FragmentActivity {
|
||||
private void installNewPanelAndQuit(PanelInfo panelInfo) {
|
||||
final PanelConfig newPanelConfig = panelInfo.toPanelConfig();
|
||||
HomeConfigInvalidator.getInstance().installPanel(newPanelConfig);
|
||||
showToastForNewPanel(newPanelConfig);
|
||||
|
||||
setResult(Activity.RESULT_OK);
|
||||
finish();
|
||||
}
|
||||
|
||||
private void showToastForNewPanel(PanelConfig panelConfig) {
|
||||
String panelName = panelConfig.getTitle();
|
||||
|
||||
// Display toast.
|
||||
final String successMsg = getResources().getString(R.string.home_add_panel_installed, panelName);
|
||||
Toast.makeText(this, successMsg, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
// ViewHolder for rows of the panel picker.
|
||||
private static class PanelRow {
|
||||
final TextView title;
|
||||
|
@ -93,6 +93,10 @@
|
||||
<!ENTITY pref_home_add_panel "Add panel">
|
||||
<!ENTITY home_add_panel_title "Add new panel">
|
||||
<!ENTITY home_add_panel_empty "Sorry, we couldn\'t find any panels for you to add.">
|
||||
<!-- Localization note (home_add_panel_installed):
|
||||
The &formatS; will be replaced with the name of the new panel the user just
|
||||
selected to be added to the home page. -->
|
||||
<!ENTITY home_add_panel_installed "\'&formatS;\' added to homepage">
|
||||
<!ENTITY pref_category_home_content_settings "Content settings">
|
||||
<!ENTITY pref_home_updates "Automatic updates">
|
||||
<!ENTITY pref_home_updates_enabled "Enabled">
|
||||
|
@ -75,6 +75,7 @@ public class GeckoPreferences
|
||||
private static boolean sIsCharEncodingEnabled = false;
|
||||
private boolean mInitialized = false;
|
||||
private int mPrefsRequestId = 0;
|
||||
private PanelsPreferenceCategory mPanelsPreferenceCategory;
|
||||
|
||||
// These match keys in resources/xml*/preferences*.xml
|
||||
private static final String PREFS_SEARCH_RESTORE_DEFAULTS = NON_PREF_PREFIX + "search.restore_defaults";
|
||||
@ -279,9 +280,8 @@ public class GeckoPreferences
|
||||
case HomePanelPicker.REQUEST_CODE_ADD_PANEL:
|
||||
switch (resultCode) {
|
||||
case Activity.RESULT_OK:
|
||||
// XXX: Bug 976925 - UI after adding a panel.
|
||||
setResult(RESULT_CODE_EXIT_SETTINGS);
|
||||
finish();
|
||||
// Panel installed, refresh panels list.
|
||||
mPanelsPreferenceCategory.refresh();
|
||||
break;
|
||||
case Activity.RESULT_CANCELED:
|
||||
// Dialog was cancelled, do nothing.
|
||||
@ -348,6 +348,8 @@ public class GeckoPreferences
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
} else if (pref instanceof PanelsPreferenceCategory) {
|
||||
mPanelsPreferenceCategory = (PanelsPreferenceCategory) pref;
|
||||
}
|
||||
setupPreferences((PreferenceGroup) pref, prefs);
|
||||
} else {
|
||||
|
@ -69,6 +69,21 @@ public class PanelsPreferenceCategory extends CustomListCategory {
|
||||
mLoadTask.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload the Home Panels list from HomeConfig.
|
||||
*/
|
||||
public void refresh() {
|
||||
// Clear all the existing home panels, but leave the
|
||||
// first item (Add panels).
|
||||
int prefCount = getPreferenceCount();
|
||||
while (prefCount > 1) {
|
||||
removePreference(getPreference(1));
|
||||
prefCount--;
|
||||
}
|
||||
|
||||
loadHomeConfig();
|
||||
}
|
||||
|
||||
private void displayHomeConfig(HomeConfig.State configState) {
|
||||
for (PanelConfig panelConfig : configState) {
|
||||
// Create and add the pref.
|
||||
|
@ -116,6 +116,7 @@
|
||||
<string name="pref_home_add_panel">&pref_home_add_panel;</string>
|
||||
<string name="home_add_panel_title">&home_add_panel_title;</string>
|
||||
<string name="home_add_panel_empty">&home_add_panel_empty;</string>
|
||||
<string name="home_add_panel_installed">&home_add_panel_installed;</string>
|
||||
<string name="pref_category_home_content_settings">&pref_category_home_content_settings;</string>
|
||||
<string name="pref_home_updates">&pref_home_updates;</string>
|
||||
<string name="pref_home_updates_enabled">&pref_home_updates_enabled;</string>
|
||||
|
@ -7953,7 +7953,7 @@ var ExternalApps = {
|
||||
Strings.browser.GetStringFromName("openInApp.ok"),
|
||||
Strings.browser.GetStringFromName("openInApp.cancel")
|
||||
]
|
||||
}, function(result) {
|
||||
}, (result) => {
|
||||
if (result.button != 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -342,6 +342,7 @@ add_task(function test_multiple_401_retry_once() {
|
||||
// Request will have bad timestamp; client will retry once
|
||||
try {
|
||||
yield client.request("/maybe", method, credentials);
|
||||
do_throw("Expected an error");
|
||||
} catch (err) {
|
||||
do_check_eq(err.code, 401);
|
||||
}
|
||||
|
@ -67,22 +67,22 @@ AccountState.prototype = {
|
||||
cert: null,
|
||||
keyPair: null,
|
||||
signedInUser: null,
|
||||
whenVerifiedPromise: null,
|
||||
whenKeysReadyPromise: null,
|
||||
whenVerifiedDeferred: null,
|
||||
whenKeysReadyDeferred: null,
|
||||
|
||||
get isCurrent() this.fxaInternal && this.fxaInternal.currentAccountState === this,
|
||||
|
||||
abort: function() {
|
||||
if (this.whenVerifiedPromise) {
|
||||
this.whenVerifiedPromise.reject(
|
||||
if (this.whenVerifiedDeferred) {
|
||||
this.whenVerifiedDeferred.reject(
|
||||
new Error("Verification aborted; Another user signing in"));
|
||||
this.whenVerifiedPromise = null;
|
||||
this.whenVerifiedDeferred = null;
|
||||
}
|
||||
|
||||
if (this.whenKeysReadyPromise) {
|
||||
this.whenKeysReadyPromise.reject(
|
||||
if (this.whenKeysReadyDeferred) {
|
||||
this.whenKeysReadyDeferred.reject(
|
||||
new Error("Verification aborted; Another user signing in"));
|
||||
this.whenKeysReadyPromise = null;
|
||||
this.whenKeysReadyDeferred = null;
|
||||
}
|
||||
this.cert = null;
|
||||
this.keyPair = null;
|
||||
@ -484,13 +484,13 @@ FxAccountsInternal.prototype = {
|
||||
if (data.kA && data.kB) {
|
||||
return data;
|
||||
}
|
||||
if (!currentState.whenKeysReadyPromise) {
|
||||
currentState.whenKeysReadyPromise = Promise.defer();
|
||||
if (!currentState.whenKeysReadyDeferred) {
|
||||
currentState.whenKeysReadyDeferred = Promise.defer();
|
||||
this.fetchAndUnwrapKeys(data.keyFetchToken).then(data => {
|
||||
currentState.whenKeysReadyPromise.resolve(data);
|
||||
currentState.whenKeysReadyDeferred.resolve(data);
|
||||
});
|
||||
}
|
||||
return currentState.whenKeysReadyPromise.promise;
|
||||
return currentState.whenKeysReadyDeferred.promise;
|
||||
}).then(result => currentState.resolve(result));
|
||||
},
|
||||
|
||||
@ -607,11 +607,11 @@ FxAccountsInternal.prototype = {
|
||||
log.debug("already verified");
|
||||
return currentState.resolve(data);
|
||||
}
|
||||
if (!currentState.whenVerifiedPromise) {
|
||||
if (!currentState.whenVerifiedDeferred) {
|
||||
log.debug("whenVerified promise starts polling for verified email");
|
||||
this.pollEmailStatus(currentState, data.sessionToken, "start");
|
||||
}
|
||||
return currentState.whenVerifiedPromise.promise.then(
|
||||
return currentState.whenVerifiedDeferred.promise.then(
|
||||
result => currentState.resolve(result)
|
||||
);
|
||||
},
|
||||
@ -629,8 +629,8 @@ FxAccountsInternal.prototype = {
|
||||
// if the user requested the verification email to be resent while we
|
||||
// were already polling for receipt of an earlier email.
|
||||
this.pollTimeRemaining = this.POLL_SESSION;
|
||||
if (!currentState.whenVerifiedPromise) {
|
||||
currentState.whenVerifiedPromise = Promise.defer();
|
||||
if (!currentState.whenVerifiedDeferred) {
|
||||
currentState.whenVerifiedDeferred = Promise.defer();
|
||||
}
|
||||
}
|
||||
|
||||
@ -647,9 +647,9 @@ FxAccountsInternal.prototype = {
|
||||
})
|
||||
.then((data) => {
|
||||
// Now that the user is verified, we can proceed to fetch keys
|
||||
if (currentState.whenVerifiedPromise) {
|
||||
currentState.whenVerifiedPromise.resolve(data);
|
||||
delete currentState.whenVerifiedPromise;
|
||||
if (currentState.whenVerifiedDeferred) {
|
||||
currentState.whenVerifiedDeferred.resolve(data);
|
||||
delete currentState.whenVerifiedDeferred;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
@ -661,11 +661,11 @@ FxAccountsInternal.prototype = {
|
||||
this.pollEmailStatus(currentState, sessionToken, "timer")}, this.POLL_STEP);
|
||||
log.debug("started timer " + this.currentTimer);
|
||||
} else {
|
||||
if (currentState.whenVerifiedPromise) {
|
||||
currentState.whenVerifiedPromise.reject(
|
||||
if (currentState.whenVerifiedDeferred) {
|
||||
currentState.whenVerifiedDeferred.reject(
|
||||
new Error("User email verification timed out.")
|
||||
);
|
||||
delete currentState.whenVerifiedPromise;
|
||||
delete currentState.whenVerifiedDeferred;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
Cu.import("resource://gre/modules/FxAccountsClient.jsm");
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
Cu.import("resource://services-common/utils.js");
|
||||
@ -12,24 +14,43 @@ function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
// https://wiki.mozilla.org/Identity/AttachedServices/KeyServerProtocol#.2Faccount.2Fkeys
|
||||
let ACCOUNT_KEYS = {
|
||||
keyFetch: h("8081828384858687 88898a8b8c8d8e8f"+
|
||||
"9091929394959697 98999a9b9c9d9e9f"),
|
||||
|
||||
response: h("ee5c58845c7c9412 b11bbd20920c2fdd"+
|
||||
"d83c33c9cd2c2de2 d66b222613364636"+
|
||||
"c2c0f8cfbb7c6304 72c0bd88451342c6"+
|
||||
"c05b14ce342c5ad4 6ad89e84464c993c"+
|
||||
"3927d30230157d08 17a077eef4b20d97"+
|
||||
"6f7a97363faf3f06 4c003ada7d01aa70"),
|
||||
|
||||
kA: h("2021222324252627 28292a2b2c2d2e2f"+
|
||||
"3031323334353637 38393a3b3c3d3e3f"),
|
||||
|
||||
wrapKB: h("4041424344454647 48494a4b4c4d4e4f"+
|
||||
"5051525354555657 58595a5b5c5d5e5f"),
|
||||
};
|
||||
|
||||
// https://github.com/mozilla/fxa-auth-server/wiki/onepw-protocol#wiki-use-session-certificatesign-etc
|
||||
let SESSION_KEYS = {
|
||||
sessionToken: h("a0a1a2a3a4a5a6a7 a8a9aaabacadaeaf"+
|
||||
"b0b1b2b3b4b5b6b7 b8b9babbbcbdbebf"),
|
||||
|
||||
tokenID: h("c0a29dcf46174973 da1378696e4c82ae"+
|
||||
"10f723cf4f4d9f75 e39f4ae3851595ab"),
|
||||
|
||||
reqHMACkey: h("9d8f22998ee7f579 8b887042466b72d5"+
|
||||
"3e56ab0c094388bf 65831f702d2febc0"),
|
||||
};
|
||||
|
||||
function deferredStop(server) {
|
||||
let deferred = Promise.defer();
|
||||
server.stop(deferred.resolve);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
add_test(function test_hawk_credentials() {
|
||||
let client = new FxAccountsClient();
|
||||
|
||||
let sessionToken = "a0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf";
|
||||
let result = client._deriveHawkCredentials(sessionToken, "session");
|
||||
|
||||
do_check_eq(result.id, "639503a218ffbb62983e9628be5cd64a0438d0ae81b2b9dadeb900a83470bc6b");
|
||||
do_check_eq(CommonUtils.bytesAsHex(result.key), "3a0188943837ab228fe74e759566d0e4837cbcc7494157aac4da82025b2811b2");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_task(function test_authenticated_get_request() {
|
||||
let message = "{\"msg\": \"Great Success!\"}";
|
||||
let credentials = {
|
||||
@ -94,6 +115,7 @@ add_task(function test_500_error() {
|
||||
|
||||
try {
|
||||
yield client._request("/foo", method);
|
||||
do_throw("Expected to catch an exception");
|
||||
} catch (e) {
|
||||
do_check_eq(500, e.code);
|
||||
do_check_eq("Internal Server Error", e.message);
|
||||
@ -171,12 +193,14 @@ add_task(function test_signUp() {
|
||||
created = true;
|
||||
|
||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||
return response.bodyOutputStream.write(creationMessage, creationMessage.length);
|
||||
response.bodyOutputStream.write(creationMessage, creationMessage.length);
|
||||
return;
|
||||
}
|
||||
|
||||
// Error trying to create same account a second time
|
||||
response.setStatusLine(request.httpVersion, 400, "Bad request");
|
||||
return response.bodyOutputStream.write(errorMessage, errorMessage.length);
|
||||
response.bodyOutputStream.write(errorMessage, errorMessage.length);
|
||||
return;
|
||||
},
|
||||
});
|
||||
|
||||
@ -189,6 +213,7 @@ add_task(function test_signUp() {
|
||||
// Try to create account again. Triggers error path.
|
||||
try {
|
||||
result = yield client.signUp('andré@example.org', 'pässwörd');
|
||||
do_throw("Expected to catch an exception");
|
||||
} catch(expectedError) {
|
||||
do_check_eq(101, expectedError.errno);
|
||||
}
|
||||
@ -199,6 +224,7 @@ add_task(function test_signUp() {
|
||||
add_task(function test_signIn() {
|
||||
let sessionMessage = JSON.stringify({sessionToken: FAKE_SESSION_TOKEN});
|
||||
let errorMessage = JSON.stringify({code: 400, errno: 102, error: "doesn't exist"});
|
||||
|
||||
let server = httpd_setup({
|
||||
"/account/login": function(request, response) {
|
||||
let body = CommonUtils.readBytesFromInputStream(request.bodyInputStream);
|
||||
@ -207,12 +233,14 @@ add_task(function test_signIn() {
|
||||
if (jsonBody.email == "mé@example.com") {
|
||||
do_check_eq(jsonBody.authPW, "08b9d111196b8408e8ed92439da49206c8ecfbf343df0ae1ecefcd1e0174a8b6");
|
||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||
return response.bodyOutputStream.write(sessionMessage, sessionMessage.length);
|
||||
response.bodyOutputStream.write(sessionMessage, sessionMessage.length);
|
||||
return;
|
||||
}
|
||||
|
||||
// Error trying to sign in to nonexistent account
|
||||
response.setStatusLine(request.httpVersion, 400, "Bad request");
|
||||
return response.bodyOutputStream.write(errorMessage, errorMessage.length);
|
||||
response.bodyOutputStream.write(errorMessage, errorMessage.length);
|
||||
return;
|
||||
},
|
||||
});
|
||||
|
||||
@ -223,6 +251,7 @@ add_task(function test_signIn() {
|
||||
// Trigger error path
|
||||
try {
|
||||
result = yield client.signIn("yøü@bad.example.org", "nofear");
|
||||
do_throw("Expected to catch an exception");
|
||||
} catch(expectedError) {
|
||||
do_check_eq(102, expectedError.errno);
|
||||
}
|
||||
@ -241,12 +270,14 @@ add_task(function test_signOut() {
|
||||
signedOut = true;
|
||||
do_check_true(request.hasHeader("Authorization"));
|
||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||
return response.bodyOutputStream.write(signoutMessage, signoutMessage.length);
|
||||
response.bodyOutputStream.write(signoutMessage, signoutMessage.length);
|
||||
return;
|
||||
}
|
||||
|
||||
// Error trying to sign out of nonexistent account
|
||||
response.setStatusLine(request.httpVersion, 400, "Bad request");
|
||||
return response.bodyOutputStream.write(errorMessage, errorMessage.length);
|
||||
response.bodyOutputStream.write(errorMessage, errorMessage.length);
|
||||
return;
|
||||
},
|
||||
});
|
||||
|
||||
@ -257,6 +288,7 @@ add_task(function test_signOut() {
|
||||
// Trigger error path
|
||||
try {
|
||||
result = yield client.signOut("FakeSession");
|
||||
do_throw("Expected to catch an exception");
|
||||
} catch(expectedError) {
|
||||
do_check_eq(102, expectedError.errno);
|
||||
}
|
||||
@ -274,13 +306,16 @@ add_task(function test_recoveryEmailStatus() {
|
||||
do_check_true(request.hasHeader("Authorization"));
|
||||
|
||||
if (tries === 0) {
|
||||
tries += 1;
|
||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||
return response.bodyOutputStream.write(emailStatus, emailStatus.length);
|
||||
response.bodyOutputStream.write(emailStatus, emailStatus.length);
|
||||
return;
|
||||
}
|
||||
|
||||
// Second call gets an error trying to query a nonexistent account
|
||||
response.setStatusLine(request.httpVersion, 400, "Bad request");
|
||||
return response.bodyOutputStream.write(errorMessage, errorMessage.length);
|
||||
response.bodyOutputStream.write(errorMessage, errorMessage.length);
|
||||
return;
|
||||
},
|
||||
});
|
||||
|
||||
@ -291,6 +326,7 @@ add_task(function test_recoveryEmailStatus() {
|
||||
// Trigger error path
|
||||
try {
|
||||
result = yield client.recoveryEmailStatus("some bogus session");
|
||||
do_throw("Expected to catch an exception");
|
||||
} catch(expectedError) {
|
||||
do_check_eq(102, expectedError.errno);
|
||||
}
|
||||
@ -307,13 +343,16 @@ add_task(function test_resendVerificationEmail() {
|
||||
"/recovery_email/resend_code": function(request, response) {
|
||||
do_check_true(request.hasHeader("Authorization"));
|
||||
if (tries === 0) {
|
||||
tries += 1;
|
||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||
return response.bodyOutputStream.write(emptyMessage, emptyMessage.length);
|
||||
response.bodyOutputStream.write(emptyMessage, emptyMessage.length);
|
||||
return;
|
||||
}
|
||||
|
||||
// Second call gets an error trying to query a nonexistent account
|
||||
response.setStatusLine(request.httpVersion, 400, "Bad request");
|
||||
return response.bodyOutputStream.write(errorMessage, errorMessage.length);
|
||||
response.bodyOutputStream.write(errorMessage, errorMessage.length);
|
||||
return;
|
||||
},
|
||||
});
|
||||
|
||||
@ -324,6 +363,7 @@ add_task(function test_resendVerificationEmail() {
|
||||
// Trigger error path
|
||||
try {
|
||||
result = yield client.resendVerificationEmail("some bogus session");
|
||||
do_throw("Expected to catch an exception");
|
||||
} catch(expectedError) {
|
||||
do_check_eq(102, expectedError.errno);
|
||||
}
|
||||
@ -332,25 +372,11 @@ add_task(function test_resendVerificationEmail() {
|
||||
});
|
||||
|
||||
add_task(function test_accountKeys() {
|
||||
// Vectors: https://wiki.mozilla.org/Identity/AttachedServices/KeyServerProtocol#.2Faccount.2Fkeys
|
||||
|
||||
let keyFetch = h("8081828384858687 88898a8b8c8d8e8f"+
|
||||
"9091929394959697 98999a9b9c9d9e9f");
|
||||
|
||||
let response = h("ee5c58845c7c9412 b11bbd20920c2fdd"+
|
||||
"d83c33c9cd2c2de2 d66b222613364636"+
|
||||
"c2c0f8cfbb7c6304 72c0bd88451342c6"+
|
||||
"c05b14ce342c5ad4 6ad89e84464c993c"+
|
||||
"3927d30230157d08 17a077eef4b20d97"+
|
||||
"6f7a97363faf3f06 4c003ada7d01aa70");
|
||||
|
||||
let kA = h("2021222324252627 28292a2b2c2d2e2f"+
|
||||
"3031323334353637 38393a3b3c3d3e3f");
|
||||
|
||||
let wrapKB = h("4041424344454647 48494a4b4c4d4e4f"+
|
||||
"5051525354555657 58595a5b5c5d5e5f");
|
||||
|
||||
let responseMessage = JSON.stringify({bundle: response});
|
||||
// Four calls to accountKeys(). The first one should work correctly, and we
|
||||
// should get a valid bundle back, in exchange for our keyFetch token, from
|
||||
// which we correctly derive kA and wrapKB. The subsequent three calls
|
||||
// should all trigger separate error paths.
|
||||
let responseMessage = JSON.stringify({bundle: ACCOUNT_KEYS.response});
|
||||
let errorMessage = JSON.stringify({code: 400, errno: 102, error: "doesn't exist"});
|
||||
let emptyMessage = "{}";
|
||||
let attempt = 0;
|
||||
@ -375,10 +401,12 @@ add_task(function test_accountKeys() {
|
||||
|
||||
case 3:
|
||||
// Return gibberish to trigger client MAC error
|
||||
let garbage = response;
|
||||
garbage[0] = 0; // tweak a byte
|
||||
// Tweak a byte
|
||||
let garbageResponse = JSON.stringify({
|
||||
bundle: ACCOUNT_KEYS.response.slice(0, -1) + "1"
|
||||
});
|
||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||
response.bodyOutputStream.write(responseMessage, responseMessage.length);
|
||||
response.bodyOutputStream.write(garbageResponse, garbageResponse.length);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
@ -393,27 +421,30 @@ add_task(function test_accountKeys() {
|
||||
let client = new FxAccountsClient(server.baseURI);
|
||||
|
||||
// First try, all should be good
|
||||
let result = yield client.accountKeys(keyFetch);
|
||||
do_check_eq(CommonUtils.hexToBytes(kA), result.kA);
|
||||
do_check_eq(CommonUtils.hexToBytes(wrapKB), result.wrapKB);
|
||||
let result = yield client.accountKeys(ACCOUNT_KEYS.keyFetch);
|
||||
do_check_eq(CommonUtils.hexToBytes(ACCOUNT_KEYS.kA), result.kA);
|
||||
do_check_eq(CommonUtils.hexToBytes(ACCOUNT_KEYS.wrapKB), result.wrapKB);
|
||||
|
||||
// Second try, empty bundle should trigger error
|
||||
try {
|
||||
result = yield client.accountKeys(keyFetch);
|
||||
result = yield client.accountKeys(ACCOUNT_KEYS.keyFetch);
|
||||
do_throw("Expected to catch an exception");
|
||||
} catch(expectedError) {
|
||||
do_check_eq(expectedError.message, "failed to retrieve keys");
|
||||
}
|
||||
|
||||
// Third try, bad bundle results in MAC error
|
||||
try {
|
||||
result = yield client.accountKeys(keyFetch);
|
||||
result = yield client.accountKeys(ACCOUNT_KEYS.keyFetch);
|
||||
do_throw("Expected to catch an exception");
|
||||
} catch(expectedError) {
|
||||
do_check_eq(expectedError.message, "error unbundling encryption keys");
|
||||
}
|
||||
|
||||
// Fourth try, pretend account doesn't exist
|
||||
try {
|
||||
result = yield client.accountKeys(keyFetch);
|
||||
result = yield client.accountKeys(ACCOUNT_KEYS.keyFetch);
|
||||
do_throw("Expected to catch an exception");
|
||||
} catch(expectedError) {
|
||||
do_check_eq(102, expectedError.errno);
|
||||
}
|
||||
@ -437,12 +468,14 @@ add_task(function test_signCertificate() {
|
||||
do_check_eq(JSON.parse(jsonBody.publicKey).foo, "bar");
|
||||
do_check_eq(jsonBody.duration, 600);
|
||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||
return response.bodyOutputStream.write(certSignMessage, certSignMessage.length);
|
||||
response.bodyOutputStream.write(certSignMessage, certSignMessage.length);
|
||||
return;
|
||||
}
|
||||
|
||||
// Second attempt, trigger error
|
||||
response.setStatusLine(request.httpVersion, 400, "Bad request");
|
||||
return response.bodyOutputStream.write(errorMessage, errorMessage.length);
|
||||
response.bodyOutputStream.write(errorMessage, errorMessage.length);
|
||||
return;
|
||||
},
|
||||
});
|
||||
|
||||
@ -453,6 +486,7 @@ add_task(function test_signCertificate() {
|
||||
// Account doesn't exist
|
||||
try {
|
||||
result = yield client.signCertificate("bogus", JSON.stringify({foo: "bar"}), 600);
|
||||
do_throw("Expected to catch an exception");
|
||||
} catch(expectedError) {
|
||||
do_check_eq(102, expectedError.errno);
|
||||
}
|
||||
@ -502,27 +536,18 @@ add_task(function test_accountExists() {
|
||||
let client = new FxAccountsClient(server.baseURI);
|
||||
let result;
|
||||
|
||||
try {
|
||||
result = yield client.accountExists("i.exist@example.com");
|
||||
} catch(expectedError) {
|
||||
do_check_eq(expectedError.code, 400);
|
||||
do_check_eq(expectedError.errno, 103);
|
||||
}
|
||||
result = yield client.accountExists("i.exist@example.com");
|
||||
do_check_true(result);
|
||||
|
||||
try {
|
||||
result = yield client.accountExists("i.also.exist@example.com");
|
||||
} catch(expectedError) {
|
||||
do_check_eq(expectedError.errno, 103);
|
||||
}
|
||||
result = yield client.accountExists("i.also.exist@example.com");
|
||||
do_check_true(result);
|
||||
|
||||
try {
|
||||
result = yield client.accountExists("i.dont.exist@example.com");
|
||||
} catch(expectedError) {
|
||||
do_check_eq(expectedError.errno, 102);
|
||||
}
|
||||
result = yield client.accountExists("i.dont.exist@example.com");
|
||||
do_check_false(result);
|
||||
|
||||
try {
|
||||
result = yield client.accountExists("i.break.things@example.com");
|
||||
do_throw("Expected to catch an exception");
|
||||
} catch(unexpectedError) {
|
||||
do_check_eq(unexpectedError.code, 500);
|
||||
}
|
||||
@ -583,6 +608,17 @@ add_task(function test_email_case() {
|
||||
yield deferredStop(server);
|
||||
});
|
||||
|
||||
add_task(function test__deriveHawkCredentials() {
|
||||
let client = new FxAccountsClient("https://example.org");
|
||||
|
||||
let credentials = client._deriveHawkCredentials(
|
||||
SESSION_KEYS.sessionToken, "sessionToken");
|
||||
|
||||
do_check_eq(credentials.algorithm, "sha256");
|
||||
do_check_eq(credentials.id, SESSION_KEYS.tokenID);
|
||||
do_check_eq(CommonUtils.bytesAsHex(credentials.key), SESSION_KEYS.reqHMACkey);
|
||||
});
|
||||
|
||||
// turn formatted test vectors into normal hex strings
|
||||
function h(hexStr) {
|
||||
return hexStr.replace(/\s+/g, "");
|
||||
|
@ -79,14 +79,17 @@ exports.dirname = dirname;
|
||||
*
|
||||
* Under Unix, this will return "/tmp/foo/bar".
|
||||
*/
|
||||
let join = function(path /*...*/) {
|
||||
let join = function(...path) {
|
||||
// If there is a path that starts with a "/", eliminate everything before
|
||||
let paths = [];
|
||||
for each(let i in arguments) {
|
||||
if (i.length != 0 && i[0] == "/") {
|
||||
paths = [i];
|
||||
for (let subpath of path) {
|
||||
if (subpath == null) {
|
||||
throw new TypeError("invalid path component");
|
||||
}
|
||||
if (subpath.length != 0 && subpath[0] == "/") {
|
||||
paths = [subpath];
|
||||
} else {
|
||||
paths.push(i);
|
||||
paths.push(subpath);
|
||||
}
|
||||
}
|
||||
return paths.join("/");
|
||||
|
@ -140,7 +140,10 @@ let join = function(...path) {
|
||||
let paths = [];
|
||||
let root;
|
||||
let absolute = false;
|
||||
for each(let subpath in path) {
|
||||
for (let subpath of path) {
|
||||
if (subpath == null) {
|
||||
throw new TypeError("invalid path component");
|
||||
}
|
||||
let drive = this.winGetDrive(subpath);
|
||||
if (drive) {
|
||||
root = drive;
|
||||
@ -179,6 +182,10 @@ exports.join = join;
|
||||
* includes "\\\\").
|
||||
*/
|
||||
let winGetDrive = function(path) {
|
||||
if (path == null) {
|
||||
throw new TypeError("path is invalid");
|
||||
}
|
||||
|
||||
if (path.startsWith("\\\\")) {
|
||||
// UNC path
|
||||
if (path.length == 2) {
|
||||
|
@ -4901,6 +4901,20 @@
|
||||
"n_buckets": "1000",
|
||||
"description": "The time (in milliseconds) that it took a 'detach' request to go round trip."
|
||||
},
|
||||
"DEVTOOLS_DEBUGGER_RDP_LOCAL_ADDONDETACH_MS": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
"high": "10000",
|
||||
"n_buckets": "1000",
|
||||
"description": "The time (in milliseconds) that it took a 'detach' request to go round trip."
|
||||
},
|
||||
"DEVTOOLS_DEBUGGER_RDP_REMOTE_ADDONDETACH_MS": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
"high": "10000",
|
||||
"n_buckets": "1000",
|
||||
"description": "The time (in milliseconds) that it took a 'detach' request to go round trip."
|
||||
},
|
||||
"DEVTOOLS_DEBUGGER_RDP_LOCAL_TABDETACH_MS": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
|
@ -259,7 +259,7 @@ function reloadApp(client, webappsActor, manifestURL) {
|
||||
webappsActor,
|
||||
manifestURL).
|
||||
then((target) => {
|
||||
// Request the ContentAppActor to reload the app
|
||||
// Request the ContentActor to reload the app
|
||||
let request = {
|
||||
to: target.form.actor,
|
||||
type: "reload",
|
||||
|
@ -237,6 +237,7 @@ this.DebuggerClient = function (aTransport)
|
||||
|
||||
// Map actor ID to client instance for each actor type.
|
||||
this._threadClients = new Map;
|
||||
this._addonClients = new Map;
|
||||
this._tabClients = new Map;
|
||||
this._tracerClients = new Map;
|
||||
this._consoleClients = new Map;
|
||||
@ -413,8 +414,10 @@ DebuggerClient.prototype = {
|
||||
detachClients(this._consoleClients, () => {
|
||||
detachClients(this._threadClients, () => {
|
||||
detachClients(this._tabClients, () => {
|
||||
this._transport.close();
|
||||
this._transport = null;
|
||||
detachClients(this._addonClients, () => {
|
||||
this._transport.close();
|
||||
this._transport = null;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -466,6 +469,31 @@ DebuggerClient.prototype = {
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Attach to an addon actor.
|
||||
*
|
||||
* @param string aAddonActor
|
||||
* The actor ID for the addon to attach.
|
||||
* @param function aOnResponse
|
||||
* Called with the response packet and a AddonClient
|
||||
* (which will be undefined on error).
|
||||
*/
|
||||
attachAddon: function DC_attachAddon(aAddonActor, aOnResponse) {
|
||||
let packet = {
|
||||
to: aAddonActor,
|
||||
type: "attach"
|
||||
};
|
||||
this.request(packet, aResponse => {
|
||||
let addonClient;
|
||||
if (!aResponse.error) {
|
||||
addonClient = new AddonClient(this, aAddonActor);
|
||||
this._addonClients[aAddonActor] = addonClient;
|
||||
this.activeAddon = addonClient;
|
||||
}
|
||||
aOnResponse(aResponse, addonClient);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Attach to a Web Console actor.
|
||||
*
|
||||
@ -743,7 +771,12 @@ DebuggerClient.prototype = {
|
||||
if (pool.has(actorID)) return pool;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Currently attached addon.
|
||||
*/
|
||||
activeAddon: null
|
||||
}
|
||||
|
||||
eventSource(DebuggerClient.prototype);
|
||||
@ -1049,6 +1082,36 @@ TabClient.prototype = {
|
||||
|
||||
eventSource(TabClient.prototype);
|
||||
|
||||
function AddonClient(aClient, aActor) {
|
||||
this._client = aClient;
|
||||
this._actor = aActor;
|
||||
this.request = this._client.request;
|
||||
}
|
||||
|
||||
AddonClient.prototype = {
|
||||
get actor() { return this._actor; },
|
||||
get _transport() { return this._client._transport; },
|
||||
|
||||
/**
|
||||
* Detach the client from the addon actor.
|
||||
*
|
||||
* @param function aOnResponse
|
||||
* Called with the response packet.
|
||||
*/
|
||||
detach: DebuggerClient.requester({
|
||||
type: "detach"
|
||||
}, {
|
||||
after: function(aResponse) {
|
||||
if (this._client.activeAddon === this._client._addonClients[this.actor]) {
|
||||
this._client.activeAddon = null
|
||||
}
|
||||
delete this._client._addonClients[this.actor];
|
||||
return aResponse;
|
||||
},
|
||||
telemetry: "ADDONDETACH"
|
||||
})
|
||||
};
|
||||
|
||||
/**
|
||||
* A RootClient object represents a root actor on the server. Each
|
||||
* DebuggerClient keeps a RootClient instance representing the root actor
|
||||
|
@ -7,57 +7,47 @@
|
||||
/**
|
||||
* Tab actor for documents living in a child process.
|
||||
*
|
||||
* Depends on BrowserTabActor, defined in webbrowser.js actor.
|
||||
* Depends on TabActor, defined in webbrowser.js.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Creates a tab actor for handling requests to the single tab, like
|
||||
* attaching and detaching. ContentAppActor respects the actor factories
|
||||
* attaching and detaching. ContentActor respects the actor factories
|
||||
* registered with DebuggerServer.addTabActor.
|
||||
*
|
||||
* @param connection DebuggerServerConnection
|
||||
* The conection to the client.
|
||||
* @param browser browser
|
||||
* The browser instance that contains this tab.
|
||||
* @param chromeGlobal
|
||||
* The content script global holding |content| and |docShell| properties for a tab.
|
||||
*/
|
||||
function ContentAppActor(connection, browser)
|
||||
function ContentActor(connection, chromeGlobal)
|
||||
{
|
||||
BrowserTabActor.call(this, connection, browser);
|
||||
TabActor.call(this, connection, chromeGlobal);
|
||||
this._chromeGlobal = chromeGlobal;
|
||||
}
|
||||
|
||||
ContentAppActor.prototype = Object.create(BrowserTabActor.prototype);
|
||||
ContentActor.prototype = Object.create(TabActor.prototype);
|
||||
|
||||
ContentAppActor.prototype.constructor = ContentAppActor;
|
||||
ContentActor.prototype.constructor = ContentActor;
|
||||
|
||||
Object.defineProperty(ContentAppActor.prototype, "title", {
|
||||
Object.defineProperty(ContentActor.prototype, "docShell", {
|
||||
get: function() {
|
||||
return this.browser.title;
|
||||
return this._chromeGlobal.docShell;
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: false
|
||||
});
|
||||
|
||||
Object.defineProperty(ContentAppActor.prototype, "url", {
|
||||
get: function() {
|
||||
return this.browser.document.documentURI;
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: false
|
||||
});
|
||||
|
||||
Object.defineProperty(ContentAppActor.prototype, "window", {
|
||||
get: function() {
|
||||
return this.browser;
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: false
|
||||
});
|
||||
ContentActor.prototype.exit = function() {
|
||||
TabActor.prototype.exit.call(this);
|
||||
this._chromeGlobal = null;
|
||||
};
|
||||
|
||||
// Override grip just to rename this._tabActorPool to this._tabActorPool2
|
||||
// in order to prevent it to be cleaned on detach.
|
||||
// We have to keep tab actors alive as we keep the ContentAppActor
|
||||
// We have to keep tab actors alive as we keep the ContentActor
|
||||
// alive after detach and reuse it for multiple debug sessions.
|
||||
ContentAppActor.prototype.grip = function () {
|
||||
ContentActor.prototype.grip = function () {
|
||||
let response = {
|
||||
'actor': this.actorID,
|
||||
'title': this.title,
|
||||
|
@ -187,7 +187,7 @@ let HighlighterActor = protocol.ActorClass({
|
||||
* - On a firefox desktop content page: tabActor is a BrowserTabActor from
|
||||
* which the browser property will give us a target we can use to listen to
|
||||
* events, even in nested iframes.
|
||||
* - On B2G: tabActor is a ContentAppActor which doesn't have a browser but
|
||||
* - On B2G: tabActor is a ContentActor which doesn't have a browser but
|
||||
* since it overrides BrowserTabActor, it does get a browser property
|
||||
* anyway, which points to its window object.
|
||||
* - When using the Browser Toolbox (to inspect firefox desktop): tabActor is
|
||||
@ -196,7 +196,7 @@ let HighlighterActor = protocol.ActorClass({
|
||||
*/
|
||||
_getPickerListenerTarget: function() {
|
||||
let actor = this._tabActor;
|
||||
return actor.isRootActor ? actor.window : actor.browser;
|
||||
return actor.isRootActor ? actor.window : actor.chromeEventHandler;
|
||||
},
|
||||
|
||||
_startPickerListeners: function() {
|
||||
|
@ -4562,6 +4562,96 @@ update(ChromeDebuggerActor.prototype, {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Creates an actor for handling add-on debugging. AddonThreadActor is
|
||||
* a thin wrapper over ThreadActor.
|
||||
*
|
||||
* @param aConnection object
|
||||
* The DebuggerServerConnection with which this AddonThreadActor
|
||||
* is associated. (Currently unused, but required to make this
|
||||
* constructor usable with addGlobalActor.)
|
||||
*
|
||||
* @param aHooks object
|
||||
* An object with preNest and postNest methods for calling
|
||||
* when entering and exiting a nested event loops.
|
||||
*
|
||||
* @param aAddonID string
|
||||
* ID of the add-on this actor will debug. It will be used to
|
||||
* filter out globals marked for debugging.
|
||||
*/
|
||||
|
||||
function AddonThreadActor(aConnect, aHooks, aAddonID) {
|
||||
this.addonID = aAddonID;
|
||||
ThreadActor.call(this, aHooks);
|
||||
}
|
||||
|
||||
AddonThreadActor.prototype = Object.create(ThreadActor.prototype);
|
||||
|
||||
update(AddonThreadActor.prototype, {
|
||||
constructor: AddonThreadActor,
|
||||
|
||||
// A constant prefix that will be used to form the actor ID by the server.
|
||||
actorPrefix: "addonThread",
|
||||
|
||||
/**
|
||||
* Override the eligibility check for scripts and sources to make
|
||||
* sure every script and source with a URL is stored when debugging
|
||||
* add-ons.
|
||||
*/
|
||||
_allowSource: (aSourceURL) => !!aSourceURL,
|
||||
|
||||
/**
|
||||
* An object that will be used by ThreadActors to tailor their
|
||||
* behaviour depending on the debugging context being required (chrome,
|
||||
* addon or content). The methods that this object provides must
|
||||
* be bound to the ThreadActor before use.
|
||||
*/
|
||||
globalManager: {
|
||||
findGlobals: function ADA_findGlobals() {
|
||||
for (let global of this.dbg.findAllGlobals()) {
|
||||
if (this._checkGlobal(global)) {
|
||||
this.dbg.addDebuggee(global);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* A function that the engine calls when a new global object
|
||||
* has been created.
|
||||
*
|
||||
* @param aGlobal Debugger.Object
|
||||
* The new global object that was created.
|
||||
*/
|
||||
onNewGlobal: function ADA_onNewGlobal(aGlobal) {
|
||||
if (this._checkGlobal(aGlobal)) {
|
||||
this.addDebuggee(aGlobal);
|
||||
// Notify the client.
|
||||
this.conn.send({
|
||||
from: this.actorID,
|
||||
type: "newGlobal",
|
||||
// TODO: after bug 801084 lands see if we need to JSONify this.
|
||||
hostAnnotations: aGlobal.hostAnnotations
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if the provided global belongs to the debugged add-on.
|
||||
*
|
||||
* @param aGlobal Debugger.Object
|
||||
*/
|
||||
_checkGlobal: function ADA_checkGlobal(aGlobal) {
|
||||
let metadata;
|
||||
try {
|
||||
// This will fail for non-Sandbox objects, hence the try-catch block.
|
||||
metadata = Cu.getSandboxMetadata(aGlobal.unsafeDereference());
|
||||
} catch (e) {
|
||||
}
|
||||
|
||||
return metadata && metadata.addonID === this.addonID;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Manages the sources for a thread. Handles source maps, locations in the
|
||||
|
@ -120,7 +120,7 @@ function WebappsActor(aConnection) {
|
||||
promise = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js").Promise;
|
||||
|
||||
// Keep reference of already created app actors.
|
||||
// key: app frame message manager, value: ContentTabActor's grip() value
|
||||
// key: app frame message manager, value: ContentActor's grip() value
|
||||
this._appActorsMap = new Map();
|
||||
|
||||
this.conn = aConnection;
|
||||
@ -789,73 +789,6 @@ WebappsActor.prototype = {
|
||||
});
|
||||
},
|
||||
|
||||
_connectToApp: function (aFrame) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
let mm = aFrame.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader.messageManager;
|
||||
mm.loadFrameScript("resource://gre/modules/devtools/server/child.js", false);
|
||||
|
||||
let childTransport, prefix;
|
||||
|
||||
let onActorCreated = DevToolsUtils.makeInfallible(function (msg) {
|
||||
mm.removeMessageListener("debug:actor", onActorCreated);
|
||||
|
||||
dump("***** Got debug:actor\n");
|
||||
let { actor, appId } = msg.json;
|
||||
prefix = msg.json.prefix;
|
||||
|
||||
// Pipe Debugger message from/to parent/child via the message manager
|
||||
childTransport = new ChildDebuggerTransport(mm, prefix);
|
||||
childTransport.hooks = {
|
||||
onPacket: this.conn.send.bind(this.conn),
|
||||
onClosed: function () {}
|
||||
};
|
||||
childTransport.ready();
|
||||
|
||||
this.conn.setForwarding(prefix, childTransport);
|
||||
|
||||
debug("establishing forwarding for app with prefix " + prefix);
|
||||
|
||||
this._appActorsMap.set(mm, actor);
|
||||
|
||||
deferred.resolve(actor);
|
||||
}).bind(this);
|
||||
mm.addMessageListener("debug:actor", onActorCreated);
|
||||
|
||||
let onMessageManagerDisconnect = DevToolsUtils.makeInfallible(function (subject, topic, data) {
|
||||
if (subject == mm) {
|
||||
Services.obs.removeObserver(onMessageManagerDisconnect, topic);
|
||||
if (childTransport) {
|
||||
// If we have a child transport, the actor has already
|
||||
// been created. We need to stop using this message manager.
|
||||
childTransport.close();
|
||||
this.conn.cancelForwarding(prefix);
|
||||
} else {
|
||||
// Otherwise, the app has been closed before the actor
|
||||
// had a chance to be created, so we are not able to create
|
||||
// the actor.
|
||||
deferred.resolve(null);
|
||||
}
|
||||
let actor = this._appActorsMap.get(mm);
|
||||
if (actor) {
|
||||
// The ContentAppActor within the child process doesn't necessary
|
||||
// have to time to uninitialize itself when the app is closed/killed.
|
||||
// So ensure telling the client that the related actor is detached.
|
||||
this.conn.send({ from: actor.actor,
|
||||
type: "tabDetached" });
|
||||
this._appActorsMap.delete(mm);
|
||||
}
|
||||
}
|
||||
}).bind(this);
|
||||
Services.obs.addObserver(onMessageManagerDisconnect,
|
||||
"message-manager-disconnect", false);
|
||||
|
||||
let prefixStart = this.conn.prefix + "child";
|
||||
mm.sendAsyncMessage("debug:connect", { prefix: prefixStart });
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
getAppActor: function ({ manifestURL }) {
|
||||
debug("getAppActor\n");
|
||||
|
||||
@ -884,13 +817,21 @@ WebappsActor.prototype = {
|
||||
|
||||
// Only create a new actor, if we haven't already
|
||||
// instanciated one for this connection.
|
||||
let map = this._appActorsMap;
|
||||
let mm = appFrame.QueryInterface(Ci.nsIFrameLoaderOwner)
|
||||
.frameLoader
|
||||
.messageManager;
|
||||
let actor = this._appActorsMap.get(mm);
|
||||
let actor = map.get(mm);
|
||||
if (!actor) {
|
||||
return this._connectToApp(appFrame)
|
||||
.then(function (actor) ({ actor: actor }));
|
||||
let onConnect = actor => {
|
||||
map.set(mm, actor);
|
||||
return { actor: actor };
|
||||
};
|
||||
let onDisconnect = mm => {
|
||||
map.delete(mm);
|
||||
};
|
||||
return DebuggerServer.connectToChild(this.conn, mm, onDisconnect)
|
||||
.then(onConnect);
|
||||
}
|
||||
|
||||
return { actor: actor };
|
||||
|
@ -217,6 +217,8 @@ BrowserTabList.prototype.getList = function() {
|
||||
// the actors. Thus, the sequence yielded is always a snapshot of the
|
||||
// actors that were live when we began the iteration.
|
||||
|
||||
let actorPromises = [];
|
||||
|
||||
// Iterate over all navigator:browser XUL windows.
|
||||
for (let win of allAppShellDOMWindows(DebuggerServer.chromeWindowType)) {
|
||||
let selectedBrowser = this._getSelectedBrowser(win);
|
||||
@ -232,10 +234,20 @@ BrowserTabList.prototype.getList = function() {
|
||||
// Do we have an existing actor for this browser? If not, create one.
|
||||
let actor = this._actorByBrowser.get(browser);
|
||||
if (actor) {
|
||||
actorPromises.push(promise.resolve(actor));
|
||||
foundCount++;
|
||||
} else if (browser.isRemoteBrowser) {
|
||||
actor = new RemoteBrowserTabActor(this._connection, browser);
|
||||
this._actorByBrowser.set(browser, actor);
|
||||
let promise = actor.connect().then((form) => {
|
||||
actor._form = form;
|
||||
return actor;
|
||||
});
|
||||
actorPromises.push(promise);
|
||||
} else {
|
||||
actor = new BrowserTabActor(this._connection, browser, win.gBrowser);
|
||||
this._actorByBrowser.set(browser, actor);
|
||||
actorPromises.push(promise.resolve(actor));
|
||||
}
|
||||
|
||||
// Set the 'selected' properties on all actors correctly.
|
||||
@ -249,7 +261,7 @@ BrowserTabList.prototype.getList = function() {
|
||||
this._mustNotify = true;
|
||||
this._checkListening();
|
||||
|
||||
return promise.resolve([actor for ([_, actor] of this._actorByBrowser)]);
|
||||
return promise.all(actorPromises);
|
||||
};
|
||||
|
||||
Object.defineProperty(BrowserTabList.prototype, 'onListChanged', {
|
||||
@ -461,21 +473,22 @@ BrowserTabList.prototype.onCloseWindow = DevToolsUtils.makeInfallible(function(a
|
||||
|
||||
/**
|
||||
* Creates a tab actor for handling requests to a browser tab, like
|
||||
* attaching and detaching. BrowserTabActor respects the actor factories
|
||||
* attaching and detaching. TabActor respects the actor factories
|
||||
* registered with DebuggerServer.addTabActor.
|
||||
*
|
||||
* This class is subclassed by BrowserTabActor and
|
||||
* ContentActor. Subclasses are expected to implement a getter
|
||||
* the docShell properties.
|
||||
*
|
||||
* @param aConnection DebuggerServerConnection
|
||||
* The conection to the client.
|
||||
* @param aBrowser browser
|
||||
* The browser instance that contains this tab.
|
||||
* @param aTabBrowser tabbrowser
|
||||
* The tabbrowser that can receive nsIWebProgressListener events.
|
||||
* @param aChromeEventHandler
|
||||
* An object on which listen for DOMWindowCreated and pageshow events.
|
||||
*/
|
||||
function BrowserTabActor(aConnection, aBrowser, aTabBrowser)
|
||||
function TabActor(aConnection, aChromeEventHandler)
|
||||
{
|
||||
this.conn = aConnection;
|
||||
this._browser = aBrowser;
|
||||
this._tabbrowser = aTabBrowser;
|
||||
this._chromeEventHandler = aChromeEventHandler;
|
||||
this._tabActorPool = null;
|
||||
// A map of actor names to actor instances provided by extensions.
|
||||
this._extraActors = {};
|
||||
@ -483,13 +496,11 @@ function BrowserTabActor(aConnection, aBrowser, aTabBrowser)
|
||||
this._onWindowCreated = this.onWindowCreated.bind(this);
|
||||
}
|
||||
|
||||
// XXX (bug 710213): BrowserTabActor attach/detach/exit/disconnect is a
|
||||
// XXX (bug 710213): TabActor attach/detach/exit/disconnect is a
|
||||
// *complete* mess, needs to be rethought asap.
|
||||
|
||||
BrowserTabActor.prototype = {
|
||||
get browser() { return this._browser; },
|
||||
|
||||
get exited() { return !this.browser; },
|
||||
TabActor.prototype = {
|
||||
get exited() { return !this._chromeEventHandler; },
|
||||
get attached() { return !!this._attached; },
|
||||
|
||||
_tabPool: null,
|
||||
@ -503,21 +514,61 @@ BrowserTabActor.prototype = {
|
||||
// A constant prefix that will be used to form the actor ID by the server.
|
||||
actorPrefix: "tab",
|
||||
|
||||
/**
|
||||
* An object on which listen for DOMWindowCreated and pageshow events.
|
||||
*/
|
||||
get chromeEventHandler() {
|
||||
return this._chromeEventHandler;
|
||||
},
|
||||
|
||||
/**
|
||||
* Getter for the tab's doc shell.
|
||||
*/
|
||||
get docShell() {
|
||||
throw "The docShell getter should be implemented by a subclass of TabActor";
|
||||
},
|
||||
|
||||
/**
|
||||
* Getter for the tab content's DOM window.
|
||||
*/
|
||||
get window() {
|
||||
return this.docShell
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindow);
|
||||
},
|
||||
|
||||
/**
|
||||
* Getter for the nsIWebProgress for watching this window.
|
||||
*/
|
||||
get webProgress() {
|
||||
return this.docShell
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebProgress);
|
||||
},
|
||||
|
||||
/**
|
||||
* Getter for the nsIWebNavigation for the tab.
|
||||
*/
|
||||
get webNavigation() {
|
||||
return this.docShell
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation);
|
||||
},
|
||||
|
||||
/**
|
||||
* Getter for the tab's document.
|
||||
*/
|
||||
get contentDocument() {
|
||||
return this.webNavigation.document;
|
||||
},
|
||||
|
||||
/**
|
||||
* Getter for the tab title.
|
||||
* @return string
|
||||
* Tab title.
|
||||
*/
|
||||
get title() {
|
||||
let title = this.browser.contentTitle;
|
||||
// If contentTitle is empty (e.g. on a not-yet-restored tab), but there is a
|
||||
// tabbrowser (i.e. desktop Firefox, but not Fennec), we can use the label
|
||||
// as the title.
|
||||
if (!title && this._tabbrowser) {
|
||||
title = this._tabbrowser
|
||||
._getTabForContentWindow(this.window).label;
|
||||
}
|
||||
return title;
|
||||
return this.contentDocument.contentTitle;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -526,41 +577,14 @@ BrowserTabActor.prototype = {
|
||||
* Tab URL.
|
||||
*/
|
||||
get url() {
|
||||
if (this.browser.currentURI) {
|
||||
return this.browser.currentURI.spec;
|
||||
if (this.webNavigation.currentURI) {
|
||||
return this.webNavigation.currentURI.spec;
|
||||
}
|
||||
// Abrupt closing of the browser window may leave callbacks without a
|
||||
// currentURI.
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Getter for the tab content window, will be used by child actors to target
|
||||
* the right window.
|
||||
* @return nsIDOMWindow
|
||||
* Tab content window.
|
||||
*/
|
||||
get window() {
|
||||
if (this.browser instanceof Ci.nsIDOMWindow) {
|
||||
return this.browser;
|
||||
} else if (this.browser instanceof Ci.nsIDOMElement) {
|
||||
return this.browser.contentWindow;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Getter for the best nsIWebProgress for to watching this window.
|
||||
*/
|
||||
get webProgress() {
|
||||
return this.window
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDocShell)
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebProgress);
|
||||
},
|
||||
|
||||
form: function BTA_form() {
|
||||
dbg_assert(!this.exited,
|
||||
"grip() shouldn't be called on exited browser actor.");
|
||||
@ -611,8 +635,7 @@ BrowserTabActor.prototype = {
|
||||
type: "tabDetached" });
|
||||
}
|
||||
|
||||
this._browser = null;
|
||||
this._tabbrowser = null;
|
||||
this._chromeEventHandler = null;
|
||||
},
|
||||
|
||||
/* Support for DebuggerServer.addTabActor. */
|
||||
@ -636,11 +659,9 @@ BrowserTabActor.prototype = {
|
||||
this._pushContext();
|
||||
|
||||
// Watch for globals being created in this tab.
|
||||
this.browser.addEventListener("DOMWindowCreated", this._onWindowCreated, true);
|
||||
this.browser.addEventListener("pageshow", this._onWindowCreated, true);
|
||||
if (this._tabbrowser) {
|
||||
this._progressListener = new DebuggerProgressListener(this);
|
||||
}
|
||||
this.chromeEventHandler.addEventListener("DOMWindowCreated", this._onWindowCreated, true);
|
||||
this.chromeEventHandler.addEventListener("pageshow", this._onWindowCreated, true);
|
||||
this._progressListener = new DebuggerProgressListener(this);
|
||||
|
||||
this._attached = true;
|
||||
},
|
||||
@ -682,12 +703,10 @@ BrowserTabActor.prototype = {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this._progressListener) {
|
||||
this._progressListener.destroy();
|
||||
}
|
||||
this._progressListener.destroy();
|
||||
|
||||
this.browser.removeEventListener("DOMWindowCreated", this._onWindowCreated, true);
|
||||
this.browser.removeEventListener("pageshow", this._onWindowCreated, true);
|
||||
this.chromeEventHandler.removeEventListener("DOMWindowCreated", this._onWindowCreated, true);
|
||||
this.chromeEventHandler.removeEventListener("pageshow", this._onWindowCreated, true);
|
||||
|
||||
this._popContext();
|
||||
|
||||
@ -736,7 +755,7 @@ BrowserTabActor.prototype = {
|
||||
// subsequent navigation event packet.
|
||||
Services.tm.currentThread.dispatch(DevToolsUtils.makeInfallible(() => {
|
||||
this.window.location.reload();
|
||||
}, "BrowserTabActor.prototype.onReload's delayed body"), 0);
|
||||
}, "TabActor.prototype.onReload's delayed body"), 0);
|
||||
return {};
|
||||
},
|
||||
|
||||
@ -748,7 +767,7 @@ BrowserTabActor.prototype = {
|
||||
// subsequent navigation event packet.
|
||||
Services.tm.currentThread.dispatch(DevToolsUtils.makeInfallible(() => {
|
||||
this.window.location = aRequest.url;
|
||||
}, "BrowserTabActor.prototype.onNavigateTo's delayed body"), 0);
|
||||
}, "TabActor.prototype.onNavigateTo's delayed body"), 0);
|
||||
return {};
|
||||
},
|
||||
|
||||
@ -798,12 +817,8 @@ BrowserTabActor.prototype = {
|
||||
let enable = Ci.nsIRequest.LOAD_NORMAL;
|
||||
let disable = Ci.nsIRequest.LOAD_BYPASS_CACHE |
|
||||
Ci.nsIRequest.INHIBIT_CACHING;
|
||||
if (this.window) {
|
||||
let docShell = this.window
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDocShell);
|
||||
|
||||
docShell.defaultLoadFlags = allow ? enable : disable;
|
||||
if (this.docShell) {
|
||||
this.docShell.defaultLoadFlags = allow ? enable : disable;
|
||||
}
|
||||
},
|
||||
|
||||
@ -811,12 +826,8 @@ BrowserTabActor.prototype = {
|
||||
* Disable or enable JS via docShell.
|
||||
*/
|
||||
_setJavascriptEnabled: function(allow) {
|
||||
if (this.window) {
|
||||
let docShell = this.window
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDocShell);
|
||||
|
||||
docShell.allowJavascript = allow;
|
||||
if (this.docShell) {
|
||||
this.docShell.allowJavascript = allow;
|
||||
}
|
||||
},
|
||||
|
||||
@ -824,34 +835,26 @@ BrowserTabActor.prototype = {
|
||||
* Return cache allowed status.
|
||||
*/
|
||||
_getCacheEnabled: function() {
|
||||
if (!this.window) {
|
||||
if (!this.docShell) {
|
||||
// The tab is already closed.
|
||||
return null;
|
||||
}
|
||||
|
||||
let disable = Ci.nsIRequest.LOAD_BYPASS_CACHE |
|
||||
Ci.nsIRequest.INHIBIT_CACHING;
|
||||
let docShell = this.window
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDocShell);
|
||||
|
||||
return docShell.defaultLoadFlags !== disable;
|
||||
return this.docShell.defaultLoadFlags !== disable;
|
||||
},
|
||||
|
||||
/**
|
||||
* Return JS allowed status.
|
||||
*/
|
||||
_getJavascriptEnabled: function() {
|
||||
if (!this.window) {
|
||||
if (!this.docShell) {
|
||||
// The tab is already closed.
|
||||
return null;
|
||||
}
|
||||
|
||||
let docShell = this.window
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDocShell);
|
||||
|
||||
return docShell.allowJavascript;
|
||||
return this.docShell.allowJavascript;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -900,7 +903,7 @@ BrowserTabActor.prototype = {
|
||||
if (!this._attached || (evt.type == "pageshow" && !evt.persisted)) {
|
||||
return;
|
||||
}
|
||||
if (evt.target === this.browser.contentDocument ) {
|
||||
if (evt.target === this.contentDocument) {
|
||||
this.threadActor.clearDebuggees();
|
||||
if (this.threadActor.dbg) {
|
||||
this.threadActor.dbg.enabled = true;
|
||||
@ -914,7 +917,7 @@ BrowserTabActor.prototype = {
|
||||
if (this.threadActor.attached) {
|
||||
this.threadActor.findGlobals();
|
||||
}
|
||||
}, "BrowserTabActor.prototype.onWindowCreated"),
|
||||
}, "TabActor.prototype.onWindowCreated"),
|
||||
|
||||
/**
|
||||
* Tells if the window.console object is native or overwritten by script in
|
||||
@ -935,12 +938,100 @@ BrowserTabActor.prototype = {
|
||||
/**
|
||||
* The request types this actor can handle.
|
||||
*/
|
||||
BrowserTabActor.prototype.requestTypes = {
|
||||
"attach": BrowserTabActor.prototype.onAttach,
|
||||
"detach": BrowserTabActor.prototype.onDetach,
|
||||
"reload": BrowserTabActor.prototype.onReload,
|
||||
"navigateTo": BrowserTabActor.prototype.onNavigateTo,
|
||||
"reconfigure": BrowserTabActor.prototype.onReconfigure
|
||||
TabActor.prototype.requestTypes = {
|
||||
"attach": TabActor.prototype.onAttach,
|
||||
"detach": TabActor.prototype.onDetach,
|
||||
"reload": TabActor.prototype.onReload,
|
||||
"navigateTo": TabActor.prototype.onNavigateTo,
|
||||
"reconfigure": TabActor.prototype.onReconfigure
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a tab actor for handling requests to a single in-process
|
||||
* <browser> tab. Most of the implementation comes from TabActor.
|
||||
*
|
||||
* @param aConnection DebuggerServerConnection
|
||||
* The conection to the client.
|
||||
* @param aBrowser browser
|
||||
* The browser instance that contains this tab.
|
||||
* @param aTabBrowser tabbrowser
|
||||
* The tabbrowser that can receive nsIWebProgressListener events.
|
||||
*/
|
||||
function BrowserTabActor(aConnection, aBrowser, aTabBrowser)
|
||||
{
|
||||
TabActor.call(this, aConnection, aBrowser);
|
||||
this._browser = aBrowser;
|
||||
this._tabbrowser = aTabBrowser;
|
||||
}
|
||||
|
||||
BrowserTabActor.prototype = Object.create(TabActor.prototype);
|
||||
|
||||
BrowserTabActor.prototype.constructor = BrowserTabActor;
|
||||
|
||||
Object.defineProperty(BrowserTabActor.prototype, "docShell", {
|
||||
get: function() {
|
||||
return this._browser.docShell;
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: false
|
||||
});
|
||||
|
||||
Object.defineProperty(BrowserTabActor.prototype, "title", {
|
||||
get: function() {
|
||||
let title = this.contentDocument.contentTitle;
|
||||
// If contentTitle is empty (e.g. on a not-yet-restored tab), but there is a
|
||||
// tabbrowser (i.e. desktop Firefox, but not Fennec), we can use the label
|
||||
// as the title.
|
||||
if (!title && this._tabbrowser) {
|
||||
title = this._tabbrowser._getTabForContentWindow(this.window).label;
|
||||
}
|
||||
return title;
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: false
|
||||
});
|
||||
|
||||
Object.defineProperty(BrowserTabActor.prototype, "browser", {
|
||||
get: function() {
|
||||
return this._browser;
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: false
|
||||
});
|
||||
|
||||
BrowserTabActor.prototype.exit = function() {
|
||||
TabActor.prototype.exit.call(this);
|
||||
this._browser = null;
|
||||
this._tabbrowser = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* This actor is a shim that connects to a ContentActor in a remote
|
||||
* browser process. All RDP packets get forwarded using the message
|
||||
* manager.
|
||||
*
|
||||
* @param aConnection The main RDP connection.
|
||||
* @param aBrowser XUL <browser> element to connect to.
|
||||
*/
|
||||
function RemoteBrowserTabActor(aConnection, aBrowser)
|
||||
{
|
||||
this._conn = aConnection;
|
||||
this._browser = aBrowser;
|
||||
this._form = null;
|
||||
}
|
||||
|
||||
RemoteBrowserTabActor.prototype = {
|
||||
connect: function() {
|
||||
return DebuggerServer.connectToChild(this._conn, this._browser.messageManager);
|
||||
},
|
||||
|
||||
form: function() {
|
||||
return this._form;
|
||||
},
|
||||
|
||||
exit: function() {
|
||||
this._browser = null;
|
||||
},
|
||||
};
|
||||
|
||||
function BrowserAddonList(aConnection)
|
||||
@ -993,12 +1084,18 @@ BrowserAddonList.prototype.onUninstalled = function (aAddon) {
|
||||
function BrowserAddonActor(aConnection, aAddon) {
|
||||
this.conn = aConnection;
|
||||
this._addon = aAddon;
|
||||
this._contextPool = null;
|
||||
this._threadActor = null;
|
||||
AddonManager.addAddonListener(this);
|
||||
}
|
||||
|
||||
BrowserAddonActor.prototype = {
|
||||
actorPrefix: "addon",
|
||||
|
||||
get exited() {
|
||||
return !this._addon;
|
||||
},
|
||||
|
||||
get id() {
|
||||
return this._addon.id;
|
||||
},
|
||||
@ -1007,6 +1104,10 @@ BrowserAddonActor.prototype = {
|
||||
return this._addon.sourceURI ? this._addon.sourceURI.spec : undefined;
|
||||
},
|
||||
|
||||
get attached() {
|
||||
return this._threadActor;
|
||||
},
|
||||
|
||||
form: function BAA_form() {
|
||||
dbg_assert(this.actorID, "addon should have an actorID.");
|
||||
|
||||
@ -1024,9 +1125,72 @@ BrowserAddonActor.prototype = {
|
||||
onUninstalled: function BAA_onUninstalled(aAddon) {
|
||||
if (aAddon != this._addon)
|
||||
return;
|
||||
|
||||
if (this.attached) {
|
||||
this.onDetach();
|
||||
this.conn.send({ from: this.actorID, type: "tabDetached" });
|
||||
}
|
||||
|
||||
this._addon = null;
|
||||
AddonManager.removeAddonListener(this);
|
||||
},
|
||||
|
||||
onAttach: function BAA_onAttach() {
|
||||
if (this.exited) {
|
||||
return { type: "exited" };
|
||||
}
|
||||
|
||||
if (!this.attached) {
|
||||
this._contextPool = new ActorPool(this.conn);
|
||||
this.conn.addActorPool(this._contextPool);
|
||||
|
||||
this._threadActor = new AddonThreadActor(this.conn, this,
|
||||
this._addon.id);
|
||||
this._contextPool.addActor(this._threadActor);
|
||||
}
|
||||
|
||||
return { type: "tabAttached", threadActor: this._threadActor.actorID };
|
||||
},
|
||||
|
||||
onDetach: function BAA_onDetach() {
|
||||
if (!this.attached) {
|
||||
return { error: "wrongState" };
|
||||
}
|
||||
|
||||
this.conn.removeActorPool(this._contextPool);
|
||||
this._contextPool = null;
|
||||
|
||||
this._threadActor = null;
|
||||
|
||||
return { type: "detached" };
|
||||
},
|
||||
|
||||
preNest: function() {
|
||||
let e = Services.wm.getEnumerator(null);
|
||||
while (e.hasMoreElements()) {
|
||||
let win = e.getNext();
|
||||
let windowUtils = win.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
windowUtils.suppressEventHandling(true);
|
||||
windowUtils.suspendTimeouts();
|
||||
}
|
||||
},
|
||||
|
||||
postNest: function() {
|
||||
let e = Services.wm.getEnumerator(null);
|
||||
while (e.hasMoreElements()) {
|
||||
let win = e.getNext();
|
||||
let windowUtils = win.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
windowUtils.resumeTimeouts();
|
||||
windowUtils.suppressEventHandling(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BrowserAddonActor.prototype.requestTypes = {
|
||||
"attach": BrowserAddonActor.prototype.onAttach,
|
||||
"detach": BrowserAddonActor.prototype.onDetach
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1035,17 +1199,23 @@ BrowserAddonActor.prototype = {
|
||||
* navigate away from a paused page, the listener makes sure that the debuggee
|
||||
* is resumed before the navigation begins.
|
||||
*
|
||||
* @param BrowserTabActor aBrowserTabActor
|
||||
* @param TabActor aTabActor
|
||||
* The tab actor associated with this listener.
|
||||
*/
|
||||
function DebuggerProgressListener(aBrowserTabActor) {
|
||||
this._tabActor = aBrowserTabActor;
|
||||
this._tabActor._tabbrowser.addProgressListener(this);
|
||||
function DebuggerProgressListener(aTabActor) {
|
||||
this._tabActor = aTabActor;
|
||||
this._tabActor.webProgress.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_STATE_ALL);
|
||||
let EventEmitter = devtools.require("devtools/toolkit/event-emitter");
|
||||
EventEmitter.decorate(this);
|
||||
}
|
||||
|
||||
DebuggerProgressListener.prototype = {
|
||||
QueryInterface: XPCOMUtils.generateQI([
|
||||
Ci.nsIWebProgressListener,
|
||||
Ci.nsISupportsWeakReference,
|
||||
Ci.nsISupports,
|
||||
]),
|
||||
|
||||
onStateChange:
|
||||
DevToolsUtils.makeInfallible(function DPL_onStateChange(aProgress, aRequest, aFlag, aStatus) {
|
||||
let isStart = aFlag & Ci.nsIWebProgressListener.STATE_START;
|
||||
@ -1103,13 +1273,12 @@ DebuggerProgressListener.prototype = {
|
||||
* Destroy the progress listener instance.
|
||||
*/
|
||||
destroy: function DPL_destroy() {
|
||||
if (this._tabActor._tabbrowser.removeProgressListener) {
|
||||
try {
|
||||
this._tabActor._tabbrowser.removeProgressListener(this);
|
||||
} catch (ex) {
|
||||
// This can throw during browser shutdown.
|
||||
}
|
||||
try {
|
||||
this._tabActor.webProgress.removeProgressListener(this);
|
||||
} catch (ex) {
|
||||
// This can throw during browser shutdown.
|
||||
}
|
||||
|
||||
this._tabActor._progressListener = null;
|
||||
this._tabActor = null;
|
||||
}
|
||||
|
@ -4,6 +4,8 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
let chromeGlobal = this;
|
||||
|
||||
// Encapsulate in its own scope to allows loading this frame script
|
||||
// more than once.
|
||||
(function () {
|
||||
@ -26,18 +28,14 @@
|
||||
|
||||
let mm = msg.target;
|
||||
|
||||
let prefix = msg.data.prefix + docShell.appId;
|
||||
let conn = DebuggerServer.connectToParent(msg.data.prefix, mm);
|
||||
|
||||
let conn = DebuggerServer.connectToParent(prefix, mm);
|
||||
|
||||
let actor = new DebuggerServer.ContentAppActor(conn, content);
|
||||
let actor = new DebuggerServer.ContentActor(conn, chromeGlobal);
|
||||
let actorPool = new ActorPool(conn);
|
||||
actorPool.addActor(actor);
|
||||
conn.addActorPool(actorPool);
|
||||
|
||||
sendAsyncMessage("debug:actor", {actor: actor.grip(),
|
||||
appId: docShell.appId,
|
||||
prefix: prefix});
|
||||
sendAsyncMessage("debug:actor", {actor: actor.grip()});
|
||||
});
|
||||
|
||||
addMessageListener("debug:connect", onConnect);
|
||||
|
@ -38,6 +38,7 @@ Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
let wantLogging = Services.prefs.getBoolPref("devtools.debugger.log");
|
||||
|
||||
Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js");
|
||||
Cu.import("resource://gre/modules/jsdebugger.jsm");
|
||||
addDebuggerToGlobal(this);
|
||||
|
||||
@ -375,7 +376,7 @@ var DebuggerServer = {
|
||||
if (!("BrowserTabActor" in this)) {
|
||||
this.addActors("resource://gre/modules/devtools/server/actors/webbrowser.js");
|
||||
}
|
||||
if (!("ContentAppActor" in this)) {
|
||||
if (!("ContentActor" in this)) {
|
||||
this.addActors("resource://gre/modules/devtools/server/actors/childtab.js");
|
||||
}
|
||||
},
|
||||
@ -524,6 +525,71 @@ var DebuggerServer = {
|
||||
return this._onConnection(transport, aPrefix, true);
|
||||
},
|
||||
|
||||
connectToChild: function(aConnection, aMessageManager, aOnDisconnect) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
let mm = aMessageManager;
|
||||
mm.loadFrameScript("resource://gre/modules/devtools/server/child.js", false);
|
||||
|
||||
let actor, childTransport;
|
||||
let prefix = aConnection.allocID("child");
|
||||
|
||||
let onActorCreated = DevToolsUtils.makeInfallible(function (msg) {
|
||||
mm.removeMessageListener("debug:actor", onActorCreated);
|
||||
|
||||
// Pipe Debugger message from/to parent/child via the message manager
|
||||
childTransport = new ChildDebuggerTransport(mm, prefix);
|
||||
childTransport.hooks = {
|
||||
onPacket: aConnection.send.bind(aConnection),
|
||||
onClosed: function () {}
|
||||
};
|
||||
childTransport.ready();
|
||||
|
||||
aConnection.setForwarding(prefix, childTransport);
|
||||
|
||||
dumpn("establishing forwarding for app with prefix " + prefix);
|
||||
|
||||
actor = msg.json.actor;
|
||||
|
||||
deferred.resolve(actor);
|
||||
}).bind(this);
|
||||
mm.addMessageListener("debug:actor", onActorCreated);
|
||||
|
||||
let onMessageManagerDisconnect = DevToolsUtils.makeInfallible(function (subject, topic, data) {
|
||||
if (subject == mm) {
|
||||
Services.obs.removeObserver(onMessageManagerDisconnect, topic);
|
||||
if (childTransport) {
|
||||
// If we have a child transport, the actor has already
|
||||
// been created. We need to stop using this message manager.
|
||||
childTransport.close();
|
||||
aConnection.cancelForwarding(prefix);
|
||||
} else {
|
||||
// Otherwise, the app has been closed before the actor
|
||||
// had a chance to be created, so we are not able to create
|
||||
// the actor.
|
||||
deferred.resolve(null);
|
||||
}
|
||||
if (actor) {
|
||||
// The ContentActor within the child process doesn't necessary
|
||||
// have to time to uninitialize itself when the app is closed/killed.
|
||||
// So ensure telling the client that the related actor is detached.
|
||||
aConnection.send({ from: actor.actor, type: "tabDetached" });
|
||||
actor = null;
|
||||
}
|
||||
|
||||
if (aOnDisconnect) {
|
||||
aOnDisconnect(mm);
|
||||
}
|
||||
}
|
||||
}).bind(this);
|
||||
Services.obs.addObserver(onMessageManagerDisconnect,
|
||||
"message-manager-disconnect", false);
|
||||
|
||||
mm.sendAsyncMessage("debug:connect", { prefix: prefix });
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
// nsIServerSocketListener implementation
|
||||
|
||||
onSocketAccepted:
|
||||
|
@ -30,3 +30,7 @@ localhost=(local files)
|
||||
# %2$S is the file size unit
|
||||
backupFileSizeText=%1$S %2$S
|
||||
|
||||
# LOCALIZATION NOTE (windows8TouchTitle): this is the name of the folder used
|
||||
# to store bookmarks created in Metro mode and share bookmarks between Metro
|
||||
# and Desktop.
|
||||
windows8TouchTitle=Windows 8 Touch
|
Loading…
Reference in New Issue
Block a user