зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to mozilla-inbound
This commit is contained in:
Коммит
57df885881
|
@ -19,7 +19,7 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="d2cfef555dabab415085e548ed44c48a99be5c32"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="908f94fda04462001ece86e6b6c15ad8b05f7526"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="531cf670e485649c69746e46d567929fcd54cbc5"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="8e4420c0c5c8e8c8e58a000278a7129403769f96"/>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</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="d2cfef555dabab415085e548ed44c48a99be5c32"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="908f94fda04462001ece86e6b6c15ad8b05f7526"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="531cf670e485649c69746e46d567929fcd54cbc5"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="994fa9a1f7ce0e63c880a48d571c3ab3e01884a3"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="276ce45e78b09c4a4ee643646f691d22804754c1">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="d2cfef555dabab415085e548ed44c48a99be5c32"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="908f94fda04462001ece86e6b6c15ad8b05f7526"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="531cf670e485649c69746e46d567929fcd54cbc5"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="d2cfef555dabab415085e548ed44c48a99be5c32"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="908f94fda04462001ece86e6b6c15ad8b05f7526"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="531cf670e485649c69746e46d567929fcd54cbc5"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="8e4420c0c5c8e8c8e58a000278a7129403769f96"/>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</project>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="d2cfef555dabab415085e548ed44c48a99be5c32"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="908f94fda04462001ece86e6b6c15ad8b05f7526"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="531cf670e485649c69746e46d567929fcd54cbc5"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="994fa9a1f7ce0e63c880a48d571c3ab3e01884a3"/>
|
||||
|
|
|
@ -4,6 +4,6 @@
|
|||
"remote": "",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "7f0af1e164a39efb732c0c341c2a8e51f681d913",
|
||||
"revision": "c3d40600c0090c5ca6ca4427f3a870ff443a109d",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
|
|
@ -17,7 +17,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="d2cfef555dabab415085e548ed44c48a99be5c32"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="908f94fda04462001ece86e6b6c15ad8b05f7526"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="531cf670e485649c69746e46d567929fcd54cbc5"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
|
|
@ -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="d2cfef555dabab415085e548ed44c48a99be5c32"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="908f94fda04462001ece86e6b6c15ad8b05f7526"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="531cf670e485649c69746e46d567929fcd54cbc5"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</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="d2cfef555dabab415085e548ed44c48a99be5c32"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="908f94fda04462001ece86e6b6c15ad8b05f7526"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="531cf670e485649c69746e46d567929fcd54cbc5"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="994fa9a1f7ce0e63c880a48d571c3ab3e01884a3"/>
|
||||
|
|
|
@ -17,7 +17,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="d2cfef555dabab415085e548ed44c48a99be5c32"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="908f94fda04462001ece86e6b6c15ad8b05f7526"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="531cf670e485649c69746e46d567929fcd54cbc5"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
|
|
@ -45,6 +45,7 @@ svg|line.box-model-guide-bottom[hidden] {
|
|||
html|*.highlighter-nodeinfobar-id,
|
||||
html|*.highlighter-nodeinfobar-classes,
|
||||
html|*.highlighter-nodeinfobar-pseudo-classes,
|
||||
html|*.highlighter-nodeinfobar-dimensions,
|
||||
html|*.highlighter-nodeinfobar-tagname {
|
||||
-moz-user-select: text;
|
||||
-moz-user-focus: normal;
|
||||
|
|
|
@ -210,7 +210,19 @@ let gPage = {
|
|||
for (let site of gGrid.sites) {
|
||||
if (site) {
|
||||
site.captureIfMissing();
|
||||
let {type} = site.link;
|
||||
|
||||
// Record which tile index a directory link was shown
|
||||
let {directoryIndex, type} = site.link;
|
||||
if (directoryIndex !== undefined) {
|
||||
let tileIndex = site.cell.index;
|
||||
// For telemetry, only handle the first 9 links in the first 9 cells
|
||||
if (directoryIndex < 9) {
|
||||
let shownId = "NEWTAB_PAGE_DIRECTORY_LINK" + directoryIndex + "_SHOWN";
|
||||
Services.telemetry.getHistogramById(shownId).add(Math.min(9, tileIndex));
|
||||
}
|
||||
}
|
||||
|
||||
// Aggregate tile impression counts into directory types
|
||||
if (type in directoryCount) {
|
||||
directoryCount[type]++;
|
||||
}
|
||||
|
|
|
@ -12,6 +12,70 @@ function test() {
|
|||
}
|
||||
|
||||
function spawnTest() {
|
||||
let lines = [
|
||||
'Manifest has a character encoding of ISO-8859-1. Manifests must have the ' +
|
||||
'utf-8 character encoding.',
|
||||
'The first line of the manifest must be "CACHE MANIFEST" at line 1.',
|
||||
'"CACHE MANIFEST" is only valid on the first line but was found at line 3.',
|
||||
'images/sound-icon.png points to a resource that is not available at line 9.',
|
||||
'images/background.png points to a resource that is not available at line 10.',
|
||||
'/checking.cgi points to a resource that is not available at line 13.',
|
||||
'Asterisk (*) incorrectly used in the NETWORK section at line 14. If a line ' +
|
||||
'in the NETWORK section contains only a single asterisk character, then any ' +
|
||||
'URI not listed in the manifest will be treated as if the URI was listed in ' +
|
||||
'the NETWORK section. Otherwise such URIs will be treated as unavailable. ' +
|
||||
'Other uses of the * character are prohibited',
|
||||
'../rel.html points to a resource that is not available at line 17.',
|
||||
'../../rel.html points to a resource that is not available at line 18.',
|
||||
'../../../rel.html points to a resource that is not available at line 19.',
|
||||
'../../../../rel.html points to a resource that is not available at line 20.',
|
||||
'../../../../../rel.html points to a resource that is not available at line 21.',
|
||||
'/../ is not a valid URI prefix at line 22.',
|
||||
'/test.css points to a resource that is not available at line 23.',
|
||||
'/test.js points to a resource that is not available at line 24.',
|
||||
'test.png points to a resource that is not available at line 25.',
|
||||
'/main/features.js points to a resource that is not available at line 27.',
|
||||
'/main/settings/index.css points to a resource that is not available at line 28.',
|
||||
'http://example.com/scene.jpg points to a resource that is not available at line 29.',
|
||||
'/section1/blockedbyfallback.html points to a resource that is not available at line 30.',
|
||||
'http://example.com/images/world.jpg points to a resource that is not available at line 31.',
|
||||
'/section2/blockedbyfallback.html points to a resource that is not available at line 32.',
|
||||
'/main/home points to a resource that is not available at line 34.',
|
||||
'main/app.js points to a resource that is not available at line 35.',
|
||||
'/settings/home points to a resource that is not available at line 37.',
|
||||
'/settings/app.js points to a resource that is not available at line 38.',
|
||||
'The file http://sub1.test1.example.com/browser/browser/devtools/' +
|
||||
'commandline/test/browser_cmd_appcache_invalid_page3.html was modified ' +
|
||||
'after http://sub1.test1.example.com/browser/browser/devtools/' +
|
||||
'commandline/test/browser_cmd_appcache_invalid_appcache.appcache. Unless ' +
|
||||
'the text in the manifest file is changed the cached version will be used ' +
|
||||
'instead at line 39.',
|
||||
'browser_cmd_appcache_invalid_page3.html has cache-control set to no-store. ' +
|
||||
'This will prevent the application cache from storing the file at line 39.',
|
||||
'http://example.com/logo.png points to a resource that is not available at line 40.',
|
||||
'http://example.com/check.png points to a resource that is not available at line 41.',
|
||||
'Spaces in URIs need to be replaced with % at line 42.',
|
||||
'http://example.com/cr oss.png points to a resource that is not available at line 42.',
|
||||
'Asterisk (*) incorrectly used in the CACHE section at line 43. If a line ' +
|
||||
'in the NETWORK section contains only a single asterisk character, then ' +
|
||||
'any URI not listed in the manifest will be treated as if the URI was ' +
|
||||
'listed in the NETWORK section. Otherwise such URIs will be treated as ' +
|
||||
'unavailable. Other uses of the * character are prohibited',
|
||||
'The SETTINGS section may only contain a single value, "prefer-online" or "fast" at line 47.',
|
||||
'FALLBACK section line 50 (/section1/ /offline1.html) prevents caching of ' +
|
||||
'line 30 (/section1/blockedbyfallback.html) in the CACHE section.',
|
||||
'/offline1.html points to a resource that is not available at line 50.',
|
||||
'FALLBACK section line 51 (/section2/ offline2.html) prevents caching of ' +
|
||||
'line 32 (/section2/blockedbyfallback.html) in the CACHE section.',
|
||||
'offline2.html points to a resource that is not available at line 51.',
|
||||
'Only two URIs separated by spaces are allowed in the FALLBACK section at line 52.',
|
||||
'Asterisk (*) incorrectly used in the FALLBACK section at line 53. URIs ' +
|
||||
'in the FALLBACK section simply need to match a prefix of the request URI.',
|
||||
'offline3.html points to a resource that is not available at line 53.',
|
||||
'Invalid section name (BLAH) at line 55.',
|
||||
'Only two URIs separated by spaces are allowed in the FALLBACK section at line 55.'
|
||||
];
|
||||
|
||||
let options = yield helpers.openTab(TEST_URI);
|
||||
info("window open");
|
||||
|
||||
|
@ -25,7 +89,8 @@ function spawnTest() {
|
|||
// Pages containing an appcache the notification bar gives options to allow
|
||||
// or deny permission for the app to save data offline. Let's click Allow.
|
||||
let notificationID = "offline-app-requested-sub1.test1.example.com";
|
||||
let notification = PopupNotifications.getNotification(notificationID, gBrowser.selectedBrowser);
|
||||
let notification =
|
||||
PopupNotifications.getNotification(notificationID, gBrowser.selectedBrowser);
|
||||
|
||||
if (notification) {
|
||||
info("Authorizing offline storage.");
|
||||
|
@ -45,52 +110,7 @@ function spawnTest() {
|
|||
args: {}
|
||||
},
|
||||
exec: {
|
||||
output: [
|
||||
/Manifest has a character encoding of ISO-8859-1\. Manifests must have the utf-8 character encoding\./,
|
||||
/The first line of the manifest must be "CACHE MANIFEST" at line 1\./,
|
||||
/"CACHE MANIFEST" is only valid on the first line but was found at line 3\./,
|
||||
/images\/sound-icon\.png points to a resource that is not available at line 9\./,
|
||||
/images\/background\.png points to a resource that is not available at line 10\./,
|
||||
/NETWORK section line 13 \(\/checking\.cgi\) prevents caching of line 13 \(\/checking\.cgi\) in the NETWORK section\./,
|
||||
/\/checking\.cgi points to a resource that is not available at line 13\./,
|
||||
/Asterisk \(\*\) incorrectly used in the NETWORK section at line 14\. If a line in the NETWORK section contains only a single asterisk character, then any URI not listed in the manifest will be treated as if the URI was listed in the NETWORK section\. Otherwise such URIs will be treated as unavailable\. Other uses of the \* character are prohibited/,
|
||||
/\.\.\/rel\.html points to a resource that is not available at line 17\./,
|
||||
/\.\.\/\.\.\/rel\.html points to a resource that is not available at line 18\./,
|
||||
/\.\.\/\.\.\/\.\.\/rel\.html points to a resource that is not available at line 19\./,
|
||||
/\.\.\/\.\.\/\.\.\/\.\.\/rel\.html points to a resource that is not available at line 20\./,
|
||||
/\.\.\/\.\.\/\.\.\/\.\.\/\.\.\/rel\.html points to a resource that is not available at line 21\./,
|
||||
/\/\.\.\/ is not a valid URI prefix at line 22\./,
|
||||
/\/test\.css points to a resource that is not available at line 23\./,
|
||||
/\/test\.js points to a resource that is not available at line 24\./,
|
||||
/test\.png points to a resource that is not available at line 25\./,
|
||||
/\/main\/features\.js points to a resource that is not available at line 27\./,
|
||||
/\/main\/settings\/index\.css points to a resource that is not available at line 28\./,
|
||||
/http:\/\/example\.com\/scene\.jpg points to a resource that is not available at line 29\./,
|
||||
/\/section1\/blockedbyfallback\.html points to a resource that is not available at line 30\./,
|
||||
/http:\/\/example\.com\/images\/world\.jpg points to a resource that is not available at line 31\./,
|
||||
/\/section2\/blockedbyfallback\.html points to a resource that is not available at line 32\./,
|
||||
/\/main\/home points to a resource that is not available at line 34\./,
|
||||
/main\/app\.js points to a resource that is not available at line 35\./,
|
||||
/\/settings\/home points to a resource that is not available at line 37\./,
|
||||
/\/settings\/app\.js points to a resource that is not available at line 38\./,
|
||||
/The file http:\/\/sub1\.test1\.example\.com\/browser\/browser\/devtools\/commandline\/test\/browser_cmd_appcache_invalid_page3\.html was modified after http:\/\/sub1\.test1\.example\.com\/browser\/browser\/devtools\/commandline\/test\/browser_cmd_appcache_invalid_appcache\.appcache\. Unless the text in the manifest file is changed the cached version will be used instead at line 39\./,
|
||||
/browser_cmd_appcache_invalid_page3\.html has cache-control set to no-store\. This will prevent the application cache from storing the file at line 39\./,
|
||||
/http:\/\/example\.com\/logo\.png points to a resource that is not available at line 40\./,
|
||||
/http:\/\/example\.com\/check\.png points to a resource that is not available at line 41\./,
|
||||
/Spaces in URIs need to be replaced with % at line 42\./,
|
||||
/http:\/\/example\.com\/cr oss\.png points to a resource that is not available at line 42\./,
|
||||
/Asterisk \(\*\) incorrectly used in the CACHE section at line 43\. If a line in the NETWORK section contains only a single asterisk character, then any URI not listed in the manifest will be treated as if the URI was listed in the NETWORK section\. Otherwise such URIs will be treated as unavailable\. Other uses of the \* character are prohibited/,
|
||||
/The SETTINGS section may only contain a single value, "prefer-online" or "fast" at line 47\./,
|
||||
/FALLBACK section line 50 \(\/section1\/ \/offline1\.html\) prevents caching of line 30 \(\/section1\/blockedbyfallback\.html\) in the CACHE section\./,
|
||||
/\/offline1\.html points to a resource that is not available at line 50\./,
|
||||
/FALLBACK section line 51 \(\/section2\/ offline2\.html\) prevents caching of line 32 \(\/section2\/blockedbyfallback\.html\) in the CACHE section\./,
|
||||
/offline2\.html points to a resource that is not available at line 51\./,
|
||||
/Only two URIs separated by spaces are allowed in the FALLBACK section at line 52\./,
|
||||
/Asterisk \(\*\) incorrectly used in the FALLBACK section at line 53\. URIs in the FALLBACK section simply need to match a prefix of the request URI\./,
|
||||
/offline3\.html points to a resource that is not available at line 53\./,
|
||||
/Invalid section name \(BLAH\) at line 55\./,
|
||||
/Only two URIs separated by spaces are allowed in the FALLBACK section at line 55\./
|
||||
]
|
||||
output: lines.map(getRegexForString)
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
|
|
@ -27,6 +27,20 @@ function whenDelayedStartupFinished(aWindow, aCallback) {
|
|||
}, "browser-delayed-startup-finished", false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a regular expression that matches a string. This greatly simplifies
|
||||
* matching and debugging long strings.
|
||||
*
|
||||
* @param {String} text
|
||||
* Text to convert
|
||||
* @return {RegExp}
|
||||
* Regular expression matching text
|
||||
*/
|
||||
function getRegexForString(str) {
|
||||
str = str.replace(/(\.|\\|\/|\(|\)|\[|\]|\*|\+|\?|\$|\^|\|)/g, "\\$1");
|
||||
return new RegExp(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Force GC on shutdown, because it seems that GCLI can outrun the garbage
|
||||
* collector in some situations, which causes test failures in later tests
|
||||
|
|
|
@ -5,106 +5,71 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
let doc;
|
||||
let h1;
|
||||
let div;
|
||||
let rotated;
|
||||
let inspector;
|
||||
let contentViewer;
|
||||
|
||||
function createDocument() {
|
||||
let div = doc.createElement("div");
|
||||
h1 = doc.createElement("h1");
|
||||
let p1 = doc.createElement("p");
|
||||
let p2 = doc.createElement("p");
|
||||
let div2 = doc.createElement("div");
|
||||
let p3 = doc.createElement("p");
|
||||
doc.title = "Inspector Highlighter Meatballs";
|
||||
h1.textContent = "Inspector Tree Selection Test";
|
||||
p1.textContent = "This is some example text";
|
||||
p2.textContent = "Lorem ipsum dolor sit amet, consectetur adipisicing " +
|
||||
"elit, sed do eiusmod tempor incididunt ut labore et dolore magna " +
|
||||
"aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco " +
|
||||
"laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure " +
|
||||
"dolor in reprehenderit in voluptate velit esse cillum dolore eu " +
|
||||
"fugiat nulla pariatur. Excepteur sint occaecat cupidatat non " +
|
||||
"proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
|
||||
p3.textContent = "Lorem ipsum dolor sit amet, consectetur adipisicing " +
|
||||
"elit, sed do eiusmod tempor incididunt ut labore et dolore magna " +
|
||||
"aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco " +
|
||||
"laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure " +
|
||||
"dolor in reprehenderit in voluptate velit esse cillum dolore eu " +
|
||||
"fugiat nulla pariatur. Excepteur sint occaecat cupidatat non " +
|
||||
"proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
|
||||
let div3 = doc.createElement("div");
|
||||
div3.id = "checkOutThisWickedSpread";
|
||||
div3.setAttribute("style", "position: absolute; top: 20px; right: 20px; height: 20px; width: 20px; background-color: yellow; border: 1px dashed black;");
|
||||
let p4 = doc.createElement("p");
|
||||
p4.setAttribute("style", "font-weight: 200; font-size: 8px; text-align: center;");
|
||||
p4.textContent = "Smörgåsbord!";
|
||||
div.appendChild(h1);
|
||||
div.appendChild(p1);
|
||||
div.appendChild(p2);
|
||||
div2.appendChild(p3);
|
||||
div3.appendChild(p4);
|
||||
div = doc.createElement("div");
|
||||
div.setAttribute("style",
|
||||
"padding:5px; border:7px solid red; margin: 9px; " +
|
||||
"position:absolute; top:30px; left:150px;");
|
||||
let textNode = doc.createTextNode("Gort! Klaatu barada nikto!");
|
||||
rotated = doc.createElement("div");
|
||||
rotated.setAttribute("style",
|
||||
"padding:5px; border:7px solid red; margin: 9px; " +
|
||||
"transform:rotate(45deg); " +
|
||||
"position:absolute; top:30px; left:80px;");
|
||||
div.appendChild(textNode);
|
||||
doc.body.appendChild(div);
|
||||
doc.body.appendChild(div2);
|
||||
doc.body.appendChild(div3);
|
||||
doc.body.appendChild(rotated);
|
||||
|
||||
openInspector(aInspector => {
|
||||
inspector = aInspector;
|
||||
inspector.selection.setNode(div, null);
|
||||
inspector.once("inspector-updated", () => {
|
||||
inspector.toolbox.highlighterUtils.startPicker().then(testMouseOverH1Highlights);
|
||||
});
|
||||
inspector.once("inspector-updated", testMouseOverDivHighlights);
|
||||
});
|
||||
}
|
||||
|
||||
function testMouseOverH1Highlights() {
|
||||
function testMouseOverDivHighlights() {
|
||||
ok(isHighlighting(), "Highlighter is shown");
|
||||
is(getHighlitNode(), div, "Highlighter's outline correspond to the non-rotated div");
|
||||
testNonTransformedBoxModelDimensionsNoZoom();
|
||||
}
|
||||
|
||||
function testNonTransformedBoxModelDimensionsNoZoom() {
|
||||
info("Highlighted the non-rotated div");
|
||||
isNodeCorrectlyHighlighted(div, "non-zoomed");
|
||||
|
||||
inspector.toolbox.once("highlighter-ready", testNonTransformedBoxModelDimensionsZoomed);
|
||||
contentViewer = gBrowser.selectedBrowser.docShell.contentViewer
|
||||
.QueryInterface(Ci.nsIMarkupDocumentViewer);
|
||||
contentViewer.fullZoom = 2;
|
||||
}
|
||||
|
||||
function testNonTransformedBoxModelDimensionsZoomed() {
|
||||
info("Highlighted the zoomed, non-rotated div");
|
||||
isNodeCorrectlyHighlighted(div, "zoomed");
|
||||
|
||||
inspector.toolbox.once("highlighter-ready", testMouseOverRotatedHighlights);
|
||||
contentViewer.fullZoom = 1;
|
||||
}
|
||||
|
||||
function testMouseOverRotatedHighlights() {
|
||||
inspector.toolbox.once("highlighter-ready", () => {
|
||||
ok(isHighlighting(), "Highlighter is shown");
|
||||
is(getHighlitNode(), h1, "Highlighter's outline correspond to the selected node");
|
||||
testBoxModelDimensions();
|
||||
info("Highlighted the rotated div");
|
||||
isNodeCorrectlyHighlighted(rotated, "rotated");
|
||||
|
||||
executeSoon(finishUp);
|
||||
});
|
||||
|
||||
EventUtils.synthesizeMouse(h1, 2, 2, {type: "mousemove"}, content);
|
||||
}
|
||||
|
||||
function testBoxModelDimensions() {
|
||||
let h1Dims = h1.getBoundingClientRect();
|
||||
let h1Width = Math.ceil(h1Dims.width);
|
||||
let h1Height = Math.ceil(h1Dims.height);
|
||||
|
||||
let outlineDims = getSimpleBorderRect();
|
||||
let outlineWidth = Math.ceil(outlineDims.width);
|
||||
let outlineHeight = Math.ceil(outlineDims.height);
|
||||
|
||||
// Disabled due to bug 716245
|
||||
is(outlineWidth, h1Width, "outline width matches dimensions of element (no zoom)");
|
||||
is(outlineHeight, h1Height, "outline height matches dimensions of element (no zoom)");
|
||||
|
||||
// zoom the page by a factor of 2
|
||||
let contentViewer = gBrowser.selectedBrowser.docShell.contentViewer
|
||||
.QueryInterface(Ci.nsIMarkupDocumentViewer);
|
||||
contentViewer.fullZoom = 2;
|
||||
|
||||
// simulate the zoomed dimensions of the div element
|
||||
let h1Dims = h1.getBoundingClientRect();
|
||||
// There seems to be some very minor differences in the floats, so let's
|
||||
// floor the values
|
||||
let h1Width = Math.floor(h1Dims.width * contentViewer.fullZoom);
|
||||
let h1Height = Math.floor(h1Dims.height * contentViewer.fullZoom);
|
||||
|
||||
let outlineDims = getSimpleBorderRect();
|
||||
let outlineWidth = Math.floor(outlineDims.width);
|
||||
let outlineHeight = Math.floor(outlineDims.height);
|
||||
|
||||
is(outlineWidth, h1Width, "outline width matches dimensions of element (zoomed)");
|
||||
|
||||
is(outlineHeight, h1Height, "outline height matches dimensions of element (zoomed)");
|
||||
|
||||
executeSoon(finishUp);
|
||||
inspector.selection.setNode(rotated);
|
||||
}
|
||||
|
||||
function finishUp() {
|
||||
inspector.toolbox.highlighterUtils.stopPicker().then(() => {
|
||||
doc = h1 = inspector = null;
|
||||
doc = div = rotated = inspector = contentViewer = null;
|
||||
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||
gDevTools.closeToolbox(target);
|
||||
gBrowser.removeCurrentTab();
|
||||
|
|
|
@ -17,19 +17,58 @@ function test() {
|
|||
waitForFocus(setupInfobarTest, content);
|
||||
}, true);
|
||||
|
||||
let style = "body{width:100%;height: 100%} div {position: absolute;height: 100px;width: 500px}#bottom {bottom: 0px}#vertical {height: 100%}#farbottom{bottom: -200px}";
|
||||
let html = "<style>" + style + "</style><div id=vertical></div><div id=top class='class1 class2'></div><div id=bottom></div><div id=farbottom></div>"
|
||||
let style = "body{width:100%;height: 100%} div {position: absolute;" +
|
||||
"height: 100px;width: 500px}#bottom {bottom: 0px}#vertical {"+
|
||||
"height: 100%}#farbottom{bottom: -200px}";
|
||||
let html = "<style>" + style + "</style><div id=vertical></div>" +
|
||||
"<div id=top class='class1 class2'></div><div id=bottom></div>" +
|
||||
"<div id=farbottom></div>"
|
||||
|
||||
content.location = "data:text/html," + encodeURIComponent(html);
|
||||
content.location = "data:text/html;charset=utf-8," + encodeURIComponent(html);
|
||||
|
||||
function setupInfobarTest() {
|
||||
nodes = [
|
||||
{node: doc.querySelector("#top"), position: "bottom", tag: "DIV", id: "#top", classes: ".class1.class2"},
|
||||
{node: doc.querySelector("#vertical"), position: "overlap", tag: "DIV", id: "#vertical", classes: ""},
|
||||
{node: doc.querySelector("#bottom"), position: "top", tag: "DIV", id: "#bottom", classes: ""},
|
||||
{node: doc.querySelector("body"), position: "overlap", tag: "BODY", id: "", classes: ""},
|
||||
{node: doc.querySelector("#farbottom"), position: "top", tag: "DIV", id: "#farbottom", classes: ""},
|
||||
]
|
||||
{
|
||||
node: doc.querySelector("#top"),
|
||||
position: "bottom",
|
||||
tag: "DIV",
|
||||
id: "#top",
|
||||
classes: ".class1.class2",
|
||||
dims: "500 x 100"
|
||||
},
|
||||
{
|
||||
node: doc.querySelector("#vertical"),
|
||||
position: "overlap",
|
||||
tag: "DIV",
|
||||
id: "#vertical",
|
||||
classes: ""
|
||||
// No dims as they will vary between computers
|
||||
},
|
||||
{
|
||||
node: doc.querySelector("#bottom"),
|
||||
position: "top",
|
||||
tag: "DIV",
|
||||
id: "#bottom",
|
||||
classes: "",
|
||||
dims: "500 x 100"
|
||||
},
|
||||
{
|
||||
node: doc.querySelector("body"),
|
||||
position: "overlap",
|
||||
tag: "BODY",
|
||||
id: "",
|
||||
classes: ""
|
||||
// No dims as they will vary between computers
|
||||
},
|
||||
{
|
||||
node: doc.querySelector("#farbottom"),
|
||||
position: "top",
|
||||
tag: "DIV",
|
||||
id: "#farbottom",
|
||||
classes: "",
|
||||
dims: "500 x 100"
|
||||
},
|
||||
];
|
||||
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
ok(nodes[i].node, "node " + i + " found");
|
||||
|
@ -74,16 +113,24 @@ function test() {
|
|||
let stack = browser.parentNode;
|
||||
|
||||
let container = stack.querySelector(".highlighter-nodeinfobar-positioner");
|
||||
is(container.getAttribute("position"), nodes[cursor].position, "node " + cursor + ": position matches.");
|
||||
is(container.getAttribute("position"),
|
||||
nodes[cursor].position, "node " + cursor + ": position matches.");
|
||||
|
||||
let tagNameLabel = stack.querySelector(".highlighter-nodeinfobar-tagname");
|
||||
is(tagNameLabel.textContent, nodes[cursor].tag, "node " + cursor + ": tagName matches.");
|
||||
is(tagNameLabel.textContent, nodes[cursor].tag,
|
||||
"node " + cursor + ": tagName matches.");
|
||||
|
||||
let idLabel = stack.querySelector(".highlighter-nodeinfobar-id");
|
||||
is(idLabel.textContent, nodes[cursor].id, "node " + cursor + ": id matches.");
|
||||
|
||||
let classesBox = stack.querySelector(".highlighter-nodeinfobar-classes");
|
||||
is(classesBox.textContent, nodes[cursor].classes, "node " + cursor + ": classes match.");
|
||||
is(classesBox.textContent, nodes[cursor].classes,
|
||||
"node " + cursor + ": classes match.");
|
||||
|
||||
if (nodes[cursor].dims) {
|
||||
let dimBox = stack.querySelector(".highlighter-nodeinfobar-dimensions");
|
||||
is(dimBox.textContent, nodes[cursor].dims, "node " + cursor + ": dims match.");
|
||||
}
|
||||
}
|
||||
|
||||
function finishUp() {
|
||||
|
|
|
@ -400,7 +400,7 @@ function focusSearchBoxUsingShortcut(panelWin, callback) {
|
|||
altKey: modifiersAttr.match("alt"),
|
||||
metaKey: modifiersAttr.match("meta"),
|
||||
accelKey: modifiersAttr.match("accel")
|
||||
}
|
||||
};
|
||||
|
||||
let searchBox = panelWin.document.getElementById("inspector-searchbox");
|
||||
searchBox.addEventListener("focus", function onFocus() {
|
||||
|
@ -425,6 +425,60 @@ function getComputedPropertyValue(aName)
|
|||
}
|
||||
}
|
||||
|
||||
function isNodeCorrectlyHighlighted(node, prefix="") {
|
||||
let boxModel = getBoxModelStatus();
|
||||
let helper = new LayoutHelpers(window.content);
|
||||
|
||||
prefix += (prefix ? " " : "") + node.nodeName;
|
||||
prefix += (node.id ? "#" + node.id : "");
|
||||
prefix += (node.classList.length ? "." + [...node.classList].join(".") : "");
|
||||
prefix += " ";
|
||||
|
||||
let quads = helper.getAdjustedQuads(node, "content");
|
||||
let {p1:cp1, p2:cp2, p3:cp3, p4:cp4} = boxModel.content.points;
|
||||
is(cp1.x, quads.p1.x, prefix + "content point 1 x co-ordinate is correct");
|
||||
is(cp1.y, quads.p1.y, prefix + "content point 1 y co-ordinate is correct");
|
||||
is(cp2.x, quads.p2.x, prefix + "content point 2 x co-ordinate is correct");
|
||||
is(cp2.y, quads.p2.y, prefix + "content point 2 y co-ordinate is correct");
|
||||
is(cp3.x, quads.p3.x, prefix + "content point 3 x co-ordinate is correct");
|
||||
is(cp3.y, quads.p3.y, prefix + "content point 3 y co-ordinate is correct");
|
||||
is(cp4.x, quads.p4.x, prefix + "content point 4 x co-ordinate is correct");
|
||||
is(cp4.y, quads.p4.y, prefix + "content point 4 y co-ordinate is correct");
|
||||
|
||||
quads = helper.getAdjustedQuads(node, "padding");
|
||||
let {p1:pp1, p2:pp2, p3:pp3, p4:pp4} = boxModel.padding.points;
|
||||
is(pp1.x, quads.p1.x, prefix + "padding point 1 x co-ordinate is correct");
|
||||
is(pp1.y, quads.p1.y, prefix + "padding point 1 y co-ordinate is correct");
|
||||
is(pp2.x, quads.p2.x, prefix + "padding point 2 x co-ordinate is correct");
|
||||
is(pp2.y, quads.p2.y, prefix + "padding point 2 y co-ordinate is correct");
|
||||
is(pp3.x, quads.p3.x, prefix + "padding point 3 x co-ordinate is correct");
|
||||
is(pp3.y, quads.p3.y, prefix + "padding point 3 y co-ordinate is correct");
|
||||
is(pp4.x, quads.p4.x, prefix + "padding point 4 x co-ordinate is correct");
|
||||
is(pp4.y, quads.p4.y, prefix + "padding point 4 y co-ordinate is correct");
|
||||
|
||||
quads = helper.getAdjustedQuads(node, "border");
|
||||
let {p1:bp1, p2:bp2, p3:bp3, p4:bp4} = boxModel.border.points;
|
||||
is(bp1.x, quads.p1.x, prefix + "border point 1 x co-ordinate is correct");
|
||||
is(bp1.y, quads.p1.y, prefix + "border point 1 y co-ordinate is correct");
|
||||
is(bp2.x, quads.p2.x, prefix + "border point 2 x co-ordinate is correct");
|
||||
is(bp2.y, quads.p2.y, prefix + "border point 2 y co-ordinate is correct");
|
||||
is(bp3.x, quads.p3.x, prefix + "border point 3 x co-ordinate is correct");
|
||||
is(bp3.y, quads.p3.y, prefix + "border point 3 y co-ordinate is correct");
|
||||
is(bp4.x, quads.p4.x, prefix + "border point 4 x co-ordinate is correct");
|
||||
is(bp4.y, quads.p4.y, prefix + "border point 4 y co-ordinate is correct");
|
||||
|
||||
quads = helper.getAdjustedQuads(node, "margin");
|
||||
let {p1:mp1, p2:mp2, p3:mp3, p4:mp4} = boxModel.margin.points;
|
||||
is(mp1.x, quads.p1.x, prefix + "margin point 1 x co-ordinate is correct");
|
||||
is(mp1.y, quads.p1.y, prefix + "margin point 1 y co-ordinate is correct");
|
||||
is(mp2.x, quads.p2.x, prefix + "margin point 2 x co-ordinate is correct");
|
||||
is(mp2.y, quads.p2.y, prefix + "margin point 2 y co-ordinate is correct");
|
||||
is(mp3.x, quads.p3.x, prefix + "margin point 3 x co-ordinate is correct");
|
||||
is(mp3.y, quads.p3.y, prefix + "margin point 3 y co-ordinate is correct");
|
||||
is(mp4.x, quads.p4.x, prefix + "margin point 4 x co-ordinate is correct");
|
||||
is(mp4.y, quads.p4.y, prefix + "margin point 4 y co-ordinate is correct");
|
||||
}
|
||||
|
||||
function getContainerForRawNode(markupView, rawNode)
|
||||
{
|
||||
let front = markupView.walker.frontForRawNode(rawNode);
|
||||
|
|
|
@ -118,7 +118,8 @@ AppCacheUtils.prototype = {
|
|||
for (let neturi of parsed.uris) {
|
||||
if (neturi.section == "NETWORK") {
|
||||
for (let parsedUri of parsed.uris) {
|
||||
if (parsedUri.uri.startsWith(neturi.uri)) {
|
||||
if (parsedUri.section !== "NETWORK" &&
|
||||
parsedUri.uri.startsWith(neturi.uri)) {
|
||||
this._addError(neturi.line, "networkBlocksURI", neturi.line,
|
||||
neturi.original, parsedUri.line, parsedUri.original,
|
||||
parsedUri.section);
|
||||
|
@ -164,7 +165,7 @@ AppCacheUtils.prototype = {
|
|||
this._addError(parsedUri.line, "cacheControlNoStore",
|
||||
parsedUri.original, parsedUri.line);
|
||||
}
|
||||
} else {
|
||||
} else if (parsedUri.original !== "*") {
|
||||
this._addError(parsedUri.line, "notAvailable",
|
||||
parsedUri.original, parsedUri.line);
|
||||
}
|
||||
|
@ -182,7 +183,6 @@ AppCacheUtils.prototype = {
|
|||
let inputStream = Cc["@mozilla.org/scriptableinputstream;1"]
|
||||
.createInstance(Ci.nsIScriptableInputStream);
|
||||
let deferred = promise.defer();
|
||||
let channelCharset = "";
|
||||
let buffer = "";
|
||||
let channel = Services.io.newChannel(uri, null, null);
|
||||
|
||||
|
@ -203,7 +203,7 @@ AppCacheUtils.prototype = {
|
|||
},
|
||||
|
||||
onStopRequest: function onStartRequest(request, context, statusCode) {
|
||||
if (statusCode == 0) {
|
||||
if (statusCode === 0) {
|
||||
request.QueryInterface(Ci.nsIHttpChannel);
|
||||
|
||||
let result = {
|
||||
|
@ -279,7 +279,7 @@ AppCacheUtils.prototype = {
|
|||
}
|
||||
});
|
||||
|
||||
if (entries.length == 0) {
|
||||
if (entries.length === 0) {
|
||||
throw new Error(l10n.GetStringFromName("noResults"));
|
||||
}
|
||||
return entries;
|
||||
|
@ -320,17 +320,23 @@ AppCacheUtils.prototype = {
|
|||
_getManifestURI: function ACU__getManifestURI() {
|
||||
let deferred = promise.defer();
|
||||
|
||||
let getURI = node => {
|
||||
let getURI = () => {
|
||||
let htmlNode = this.doc.querySelector("html[manifest]");
|
||||
if (htmlNode) {
|
||||
let pageUri = this.doc.location ? this.doc.location.href : this.uri;
|
||||
let origin = pageUri.substr(0, pageUri.lastIndexOf("/") + 1);
|
||||
return origin + htmlNode.getAttribute("manifest");
|
||||
let manifestURI = htmlNode.getAttribute("manifest");
|
||||
|
||||
if (manifestURI.startsWith("/")) {
|
||||
manifestURI = manifestURI.substr(1);
|
||||
}
|
||||
|
||||
return origin + manifestURI;
|
||||
}
|
||||
};
|
||||
|
||||
if (this.doc) {
|
||||
let uri = getURI(this.doc);
|
||||
let uri = getURI();
|
||||
return promise.resolve(uri);
|
||||
} else {
|
||||
this._getURIInfo(this.uri).then(uriInfo => {
|
||||
|
@ -338,7 +344,7 @@ AppCacheUtils.prototype = {
|
|||
let html = uriInfo.text;
|
||||
let parser = _DOMParser;
|
||||
this.doc = parser.parseFromString(html, "text/html");
|
||||
let uri = getURI(this.doc);
|
||||
let uri = getURI();
|
||||
deferred.resolve(uri);
|
||||
} else {
|
||||
this.errors.push({
|
||||
|
@ -394,10 +400,10 @@ ManifestParser.prototype = {
|
|||
this.currSection = "CACHE";
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
let text = this.text = lines[i].replace(/^\s+|\s+$/g);
|
||||
let text = this.text = lines[i].trim();
|
||||
this.currentLine = i + 1;
|
||||
|
||||
if (i == 0 && text != "CACHE MANIFEST") {
|
||||
if (i === 0 && text !== "CACHE MANIFEST") {
|
||||
this._addError(1, "firstLineMustBeCacheManifest", 1);
|
||||
}
|
||||
|
||||
|
@ -453,7 +459,7 @@ ManifestParser.prototype = {
|
|||
|
||||
if (/\s/.test(text)) {
|
||||
this._addError(this.currentLine, "escapeSpaces", this.currentLine);
|
||||
text = text.replace(/\s/g, "%20")
|
||||
text = text.replace(/\s/g, "%20");
|
||||
}
|
||||
|
||||
if (text[0] == "/") {
|
||||
|
@ -506,7 +512,7 @@ ManifestParser.prototype = {
|
|||
|
||||
if (/\s/.test(namespace)) {
|
||||
this._addError(this.currentLine, "escapeSpaces", this.currentLine);
|
||||
namespace = namespace.replace(/\s/g, "%20")
|
||||
namespace = namespace.replace(/\s/g, "%20");
|
||||
}
|
||||
|
||||
if (namespace.substr(0, 4) == "/../") {
|
||||
|
|
|
@ -64,7 +64,7 @@ function StyleEditorUI(debuggee, target, panelDoc) {
|
|||
this.selectedEditor = null;
|
||||
this.savedLocations = {};
|
||||
|
||||
this._updateContextMenu = this._updateContextMenu.bind(this);
|
||||
this._updateOptionsMenu = this._updateOptionsMenu.bind(this);
|
||||
this._onStyleSheetCreated = this._onStyleSheetCreated.bind(this);
|
||||
this._onNewDocument = this._onNewDocument.bind(this);
|
||||
this._onMediaPrefChanged = this._onMediaPrefChanged.bind(this);
|
||||
|
@ -142,36 +142,26 @@ StyleEditorUI.prototype = {
|
|||
this._importFromFile(this._mockImportFile || null, this._window);
|
||||
}.bind(this));
|
||||
|
||||
this._contextMenu = this._panelDoc.getElementById("sidebar-context");
|
||||
this._contextMenu.addEventListener("popupshowing",
|
||||
this._updateContextMenu);
|
||||
this._optionsMenu = this._panelDoc.getElementById("style-editor-options-popup");
|
||||
this._optionsMenu.addEventListener("popupshowing",
|
||||
this._updateOptionsMenu);
|
||||
|
||||
this._sourcesItem = this._panelDoc.getElementById("context-origsources");
|
||||
this._sourcesItem = this._panelDoc.getElementById("options-origsources");
|
||||
this._sourcesItem.addEventListener("command",
|
||||
this._toggleOrigSources);
|
||||
this._mediaItem = this._panelDoc.getElementById("context-show-media");
|
||||
this._mediaItem = this._panelDoc.getElementById("options-show-media");
|
||||
this._mediaItem.addEventListener("command",
|
||||
this._toggleMediaSidebar);
|
||||
},
|
||||
|
||||
/**
|
||||
* Update text of context menu option to reflect current preference
|
||||
* settings
|
||||
* Update options menu items to reflect current preference settings.
|
||||
*/
|
||||
_updateContextMenu: function() {
|
||||
let sourceString = "showOriginalSources";
|
||||
if (Services.prefs.getBoolPref(PREF_ORIG_SOURCES)) {
|
||||
sourceString = "showCSSSources";
|
||||
}
|
||||
this._sourcesItem.setAttribute("label", _(sourceString + ".label"));
|
||||
this._sourcesItem.setAttribute("accesskey", _(sourceString + ".accesskey"));
|
||||
|
||||
let mediaString = "showMediaSidebar"
|
||||
if (Services.prefs.getBoolPref(PREF_MEDIA_SIDEBAR)) {
|
||||
mediaString = "hideMediaSidebar";
|
||||
}
|
||||
this._mediaItem.setAttribute("label", _(mediaString + ".label"));
|
||||
this._mediaItem.setAttribute("accesskey", _(mediaString + ".accesskey"));
|
||||
_updateOptionsMenu: function() {
|
||||
this._sourcesItem.setAttribute("checked",
|
||||
Services.prefs.getBoolPref(PREF_ORIG_SOURCES));
|
||||
this._mediaItem.setAttribute("checked",
|
||||
Services.prefs.getBoolPref(PREF_MEDIA_SIDEBAR));
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -771,6 +761,9 @@ StyleEditorUI.prototype = {
|
|||
destroy: function() {
|
||||
this._clearStyleSheetEditors();
|
||||
|
||||
this._optionsMenu.removeEventListener("popupshowing",
|
||||
this._updateOptionsMenu);
|
||||
|
||||
this._prefObserver.off(PREF_ORIG_SOURCES, this._onNewDocument);
|
||||
this._prefObserver.off(PREF_MEDIA_SIDEBAR, this._onMediaPrefChanged);
|
||||
this._prefObserver.destroy();
|
||||
|
|
|
@ -15,6 +15,10 @@ li.error > .stylesheet-info > .stylesheet-more > .stylesheet-error-message {
|
|||
display: block;
|
||||
}
|
||||
|
||||
.devtools-toolbar > spacer {
|
||||
-moz-box-flex: 1;
|
||||
}
|
||||
|
||||
.splitview-nav > li,
|
||||
.stylesheet-info,
|
||||
.stylesheet-more {
|
||||
|
|
|
@ -61,9 +61,16 @@
|
|||
key="key_gotoLine"
|
||||
command="cmd_gotoLine"/>
|
||||
</xul:menupopup>
|
||||
<xul:menupopup id="sidebar-context">
|
||||
<xul:menuitem id="context-origsources"/>
|
||||
<xul:menuitem id="context-show-media"/>
|
||||
<xul:menupopup id="style-editor-options-popup"
|
||||
position="before_start">
|
||||
<xul:menuitem id="options-origsources"
|
||||
type="checkbox"
|
||||
label="&showOriginalSources.label;"
|
||||
accesskey="&showOriginalSources.accesskey;"/>
|
||||
<xul:menuitem id="options-show-media"
|
||||
type="checkbox"
|
||||
label="&showMediaSidebar.label;"
|
||||
accesskey="&showMediaSidebar.accesskey;"/>
|
||||
</xul:menupopup>
|
||||
</xul:popupset>
|
||||
|
||||
|
@ -91,6 +98,11 @@
|
|||
accesskey="&importButton.accesskey;"
|
||||
tooltiptext="&importButton.tooltip;"
|
||||
label="&importButton.label;"/>
|
||||
<xul:spacer/>
|
||||
<xul:toolbarbutton id="style-editor-options"
|
||||
class="devtools-option-toolbarbutton"
|
||||
tooltiptext="&optionsButton.tooltip;"
|
||||
popup="style-editor-options-popup"/>
|
||||
</xul:toolbar>
|
||||
</xul:box>
|
||||
<xul:box id="splitview-resizer-target" class="theme-sidebar splitview-nav-container"
|
||||
|
|
|
@ -24,6 +24,24 @@
|
|||
<!ENTITY saveButton.tooltip "Save this style sheet to a file">
|
||||
<!ENTITY saveButton.accesskey "S">
|
||||
|
||||
<!ENTITY optionsButton.tooltip "Style Editor options">
|
||||
|
||||
<!-- LOCALIZATION NOTE (showOriginalSources.label): This is the label on the context
|
||||
menu item to toggle showing original sources in the editor. -->
|
||||
<!ENTITY showOriginalSources.label "Show original sources">
|
||||
|
||||
<!-- LOCALIZATION NOTE (showOriginalSources.accesskey): This is the access key for
|
||||
the menu item to toggle showing original sources in the editor. -->
|
||||
<!ENTITY showOriginalSources.accesskey "o">
|
||||
|
||||
<!-- LOCALIZATION NOTE (showMediaSidebar.label): This is the label on the context
|
||||
menu item to toggle showing @media rule shortcuts in a sidebar. -->
|
||||
<!ENTITY showMediaSidebar.label "Show @media sidebar">
|
||||
|
||||
<!-- LOCALIZATION NOTE (showMediaSidebar.accesskey): This is the access key for
|
||||
the menu item to toggle showing the @media sidebar. -->
|
||||
<!ENTITY showMediaSidebar.accesskey "m">
|
||||
|
||||
<!-- LOCALICATION NOTE (mediaRules.label): This is shown above the list of @media rules
|
||||
in each stylesheet editor sidebar. -->
|
||||
<!ENTITY mediaRules.label "@media rules">
|
||||
|
|
|
@ -68,38 +68,6 @@ open.accesskey=l
|
|||
# conjunction with accel (Command on Mac or Ctrl on other platforms) to Save
|
||||
saveStyleSheet.commandkey=S
|
||||
|
||||
# LOCALIZATION NOTE (showOriginalSources.label): This is the label on the context
|
||||
# menu item to toggle showing original sources in the editor.
|
||||
showOriginalSources.label=Show original sources
|
||||
|
||||
# LOCALIZATION NOTE (showOriginalSources.accesskey): This is the access key for
|
||||
# the menu item to toggle showing original sources in the editor.
|
||||
showOriginalSources.accesskey=O
|
||||
|
||||
# LOCALIZATION NOTE (showCSSSources.label): This is the label on the context
|
||||
# menu item to toggle back to showing only CSS sources in the editor.
|
||||
showCSSSources.label=Show CSS sources
|
||||
|
||||
# LOCALIZATION NOTE (showCSSSources.accesskey): This is the access key for the
|
||||
# menu item to toggle back to showing only CSS sources in the editor.
|
||||
showCSSSources.accesskey=C
|
||||
|
||||
# LOCALIZATION NOTE (showMediaSidebar.label): This is the label on the context
|
||||
# menu item to toggle showing @media rule shortcuts in a sidebar.
|
||||
showMediaSidebar.label=Show @media sidebar
|
||||
|
||||
# LOCALIZATION NOTE (showMediaSidebar.accesskey): This is the access key for
|
||||
# the menu item to toggle showing the @media sidebar.
|
||||
showMediaSidebar.accesskey=M
|
||||
|
||||
# LOCALIZATION NOTE (hideMediaSidebar.label): This is the label on the context
|
||||
# menu item to stop showing @media rule shortcuts in a sidebar.
|
||||
hideMediaSidebar.label=Hide @media sidebar
|
||||
|
||||
# LOCALIZATION NOTE (hideMediaSidebar.accesskey): This is the access key for
|
||||
# the menu item to stop showing the @media sidebar.
|
||||
hideMediaSidebar.accesskey=H
|
||||
|
||||
# LOCALIZATION NOTE (ToolboxStyleEditor.label):
|
||||
# This string is displayed in the title of the tab when the style editor is
|
||||
# displayed inside the developer tools window and in the Developer Tools Menu.
|
||||
|
|
|
@ -285,6 +285,7 @@ browser.jar:
|
|||
skin/classic/browser/devtools/responsiveui-screenshot.png (../shared/devtools/images/responsivemode/responsiveui-screenshot.png)
|
||||
skin/classic/browser/devtools/responsiveui-screenshot@2x.png (../shared/devtools/images/responsivemode/responsiveui-screenshot@2x.png)
|
||||
skin/classic/browser/devtools/toggle-tools.png (../shared/devtools/images/toggle-tools.png)
|
||||
skin/classic/browser/devtools/toggle-tools@2x.png (../shared/devtools/images/toggle-tools@2x.png)
|
||||
skin/classic/browser/devtools/dock-bottom@2x.png (../shared/devtools/images/dock-bottom@2x.png)
|
||||
skin/classic/browser/devtools/dock-side@2x.png (../shared/devtools/images/dock-side@2x.png)
|
||||
skin/classic/browser/devtools/floating-scrollbars.css (devtools/floating-scrollbars.css)
|
||||
|
|
|
@ -405,6 +405,7 @@ browser.jar:
|
|||
skin/classic/browser/devtools/responsiveui-screenshot.png (../shared/devtools/images/responsivemode/responsiveui-screenshot.png)
|
||||
skin/classic/browser/devtools/responsiveui-screenshot@2x.png (../shared/devtools/images/responsivemode/responsiveui-screenshot@2x.png)
|
||||
skin/classic/browser/devtools/toggle-tools.png (../shared/devtools/images/toggle-tools.png)
|
||||
skin/classic/browser/devtools/toggle-tools@2x.png (../shared/devtools/images/toggle-tools@2x.png)
|
||||
skin/classic/browser/devtools/dock-bottom@2x.png (../shared/devtools/images/dock-bottom@2x.png)
|
||||
skin/classic/browser/devtools/dock-side@2x.png (../shared/devtools/images/dock-side@2x.png)
|
||||
* skin/classic/browser/devtools/inspector.css (../shared/devtools/inspector.css)
|
||||
|
|
|
@ -28,6 +28,12 @@
|
|||
margin: auto 10px;
|
||||
}
|
||||
|
||||
.developer-toolbar-button > .toolbarbutton-icon,
|
||||
#developer-toolbar-closebutton > .toolbarbutton-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
#developer-toolbar-toolbox-button {
|
||||
list-style-image: url("chrome://browser/skin/devtools/toggle-tools.png");
|
||||
-moz-image-region: rect(0px, 16px, 16px, 0px);
|
||||
|
@ -49,6 +55,25 @@
|
|||
-moz-image-region: rect(0px, 64px, 16px, 48px);
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
#developer-toolbar-toolbox-button {
|
||||
list-style-image: url("chrome://browser/skin/devtools/toggle-tools@2x.png");
|
||||
-moz-image-region: rect(0px, 32px, 32px, 0px);
|
||||
}
|
||||
|
||||
#developer-toolbar-toolbox-button:hover {
|
||||
-moz-image-region: rect(0px, 64px, 32px, 32px);
|
||||
}
|
||||
|
||||
#developer-toolbar-toolbox-button:hover:active {
|
||||
-moz-image-region: rect(0px, 96px, 32px, 64px);
|
||||
}
|
||||
|
||||
#developer-toolbar-toolbox-button[checked=true] {
|
||||
-moz-image-region: rect(0px, 128px, 32px, 96px);
|
||||
}
|
||||
}
|
||||
|
||||
#developer-toolbar-closebutton {
|
||||
list-style-image: url("chrome://browser/skin/devtools/close.png");
|
||||
-moz-appearance: none;
|
||||
|
@ -59,6 +84,12 @@
|
|||
opacity: 0.6;
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
#developer-toolbar-closebutton {
|
||||
list-style-image: url("chrome://browser/skin/devtools/close@2x.png");
|
||||
}
|
||||
}
|
||||
|
||||
#developer-toolbar-closebutton > .toolbarbutton-icon {
|
||||
/* XXX Buttons have padding in widget/ that we don't want here but can't override with good CSS, so we must
|
||||
use evil CSS to give the impression of smaller content */
|
||||
|
@ -109,7 +140,6 @@ html|*#gcli-output-frame {
|
|||
.gclitoolbar-input-node {
|
||||
-moz-appearance: none;
|
||||
color: hsl(210,30%,85%);
|
||||
padding-left: 20px;
|
||||
background-color: #242b33;
|
||||
background-repeat: no-repeat;
|
||||
background-position: 4px center;
|
||||
|
@ -118,14 +148,35 @@ html|*#gcli-output-frame {
|
|||
-1px 0 0 hsla(206,37%,4%,.2) inset;
|
||||
line-height: 32px;
|
||||
outline-style: none;
|
||||
background-image: -moz-image-rect(url("chrome://browser/skin/devtools/commandline-icon.png"), 0, 16, 16, 0);
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.gclitoolbar-input-node[focused="true"] {
|
||||
background-image: -moz-image-rect(url("chrome://browser/skin/devtools/commandline-icon.png"), 0, 32, 16, 16);
|
||||
background-color: #232e38;
|
||||
}
|
||||
|
||||
.gclitoolbar-input-node::before {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
-moz-box-ordinal-group: 0;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin: 0 2px;
|
||||
background-image: url("chrome://browser/skin/devtools/commandline-icon.png");
|
||||
background-position: 0 center;
|
||||
background-size: 32px 16px;
|
||||
}
|
||||
|
||||
.gclitoolbar-input-node[focused="true"]::before {
|
||||
background-position: -16px center;
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
.gclitoolbar-input-node::before {
|
||||
background-image: url("chrome://browser/skin/devtools/commandline-icon@2x.png");
|
||||
}
|
||||
}
|
||||
|
||||
.gclitoolbar-input-node > .textbox-input-box > html|*.textbox-input::-moz-selection {
|
||||
background-color: hsl(210,30%,85%);
|
||||
color: hsl(210,24%,16%);
|
||||
|
|
|
@ -74,6 +74,13 @@ html|*.highlighter-nodeinfobar-pseudo-classes {
|
|||
color: hsl(200,74%,57%);
|
||||
}
|
||||
|
||||
html|*.highlighter-nodeinfobar-dimensions {
|
||||
color: hsl(210,30%,85%);
|
||||
-moz-border-start: 1px solid #5a6169;
|
||||
-moz-margin-start: 6px;
|
||||
-moz-padding-start: 6px;
|
||||
}
|
||||
|
||||
/* Highlighter - Node Infobar - box & arrow */
|
||||
|
||||
.highlighter-nodeinfobar-arrow {
|
||||
|
|
Двоичные данные
browser/themes/shared/devtools/images/toggle-tools.png
Двоичные данные
browser/themes/shared/devtools/images/toggle-tools.png
Двоичный файл не отображается.
До Ширина: | Высота: | Размер: 1.7 KiB После Ширина: | Высота: | Размер: 883 B |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 1.8 KiB |
|
@ -119,6 +119,11 @@
|
|||
background-position: -24px 8px;
|
||||
}
|
||||
|
||||
#style-editor-options {
|
||||
width: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Invert all toggle icons but the one in the active row for light theme */
|
||||
.theme-light .splitview-nav > li:not(.splitview-active) .stylesheet-enabled {
|
||||
filter: url(filters.svg#invert);
|
||||
|
|
|
@ -323,6 +323,7 @@ browser.jar:
|
|||
skin/classic/browser/devtools/responsiveui-screenshot.png (../shared/devtools/images/responsivemode/responsiveui-screenshot.png)
|
||||
skin/classic/browser/devtools/responsiveui-screenshot@2x.png (../shared/devtools/images/responsivemode/responsiveui-screenshot@2x.png)
|
||||
skin/classic/browser/devtools/toggle-tools.png (../shared/devtools/images/toggle-tools.png)
|
||||
skin/classic/browser/devtools/toggle-tools@2x.png (../shared/devtools/images/toggle-tools@2x.png)
|
||||
skin/classic/browser/devtools/dock-bottom@2x.png (../shared/devtools/images/dock-bottom@2x.png)
|
||||
skin/classic/browser/devtools/dock-side@2x.png (../shared/devtools/images/dock-side@2x.png)
|
||||
skin/classic/browser/devtools/floating-scrollbars.css (devtools/floating-scrollbars.css)
|
||||
|
@ -723,6 +724,7 @@ browser.jar:
|
|||
skin/classic/aero/browser/devtools/responsiveui-screenshot.png (../shared/devtools/images/responsivemode/responsiveui-screenshot.png)
|
||||
skin/classic/aero/browser/devtools/responsiveui-screenshot@2x.png (../shared/devtools/images/responsivemode/responsiveui-screenshot@2x.png)
|
||||
skin/classic/aero/browser/devtools/toggle-tools.png (../shared/devtools/images/toggle-tools.png)
|
||||
skin/classic/aero/browser/devtools/toggle-tools@2x.png (../shared/devtools/images/toggle-tools@2x.png)
|
||||
skin/classic/aero/browser/devtools/dock-bottom@2x.png (../shared/devtools/images/dock-bottom@2x.png)
|
||||
skin/classic/aero/browser/devtools/dock-side@2x.png (../shared/devtools/images/dock-side@2x.png)
|
||||
skin/classic/aero/browser/devtools/floating-scrollbars.css (devtools/floating-scrollbars.css)
|
||||
|
|
|
@ -3938,6 +3938,7 @@ MOZ_ANDROID_HISTORY=
|
|||
MOZ_WEBSMS_BACKEND=
|
||||
MOZ_ANDROID_BEAM=
|
||||
MOZ_ANDROID_SYNTHAPKS=
|
||||
MOZ_LOCALE_SWITCHER=
|
||||
ACCESSIBILITY=1
|
||||
MOZ_TIME_MANAGER=
|
||||
MOZ_PAY=
|
||||
|
@ -4947,6 +4948,13 @@ if test -n "$MOZ_WEBSMS_BACKEND"; then
|
|||
AC_DEFINE(MOZ_WEBSMS_BACKEND)
|
||||
fi
|
||||
|
||||
dnl ========================================================
|
||||
dnl = Enable runtime locale switching on Android
|
||||
dnl ========================================================
|
||||
if test -n "$MOZ_LOCALE_SWITCHER"; then
|
||||
AC_DEFINE(MOZ_LOCALE_SWITCHER)
|
||||
fi
|
||||
|
||||
dnl ========================================================
|
||||
dnl = Enable NFC permission on Android
|
||||
dnl ========================================================
|
||||
|
@ -8567,6 +8575,7 @@ AC_SUBST(MOZ_ANDROID_HISTORY)
|
|||
AC_SUBST(MOZ_WEBSMS_BACKEND)
|
||||
AC_SUBST(MOZ_ANDROID_BEAM)
|
||||
AC_SUBST(MOZ_ANDROID_SYNTHAPKS)
|
||||
AC_SUBST(MOZ_LOCALE_SWITCHER)
|
||||
AC_SUBST(MOZ_DISABLE_GECKOVIEW)
|
||||
AC_SUBST(ENABLE_STRIP)
|
||||
AC_SUBST(PKG_SKIP_STRIP)
|
||||
|
|
|
@ -24,6 +24,9 @@
|
|||
#include "BluetoothService.h"
|
||||
#include "BluetoothUtils.h"
|
||||
|
||||
#define ERR_INVALID_ADAPTER_STATE "InvalidAdapterStateError"
|
||||
#define ERR_CHANGE_ADAPTER_STATE "ChangeAdapterStateError"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
|
@ -121,6 +124,7 @@ public:
|
|||
BluetoothReplyRunnable::ReleaseMembers();
|
||||
mAdapterPtr = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<BluetoothAdapter> mAdapterPtr;
|
||||
};
|
||||
|
@ -156,6 +160,38 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
class EnableDisableAdapterTask : public BluetoothReplyRunnable
|
||||
{
|
||||
public:
|
||||
EnableDisableAdapterTask(Promise* aPromise)
|
||||
: BluetoothReplyRunnable(nullptr)
|
||||
, mPromise(aPromise)
|
||||
{ }
|
||||
|
||||
bool
|
||||
ParseSuccessfulReply(JS::MutableHandle<JS::Value> aValue)
|
||||
{
|
||||
/*
|
||||
* It is supposed to be Promise<void> according to BluetoothAdapter.webidl,
|
||||
* but we have to pass "true" since it is mandatory to pass an
|
||||
* argument while calling MaybeResolve.
|
||||
*/
|
||||
mPromise->MaybeResolve(true);
|
||||
aValue.setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
ReleaseMembers()
|
||||
{
|
||||
BluetoothReplyRunnable::ReleaseMembers();
|
||||
mPromise = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<Promise> mPromise;
|
||||
};
|
||||
|
||||
static int kCreatePairedDeviceTimeout = 50000; // unit: msec
|
||||
|
||||
BluetoothAdapter::BluetoothAdapter(nsPIDOMWindow* aWindow,
|
||||
|
@ -164,12 +200,13 @@ BluetoothAdapter::BluetoothAdapter(nsPIDOMWindow* aWindow,
|
|||
, BluetoothPropertyContainer(BluetoothObjectType::TYPE_ADAPTER)
|
||||
, mJsUuids(nullptr)
|
||||
, mJsDeviceAddresses(nullptr)
|
||||
// TODO: Change to Disabled after Bug 1006309 landed
|
||||
, mState(BluetoothAdapterState::Enabled)
|
||||
, mDiscoverable(false)
|
||||
, mDiscovering(false)
|
||||
, mPairable(false)
|
||||
, mPowered(false)
|
||||
, mIsRooted(false)
|
||||
, mState(BluetoothAdapterState::Disabled)
|
||||
{
|
||||
MOZ_ASSERT(aWindow);
|
||||
MOZ_ASSERT(IsDOMBinding());
|
||||
|
@ -231,7 +268,11 @@ BluetoothAdapter::SetPropertyByValue(const BluetoothNamedValue& aValue)
|
|||
{
|
||||
const nsString& name = aValue.name();
|
||||
const BluetoothValue& value = aValue.value();
|
||||
if (name.EqualsLiteral("Name")) {
|
||||
if (name.EqualsLiteral("State")) {
|
||||
bool isEnabled = value.get_bool();
|
||||
mState = isEnabled ? BluetoothAdapterState::Enabled
|
||||
: BluetoothAdapterState::Disabled;
|
||||
} else if (name.EqualsLiteral("Name")) {
|
||||
mName = value.get_nsString();
|
||||
} else if (name.EqualsLiteral("Address")) {
|
||||
mAddress = value.get_nsString();
|
||||
|
@ -669,19 +710,55 @@ BluetoothAdapter::SetPairingConfirmation(const nsAString& aDeviceAddress,
|
|||
return request.forget();
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: Implement Enable/Disable functions
|
||||
*/
|
||||
already_AddRefed<Promise>
|
||||
BluetoothAdapter::EnableDisable(bool aEnable)
|
||||
{
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
|
||||
NS_ENSURE_TRUE(global, nullptr);
|
||||
|
||||
nsRefPtr<Promise> promise = new Promise(global);
|
||||
|
||||
// Make sure BluetoothService is available before modifying adapter state
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
if (!bs) {
|
||||
promise->MaybeReject(ERR_CHANGE_ADAPTER_STATE);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
if (aEnable) {
|
||||
if (mState != BluetoothAdapterState::Disabled) {
|
||||
promise->MaybeReject(ERR_INVALID_ADAPTER_STATE);
|
||||
return promise.forget();
|
||||
}
|
||||
mState = BluetoothAdapterState::Enabling;
|
||||
} else {
|
||||
if (mState != BluetoothAdapterState::Enabled) {
|
||||
promise->MaybeReject(ERR_INVALID_ADAPTER_STATE);
|
||||
return promise.forget();
|
||||
}
|
||||
mState = BluetoothAdapterState::Disabling;
|
||||
}
|
||||
|
||||
// TODO: Fire attr changed event for this state change
|
||||
nsRefPtr<BluetoothReplyRunnable> result = new EnableDisableAdapterTask(promise);
|
||||
|
||||
if(NS_FAILED(bs->EnableDisable(aEnable, result))) {
|
||||
promise->MaybeReject(ERR_CHANGE_ADAPTER_STATE);
|
||||
}
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
BluetoothAdapter::Enable()
|
||||
{
|
||||
return nullptr;
|
||||
return EnableDisable(true);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
BluetoothAdapter::Disable()
|
||||
{
|
||||
return nullptr;
|
||||
return EnableDisable(false);
|
||||
}
|
||||
|
||||
already_AddRefed<DOMRequest>
|
||||
|
|
|
@ -122,10 +122,9 @@ public:
|
|||
SetAuthorization(const nsAString& aDeviceAddress, bool aAllow,
|
||||
ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Enable();
|
||||
already_AddRefed<Promise>
|
||||
Disable();
|
||||
already_AddRefed<Promise> EnableDisable(bool aEnable);
|
||||
already_AddRefed<Promise> Enable();
|
||||
already_AddRefed<Promise> Disable();
|
||||
|
||||
already_AddRefed<DOMRequest>
|
||||
Connect(BluetoothDevice& aDevice,
|
||||
|
|
|
@ -163,14 +163,8 @@ BluetoothService::ToggleBtAck::Run()
|
|||
sBluetoothService->SetEnabled(mEnabled);
|
||||
sToggleInProgress = false;
|
||||
|
||||
nsAutoString signalName;
|
||||
signalName = mEnabled ? NS_LITERAL_STRING("Enabled")
|
||||
: NS_LITERAL_STRING("Disabled");
|
||||
BluetoothSignal signal(signalName, NS_LITERAL_STRING(KEY_MANAGER), true);
|
||||
sBluetoothService->DistributeSignal(signal);
|
||||
|
||||
// Event 'AdapterAdded' has to be fired after firing 'Enabled'
|
||||
sBluetoothService->TryFiringAdapterAdded();
|
||||
sBluetoothService->FireAdapterStateChanged(mEnabled);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -220,19 +214,6 @@ BluetoothService::~BluetoothService()
|
|||
Cleanup();
|
||||
}
|
||||
|
||||
PLDHashOperator
|
||||
RemoveObserversExceptBluetoothManager
|
||||
(const nsAString& key,
|
||||
nsAutoPtr<BluetoothSignalObserverList>& value,
|
||||
void* arg)
|
||||
{
|
||||
if (!key.EqualsLiteral(KEY_MANAGER)) {
|
||||
return PL_DHASH_REMOVE;
|
||||
}
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
// static
|
||||
BluetoothService*
|
||||
BluetoothService::Create()
|
||||
|
@ -384,7 +365,8 @@ BluetoothService::DistributeSignal(const BluetoothSignal& aSignal)
|
|||
}
|
||||
|
||||
nsresult
|
||||
BluetoothService::StartBluetooth(bool aIsStartup)
|
||||
BluetoothService::StartBluetooth(bool aIsStartup,
|
||||
BluetoothReplyRunnable* aRunnable)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
|
@ -405,7 +387,7 @@ BluetoothService::StartBluetooth(bool aIsStartup)
|
|||
*/
|
||||
if (aIsStartup || !sBluetoothService->IsEnabled()) {
|
||||
// Switch Bluetooth on
|
||||
if (NS_FAILED(sBluetoothService->StartInternal())) {
|
||||
if (NS_FAILED(sBluetoothService->StartInternal(aRunnable))) {
|
||||
BT_WARNING("Bluetooth service failed to start!");
|
||||
}
|
||||
} else {
|
||||
|
@ -420,7 +402,8 @@ BluetoothService::StartBluetooth(bool aIsStartup)
|
|||
}
|
||||
|
||||
nsresult
|
||||
BluetoothService::StopBluetooth(bool aIsStartup)
|
||||
BluetoothService::StopBluetooth(bool aIsStartup,
|
||||
BluetoothReplyRunnable* aRunnable)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
|
@ -466,7 +449,7 @@ BluetoothService::StopBluetooth(bool aIsStartup)
|
|||
*/
|
||||
if (aIsStartup || sBluetoothService->IsEnabled()) {
|
||||
// Switch Bluetooth off
|
||||
if (NS_FAILED(sBluetoothService->StopInternal())) {
|
||||
if (NS_FAILED(sBluetoothService->StopInternal(aRunnable))) {
|
||||
BT_WARNING("Bluetooth service failed to stop!");
|
||||
}
|
||||
} else {
|
||||
|
@ -481,13 +464,15 @@ BluetoothService::StopBluetooth(bool aIsStartup)
|
|||
}
|
||||
|
||||
nsresult
|
||||
BluetoothService::StartStopBluetooth(bool aStart, bool aIsStartup)
|
||||
BluetoothService::StartStopBluetooth(bool aStart,
|
||||
bool aIsStartup,
|
||||
BluetoothReplyRunnable* aRunnable)
|
||||
{
|
||||
nsresult rv;
|
||||
if (aStart) {
|
||||
rv = StartBluetooth(aIsStartup);
|
||||
rv = StartBluetooth(aIsStartup, aRunnable);
|
||||
} else {
|
||||
rv = StopBluetooth(aIsStartup);
|
||||
rv = StopBluetooth(aIsStartup, aRunnable);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
@ -504,17 +489,6 @@ BluetoothService::SetEnabled(bool aEnabled)
|
|||
unused << childActors[index]->SendEnabled(aEnabled);
|
||||
}
|
||||
|
||||
if (!aEnabled) {
|
||||
/**
|
||||
* Remove all handlers except BluetoothManager when turning off bluetooth
|
||||
* since it is possible that the event 'onAdapterAdded' would be fired after
|
||||
* BluetoothManagers of child process are registered. Please see Bug 827759
|
||||
* for more details.
|
||||
*/
|
||||
mBluetoothSignalObserverTable.Enumerate(
|
||||
RemoveObserversExceptBluetoothManager, nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* mEnabled: real status of bluetooth
|
||||
* aEnabled: expected status of bluetooth
|
||||
|
@ -553,7 +527,7 @@ nsresult
|
|||
BluetoothService::HandleStartupSettingsCheck(bool aEnable)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return StartStopBluetooth(aEnable, true);
|
||||
return StartStopBluetooth(aEnable, true, nullptr);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -610,37 +584,6 @@ BluetoothService::HandleSettingsChanged(const nsAString& aData)
|
|||
}
|
||||
|
||||
SWITCH_BT_DEBUG(value.toBoolean());
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Second, check if the string is BLUETOOTH_ENABLED_SETTING
|
||||
if (!JS_StringEqualsAscii(cx, key.toString(), BLUETOOTH_ENABLED_SETTING, &match)) {
|
||||
MOZ_ASSERT(!JS_IsExceptionPending(cx));
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
if (match) {
|
||||
JS::Rooted<JS::Value> value(cx);
|
||||
if (!JS_GetProperty(cx, obj, "value", &value)) {
|
||||
MOZ_ASSERT(!JS_IsExceptionPending(cx));
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
if (!value.isBoolean()) {
|
||||
MOZ_ASSERT(false, "Expecting a boolean for 'bluetooth.enabled'!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
if (sToggleInProgress || value.toBoolean() == IsEnabled()) {
|
||||
// Nothing to do here.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
sToggleInProgress = true;
|
||||
|
||||
nsresult rv = StartStopBluetooth(value.toBoolean(), false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -704,7 +647,7 @@ BluetoothService::HandleShutdown()
|
|||
}
|
||||
}
|
||||
|
||||
if (IsEnabled() && NS_FAILED(StopBluetooth(false))) {
|
||||
if (IsEnabled() && NS_FAILED(StopBluetooth(false, nullptr))) {
|
||||
MOZ_ASSERT(false, "Failed to deliver stop message!");
|
||||
}
|
||||
|
||||
|
@ -785,6 +728,38 @@ BluetoothService::AdapterAddedReceived()
|
|||
mAdapterAddedReceived = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/Disable the local adapter.
|
||||
*
|
||||
* There is only one adapter on the mobile in current use cases.
|
||||
* In addition, bluedroid couldn't enable/disable a single adapter.
|
||||
* So currently we will turn on/off BT to enable/disable the adapter.
|
||||
*
|
||||
* TODO: To support enable/disable single adapter in the future,
|
||||
* we will need to implement EnableDisableInternal for different stacks.
|
||||
*/
|
||||
nsresult
|
||||
BluetoothService::EnableDisable(bool aEnable,
|
||||
BluetoothReplyRunnable* aRunnable)
|
||||
{
|
||||
sToggleInProgress = true;
|
||||
return StartStopBluetooth(aEnable, false, aRunnable);
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothService::FireAdapterStateChanged(bool aEnable)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
InfallibleTArray<BluetoothNamedValue> props;
|
||||
BT_APPEND_NAMED_VALUE(props, "State", aEnable);
|
||||
BluetoothValue value(props);
|
||||
|
||||
BluetoothSignal signal(NS_LITERAL_STRING("PropertyChanged"),
|
||||
NS_LITERAL_STRING(KEY_ADAPTER), value);
|
||||
DistributeSignal(signal);
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothService::Notify(const BluetoothSignal& aData)
|
||||
{
|
||||
|
|
|
@ -315,6 +315,28 @@ public:
|
|||
void TryFiringAdapterAdded();
|
||||
void AdapterAddedReceived();
|
||||
|
||||
void FireAdapterStateChanged(bool aEnable);
|
||||
nsresult EnableDisable(bool aEnable,
|
||||
BluetoothReplyRunnable* aRunnable);
|
||||
|
||||
/**
|
||||
* Platform specific startup functions go here. Usually deals with member
|
||||
* variables, so not static. Guaranteed to be called outside of main thread.
|
||||
*
|
||||
* @return NS_OK on correct startup, NS_ERROR_FAILURE otherwise
|
||||
*/
|
||||
virtual nsresult
|
||||
StartInternal(BluetoothReplyRunnable* aRunnable) = 0;
|
||||
|
||||
/**
|
||||
* Platform specific startup functions go here. Usually deals with member
|
||||
* variables, so not static. Guaranteed to be called outside of main thread.
|
||||
*
|
||||
* @return NS_OK on correct startup, NS_ERROR_FAILURE otherwise
|
||||
*/
|
||||
virtual nsresult
|
||||
StopInternal(BluetoothReplyRunnable* aRunnable) = 0;
|
||||
|
||||
protected:
|
||||
BluetoothService() : mEnabled(false)
|
||||
, mAdapterAddedReceived(false)
|
||||
|
@ -330,31 +352,15 @@ protected:
|
|||
Cleanup();
|
||||
|
||||
nsresult
|
||||
StartBluetooth(bool aIsStartup);
|
||||
StartBluetooth(bool aIsStartup, BluetoothReplyRunnable* aRunnable);
|
||||
|
||||
nsresult
|
||||
StopBluetooth(bool aIsStartup);
|
||||
StopBluetooth(bool aIsStartup, BluetoothReplyRunnable* aRunnable);
|
||||
|
||||
nsresult
|
||||
StartStopBluetooth(bool aStart, bool aIsStartup);
|
||||
|
||||
/**
|
||||
* Platform specific startup functions go here. Usually deals with member
|
||||
* variables, so not static. Guaranteed to be called outside of main thread.
|
||||
*
|
||||
* @return NS_OK on correct startup, NS_ERROR_FAILURE otherwise
|
||||
*/
|
||||
virtual nsresult
|
||||
StartInternal() = 0;
|
||||
|
||||
/**
|
||||
* Platform specific startup functions go here. Usually deals with member
|
||||
* variables, so not static. Guaranteed to be called outside of main thread.
|
||||
*
|
||||
* @return NS_OK on correct startup, NS_ERROR_FAILURE otherwise
|
||||
*/
|
||||
virtual nsresult
|
||||
StopInternal() = 0;
|
||||
StartStopBluetooth(bool aStart,
|
||||
bool aIsStartup,
|
||||
BluetoothReplyRunnable* aRunnable);
|
||||
|
||||
/**
|
||||
* Called when XPCOM first creates this service.
|
||||
|
|
|
@ -33,34 +33,47 @@
|
|||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/unused.h"
|
||||
|
||||
#define ENSURE_BLUETOOTH_IS_READY(runnable, result) \
|
||||
do { \
|
||||
if (!sBtInterface || !IsEnabled()) { \
|
||||
NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth is not ready"); \
|
||||
DispatchBluetoothReply(runnable, BluetoothValue(), errorStr); \
|
||||
return result; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::ipc;
|
||||
USING_BLUETOOTH_NAMESPACE
|
||||
|
||||
/**
|
||||
* Static variables
|
||||
*/
|
||||
static bluetooth_device_t* sBtDevice;
|
||||
static const bt_interface_t* sBtInterface;
|
||||
static bool sAdapterDiscoverable = false;
|
||||
static bool sIsBtEnabled = false;
|
||||
// TODO: Non thread-safe static variables
|
||||
static nsString sAdapterBdAddress;
|
||||
static nsString sAdapterBdName;
|
||||
static uint32_t sAdapterDiscoverableTimeout;
|
||||
static InfallibleTArray<nsString> sAdapterBondedAddressArray;
|
||||
static InfallibleTArray<BluetoothNamedValue> sRemoteDevicesPack;
|
||||
static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sChangeAdapterStateRunnableArray;
|
||||
|
||||
// Static variables below should only be used on *main thread*
|
||||
static const bt_interface_t* sBtInterface;
|
||||
static nsTArray<nsRefPtr<BluetoothProfileController> > sControllerArray;
|
||||
static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sBondingRunnableArray;
|
||||
static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sChangeDiscoveryRunnableArray;
|
||||
static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sGetDeviceRunnableArray;
|
||||
static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sSetPropertyRunnableArray;
|
||||
static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sUnbondingRunnableArray;
|
||||
static nsTArray<int> sRequestedDeviceCountArray;
|
||||
static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sChangeDiscoveryRunnableArray;
|
||||
static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sSetPropertyRunnableArray;
|
||||
static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sGetDeviceRunnableArray;
|
||||
static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sBondingRunnableArray;
|
||||
static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sUnbondingRunnableArray;
|
||||
|
||||
// Static variables below should only be used on *callback thread*
|
||||
|
||||
|
||||
// Atomic static variables
|
||||
static Atomic<bool> sAdapterDiscoverable(false);
|
||||
static Atomic<uint32_t> sAdapterDiscoverableTimeout(0);
|
||||
|
||||
/**
|
||||
* Classes only used in this file
|
||||
*/
|
||||
class DistributeBluetoothSignalTask : public nsRunnable {
|
||||
class DistributeBluetoothSignalTask MOZ_FINAL : public nsRunnable
|
||||
{
|
||||
public:
|
||||
DistributeBluetoothSignalTask(const BluetoothSignal& aSignal) :
|
||||
mSignal(aSignal)
|
||||
|
@ -84,12 +97,9 @@ private:
|
|||
BluetoothSignal mSignal;
|
||||
};
|
||||
|
||||
class SetupAfterEnabledTask : public nsRunnable
|
||||
class SetupAfterEnabledTask MOZ_FINAL : public nsRunnable
|
||||
{
|
||||
public:
|
||||
SetupAfterEnabledTask()
|
||||
{ }
|
||||
|
||||
NS_IMETHOD
|
||||
Run()
|
||||
{
|
||||
|
@ -135,17 +145,37 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
class CleanupTask : public nsRunnable
|
||||
class CleanupTask MOZ_FINAL : public nsRunnable
|
||||
{
|
||||
public:
|
||||
CleanupTask()
|
||||
{ }
|
||||
|
||||
NS_IMETHOD
|
||||
Run()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
/*
|
||||
* Cleanup static adapter properties and notify adapter to clean them
|
||||
*
|
||||
* TODO: clean up and notify Discovering also
|
||||
*/
|
||||
sAdapterBdAddress.Truncate();
|
||||
sAdapterBdName.Truncate();
|
||||
sAdapterDiscoverable = false;
|
||||
|
||||
InfallibleTArray<BluetoothNamedValue> props;
|
||||
BT_APPEND_NAMED_VALUE(props, "Name", sAdapterBdName);
|
||||
BT_APPEND_NAMED_VALUE(props, "Address", sAdapterBdAddress);
|
||||
BT_APPEND_NAMED_VALUE(props, "Discoverable",
|
||||
BluetoothValue(sAdapterDiscoverable));
|
||||
BluetoothValue value(props);
|
||||
BluetoothSignal signal(NS_LITERAL_STRING("PropertyChanged"),
|
||||
NS_LITERAL_STRING(KEY_ADAPTER), value);
|
||||
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
NS_ENSURE_TRUE(bs, NS_ERROR_FAILURE);
|
||||
|
||||
bs->DistributeSignal(signal);
|
||||
|
||||
// Cleanup bluetooth interfaces after BT state becomes BT_STATE_OFF.
|
||||
BluetoothHfpManager::DeinitHfpInterface();
|
||||
BluetoothA2dpManager::DeinitA2dpInterface();
|
||||
|
@ -270,45 +300,69 @@ PlayStatusStringToControlPlayStatus(const nsAString& aPlayStatus)
|
|||
return playStatus;
|
||||
}
|
||||
|
||||
static bool
|
||||
IsReady()
|
||||
{
|
||||
if (!sBtInterface || !sIsBtEnabled) {
|
||||
BT_LOGR("Warning! Bluetooth Service is not ready");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bluedroid HAL callback functions
|
||||
*
|
||||
* Several callbacks are dispatched to main thread to avoid racing issues.
|
||||
*/
|
||||
static void
|
||||
AdapterStateChangeCallback(bt_state_t aStatus)
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
BT_LOGR("BT_STATE %d", aStatus);
|
||||
BT_LOGR("BT_STATE: %d", aStatus);
|
||||
|
||||
sIsBtEnabled = (aStatus == BT_STATE_ON);
|
||||
bool isBtEnabled = (aStatus == BT_STATE_ON);
|
||||
|
||||
if (!sIsBtEnabled && NS_FAILED(NS_DispatchToMainThread(new CleanupTask()))) {
|
||||
if (!isBtEnabled &&
|
||||
NS_FAILED(NS_DispatchToMainThread(new CleanupTask()))) {
|
||||
BT_WARNING("Failed to dispatch to main thread!");
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<nsRunnable> runnable =
|
||||
new BluetoothService::ToggleBtAck(sIsBtEnabled);
|
||||
new BluetoothService::ToggleBtAck(isBtEnabled);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
|
||||
BT_WARNING("Failed to dispatch to main thread!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (sIsBtEnabled &&
|
||||
if (isBtEnabled &&
|
||||
NS_FAILED(NS_DispatchToMainThread(new SetupAfterEnabledTask()))) {
|
||||
BT_WARNING("Failed to dispatch to main thread!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Resolve promise if existed
|
||||
if(!sChangeAdapterStateRunnableArray.IsEmpty()) {
|
||||
DispatchBluetoothReply(sChangeAdapterStateRunnableArray[0],
|
||||
BluetoothValue(true),
|
||||
EmptyString());
|
||||
sChangeAdapterStateRunnableArray.RemoveElementAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
class AdapterPropertiesCallbackTask MOZ_FINAL : public nsRunnable
|
||||
{
|
||||
public:
|
||||
NS_IMETHOD
|
||||
Run()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!sSetPropertyRunnableArray.IsEmpty()) {
|
||||
DispatchBluetoothReply(sSetPropertyRunnableArray[0],
|
||||
BluetoothValue(true), EmptyString());
|
||||
sSetPropertyRunnableArray.RemoveElementAt(0);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* AdapterPropertiesCallback will be called after enable() but before
|
||||
* AdapterStateChangeCallback sIsBtEnabled get updated. At that moment, both
|
||||
* AdapterStateChangeCallback is called. At that moment, both
|
||||
* BluetoothManager/BluetoothAdapter does not register observer yet.
|
||||
*/
|
||||
static void
|
||||
|
@ -385,14 +439,64 @@ AdapterPropertiesCallback(bt_status_t aStatus, int aNumProperties,
|
|||
BT_WARNING("Failed to dispatch to main thread!");
|
||||
}
|
||||
|
||||
// bluedroid BTU task was stored in the task queue, see GKI_send_msg
|
||||
if (!sSetPropertyRunnableArray.IsEmpty()) {
|
||||
DispatchBluetoothReply(sSetPropertyRunnableArray[0], BluetoothValue(true),
|
||||
EmptyString());
|
||||
sSetPropertyRunnableArray.RemoveElementAt(0);
|
||||
}
|
||||
// Redirect to main thread to avoid racing problem
|
||||
NS_DispatchToMainThread(new AdapterPropertiesCallbackTask());
|
||||
}
|
||||
|
||||
class RemoteDevicePropertiesCallbackTask : public nsRunnable
|
||||
{
|
||||
const InfallibleTArray<BluetoothNamedValue> mProps;
|
||||
nsString mRemoteDeviceBdAddress;
|
||||
public:
|
||||
RemoteDevicePropertiesCallbackTask(
|
||||
const InfallibleTArray<BluetoothNamedValue>& aProps,
|
||||
const nsAString& aRemoteDeviceBdAddress)
|
||||
: mProps(aProps)
|
||||
, mRemoteDeviceBdAddress(aRemoteDeviceBdAddress)
|
||||
{ }
|
||||
|
||||
NS_IMETHOD
|
||||
Run()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (sRequestedDeviceCountArray.IsEmpty()) {
|
||||
// This is possible because the callback would be called after turning
|
||||
// Bluetooth on.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Update to registered BluetoothDevice objects
|
||||
BluetoothSignal signal(NS_LITERAL_STRING("PropertyChanged"),
|
||||
mRemoteDeviceBdAddress, mProps);
|
||||
nsRefPtr<DistributeBluetoothSignalTask>
|
||||
t = new DistributeBluetoothSignalTask(signal);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(t))) {
|
||||
BT_WARNING("Failed to dispatch to main thread!");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static InfallibleTArray<BluetoothNamedValue> sRemoteDevicesPack;
|
||||
|
||||
// Use address as the index
|
||||
sRemoteDevicesPack.AppendElement(
|
||||
BluetoothNamedValue(mRemoteDeviceBdAddress, mProps));
|
||||
|
||||
if (--sRequestedDeviceCountArray[0] == 0) {
|
||||
if (!sGetDeviceRunnableArray.IsEmpty()) {
|
||||
DispatchBluetoothReply(sGetDeviceRunnableArray[0],
|
||||
sRemoteDevicesPack, EmptyString());
|
||||
sGetDeviceRunnableArray.RemoveElementAt(0);
|
||||
}
|
||||
|
||||
sRequestedDeviceCountArray.RemoveElementAt(0);
|
||||
sRemoteDevicesPack.Clear();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* RemoteDevicePropertiesCallback will be called, as the following conditions:
|
||||
* 1. When BT is turning on, bluedroid automatically execute this callback
|
||||
|
@ -404,13 +508,6 @@ RemoteDevicePropertiesCallback(bt_status_t aStatus, bt_bdaddr_t *aBdAddress,
|
|||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
if (sRequestedDeviceCountArray.IsEmpty()) {
|
||||
MOZ_ASSERT(sGetDeviceRunnableArray.IsEmpty());
|
||||
return;
|
||||
}
|
||||
|
||||
sRequestedDeviceCountArray[0]--;
|
||||
|
||||
InfallibleTArray<BluetoothNamedValue> props;
|
||||
|
||||
nsString remoteDeviceBdAddress;
|
||||
|
@ -435,36 +532,9 @@ RemoteDevicePropertiesCallback(bt_status_t aStatus, bt_bdaddr_t *aBdAddress,
|
|||
}
|
||||
}
|
||||
|
||||
// Update to registered BluetoothDevice objects
|
||||
BluetoothSignal signal(NS_LITERAL_STRING("PropertyChanged"),
|
||||
remoteDeviceBdAddress, props);
|
||||
nsRefPtr<DistributeBluetoothSignalTask>
|
||||
t = new DistributeBluetoothSignalTask(signal);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(t))) {
|
||||
BT_WARNING("Failed to dispatch to main thread!");
|
||||
}
|
||||
|
||||
// Use address as the index
|
||||
sRemoteDevicesPack.AppendElement(
|
||||
BluetoothNamedValue(remoteDeviceBdAddress, props));
|
||||
|
||||
if (sRequestedDeviceCountArray[0] == 0) {
|
||||
MOZ_ASSERT(!sGetDeviceRunnableArray.IsEmpty());
|
||||
|
||||
if (sGetDeviceRunnableArray.IsEmpty()) {
|
||||
BT_LOGR("No runnable to return");
|
||||
return;
|
||||
}
|
||||
|
||||
DispatchBluetoothReply(sGetDeviceRunnableArray[0],
|
||||
sRemoteDevicesPack, EmptyString());
|
||||
|
||||
// After firing it, clean up cache
|
||||
sRemoteDevicesPack.Clear();
|
||||
|
||||
sRequestedDeviceCountArray.RemoveElementAt(0);
|
||||
sGetDeviceRunnableArray.RemoveElementAt(0);
|
||||
}
|
||||
// Redirect to main thread to avoid racing problem
|
||||
NS_DispatchToMainThread(
|
||||
new RemoteDevicePropertiesCallbackTask(props, remoteDeviceBdAddress));
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -511,18 +581,33 @@ DeviceFoundCallback(int aNumProperties, bt_property_t *aProperties)
|
|||
}
|
||||
}
|
||||
|
||||
class DiscoveryStateChangedCallbackTask MOZ_FINAL : public nsRunnable
|
||||
{
|
||||
public:
|
||||
NS_IMETHOD
|
||||
Run()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!sChangeDiscoveryRunnableArray.IsEmpty()) {
|
||||
BluetoothValue values(true);
|
||||
DispatchBluetoothReply(sChangeDiscoveryRunnableArray[0],
|
||||
values, EmptyString());
|
||||
|
||||
sChangeDiscoveryRunnableArray.RemoveElementAt(0);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
static void
|
||||
DiscoveryStateChangedCallback(bt_discovery_state_t aState)
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
if (!sChangeDiscoveryRunnableArray.IsEmpty()) {
|
||||
BluetoothValue values(true);
|
||||
DispatchBluetoothReply(sChangeDiscoveryRunnableArray[0],
|
||||
values, EmptyString());
|
||||
|
||||
sChangeDiscoveryRunnableArray.RemoveElementAt(0);
|
||||
}
|
||||
// Redirect to main thread to avoid racing problem
|
||||
NS_DispatchToMainThread(new DiscoveryStateChangedCallbackTask());
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -581,26 +666,75 @@ SspRequestCallback(bt_bdaddr_t* aRemoteBdAddress, bt_bdname_t* aRemoteBdName,
|
|||
}
|
||||
}
|
||||
|
||||
class BondStateChangedCallbackTask : public nsRunnable
|
||||
{
|
||||
nsString mRemoteDeviceBdAddress;
|
||||
bool mBonded;
|
||||
public:
|
||||
BondStateChangedCallbackTask(const nsAString& aRemoteDeviceBdAddress,
|
||||
bool aBonded)
|
||||
: mRemoteDeviceBdAddress(aRemoteDeviceBdAddress)
|
||||
, mBonded(aBonded)
|
||||
{ }
|
||||
|
||||
NS_IMETHOD
|
||||
Run()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (mBonded && !sBondingRunnableArray.IsEmpty()) {
|
||||
DispatchBluetoothReply(sBondingRunnableArray[0],
|
||||
BluetoothValue(true), EmptyString());
|
||||
|
||||
sBondingRunnableArray.RemoveElementAt(0);
|
||||
} else if (!mBonded && !sUnbondingRunnableArray.IsEmpty()) {
|
||||
DispatchBluetoothReply(sUnbondingRunnableArray[0],
|
||||
BluetoothValue(true), EmptyString());
|
||||
|
||||
sUnbondingRunnableArray.RemoveElementAt(0);
|
||||
}
|
||||
|
||||
// Update bonding status to gaia
|
||||
InfallibleTArray<BluetoothNamedValue> propertiesArray;
|
||||
BT_APPEND_NAMED_VALUE(propertiesArray, "address", mRemoteDeviceBdAddress);
|
||||
BT_APPEND_NAMED_VALUE(propertiesArray, "status", mBonded);
|
||||
|
||||
BluetoothSignal signal(NS_LITERAL_STRING(PAIRED_STATUS_CHANGED_ID),
|
||||
NS_LITERAL_STRING(KEY_ADAPTER),
|
||||
BluetoothValue(propertiesArray));
|
||||
NS_DispatchToMainThread(new DistributeBluetoothSignalTask(signal));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
static void
|
||||
BondStateChangedCallback(bt_status_t aStatus, bt_bdaddr_t* aRemoteBdAddress,
|
||||
bt_bond_state_t aState)
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
nsAutoString remoteAddress;
|
||||
BdAddressTypeToString(aRemoteBdAddress, remoteAddress);
|
||||
if (aState == BT_BOND_STATE_BONDING) {
|
||||
// No need to handle bonding state
|
||||
return;
|
||||
}
|
||||
|
||||
nsAutoString remoteBdAddress;
|
||||
BdAddressTypeToString(aRemoteBdAddress, remoteBdAddress);
|
||||
|
||||
if (aState == BT_BOND_STATE_BONDED &&
|
||||
sAdapterBondedAddressArray.Contains(remoteBdAddress)) {
|
||||
// See bug 940271 for more details about this case.
|
||||
return;
|
||||
}
|
||||
|
||||
// We don't need to handle bonding state
|
||||
NS_ENSURE_TRUE_VOID(aState != BT_BOND_STATE_BONDING);
|
||||
NS_ENSURE_FALSE_VOID(aState == BT_BOND_STATE_BONDED &&
|
||||
sAdapterBondedAddressArray.Contains(remoteAddress));
|
||||
bool bonded;
|
||||
if (aState == BT_BOND_STATE_NONE) {
|
||||
bonded = false;
|
||||
sAdapterBondedAddressArray.RemoveElement(remoteAddress);
|
||||
sAdapterBondedAddressArray.RemoveElement(remoteBdAddress);
|
||||
} else if (aState == BT_BOND_STATE_BONDED) {
|
||||
bonded = true;
|
||||
sAdapterBondedAddressArray.AppendElement(remoteAddress);
|
||||
sAdapterBondedAddressArray.AppendElement(remoteBdAddress);
|
||||
}
|
||||
|
||||
// Update bonded address list to BluetoothAdapter
|
||||
|
@ -614,27 +748,9 @@ BondStateChangedCallback(bt_status_t aStatus, bt_bdaddr_t* aRemoteBdAddress,
|
|||
BluetoothValue(propertiesChangeArray));
|
||||
NS_DispatchToMainThread(new DistributeBluetoothSignalTask(signal));
|
||||
|
||||
// Update bonding status to gaia
|
||||
InfallibleTArray<BluetoothNamedValue> propertiesArray;
|
||||
BT_APPEND_NAMED_VALUE(propertiesArray, "address", remoteAddress);
|
||||
BT_APPEND_NAMED_VALUE(propertiesArray, "status", bonded);
|
||||
|
||||
BluetoothSignal newSignal(NS_LITERAL_STRING(PAIRED_STATUS_CHANGED_ID),
|
||||
NS_LITERAL_STRING(KEY_ADAPTER),
|
||||
BluetoothValue(propertiesArray));
|
||||
NS_DispatchToMainThread(new DistributeBluetoothSignalTask(newSignal));
|
||||
|
||||
if (bonded && !sBondingRunnableArray.IsEmpty()) {
|
||||
DispatchBluetoothReply(sBondingRunnableArray[0],
|
||||
BluetoothValue(true), EmptyString());
|
||||
|
||||
sBondingRunnableArray.RemoveElementAt(0);
|
||||
} else if (!bonded && !sUnbondingRunnableArray.IsEmpty()) {
|
||||
DispatchBluetoothReply(sUnbondingRunnableArray[0],
|
||||
BluetoothValue(true), EmptyString());
|
||||
|
||||
sUnbondingRunnableArray.RemoveElementAt(0);
|
||||
}
|
||||
// Redirect to main thread to avoid racing problem
|
||||
NS_DispatchToMainThread(
|
||||
new BondStateChangedCallbackTask(remoteBdAddress, bonded));
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -673,15 +789,17 @@ EnsureBluetoothHalLoad()
|
|||
{
|
||||
hw_module_t* module;
|
||||
hw_device_t* device;
|
||||
|
||||
int err = hw_get_module(BT_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
|
||||
if (err != 0) {
|
||||
BT_LOGR("Error: %s", strerror(err));
|
||||
return false;
|
||||
}
|
||||
module->methods->open(module, BT_HARDWARE_MODULE_ID, &device);
|
||||
sBtDevice = (bluetooth_device_t *)device;
|
||||
NS_ENSURE_TRUE(sBtDevice, false);
|
||||
sBtInterface = sBtDevice->get_bluetooth_interface();
|
||||
bluetooth_device_t* btDevice = (bluetooth_device_t *)device;
|
||||
NS_ENSURE_TRUE(btDevice, false);
|
||||
|
||||
sBtInterface = btDevice->get_bluetooth_interface();
|
||||
NS_ENSURE_TRUE(sBtInterface, false);
|
||||
|
||||
return true;
|
||||
|
@ -709,12 +827,16 @@ static nsresult
|
|||
StartStopGonkBluetooth(bool aShouldEnable)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
NS_ENSURE_TRUE(sBtInterface, NS_ERROR_FAILURE);
|
||||
|
||||
if (sIsBtEnabled == aShouldEnable) {
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
NS_ENSURE_TRUE(bs, NS_ERROR_FAILURE);
|
||||
|
||||
if (bs->IsEnabled() == aShouldEnable) {
|
||||
// Keep current enable status
|
||||
nsRefPtr<nsRunnable> runnable =
|
||||
new BluetoothService::ToggleBtAck(sIsBtEnabled);
|
||||
new BluetoothService::ToggleBtAck(aShouldEnable);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
|
||||
BT_WARNING("Failed to dispatch to main thread!");
|
||||
}
|
||||
|
@ -772,10 +894,15 @@ BluetoothServiceBluedroid::~BluetoothServiceBluedroid()
|
|||
}
|
||||
|
||||
nsresult
|
||||
BluetoothServiceBluedroid::StartInternal()
|
||||
BluetoothServiceBluedroid::StartInternal(BluetoothReplyRunnable* aRunnable)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// aRunnable will be a nullptr while startup
|
||||
if(aRunnable) {
|
||||
sChangeAdapterStateRunnableArray.AppendElement(aRunnable);
|
||||
}
|
||||
|
||||
nsresult ret = StartStopGonkBluetooth(true);
|
||||
if (NS_FAILED(ret)) {
|
||||
nsRefPtr<nsRunnable> runnable =
|
||||
|
@ -790,10 +917,15 @@ BluetoothServiceBluedroid::StartInternal()
|
|||
}
|
||||
|
||||
nsresult
|
||||
BluetoothServiceBluedroid::StopInternal()
|
||||
BluetoothServiceBluedroid::StopInternal(BluetoothReplyRunnable* aRunnable)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// aRunnable will be a nullptr during starup and shutdown
|
||||
if(aRunnable) {
|
||||
sChangeAdapterStateRunnableArray.AppendElement(aRunnable);
|
||||
}
|
||||
|
||||
nsresult ret = StartStopGonkBluetooth(false);
|
||||
if (NS_FAILED(ret)) {
|
||||
nsRefPtr<nsRunnable> runnable =
|
||||
|
@ -828,6 +960,11 @@ BluetoothServiceBluedroid::GetAdaptersInternal(
|
|||
uint32_t numAdapters = 1; // Bluedroid supports single adapter only
|
||||
|
||||
for (uint32_t i = 0; i < numAdapters; i++) {
|
||||
// Since Atomic<*> is not acceptable for BT_APPEND_NAMED_VALUE(),
|
||||
// create another variable to store data.
|
||||
bool discoverable = sAdapterDiscoverable;
|
||||
uint32_t discoverableTimeout = sAdapterDiscoverableTimeout;
|
||||
|
||||
BluetoothValue properties = InfallibleTArray<BluetoothNamedValue>();
|
||||
|
||||
// TODO: Revise here based on new BluetoothAdapter interface
|
||||
|
@ -836,9 +973,9 @@ BluetoothServiceBluedroid::GetAdaptersInternal(
|
|||
BT_APPEND_NAMED_VALUE(properties.get_ArrayOfBluetoothNamedValue(),
|
||||
"Name", sAdapterBdName);
|
||||
BT_APPEND_NAMED_VALUE(properties.get_ArrayOfBluetoothNamedValue(),
|
||||
"Discoverable", sAdapterDiscoverable);
|
||||
"Discoverable", discoverable);
|
||||
BT_APPEND_NAMED_VALUE(properties.get_ArrayOfBluetoothNamedValue(),
|
||||
"DiscoverableTimeout", sAdapterDiscoverableTimeout);
|
||||
"DiscoverableTimeout", discoverableTimeout);
|
||||
BT_APPEND_NAMED_VALUE(properties.get_ArrayOfBluetoothNamedValue(),
|
||||
"Devices", sAdapterBondedAddressArray);
|
||||
|
||||
|
@ -856,11 +993,7 @@ BluetoothServiceBluedroid::GetConnectedDevicePropertiesInternal(
|
|||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!IsReady()) {
|
||||
NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!");
|
||||
DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr);
|
||||
return NS_OK;
|
||||
}
|
||||
ENSURE_BLUETOOTH_IS_READY(aRunnable, NS_OK);
|
||||
|
||||
BluetoothProfileManagerBase* profile =
|
||||
BluetoothUuidHelper::GetBluetoothProfileManager(aServiceUuid);
|
||||
|
@ -910,11 +1043,7 @@ BluetoothServiceBluedroid::GetPairedDevicePropertiesInternal(
|
|||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!IsReady()) {
|
||||
NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!");
|
||||
DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr);
|
||||
return NS_OK;
|
||||
}
|
||||
ENSURE_BLUETOOTH_IS_READY(aRunnable, NS_OK);
|
||||
|
||||
int requestedDeviceCount = aDeviceAddress.Length();
|
||||
if (requestedDeviceCount == 0) {
|
||||
|
@ -947,12 +1076,8 @@ BluetoothServiceBluedroid::StartDiscoveryInternal(
|
|||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!IsReady()) {
|
||||
NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!");
|
||||
DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr);
|
||||
ENSURE_BLUETOOTH_IS_READY(aRunnable, NS_OK);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
int ret = sBtInterface->start_discovery();
|
||||
if (ret != BT_STATUS_SUCCESS) {
|
||||
ReplyStatusError(aRunnable, ret, NS_LITERAL_STRING("StartDiscovery"));
|
||||
|
@ -970,11 +1095,7 @@ BluetoothServiceBluedroid::StopDiscoveryInternal(
|
|||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!IsReady()) {
|
||||
NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!");
|
||||
DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr);
|
||||
return NS_OK;
|
||||
}
|
||||
ENSURE_BLUETOOTH_IS_READY(aRunnable, NS_OK);
|
||||
|
||||
int ret = sBtInterface->cancel_discovery();
|
||||
if (ret != BT_STATUS_SUCCESS) {
|
||||
|
@ -994,12 +1115,7 @@ BluetoothServiceBluedroid::SetProperty(BluetoothObjectType aType,
|
|||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!IsReady()) {
|
||||
NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!");
|
||||
DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
ENSURE_BLUETOOTH_IS_READY(aRunnable, NS_OK);
|
||||
|
||||
const nsString propName = aValue.name();
|
||||
bt_property_t prop;
|
||||
|
@ -1072,11 +1188,7 @@ BluetoothServiceBluedroid::CreatePairedDeviceInternal(
|
|||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!IsReady()) {
|
||||
NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!");
|
||||
DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr);
|
||||
return NS_OK;
|
||||
}
|
||||
ENSURE_BLUETOOTH_IS_READY(aRunnable, NS_OK);
|
||||
|
||||
bt_bdaddr_t remoteAddress;
|
||||
StringToBdAddressType(aDeviceAddress, &remoteAddress);
|
||||
|
@ -1097,11 +1209,7 @@ BluetoothServiceBluedroid::RemoveDeviceInternal(
|
|||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!IsReady()) {
|
||||
NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!");
|
||||
DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr);
|
||||
return NS_OK;
|
||||
}
|
||||
ENSURE_BLUETOOTH_IS_READY(aRunnable, NS_OK);
|
||||
|
||||
bt_bdaddr_t remoteAddress;
|
||||
StringToBdAddressType(aDeviceAddress, &remoteAddress);
|
||||
|
@ -1124,11 +1232,7 @@ BluetoothServiceBluedroid::SetPinCodeInternal(
|
|||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!IsReady()) {
|
||||
NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!");
|
||||
DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr);
|
||||
return false;
|
||||
}
|
||||
ENSURE_BLUETOOTH_IS_READY(aRunnable, false);
|
||||
|
||||
bt_bdaddr_t remoteAddress;
|
||||
StringToBdAddressType(aDeviceAddress, &remoteAddress);
|
||||
|
@ -1161,11 +1265,7 @@ BluetoothServiceBluedroid::SetPairingConfirmationInternal(
|
|||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!IsReady()) {
|
||||
NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!");
|
||||
DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr);
|
||||
return false;
|
||||
}
|
||||
ENSURE_BLUETOOTH_IS_READY(aRunnable, false);
|
||||
|
||||
bt_bdaddr_t remoteAddress;
|
||||
StringToBdAddressType(aDeviceAddress, &remoteAddress);
|
||||
|
|
|
@ -22,8 +22,8 @@ public:
|
|||
BluetoothServiceBluedroid();
|
||||
~BluetoothServiceBluedroid();
|
||||
|
||||
virtual nsresult StartInternal();
|
||||
virtual nsresult StopInternal();
|
||||
virtual nsresult StartInternal(BluetoothReplyRunnable* aRunnable);
|
||||
virtual nsresult StopInternal(BluetoothReplyRunnable* aRunnable);
|
||||
|
||||
virtual nsresult
|
||||
GetAdaptersInternal(BluetoothReplyRunnable* aRunnable);
|
||||
|
|
|
@ -2097,8 +2097,10 @@ public:
|
|||
};
|
||||
|
||||
nsresult
|
||||
BluetoothDBusService::StartInternal()
|
||||
BluetoothDBusService::StartInternal(BluetoothReplyRunnable* aRunnable)
|
||||
{
|
||||
MOZ_ASSERT(!aRunnable);
|
||||
|
||||
nsRefPtr<nsRunnable> runnable = new StartBluetoothRunnable();
|
||||
nsresult rv = DispatchToBtThread(runnable);
|
||||
if (NS_FAILED(rv)) {
|
||||
|
@ -2225,8 +2227,10 @@ public:
|
|||
};
|
||||
|
||||
nsresult
|
||||
BluetoothDBusService::StopInternal()
|
||||
BluetoothDBusService::StopInternal(BluetoothReplyRunnable* aRunnable)
|
||||
{
|
||||
MOZ_ASSERT(!aRunnable);
|
||||
|
||||
nsRefPtr<nsRunnable> runnable = new StopBluetoothRunnable();
|
||||
nsresult rv = DispatchToBtThread(runnable);
|
||||
if (NS_FAILED(rv)) {
|
||||
|
|
|
@ -47,9 +47,9 @@ public:
|
|||
|
||||
bool IsReady();
|
||||
|
||||
virtual nsresult StartInternal() MOZ_OVERRIDE;
|
||||
virtual nsresult StartInternal(BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
|
||||
|
||||
virtual nsresult StopInternal() MOZ_OVERRIDE;
|
||||
virtual nsresult StopInternal(BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
|
||||
|
||||
virtual nsresult
|
||||
GetAdaptersInternal(BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
|
||||
|
|
|
@ -192,6 +192,10 @@ BluetoothParent::RecvPBluetoothRequestConstructor(
|
|||
switch (aRequest.type()) {
|
||||
case Request::TGetAdaptersRequest:
|
||||
return actor->DoRequest(aRequest.get_GetAdaptersRequest());
|
||||
case Request::TStartBluetoothRequest:
|
||||
return actor->DoRequest(aRequest.get_StartBluetoothRequest());
|
||||
case Request::TStopBluetoothRequest:
|
||||
return actor->DoRequest(aRequest.get_StopBluetoothRequest());
|
||||
case Request::TSetPropertyRequest:
|
||||
return actor->DoRequest(aRequest.get_SetPropertyRequest());
|
||||
case Request::TStartDiscoveryRequest:
|
||||
|
@ -321,6 +325,30 @@ BluetoothRequestParent::DoRequest(const GetAdaptersRequest& aRequest)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BluetoothRequestParent::DoRequest(const StartBluetoothRequest& aRequest)
|
||||
{
|
||||
MOZ_ASSERT(mService);
|
||||
MOZ_ASSERT(mRequestType == Request::TStartBluetoothRequest);
|
||||
|
||||
nsresult rv = mService->StartInternal(mReplyRunnable.get());
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BluetoothRequestParent::DoRequest(const StopBluetoothRequest& aRequest)
|
||||
{
|
||||
MOZ_ASSERT(mService);
|
||||
MOZ_ASSERT(mRequestType == Request::TStopBluetoothRequest);
|
||||
|
||||
nsresult rv = mService->StopInternal(mReplyRunnable.get());
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BluetoothRequestParent::DoRequest(const SetPropertyRequest& aRequest)
|
||||
{
|
||||
|
|
|
@ -128,6 +128,12 @@ protected:
|
|||
bool
|
||||
DoRequest(const GetAdaptersRequest& aRequest);
|
||||
|
||||
bool
|
||||
DoRequest(const StartBluetoothRequest& aRequest);
|
||||
|
||||
bool
|
||||
DoRequest(const StopBluetoothRequest& aRequest);
|
||||
|
||||
bool
|
||||
DoRequest(const SetPropertyRequest& aRequest);
|
||||
|
||||
|
|
|
@ -103,6 +103,20 @@ BluetoothServiceChildProcess::GetAdaptersInternal(
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
BluetoothServiceChildProcess::StartInternal(BluetoothReplyRunnable* aRunnable)
|
||||
{
|
||||
SendRequest(aRunnable, StartBluetoothRequest());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
BluetoothServiceChildProcess::StopInternal(BluetoothReplyRunnable* aRunnable)
|
||||
{
|
||||
SendRequest(aRunnable, StopBluetoothRequest());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
BluetoothServiceChildProcess::GetConnectedDevicePropertiesInternal(
|
||||
uint16_t aServiceUuid,
|
||||
|
@ -377,18 +391,6 @@ BluetoothServiceChildProcess::HandleShutdown()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
BluetoothServiceChildProcess::StartInternal()
|
||||
{
|
||||
MOZ_CRASH("This should never be called!");
|
||||
}
|
||||
|
||||
nsresult
|
||||
BluetoothServiceChildProcess::StopInternal()
|
||||
{
|
||||
MOZ_CRASH("This should never be called!");
|
||||
}
|
||||
|
||||
bool
|
||||
BluetoothServiceChildProcess::IsConnected(uint16_t aServiceUuid)
|
||||
{
|
||||
|
|
|
@ -46,6 +46,12 @@ public:
|
|||
virtual nsresult
|
||||
GetAdaptersInternal(BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
|
||||
|
||||
virtual nsresult
|
||||
StartInternal(BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
|
||||
|
||||
virtual nsresult
|
||||
StopInternal(BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
|
||||
|
||||
virtual nsresult
|
||||
GetPairedDevicePropertiesInternal(const nsTArray<nsString>& aDeviceAddresses,
|
||||
BluetoothReplyRunnable* aRunnable)
|
||||
|
@ -199,14 +205,6 @@ protected:
|
|||
HandleShutdown() MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
// This method should never be called.
|
||||
virtual nsresult
|
||||
StartInternal() MOZ_OVERRIDE;
|
||||
|
||||
// This method should never be called.
|
||||
virtual nsresult
|
||||
StopInternal() MOZ_OVERRIDE;
|
||||
|
||||
bool
|
||||
IsSignalRegistered(const nsAString& aNodeName) {
|
||||
return !!mBluetoothSignalObserverTable.Get(aNodeName);
|
||||
|
|
|
@ -25,6 +25,14 @@ namespace bluetooth {
|
|||
struct GetAdaptersRequest
|
||||
{ };
|
||||
|
||||
struct StartBluetoothRequest
|
||||
{
|
||||
};
|
||||
|
||||
struct StopBluetoothRequest
|
||||
{
|
||||
};
|
||||
|
||||
struct SetPropertyRequest
|
||||
{
|
||||
BluetoothObjectType type;
|
||||
|
@ -166,6 +174,8 @@ struct SendPlayStatusRequest
|
|||
union Request
|
||||
{
|
||||
GetAdaptersRequest;
|
||||
StartBluetoothRequest;
|
||||
StopBluetoothRequest;
|
||||
SetPropertyRequest;
|
||||
GetPropertyRequest;
|
||||
StartDiscoveryRequest;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "domstubs.idl"
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIDOMMozCellBroadcastEtwsInfo;
|
||||
|
@ -10,7 +11,7 @@ interface nsIDOMMozCellBroadcastEtwsInfo;
|
|||
* MozCellBroadcastMessage encapsulates Cell Broadcast short message service
|
||||
* (CBS) messages.
|
||||
*/
|
||||
[scriptable, uuid(6abe65de-6729-41f7-906a-3f3a2dbe30ae)]
|
||||
[scriptable, uuid(701e74a9-5fc4-4e2d-a324-9b7693395159)]
|
||||
interface nsIDOMMozCellBroadcastMessage : nsISupports
|
||||
{
|
||||
/**
|
||||
|
@ -53,7 +54,7 @@ interface nsIDOMMozCellBroadcastMessage : nsISupports
|
|||
/**
|
||||
* System time stamp at receival.
|
||||
*/
|
||||
readonly attribute jsval timestamp; // jsval is for Date.
|
||||
readonly attribute DOMTimeStamp timestamp;
|
||||
|
||||
/**
|
||||
* Additional ETWS-specific info.
|
||||
|
|
|
@ -196,7 +196,7 @@ function testReceiving_ETWS_Timestamp() {
|
|||
doTestHelper(pdu, testReceiving_ETWS_WarningType, function(message) {
|
||||
// Cell Broadcast messages do not contain a timestamp field (however, ETWS
|
||||
// does). We only check the timestamp doesn't go too far (60 seconds) here.
|
||||
let msMessage = message.timestamp.getTime();
|
||||
let msMessage = message.timestamp;
|
||||
let msNow = Date.now();
|
||||
ok(Math.abs(msMessage - msNow) < (1000 * 60), "message.timestamp");
|
||||
});
|
||||
|
|
|
@ -361,7 +361,7 @@ function testReceiving_GSM_Timestamp() {
|
|||
doTestHelper(pdu, testReceiving_GSM_WarningType, function(message) {
|
||||
// Cell Broadcast messages do not contain a timestamp field (however, ETWS
|
||||
// does). We only check the timestamp doesn't go too far (60 seconds) here.
|
||||
let msMessage = message.timestamp.getTime();
|
||||
let msMessage = message.timestamp;
|
||||
let msNow = Date.now();
|
||||
ok(Math.abs(msMessage - msNow) < (1000 * 60), "message.timestamp");
|
||||
});
|
||||
|
|
|
@ -7,5 +7,3 @@
|
|||
DIRS += ['interfaces', 'src']
|
||||
TEST_DIRS += ['tests']
|
||||
|
||||
if CONFIG['ENABLE_TESTS']:
|
||||
XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell.ini']
|
||||
|
|
|
@ -4,5 +4,8 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
MOCHITEST_MANIFESTS += ['mochitest.ini']
|
||||
MOCHITEST_MANIFESTS += ['mochitest/mochitest.ini']
|
||||
|
||||
if CONFIG['ENABLE_TESTS']:
|
||||
XPCSHELL_TESTS_MANIFESTS += ['xpcshell/xpcshell.ini']
|
||||
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
[DEFAULT]
|
||||
head = header_helpers.js
|
||||
tail =
|
||||
support-files =
|
||||
test_sms_basics.html
|
||||
test_smsfilter.html
|
||||
|
||||
[test_smsservice_createsmsmessage.js]
|
||||
[test_wsp_pdu_helper.js]
|
|
@ -312,7 +312,7 @@ function CellBroadcastMessage(pdu) {
|
|||
this.language = pdu.language;
|
||||
this.body = pdu.fullBody;
|
||||
this.messageClass = pdu.messageClass;
|
||||
this.timestamp = new Date(pdu.timestamp);
|
||||
this.timestamp = pdu.timestamp;
|
||||
|
||||
if (pdu.etws != null) {
|
||||
this.etws = new CellBroadcastEtwsInfo(pdu.etws);
|
||||
|
|
|
@ -12791,9 +12791,17 @@ SimRecordHelperObject.prototype = {
|
|||
|
||||
let ICCUtilsHelper = this.context.ICCUtilsHelper;
|
||||
let RIL = this.context.RIL;
|
||||
// TS 31.102, clause 4.2.18 EFAD
|
||||
let mncLength = 0;
|
||||
if (ad && ad[3]) {
|
||||
mncLength = ad[3] & 0x0f;
|
||||
if (mncLength != 0x02 && mncLength != 0x03) {
|
||||
mncLength = 0;
|
||||
}
|
||||
}
|
||||
// The 4th byte of the response is the length of MNC.
|
||||
let mccMnc = ICCUtilsHelper.parseMccMncFromImsi(RIL.iccInfoPrivate.imsi,
|
||||
ad && ad[3]);
|
||||
mncLength);
|
||||
if (mccMnc) {
|
||||
RIL.iccInfo.mcc = mccMnc.mcc;
|
||||
RIL.iccInfo.mnc = mccMnc.mnc;
|
||||
|
@ -14130,6 +14138,7 @@ ICCUtilsHelperObject.prototype = {
|
|||
* The imsi of icc.
|
||||
* @param mncLength [optional]
|
||||
* The length of mnc.
|
||||
* Zero indicates we haven't got a valid mnc length.
|
||||
*
|
||||
* @return An object contains the parsing result of mcc and mnc.
|
||||
* Or null if any error occurred.
|
||||
|
|
|
@ -2504,7 +2504,7 @@ add_test(function test_reading_ad_and_parsing_mcc_mnc() {
|
|||
|
||||
io.loadTransparentEF = function fakeLoadTransparentEF(options) {
|
||||
let ad = [0x00, 0x00, 0x00];
|
||||
if (mncLengthInEf) {
|
||||
if (typeof mncLengthInEf === 'number') {
|
||||
ad.push(mncLengthInEf);
|
||||
}
|
||||
|
||||
|
@ -2531,9 +2531,20 @@ add_test(function test_reading_ad_and_parsing_mcc_mnc() {
|
|||
}
|
||||
|
||||
do_test(undefined, "466923202422409", "466", "92" );
|
||||
do_test(0x00, "466923202422409", "466", "92" );
|
||||
do_test(0x01, "466923202422409", "466", "92" );
|
||||
do_test(0x02, "466923202422409", "466", "92" );
|
||||
do_test(0x03, "466923202422409", "466", "923");
|
||||
do_test(0x04, "466923202422409", "466", "92" );
|
||||
do_test(0xff, "466923202422409", "466", "92" );
|
||||
|
||||
do_test(undefined, "310260542718417", "310", "260");
|
||||
do_test(0x00, "310260542718417", "310", "260");
|
||||
do_test(0x01, "310260542718417", "310", "260");
|
||||
do_test(0x02, "310260542718417", "310", "26" );
|
||||
do_test(0x03, "310260542718417", "310", "260");
|
||||
do_test(0x04, "310260542718417", "310", "260");
|
||||
do_test(0xff, "310260542718417", "310", "260");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
|
|
@ -121,6 +121,13 @@ public class AppConstants {
|
|||
false;
|
||||
#endif
|
||||
|
||||
public static final boolean MOZ_LOCALE_SWITCHER =
|
||||
#ifdef MOZ_LOCALE_SWITCHER
|
||||
true;
|
||||
#else
|
||||
false;
|
||||
#endif
|
||||
|
||||
public static final boolean MOZ_UPDATER =
|
||||
#ifdef MOZ_UPDATER
|
||||
true;
|
||||
|
|
|
@ -75,6 +75,10 @@ public class BrowserLocaleManager implements LocaleManager {
|
|||
}
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return AppConstants.MOZ_LOCALE_SWITCHER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gecko uses locale codes like "es-ES", whereas a Java {@link Locale}
|
||||
* stringifies as "es_ES".
|
||||
|
|
|
@ -18,6 +18,11 @@ import android.content.res.Resources;
|
|||
*/
|
||||
public interface LocaleManager {
|
||||
void initialize(Context context);
|
||||
|
||||
/**
|
||||
* @return true if locale switching is enabled.
|
||||
*/
|
||||
boolean isEnabled();
|
||||
Locale getCurrentLocale(Context context);
|
||||
String getAndApplyPersistedLocale(Context context);
|
||||
void correctLocale(Context context, Resources resources, Configuration newConfig);
|
||||
|
|
|
@ -188,13 +188,18 @@ public class Tabs implements GeckoEventListener {
|
|||
}
|
||||
}
|
||||
|
||||
private Tab addTab(int id, String url, boolean external, int parentId, String title, boolean isPrivate) {
|
||||
private Tab addTab(int id, String url, boolean external, int parentId, String title, boolean isPrivate, int tabIndex) {
|
||||
final Tab tab = isPrivate ? new PrivateTab(mAppContext, id, url, external, parentId, title) :
|
||||
new Tab(mAppContext, id, url, external, parentId, title);
|
||||
synchronized (this) {
|
||||
lazyRegisterBookmarkObserver();
|
||||
mTabs.put(id, tab);
|
||||
mOrder.add(tab);
|
||||
|
||||
if (tabIndex > -1) {
|
||||
mOrder.add(tabIndex, tab);
|
||||
} else {
|
||||
mOrder.add(tab);
|
||||
}
|
||||
}
|
||||
|
||||
// Suppress the ADDED event to prevent animation of tabs created via session restore.
|
||||
|
@ -427,7 +432,8 @@ public class Tabs implements GeckoEventListener {
|
|||
tab = addTab(id, url, message.getBoolean("external"),
|
||||
message.getInt("parentId"),
|
||||
message.getString("title"),
|
||||
message.getBoolean("isPrivate"));
|
||||
message.getBoolean("isPrivate"),
|
||||
message.getInt("tabIndex"));
|
||||
|
||||
// If we added the tab as a stub, we should have already
|
||||
// selected it, so ignore this flag for stubbed tabs.
|
||||
|
@ -799,7 +805,10 @@ public class Tabs implements GeckoEventListener {
|
|||
// long as it's a valid URI.
|
||||
String tabUrl = (url != null && Uri.parse(url).getScheme() != null) ? url : null;
|
||||
|
||||
added = addTab(tabId, tabUrl, external, parentId, url, isPrivate);
|
||||
// Add the new tab to the end of the tab order.
|
||||
final int tabIndex = -1;
|
||||
|
||||
added = addTab(tabId, tabUrl, external, parentId, url, isPrivate, tabIndex);
|
||||
added.setDesktopMode(desktopMode);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
|
|
@ -6,13 +6,11 @@ package org.mozilla.gecko.fxa.activities;
|
|||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.mozilla.gecko.AppConstants;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.background.common.log.Logger;
|
||||
import org.mozilla.gecko.background.fxa.FxAccountAgeLockoutHelper;
|
||||
import org.mozilla.gecko.fxa.FirefoxAccounts;
|
||||
import org.mozilla.gecko.fxa.FxAccountConstants;
|
||||
import org.mozilla.gecko.sync.Utils;
|
||||
import org.mozilla.gecko.sync.setup.activities.ActivityUtils;
|
||||
import org.mozilla.gecko.sync.setup.activities.LocaleAware;
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import org.mozilla.gecko.fxa.login.Married;
|
|||
import org.mozilla.gecko.fxa.login.State;
|
||||
import org.mozilla.gecko.fxa.sync.FxAccountSyncStatusHelper;
|
||||
import org.mozilla.gecko.fxa.tasks.FxAccountCodeResender;
|
||||
import org.mozilla.gecko.sync.SharedPreferencesClientsDataDelegate;
|
||||
import org.mozilla.gecko.sync.SyncConfiguration;
|
||||
|
||||
import android.accounts.Account;
|
||||
|
@ -28,10 +29,13 @@ import android.content.SharedPreferences;
|
|||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.preference.CheckBoxPreference;
|
||||
import android.preference.EditTextPreference;
|
||||
import android.preference.Preference;
|
||||
import android.preference.Preference.OnPreferenceChangeListener;
|
||||
import android.preference.Preference.OnPreferenceClickListener;
|
||||
import android.preference.PreferenceCategory;
|
||||
import android.preference.PreferenceScreen;
|
||||
import android.text.TextUtils;
|
||||
|
||||
/**
|
||||
* A fragment that displays the status of an AndroidFxAccount.
|
||||
|
@ -39,7 +43,9 @@ import android.preference.PreferenceScreen;
|
|||
* The owning activity is responsible for providing an AndroidFxAccount at
|
||||
* appropriate times.
|
||||
*/
|
||||
public class FxAccountStatusFragment extends PreferenceFragment implements OnPreferenceClickListener {
|
||||
public class FxAccountStatusFragment
|
||||
extends PreferenceFragment
|
||||
implements OnPreferenceClickListener, OnPreferenceChangeListener {
|
||||
private static final String LOG_TAG = FxAccountStatusFragment.class.getSimpleName();
|
||||
|
||||
// When a checkbox is toggled, wait 5 seconds (for other checkbox actions)
|
||||
|
@ -65,7 +71,12 @@ public class FxAccountStatusFragment extends PreferenceFragment implements OnPre
|
|||
protected CheckBoxPreference tabsPreference;
|
||||
protected CheckBoxPreference passwordsPreference;
|
||||
|
||||
protected EditTextPreference deviceNamePreference;
|
||||
|
||||
protected volatile AndroidFxAccount fxAccount;
|
||||
// The contract is: when fxAccount is non-null, then clientsDataDelegate is
|
||||
// non-null. If violated then an IllegalStateException is thrown.
|
||||
protected volatile SharedPreferencesClientsDataDelegate clientsDataDelegate;
|
||||
|
||||
// Used to post delayed sync requests.
|
||||
protected Handler handler;
|
||||
|
@ -88,6 +99,10 @@ public class FxAccountStatusFragment extends PreferenceFragment implements OnPre
|
|||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
addPreferences();
|
||||
}
|
||||
|
||||
protected void addPreferences() {
|
||||
addPreferencesFromResource(R.xml.fxaccount_status_prefscreen);
|
||||
|
||||
emailPreference = ensureFindPreference("email");
|
||||
|
@ -119,6 +134,9 @@ public class FxAccountStatusFragment extends PreferenceFragment implements OnPre
|
|||
historyPreference.setOnPreferenceClickListener(this);
|
||||
tabsPreference.setOnPreferenceClickListener(this);
|
||||
passwordsPreference.setOnPreferenceClickListener(this);
|
||||
|
||||
deviceNamePreference = (EditTextPreference) ensureFindPreference("device_name");
|
||||
deviceNamePreference.setOnPreferenceChangeListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -177,6 +195,8 @@ public class FxAccountStatusFragment extends PreferenceFragment implements OnPre
|
|||
historyPreference.setEnabled(enabled);
|
||||
tabsPreference.setEnabled(enabled);
|
||||
passwordsPreference.setEnabled(enabled);
|
||||
// Since we can't sync, we can't update our remote client record.
|
||||
deviceNamePreference.setEnabled(enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -294,6 +314,14 @@ public class FxAccountStatusFragment extends PreferenceFragment implements OnPre
|
|||
throw new IllegalArgumentException("fxAccount must not be null");
|
||||
}
|
||||
this.fxAccount = fxAccount;
|
||||
try {
|
||||
this.clientsDataDelegate = new SharedPreferencesClientsDataDelegate(fxAccount.getSyncPrefs());
|
||||
} catch (Exception e) {
|
||||
Logger.error(LOG_TAG, "Got exception fetching Sync prefs associated to Firefox Account; aborting.", e);
|
||||
// Something is terribly wrong; best to get a stack trace rather than
|
||||
// continue with a null clients delegate.
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
|
||||
handler = new Handler(); // Attached to current (assumed to be UI) thread.
|
||||
|
||||
|
@ -319,6 +347,17 @@ public class FxAccountStatusFragment extends PreferenceFragment implements OnPre
|
|||
FxAccountSyncStatusHelper.getInstance().stopObserving(syncStatusDelegate);
|
||||
}
|
||||
|
||||
protected void hardRefresh() {
|
||||
// This is the only way to guarantee that the EditText dialogs created by
|
||||
// EditTextPreferences are re-created. This works around the issue described
|
||||
// at http://androiddev.orkitra.com/?p=112079.
|
||||
final PreferenceScreen statusScreen = (PreferenceScreen) ensureFindPreference("status_screen");
|
||||
statusScreen.removeAll();
|
||||
addPreferences();
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
protected void refresh() {
|
||||
// refresh is called from our onResume, which can happen before the owning
|
||||
// Activity tells us about an account (via our public
|
||||
|
@ -372,6 +411,10 @@ public class FxAccountStatusFragment extends PreferenceFragment implements OnPre
|
|||
// No matter our state, we should update the checkboxes.
|
||||
updateSelectedEngines();
|
||||
}
|
||||
|
||||
final String clientName = clientsDataDelegate.getClientName();
|
||||
deviceNamePreference.setSummary(clientName);
|
||||
deviceNamePreference.setText(clientName);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -571,4 +614,22 @@ public class FxAccountStatusFragment extends PreferenceFragment implements OnPre
|
|||
button.setOnPreferenceClickListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
if (preference == deviceNamePreference) {
|
||||
String newClientName = (String) newValue;
|
||||
if (TextUtils.isEmpty(newClientName)) {
|
||||
newClientName = clientsDataDelegate.getDefaultClientName();
|
||||
}
|
||||
final long now = System.currentTimeMillis();
|
||||
clientsDataDelegate.setClientName(newClientName, now);
|
||||
requestDelayedSync(); // Try to update our remote client record.
|
||||
hardRefresh(); // Updates the value displayed to the user, among other things.
|
||||
return true;
|
||||
}
|
||||
|
||||
// For everything else, accept the change.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -357,7 +357,11 @@ public class FxAccountSyncAdapter extends AbstractThreadedSyncAdapter {
|
|||
|
||||
FxAccountGlobalSession globalSession = null;
|
||||
try {
|
||||
ClientsDataDelegate clientsDataDelegate = new SharedPreferencesClientsDataDelegate(sharedPrefs);
|
||||
final ClientsDataDelegate clientsDataDelegate = new SharedPreferencesClientsDataDelegate(sharedPrefs);
|
||||
if (FxAccountConstants.LOG_PERSONAL_INFORMATION) {
|
||||
FxAccountConstants.pii(LOG_TAG, "Client device name is: '" + clientsDataDelegate.getClientName() + "'.");
|
||||
FxAccountConstants.pii(LOG_TAG, "Client device data last modified: " + clientsDataDelegate.getLastModifiedTimestamp());
|
||||
}
|
||||
|
||||
// We compute skew over time using SkewHandler. This yields an unchanging
|
||||
// skew adjustment that the HawkAuthHeaderProvider uses to adjust its
|
||||
|
|
|
@ -175,6 +175,7 @@
|
|||
|
||||
<!ENTITY fxaccount_status_header2 'Firefox Account'>
|
||||
<!ENTITY fxaccount_status_signed_in_as 'Signed in as'>
|
||||
<!ENTITY fxaccount_status_device_name 'Device name'>
|
||||
<!ENTITY fxaccount_status_sync '&syncBrand.shortName.label;'>
|
||||
<!ENTITY fxaccount_status_sync_enabled '&syncBrand.shortName.label;: enabled'>
|
||||
<!ENTITY fxaccount_status_needs_verification2 'Your account needs to be verified. Tap to resend verification email.'>
|
||||
|
|
|
@ -131,6 +131,7 @@ OnSharedPreferenceChangeListener
|
|||
* Track the last locale so we know whether to redisplay.
|
||||
*/
|
||||
private Locale lastLocale = Locale.getDefault();
|
||||
private boolean localeSwitchingIsEnabled;
|
||||
|
||||
private void updateActionBarTitle(int title) {
|
||||
if (Build.VERSION.SDK_INT >= 14) {
|
||||
|
@ -268,6 +269,10 @@ OnSharedPreferenceChangeListener
|
|||
// Apply the current user-selected locale, if necessary.
|
||||
checkLocale();
|
||||
|
||||
// Track this so we can decide whether to show locale options.
|
||||
// See also the workaround below for Bug 1015209.
|
||||
localeSwitchingIsEnabled = BrowserLocaleManager.getInstance().isEnabled();
|
||||
|
||||
// For Android v11+ where we use Fragments (v11+ only due to bug 866352),
|
||||
// check that PreferenceActivity.EXTRA_SHOW_FRAGMENT has been set
|
||||
// (or set it) before super.onCreate() is called so Android can display
|
||||
|
@ -283,10 +288,17 @@ OnSharedPreferenceChangeListener
|
|||
updateTitle(getString(R.string.pref_header_customize));
|
||||
}
|
||||
|
||||
// So that Android doesn't put the fragment title (or nothing at
|
||||
// all) in the action bar.
|
||||
if (onIsMultiPane()) {
|
||||
// So that Android doesn't put the fragment title (or nothing at
|
||||
// all) in the action bar.
|
||||
updateActionBarTitle(R.string.settings_title);
|
||||
|
||||
if (Build.VERSION.SDK_INT < 13) {
|
||||
// Affected by Bug 1015209 -- no detach/attach.
|
||||
// If we try rejigging fragments, we'll crash, so don't
|
||||
// enable locale switching at all.
|
||||
localeSwitchingIsEnabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -397,6 +409,18 @@ OnSharedPreferenceChangeListener
|
|||
public void onBuildHeaders(List<Header> target) {
|
||||
if (onIsMultiPane()) {
|
||||
loadHeadersFromResource(R.xml.preference_headers, target);
|
||||
|
||||
// If locale switching is disabled, remove the section
|
||||
// entirely. This logic will need to be extended when
|
||||
// content language selection (Bug 881510) is implemented.
|
||||
if (!localeSwitchingIsEnabled) {
|
||||
for (Header header : target) {
|
||||
if (header.id == R.id.pref_header_language) {
|
||||
target.remove(header);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -564,9 +588,20 @@ OnSharedPreferenceChangeListener
|
|||
private void setupPreferences(PreferenceGroup preferences, ArrayList<String> prefs) {
|
||||
for (int i = 0; i < preferences.getPreferenceCount(); i++) {
|
||||
Preference pref = preferences.getPreference(i);
|
||||
|
||||
// Eliminate locale switching if necessary.
|
||||
// This logic will need to be extended when
|
||||
// content language selection (Bug 881510) is implemented.
|
||||
if (!localeSwitchingIsEnabled &&
|
||||
"preferences_locale".equals(pref.getExtras().getString("resource", null))) {
|
||||
preferences.removePreference(pref);
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
|
||||
String key = pref.getKey();
|
||||
if (pref instanceof PreferenceGroup) {
|
||||
// If no datareporting is enabled, remove UI.
|
||||
// If datareporting is disabled, remove UI.
|
||||
if (PREFS_DATA_REPORTING_PREFERENCES.equals(key)) {
|
||||
if (!AppConstants.MOZ_DATA_REPORTING) {
|
||||
preferences.removePreference(pref);
|
||||
|
|
|
@ -28,7 +28,8 @@
|
|||
</header>
|
||||
|
||||
<header android:fragment="org.mozilla.gecko.preferences.GeckoPreferenceFragment"
|
||||
android:title="@string/pref_header_language">
|
||||
android:title="@string/pref_header_language"
|
||||
android:id="@+id/pref_header_language">
|
||||
<extra android:name="resource"
|
||||
android:value="preferences_locale" />
|
||||
</header>
|
||||
|
|
|
@ -66,6 +66,12 @@
|
|||
android:key="passwords"
|
||||
android:persistent="false"
|
||||
android:title="@string/fxaccount_status_passwords" />
|
||||
|
||||
<EditTextPreference
|
||||
android:singleLine="true"
|
||||
android:key="device_name"
|
||||
android:persistent="false"
|
||||
android:title="@string/fxaccount_status_device_name" />
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory
|
||||
android:key="legal_category"
|
||||
|
|
|
@ -30,12 +30,33 @@ public class SharedPreferencesClientsDataDelegate implements ClientsDataDelegate
|
|||
return accountGUID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set client name.
|
||||
*
|
||||
* @param clientName to change to.
|
||||
*/
|
||||
@Override
|
||||
public synchronized void setClientName(String clientName, long now) {
|
||||
sharedPreferences
|
||||
.edit()
|
||||
.putString(SyncConfiguration.PREF_CLIENT_NAME, clientName)
|
||||
.putLong(SyncConfiguration.PREF_CLIENT_DATA_TIMESTAMP, now)
|
||||
.commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDefaultClientName() {
|
||||
// Bug 1019719: localize this string!
|
||||
return GlobalConstants.MOZ_APP_DISPLAYNAME + " on " + android.os.Build.MODEL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized String getClientName() {
|
||||
String clientName = sharedPreferences.getString(SyncConfiguration.PREF_CLIENT_NAME, null);
|
||||
if (clientName == null) {
|
||||
clientName = GlobalConstants.MOZ_APP_DISPLAYNAME + " on " + android.os.Build.MODEL;
|
||||
sharedPreferences.edit().putString(SyncConfiguration.PREF_CLIENT_NAME, clientName).commit();
|
||||
clientName = getDefaultClientName();
|
||||
long now = System.currentTimeMillis();
|
||||
setClientName(clientName, now);
|
||||
}
|
||||
return clientName;
|
||||
}
|
||||
|
@ -54,4 +75,9 @@ public class SharedPreferencesClientsDataDelegate implements ClientsDataDelegate
|
|||
public synchronized int getClientsCount() {
|
||||
return (int) sharedPreferences.getLong(SyncConfiguration.PREF_NUM_CLIENTS, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLastModifiedTimestamp() {
|
||||
return sharedPreferences.getLong(SyncConfiguration.PREF_CLIENT_DATA_TIMESTAMP, 0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -258,6 +258,7 @@ public class SyncConfiguration {
|
|||
public static final String PREF_ACCOUNT_GUID = "account.guid";
|
||||
public static final String PREF_CLIENT_NAME = "account.clientName";
|
||||
public static final String PREF_NUM_CLIENTS = "account.numClients";
|
||||
public static final String PREF_CLIENT_DATA_TIMESTAMP = "account.clientDataTimestamp";
|
||||
|
||||
private static final String API_VERSION = "1.5";
|
||||
|
||||
|
|
|
@ -6,8 +6,22 @@ package org.mozilla.gecko.sync.delegates;
|
|||
|
||||
public interface ClientsDataDelegate {
|
||||
public String getAccountGUID();
|
||||
public String getDefaultClientName();
|
||||
public void setClientName(String clientName, long now);
|
||||
public String getClientName();
|
||||
public void setClientsCount(int clientsCount);
|
||||
public int getClientsCount();
|
||||
public boolean isLocalGUID(String guid);
|
||||
|
||||
/**
|
||||
* The last time the client's data was modified in a way that should be
|
||||
* reflected remotely.
|
||||
* <p>
|
||||
* Changing the client's name should be reflected remotely, while changing the
|
||||
* clients count should not (since that data is only used to inform local
|
||||
* policy.)
|
||||
*
|
||||
* @return timestamp in milliseconds.
|
||||
*/
|
||||
public long getLastModifiedTimestamp();
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import java.util.ArrayList;
|
|||
import org.mozilla.gecko.background.common.log.Logger;
|
||||
import org.mozilla.gecko.background.db.Tab;
|
||||
import org.mozilla.gecko.db.BrowserContract;
|
||||
import org.mozilla.gecko.sync.delegates.ClientsDataDelegate;
|
||||
import org.mozilla.gecko.sync.repositories.InactiveSessionException;
|
||||
import org.mozilla.gecko.sync.repositories.NoContentProviderException;
|
||||
import org.mozilla.gecko.sync.repositories.NoStoreDelegateException;
|
||||
|
@ -30,12 +31,10 @@ import android.net.Uri;
|
|||
import android.os.RemoteException;
|
||||
|
||||
public class FennecTabsRepository extends Repository {
|
||||
protected final String localClientName;
|
||||
protected final String localClientGuid;
|
||||
protected final ClientsDataDelegate clientsDataDelegate;
|
||||
|
||||
public FennecTabsRepository(final String localClientName, final String localClientGuid) {
|
||||
this.localClientName = localClientName;
|
||||
this.localClientGuid = localClientGuid;
|
||||
public FennecTabsRepository(ClientsDataDelegate clientsDataDelegate) {
|
||||
this.clientsDataDelegate = clientsDataDelegate;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -144,14 +143,17 @@ public class FennecTabsRepository extends Repository {
|
|||
public void run() {
|
||||
// We fetch all local tabs (since the record must contain them all)
|
||||
// but only process the record if the timestamp is sufficiently
|
||||
// recent.
|
||||
// recent, or if the client data has been modified.
|
||||
try {
|
||||
final Cursor cursor = tabsHelper.safeQuery(tabsProvider, ".fetchSince()", null,
|
||||
localClientSelection, localClientSelectionArgs, positionAscending);
|
||||
try {
|
||||
final String localClientGuid = clientsDataDelegate.getAccountGUID();
|
||||
final String localClientName = clientsDataDelegate.getClientName();
|
||||
final TabsRecord tabsRecord = FennecTabsRepository.tabsRecordFromCursor(cursor, localClientGuid, localClientName);
|
||||
|
||||
if (tabsRecord.lastModified >= timestamp) {
|
||||
if (tabsRecord.lastModified >= timestamp ||
|
||||
clientsDataDelegate.getLastModifiedTimestamp() >= timestamp) {
|
||||
delegate.onFetchedRecord(tabsRecord);
|
||||
}
|
||||
} finally {
|
||||
|
|
|
@ -25,12 +25,9 @@ import org.mozilla.gecko.sync.SyncConfiguration;
|
|||
import org.mozilla.gecko.sync.SyncConstants;
|
||||
import org.mozilla.gecko.sync.repositories.NullCursorException;
|
||||
import org.mozilla.gecko.sync.repositories.android.ClientsDatabaseAccessor;
|
||||
import org.mozilla.gecko.sync.repositories.android.FennecTabsRepository;
|
||||
import org.mozilla.gecko.sync.repositories.android.FennecTabsRepository.FennecTabsRepositorySession;
|
||||
import org.mozilla.gecko.sync.repositories.domain.ClientRecord;
|
||||
import org.mozilla.gecko.sync.setup.SyncAccounts;
|
||||
import org.mozilla.gecko.sync.setup.activities.LocaleAware.LocaleAwareActivity;
|
||||
import org.mozilla.gecko.sync.stage.SyncClientsEngineStage;
|
||||
import org.mozilla.gecko.sync.syncadapter.SyncAdapter;
|
||||
|
||||
import android.accounts.Account;
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
package org.mozilla.gecko.sync.stage;
|
||||
|
||||
import org.mozilla.gecko.sync.delegates.ClientsDataDelegate;
|
||||
import org.mozilla.gecko.sync.repositories.RecordFactory;
|
||||
import org.mozilla.gecko.sync.repositories.Repository;
|
||||
import org.mozilla.gecko.sync.repositories.android.FennecTabsRepository;
|
||||
|
@ -31,8 +30,7 @@ public class FennecTabsServerSyncStage extends ServerSyncStage {
|
|||
|
||||
@Override
|
||||
protected Repository getLocalRepository() {
|
||||
final ClientsDataDelegate clientsDelegate = session.getClientsDelegate();
|
||||
return new FennecTabsRepository(clientsDelegate.getClientName(), clientsDelegate.getAccountGUID());
|
||||
return new FennecTabsRepository(session.getClientsDelegate());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -397,6 +397,11 @@ public class SyncClientsEngineStage extends AbstractSessionManagingSyncStage {
|
|||
return true;
|
||||
}
|
||||
|
||||
if (session.getClientsDelegate().getLastModifiedTimestamp() > lastUpload) {
|
||||
// Something's changed locally since we last uploaded.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Note the opportunity for clock drift problems here.
|
||||
// TODO: if we track download times, we can use the timestamp of most
|
||||
// recent download response instead of the current time.
|
||||
|
|
|
@ -921,7 +921,12 @@ var BrowserApp = {
|
|||
aParams = aParams || {};
|
||||
|
||||
let newTab = new Tab(aURI, aParams);
|
||||
this._tabs.push(newTab);
|
||||
|
||||
if (typeof aParams.tabIndex == "number") {
|
||||
this._tabs.splice(aParams.tabIndex, 0, newTab);
|
||||
} else {
|
||||
this._tabs.push(newTab);
|
||||
}
|
||||
|
||||
let selected = "selected" in aParams ? aParams.selected : true;
|
||||
if (selected)
|
||||
|
@ -972,17 +977,23 @@ var BrowserApp = {
|
|||
if (aTab == this.selectedTab)
|
||||
this.selectedTab = null;
|
||||
|
||||
let tabIndex = this._tabs.indexOf(aTab);
|
||||
|
||||
let evt = document.createEvent("UIEvents");
|
||||
evt.initUIEvent("TabClose", true, false, window, null);
|
||||
evt.initUIEvent("TabClose", true, false, window, tabIndex);
|
||||
aTab.browser.dispatchEvent(evt);
|
||||
|
||||
// Get a title for the undo close toast. Fall back to the URL if there is no title.
|
||||
let title = aTab.browser.contentDocument.title || aTab.browser.contentDocument.URL;
|
||||
|
||||
aTab.destroy();
|
||||
this._tabs.splice(this._tabs.indexOf(aTab), 1);
|
||||
this._tabs.splice(tabIndex, 1);
|
||||
|
||||
if (aShowUndoToast) {
|
||||
// Get a title for the undo close toast. Fall back to the URL if there is no title.
|
||||
let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
|
||||
let closedTabData = ss.getClosedTabs(window)[0];
|
||||
|
||||
let historyEntry = closedTabData.entries[closedTabData.index - 1];
|
||||
let title = historyEntry.title || historyEntry.url;
|
||||
|
||||
let message = Strings.browser.formatStringFromName("undoCloseToast.message", [title], 1);
|
||||
NativeWindow.toast.show(message, "short", {
|
||||
button: {
|
||||
|
@ -990,7 +1001,6 @@ var BrowserApp = {
|
|||
label: Strings.browser.GetStringFromName("undoCloseToast.action2"),
|
||||
callback: function() {
|
||||
UITelemetry.addEvent("undo.1", "toast", null, "closetab");
|
||||
let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
|
||||
ss.undoCloseTab(window, 0);
|
||||
}
|
||||
}
|
||||
|
@ -3050,6 +3060,7 @@ Tab.prototype = {
|
|||
tabID: this.id,
|
||||
uri: uri,
|
||||
parentId: ("parentId" in aParams) ? aParams.parentId : -1,
|
||||
tabIndex: ("tabIndex" in aParams) ? aParams.tabIndex : -1,
|
||||
external: ("external" in aParams) ? aParams.external : false,
|
||||
selected: ("selected" in aParams) ? aParams.selected : true,
|
||||
title: title,
|
||||
|
|
|
@ -14,7 +14,7 @@ interface nsIDOMNode;
|
|||
* tabs contained in them.
|
||||
*/
|
||||
|
||||
[scriptable, uuid(fe116b56-0226-4562-b52a-a623dad07ead)]
|
||||
[scriptable, uuid(91eca9cf-6741-4c8f-a3a0-2e957240894d)]
|
||||
interface nsISessionStore : nsISupports
|
||||
{
|
||||
/**
|
||||
|
@ -32,9 +32,9 @@ interface nsISessionStore : nsISupports
|
|||
* Get closed tab data
|
||||
*
|
||||
* @param aWindow is the browser window for which to get closed tab data
|
||||
* @returns a JSON string representing the list of closed tabs.
|
||||
* @returns a JS array of closed tabs.
|
||||
*/
|
||||
AString getClosedTabData(in nsIDOMWindow aWindow);
|
||||
jsval getClosedTabs(in nsIDOMWindow aWindow);
|
||||
|
||||
/**
|
||||
* @param aWindow is the browser window to reopen a closed tab in.
|
||||
|
|
|
@ -46,6 +46,10 @@ SessionStore.prototype = {
|
|||
_maxTabsUndo: 1,
|
||||
_pendingWrite: 0,
|
||||
|
||||
// The index where the most recently closed tab was in the tabs array
|
||||
// when it was closed.
|
||||
_lastClosedTabIndex: -1,
|
||||
|
||||
init: function ss_init() {
|
||||
// Get file references
|
||||
this._sessionFile = Services.dirsvc.get("ProfD", Ci.nsILocalFile);
|
||||
|
@ -98,6 +102,8 @@ SessionStore.prototype = {
|
|||
for (let [ssid, win] in Iterator(this._windows))
|
||||
win.closedTabs = [];
|
||||
|
||||
this._lastClosedTabIndex = -1;
|
||||
|
||||
if (this._loadState == STATE_RUNNING) {
|
||||
// Save the purged state immediately
|
||||
this.saveState();
|
||||
|
@ -164,7 +170,7 @@ SessionStore.prototype = {
|
|||
}
|
||||
case "TabClose": {
|
||||
let browser = aEvent.target;
|
||||
this.onTabClose(window, browser);
|
||||
this.onTabClose(window, browser, aEvent.detail);
|
||||
this.onTabRemove(window, browser);
|
||||
break;
|
||||
}
|
||||
|
@ -269,7 +275,7 @@ SessionStore.prototype = {
|
|||
this.saveStateDelayed();
|
||||
},
|
||||
|
||||
onTabClose: function ss_onTabClose(aWindow, aBrowser) {
|
||||
onTabClose: function ss_onTabClose(aWindow, aBrowser, aTabIndex) {
|
||||
if (this._maxTabsUndo == 0)
|
||||
return;
|
||||
|
||||
|
@ -283,6 +289,8 @@ SessionStore.prototype = {
|
|||
let length = this._windows[aWindow.__SSID].closedTabs.length;
|
||||
if (length > this._maxTabsUndo)
|
||||
this._windows[aWindow.__SSID].closedTabs.splice(this._maxTabsUndo, length - this._maxTabsUndo);
|
||||
|
||||
this._lastClosedTabIndex = aTabIndex;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -818,11 +826,11 @@ SessionStore.prototype = {
|
|||
return this._windows[aWindow.__SSID].closedTabs.length;
|
||||
},
|
||||
|
||||
getClosedTabData: function ss_getClosedTabData(aWindow) {
|
||||
getClosedTabs: function ss_getClosedTabs(aWindow) {
|
||||
if (!aWindow.__SSID)
|
||||
throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
|
||||
|
||||
return JSON.stringify(this._windows[aWindow.__SSID].closedTabs);
|
||||
return this._windows[aWindow.__SSID].closedTabs;
|
||||
},
|
||||
|
||||
undoCloseTab: function ss_undoCloseTab(aWindow, aIndex) {
|
||||
|
@ -845,11 +853,14 @@ SessionStore.prototype = {
|
|||
let params = {
|
||||
selected: true,
|
||||
isPrivate: closedTab.isPrivate,
|
||||
desktopMode: closedTab.desktopMode
|
||||
desktopMode: closedTab.desktopMode,
|
||||
tabIndex: this._lastClosedTabIndex
|
||||
};
|
||||
let tab = aWindow.BrowserApp.addTab(closedTab.entries[closedTab.index - 1].url, params);
|
||||
this._restoreHistory(closedTab, tab.browser.sessionHistory);
|
||||
|
||||
this._lastClosedTabIndex = -1;
|
||||
|
||||
// Put back the extra data
|
||||
tab.browser.__SS_extdata = closedTab.extData;
|
||||
|
||||
|
@ -869,6 +880,11 @@ SessionStore.prototype = {
|
|||
|
||||
// remove closed tab from the array
|
||||
closedTabs.splice(aIndex, 1);
|
||||
|
||||
// Forget the last closed tab index if we're forgetting the last closed tab.
|
||||
if (aIndex == 0) {
|
||||
this._lastClosedTabIndex = -1;
|
||||
}
|
||||
},
|
||||
|
||||
getTabValue: function ss_getTabValue(aTab, aKey) {
|
||||
|
|
|
@ -64,6 +64,9 @@ MOZ_SERVICES_FXACCOUNTS=1
|
|||
# Enable Wifi-AP/cell tower data reporting
|
||||
MOZ_DATA_REPORTING=1
|
||||
|
||||
# Enable runtime locale switching.
|
||||
MOZ_LOCALE_SWITCHER=1
|
||||
|
||||
# Enable the "synthetic APKs" implementation of Open Web Apps.
|
||||
MOZ_ANDROID_SYNTHAPKS=1
|
||||
|
||||
|
|
|
@ -169,6 +169,7 @@
|
|||
<string name="fxaccount_status_activity_label">&syncBrand.shortName.label;</string>
|
||||
<string name="fxaccount_status_header">&fxaccount_status_header2;</string>
|
||||
<string name="fxaccount_status_signed_in_as">&fxaccount_status_signed_in_as;</string>
|
||||
<string name="fxaccount_status_device_name">&fxaccount_status_device_name;</string>
|
||||
<string name="fxaccount_status_sync">&fxaccount_status_sync;</string>
|
||||
<string name="fxaccount_status_sync_enabled">&fxaccount_status_sync_enabled;</string>
|
||||
<string name="fxaccount_status_needs_verification">&fxaccount_status_needs_verification2;</string>
|
||||
|
|
|
@ -7,6 +7,7 @@ import org.json.simple.JSONArray;
|
|||
import org.mozilla.gecko.background.helpers.AndroidSyncTestCase;
|
||||
import org.mozilla.gecko.background.sync.helpers.ExpectFetchDelegate;
|
||||
import org.mozilla.gecko.background.sync.helpers.SessionTestHelper;
|
||||
import org.mozilla.gecko.background.testhelpers.MockClientsDataDelegate;
|
||||
import org.mozilla.gecko.db.BrowserContract;
|
||||
import org.mozilla.gecko.sync.repositories.NoContentProviderException;
|
||||
import org.mozilla.gecko.sync.repositories.RepositorySession;
|
||||
|
@ -24,8 +25,9 @@ import android.database.Cursor;
|
|||
import android.os.RemoteException;
|
||||
|
||||
public class TestFennecTabsRepositorySession extends AndroidSyncTestCase {
|
||||
public static final String TEST_CLIENT_GUID = "test guid"; // Real GUIDs never contain spaces.
|
||||
public static final String TEST_CLIENT_NAME = "test client name";
|
||||
public static final MockClientsDataDelegate clientsDataDelegate = new MockClientsDataDelegate();
|
||||
public static final String TEST_CLIENT_GUID = clientsDataDelegate.getAccountGUID();
|
||||
public static final String TEST_CLIENT_NAME = clientsDataDelegate.getClientName();
|
||||
|
||||
// Override these to test against data that is not live.
|
||||
public static final String TEST_TABS_CLIENT_GUID_IS_LOCAL_SELECTION = BrowserContract.Tabs.CLIENT_GUID + " IS ?";
|
||||
|
@ -72,7 +74,7 @@ public class TestFennecTabsRepositorySession extends AndroidSyncTestCase {
|
|||
* Override this chain in order to avoid our test code having to create two
|
||||
* sessions all the time.
|
||||
*/
|
||||
return new FennecTabsRepository(TEST_CLIENT_NAME, TEST_CLIENT_GUID) {
|
||||
return new FennecTabsRepository(clientsDataDelegate) {
|
||||
@Override
|
||||
public void createSession(RepositorySessionCreationDelegate delegate,
|
||||
Context context) {
|
||||
|
@ -197,8 +199,19 @@ public class TestFennecTabsRepositorySession extends AndroidSyncTestCase {
|
|||
// Not all tabs are modified after this, but the record should contain them all.
|
||||
performWait(fetchSinceRunnable(session, 1000, new Record[] { tabsRecord }));
|
||||
|
||||
// No tabs are modified after this, so we shouldn't get a record at all.
|
||||
performWait(fetchSinceRunnable(session, 4000, new Record[] { }));
|
||||
// No tabs are modified after this, but our client name has changed in the interim.
|
||||
performWait(fetchSinceRunnable(session, 4000, new Record[] { tabsRecord }));
|
||||
|
||||
// No tabs are modified after this, and our client name hasn't changed, so
|
||||
// we shouldn't get a record at all. Note: this runs after our static
|
||||
// initializer that sets the client data timestamp.
|
||||
final long now = System.currentTimeMillis();
|
||||
performWait(fetchSinceRunnable(session, now, new Record[] { }));
|
||||
|
||||
// No tabs are modified after this, but our client name has changed, so
|
||||
// again we get a record.
|
||||
clientsDataDelegate.setClientName("new client name", System.currentTimeMillis());
|
||||
performWait(fetchSinceRunnable(session, now, new Record[] { tabsRecord }));
|
||||
|
||||
session.abort();
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ public class MockClientsDataDelegate implements ClientsDataDelegate {
|
|||
private String accountGUID;
|
||||
private String clientName;
|
||||
private int clientsCount;
|
||||
private long clientDataTimestamp = 0;
|
||||
|
||||
@Override
|
||||
public synchronized String getAccountGUID() {
|
||||
|
@ -19,10 +20,21 @@ public class MockClientsDataDelegate implements ClientsDataDelegate {
|
|||
return accountGUID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized String getDefaultClientName() {
|
||||
return "Default client";
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setClientName(String clientName, long now) {
|
||||
this.clientName = clientName;
|
||||
this.clientDataTimestamp = now;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized String getClientName() {
|
||||
if (clientName == null) {
|
||||
clientName = "Default Name";
|
||||
setClientName(getDefaultClientName(), System.currentTimeMillis());
|
||||
}
|
||||
return clientName;
|
||||
}
|
||||
|
@ -38,7 +50,12 @@ public class MockClientsDataDelegate implements ClientsDataDelegate {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isLocalGUID(String guid) {
|
||||
public synchronized boolean isLocalGUID(String guid) {
|
||||
return getAccountGUID().equals(guid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized long getLastModifiedTimestamp() {
|
||||
return clientDataTimestamp;
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@
|
|||
[include:dom/activities/tests/unit/xpcshell.ini]
|
||||
[include:dom/apps/tests/unit/xpcshell.ini]
|
||||
[include:dom/encoding/test/unit/xpcshell.ini]
|
||||
[include:dom/mobilemessage/tests/xpcshell.ini]
|
||||
[include:dom/mobilemessage/tests/xpcshell/xpcshell.ini]
|
||||
[include:dom/network/tests/unit/xpcshell.ini]
|
||||
[include:dom/payment/tests/unit/xpcshell.ini]
|
||||
[include:dom/permission/tests/unit/xpcshell.ini]
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
[include:dom/apps/tests/unit/xpcshell.ini]
|
||||
[include:dom/mobilemessage/tests/xpcshell.ini]
|
||||
[include:dom/mobilemessage/tests/xpcshell/xpcshell.ini]
|
||||
[include:dom/network/tests/unit_stats/xpcshell.ini]
|
||||
[include:dom/system/gonk/tests/xpcshell.ini]
|
||||
[include:dom/wappush/tests/xpcshell.ini]
|
||||
|
|
|
@ -4143,6 +4143,60 @@
|
|||
"n_values": 10,
|
||||
"description": "Track click count on about:newtab tiles per index (0-8). For non-default row or column configurations all clicks into the '9' bucket."
|
||||
},
|
||||
"NEWTAB_PAGE_DIRECTORY_LINK0_SHOWN": {
|
||||
"expires_in_version": "35",
|
||||
"kind": "enumerated",
|
||||
"n_values": 10,
|
||||
"description": "Track impression count of directory link #0 per index (0-8). Grid configurations showing 9+ tiles fall into the '9' bucket."
|
||||
},
|
||||
"NEWTAB_PAGE_DIRECTORY_LINK1_SHOWN": {
|
||||
"expires_in_version": "35",
|
||||
"kind": "enumerated",
|
||||
"n_values": 10,
|
||||
"description": "Track impression count of directory link #1 per index (0-8). Grid configurations showing 9+ tiles fall into the '9' bucket."
|
||||
},
|
||||
"NEWTAB_PAGE_DIRECTORY_LINK2_SHOWN": {
|
||||
"expires_in_version": "35",
|
||||
"kind": "enumerated",
|
||||
"n_values": 10,
|
||||
"description": "Track impression count of directory link #2 per index (0-8). Grid configurations showing 9+ tiles fall into the '9' bucket."
|
||||
},
|
||||
"NEWTAB_PAGE_DIRECTORY_LINK3_SHOWN": {
|
||||
"expires_in_version": "35",
|
||||
"kind": "enumerated",
|
||||
"n_values": 10,
|
||||
"description": "Track impression count of directory link #3 per index (0-8). Grid configurations showing 9+ tiles fall into the '9' bucket."
|
||||
},
|
||||
"NEWTAB_PAGE_DIRECTORY_LINK4_SHOWN": {
|
||||
"expires_in_version": "35",
|
||||
"kind": "enumerated",
|
||||
"n_values": 10,
|
||||
"description": "Track impression count of directory link #4 per index (0-8). Grid configurations showing 9+ tiles fall into the '9' bucket."
|
||||
},
|
||||
"NEWTAB_PAGE_DIRECTORY_LINK5_SHOWN": {
|
||||
"expires_in_version": "35",
|
||||
"kind": "enumerated",
|
||||
"n_values": 10,
|
||||
"description": "Track impression count of directory link #5 per index (0-8). Grid configurations showing 9+ tiles fall into the '9' bucket."
|
||||
},
|
||||
"NEWTAB_PAGE_DIRECTORY_LINK6_SHOWN": {
|
||||
"expires_in_version": "35",
|
||||
"kind": "enumerated",
|
||||
"n_values": 10,
|
||||
"description": "Track impression count of directory link #6 per index (0-8). Grid configurations showing 9+ tiles fall into the '9' bucket."
|
||||
},
|
||||
"NEWTAB_PAGE_DIRECTORY_LINK7_SHOWN": {
|
||||
"expires_in_version": "35",
|
||||
"kind": "enumerated",
|
||||
"n_values": 10,
|
||||
"description": "Track impression count of directory link #7 per index (0-8). Grid configurations showing 9+ tiles fall into the '9' bucket."
|
||||
},
|
||||
"NEWTAB_PAGE_DIRECTORY_LINK8_SHOWN": {
|
||||
"expires_in_version": "35",
|
||||
"kind": "enumerated",
|
||||
"n_values": 10,
|
||||
"description": "Track impression count of directory link #8 per index (0-8). Grid configurations showing 9+ tiles fall into the '9' bucket."
|
||||
},
|
||||
"NEWTAB_PAGE_DIRECTORY_AFFILIATE_SHOWN": {
|
||||
"expires_in_version": "35",
|
||||
"kind": "enumerated",
|
||||
|
|
|
@ -412,98 +412,4 @@ LayoutHelpers.prototype = {
|
|||
|
||||
return [xOffset * scale, yOffset * scale];
|
||||
},
|
||||
|
||||
|
||||
|
||||
/********************************************************************
|
||||
* GetBoxQuads POLYFILL START TODO: Remove this when bug 917755 is fixed.
|
||||
********************************************************************/
|
||||
_getBoxQuadsFromRect: function(rect, node) {
|
||||
let scale = this.calculateScale(node);
|
||||
let [xOffset, yOffset] = this._getNodeOffsets(node);
|
||||
|
||||
let out = {
|
||||
p1: {
|
||||
x: rect.left * scale + xOffset,
|
||||
y: rect.top * scale + yOffset
|
||||
},
|
||||
p2: {
|
||||
x: (rect.left + rect.width) * scale + xOffset,
|
||||
y: rect.top * scale + yOffset
|
||||
},
|
||||
p3: {
|
||||
x: (rect.left + rect.width) * scale + xOffset,
|
||||
y: (rect.top + rect.height) * scale + yOffset
|
||||
},
|
||||
p4: {
|
||||
x: rect.left * scale + xOffset,
|
||||
y: (rect.top + rect.height) * scale + yOffset
|
||||
}
|
||||
};
|
||||
|
||||
out.bounds = {
|
||||
bottom: out.p4.y,
|
||||
height: out.p4.y - out.p1.y,
|
||||
left: out.p1.x,
|
||||
right: out.p2.x,
|
||||
top: out.p1.y,
|
||||
width: out.p2.x - out.p1.x,
|
||||
x: out.p1.x,
|
||||
y: out.p1.y
|
||||
};
|
||||
|
||||
return out;
|
||||
},
|
||||
|
||||
_parseNb: function(distance) {
|
||||
let nb = parseFloat(distance, 10);
|
||||
return isNaN(nb) ? 0 : nb;
|
||||
},
|
||||
|
||||
getAdjustedQuadsPolyfill: function(node, region) {
|
||||
// Get the border-box rect
|
||||
// Note that this is relative to the node's viewport, so before we can use
|
||||
// it, will need to go back up the frames like getRect
|
||||
let borderRect = node.getBoundingClientRect();
|
||||
|
||||
// If the boxType is border, no need to go any further, we're done
|
||||
if (region === "border") {
|
||||
return this._getBoxQuadsFromRect(borderRect, node);
|
||||
}
|
||||
|
||||
// Else, need to get margin/padding/border distances
|
||||
let style = node.ownerDocument.defaultView.getComputedStyle(node);
|
||||
let camel = s => s.substring(0, 1).toUpperCase() + s.substring(1);
|
||||
let distances = {border:{}, padding:{}, margin: {}};
|
||||
|
||||
for (let side of ["top", "right", "bottom", "left"]) {
|
||||
distances.border[side] = this._parseNb(style["border" + camel(side) + "Width"]);
|
||||
distances.padding[side] = this._parseNb(style["padding" + camel(side)]);
|
||||
distances.margin[side] = this._parseNb(style["margin" + camel(side)]);
|
||||
}
|
||||
|
||||
// From the border-box rect, calculate the content-box, padding-box and
|
||||
// margin-box rects
|
||||
function offsetRect(rect, offsetType, dir=1) {
|
||||
return {
|
||||
top: rect.top + (dir * distances[offsetType].top),
|
||||
left: rect.left + (dir * distances[offsetType].left),
|
||||
width: rect.width - (dir * (distances[offsetType].left + distances[offsetType].right)),
|
||||
height: rect.height - (dir * (distances[offsetType].top + distances[offsetType].bottom))
|
||||
};
|
||||
}
|
||||
|
||||
if (region === "margin") {
|
||||
return this._getBoxQuadsFromRect(offsetRect(borderRect, "margin", -1), node);
|
||||
} else if (region === "padding") {
|
||||
return this._getBoxQuadsFromRect(offsetRect(borderRect, "border"), node);
|
||||
} else if (region === "content") {
|
||||
let paddingRect = offsetRect(borderRect, "border");
|
||||
return this._getBoxQuadsFromRect(offsetRect(paddingRect, "padding"), node);
|
||||
}
|
||||
},
|
||||
|
||||
/********************************************************************
|
||||
* GetBoxQuads POLYFILL END
|
||||
********************************************************************/
|
||||
};
|
||||
|
|
|
@ -398,6 +398,9 @@ BoxModelHighlighter.prototype = {
|
|||
let pseudoClassesBox = this.chromeDoc.createElementNS(XHTML_NS, "span");
|
||||
pseudoClassesBox.className = "highlighter-nodeinfobar-pseudo-classes";
|
||||
|
||||
let dimensionBox = this.chromeDoc.createElementNS(XHTML_NS, "span");
|
||||
dimensionBox.className = "highlighter-nodeinfobar-dimensions";
|
||||
|
||||
// Add some content to force a better boundingClientRect
|
||||
pseudoClassesBox.textContent = " ";
|
||||
|
||||
|
@ -411,6 +414,7 @@ BoxModelHighlighter.prototype = {
|
|||
texthbox.appendChild(idLabel);
|
||||
texthbox.appendChild(classesBox);
|
||||
texthbox.appendChild(pseudoClassesBox);
|
||||
texthbox.appendChild(dimensionBox);
|
||||
|
||||
nodeInfobar.appendChild(texthbox);
|
||||
|
||||
|
@ -427,6 +431,7 @@ BoxModelHighlighter.prototype = {
|
|||
idLabel: idLabel,
|
||||
classesBox: classesBox,
|
||||
pseudoClassesBox: pseudoClassesBox,
|
||||
dimensionBox: dimensionBox,
|
||||
positioner: infobarPositioner,
|
||||
barHeight: barHeight,
|
||||
};
|
||||
|
@ -582,9 +587,7 @@ BoxModelHighlighter.prototype = {
|
|||
|
||||
options.region = options.region || "content";
|
||||
|
||||
// TODO: Remove this polyfill
|
||||
this.rect =
|
||||
this.layoutHelpers.getAdjustedQuadsPolyfill(this.currentNode, "margin");
|
||||
this.rect = this.layoutHelpers.getAdjustedQuads(this.currentNode, "margin");
|
||||
|
||||
if (!this.rect) {
|
||||
return null;
|
||||
|
@ -592,9 +595,8 @@ BoxModelHighlighter.prototype = {
|
|||
|
||||
if (this.rect.bounds.width > 0 && this.rect.bounds.height > 0) {
|
||||
for (let boxType in this._boxModelNodes) {
|
||||
// TODO: Remove this polyfill
|
||||
let {p1, p2, p3, p4} = boxType === "margin" ? this.rect :
|
||||
this.layoutHelpers.getAdjustedQuadsPolyfill(this.currentNode, boxType);
|
||||
this.layoutHelpers.getAdjustedQuads(this.currentNode, boxType);
|
||||
|
||||
let boxNode = this._boxModelNodes[boxType];
|
||||
boxNode.setAttribute("points",
|
||||
|
@ -705,29 +707,36 @@ BoxModelHighlighter.prototype = {
|
|||
* Update node information (tagName#id.class)
|
||||
*/
|
||||
_updateInfobar: function() {
|
||||
if (this.currentNode) {
|
||||
// Tag name
|
||||
this.nodeInfo.tagNameLabel.textContent = this.currentNode.tagName;
|
||||
|
||||
// ID
|
||||
this.nodeInfo.idLabel.textContent = this.currentNode.id ? "#" + this.currentNode.id : "";
|
||||
|
||||
// Classes
|
||||
let classes = this.nodeInfo.classesBox;
|
||||
|
||||
classes.textContent = this.currentNode.classList.length ?
|
||||
"." + Array.join(this.currentNode.classList, ".") : "";
|
||||
|
||||
// Pseudo-classes
|
||||
let pseudos = PSEUDO_CLASSES.filter(pseudo => {
|
||||
return DOMUtils.hasPseudoClassLock(this.currentNode, pseudo);
|
||||
}, this);
|
||||
|
||||
let pseudoBox = this.nodeInfo.pseudoClassesBox;
|
||||
pseudoBox.textContent = pseudos.join("");
|
||||
|
||||
this._moveInfobar();
|
||||
if (!this.currentNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Tag name
|
||||
this.nodeInfo.tagNameLabel.textContent = this.currentNode.tagName;
|
||||
|
||||
// ID
|
||||
this.nodeInfo.idLabel.textContent = this.currentNode.id ? "#" + this.currentNode.id : "";
|
||||
|
||||
// Classes
|
||||
let classes = this.nodeInfo.classesBox;
|
||||
|
||||
classes.textContent = this.currentNode.classList.length ?
|
||||
"." + Array.join(this.currentNode.classList, ".") : "";
|
||||
|
||||
// Pseudo-classes
|
||||
let pseudos = PSEUDO_CLASSES.filter(pseudo => {
|
||||
return DOMUtils.hasPseudoClassLock(this.currentNode, pseudo);
|
||||
}, this);
|
||||
|
||||
let pseudoBox = this.nodeInfo.pseudoClassesBox;
|
||||
pseudoBox.textContent = pseudos.join("");
|
||||
|
||||
// Dimensions
|
||||
let dimensionBox = this.nodeInfo.dimensionBox;
|
||||
let rect = this.currentNode.getBoundingClientRect();
|
||||
dimensionBox.textContent = Math.ceil(rect.width) + " x " +
|
||||
Math.ceil(rect.height);
|
||||
this._moveInfobar();
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -268,6 +268,7 @@ let DirectoryLinksProvider = {
|
|||
this._readDirectoryLinksFile().then(rawLinks => {
|
||||
// all directory links have a frecency of DIRECTORY_FRECENCY
|
||||
aCallback(rawLinks.map((link, position) => {
|
||||
link.directoryIndex = position;
|
||||
link.frecency = DIRECTORY_FRECENCY;
|
||||
link.lastVisitDate = rawLinks.length - position;
|
||||
return link;
|
||||
|
|
|
@ -259,7 +259,7 @@ add_task(function test_linksURL_locale() {
|
|||
|
||||
links = yield fetchData();
|
||||
do_check_eq(links.length, 1);
|
||||
expected_data = [{url: "http://example.com", title: "US", frecency: DIRECTORY_FRECENCY, lastVisitDate: 1}];
|
||||
expected_data = [{url: "http://example.com", title: "US", frecency: DIRECTORY_FRECENCY, lastVisitDate: 1, directoryIndex: 0}];
|
||||
isIdentical(links, expected_data);
|
||||
|
||||
yield promiseDirectoryDownloadOnPrefChange("general.useragent.locale", "zh-CN");
|
||||
|
@ -267,8 +267,8 @@ add_task(function test_linksURL_locale() {
|
|||
links = yield fetchData();
|
||||
do_check_eq(links.length, 2)
|
||||
expected_data = [
|
||||
{url: "http://example.net", title: "CN", frecency: DIRECTORY_FRECENCY, lastVisitDate: 2},
|
||||
{url: "http://example.net/2", title: "CN2", frecency: DIRECTORY_FRECENCY, lastVisitDate: 1}
|
||||
{url: "http://example.net", title: "CN", frecency: DIRECTORY_FRECENCY, lastVisitDate: 2, directoryIndex: 0},
|
||||
{url: "http://example.net/2", title: "CN2", frecency: DIRECTORY_FRECENCY, lastVisitDate: 1, directoryIndex: 1}
|
||||
];
|
||||
isIdentical(links, expected_data);
|
||||
|
||||
|
@ -280,7 +280,7 @@ add_task(function test_DirectoryLinksProvider__prefObserver_url() {
|
|||
|
||||
let links = yield fetchData();
|
||||
do_check_eq(links.length, 1);
|
||||
let expectedData = [{url: "http://example.com", title: "LocalSource", frecency: DIRECTORY_FRECENCY, lastVisitDate: 1}];
|
||||
let expectedData = [{url: "http://example.com", title: "LocalSource", frecency: DIRECTORY_FRECENCY, lastVisitDate: 1, directoryIndex: 0}];
|
||||
isIdentical(links, expectedData);
|
||||
|
||||
// tests these 2 things:
|
||||
|
|
Загрузка…
Ссылка в новой задаче