Merge mozilla-central to mozilla-inbound
@ -1114,8 +1114,10 @@ pref("dom.requestSync.enabled", true);
|
||||
pref("gfx.vsync.hw-vsync.enabled", true);
|
||||
pref("gfx.vsync.compositor", true);
|
||||
pref("gfx.touch.resample", true);
|
||||
pref("gfx.vsync.refreshdriver", true);
|
||||
#else
|
||||
pref("gfx.vsync.hw-vsync.enabled", false);
|
||||
pref("gfx.vsync.compositor", false);
|
||||
pref("gfx.touch.resample", false);
|
||||
pref("gfx.vsync.refreshdriver", false);
|
||||
#endif
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="cdaa0a4ac28c781709df8c318ed079e9e475503a">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f0b93e0668ef9565bd6f050b15b4f794d59feb65"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="ae02fbdeae77b2002cebe33c61aedeee4b9439fd"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
@ -19,7 +19,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="f0b93e0668ef9565bd6f050b15b4f794d59feb65"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ae02fbdeae77b2002cebe33c61aedeee4b9439fd"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="eb1795a9002eb142ac58c8d68f8f4ba094af07ca"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f0b93e0668ef9565bd6f050b15b4f794d59feb65"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="ae02fbdeae77b2002cebe33c61aedeee4b9439fd"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d0d11d190ccc50d7d66009bcc896ad4b42d3f0d"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="cdaa0a4ac28c781709df8c318ed079e9e475503a">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f0b93e0668ef9565bd6f050b15b4f794d59feb65"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="ae02fbdeae77b2002cebe33c61aedeee4b9439fd"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
@ -19,7 +19,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="f0b93e0668ef9565bd6f050b15b4f794d59feb65"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ae02fbdeae77b2002cebe33c61aedeee4b9439fd"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="eb1795a9002eb142ac58c8d68f8f4ba094af07ca"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="cdaa0a4ac28c781709df8c318ed079e9e475503a">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f0b93e0668ef9565bd6f050b15b4f794d59feb65"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="ae02fbdeae77b2002cebe33c61aedeee4b9439fd"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
</project>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f0b93e0668ef9565bd6f050b15b4f794d59feb65"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="ae02fbdeae77b2002cebe33c61aedeee4b9439fd"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d0d11d190ccc50d7d66009bcc896ad4b42d3f0d"/>
|
||||
|
@ -1,9 +1,9 @@
|
||||
{
|
||||
"git": {
|
||||
"git_revision": "f0b93e0668ef9565bd6f050b15b4f794d59feb65",
|
||||
"git_revision": "ae02fbdeae77b2002cebe33c61aedeee4b9439fd",
|
||||
"remote": "https://git.mozilla.org/releases/gaia.git",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "d170342f8e1837435749392fc2bded3b9fee01d4",
|
||||
"revision": "62d026a98ea42f2b93de000e8d0d4f1254f86730",
|
||||
"repo_path": "integration/gaia-central"
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f0b93e0668ef9565bd6f050b15b4f794d59feb65"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="ae02fbdeae77b2002cebe33c61aedeee4b9439fd"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d0d11d190ccc50d7d66009bcc896ad4b42d3f0d"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="7f2ee9f4cb926684883fc2a2e407045fd9db2199">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f0b93e0668ef9565bd6f050b15b4f794d59feb65"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="ae02fbdeae77b2002cebe33c61aedeee4b9439fd"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0"?>
|
||||
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1423263875000">
|
||||
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1423697587000">
|
||||
<emItems>
|
||||
<emItem blockID="i58" id="webmaster@buzzzzvideos.info">
|
||||
<versionRange minVersion="0" maxVersion="*">
|
||||
@ -220,6 +220,26 @@
|
||||
</versionRange>
|
||||
<prefs>
|
||||
</prefs>
|
||||
</emItem>
|
||||
<emItem blockID="i858" id="fftoolbar2014@etech.com">
|
||||
<versionRange minVersion="0" maxVersion="*" severity="1">
|
||||
</versionRange>
|
||||
<prefs>
|
||||
<pref>browser.startup.homepage</pref>
|
||||
<pref>browser.search.defaultenginename</pref>
|
||||
</prefs>
|
||||
</emItem>
|
||||
<emItem blockID="i854" id="/^(7tG@zEb\.net|ru@gfK0J\.edu)$/">
|
||||
<versionRange minVersion="0" maxVersion="*" severity="3">
|
||||
</versionRange>
|
||||
<prefs>
|
||||
</prefs>
|
||||
</emItem>
|
||||
<emItem blockID="i850" id="P2@D.edu">
|
||||
<versionRange minVersion="0" maxVersion="*" severity="1">
|
||||
</versionRange>
|
||||
<prefs>
|
||||
</prefs>
|
||||
</emItem>
|
||||
<emItem blockID="i554" id="lightningnewtab@gmail.com">
|
||||
<versionRange minVersion="0" maxVersion="*" severity="1">
|
||||
@ -268,6 +288,12 @@
|
||||
</versionRange>
|
||||
<prefs>
|
||||
</prefs>
|
||||
</emItem>
|
||||
<emItem blockID="i856" id="/^({94d62e35-4b43-494c-bf52-ba5935df36ef}|firefox@advanceelite\.com|{bb7b7a60-f574-47c2-8a0b-4c56f2da9802})$/">
|
||||
<versionRange minVersion="0" maxVersion="*" severity="3">
|
||||
</versionRange>
|
||||
<prefs>
|
||||
</prefs>
|
||||
</emItem>
|
||||
<emItem blockID="i482" id="brasilescapeeight@facebook.com">
|
||||
<versionRange minVersion="0" maxVersion="*" severity="3">
|
||||
@ -452,6 +478,12 @@
|
||||
</versionRange>
|
||||
<prefs>
|
||||
</prefs>
|
||||
</emItem>
|
||||
<emItem blockID="i848" id="bcVX5@nQm9l.org">
|
||||
<versionRange minVersion="0" maxVersion="*" severity="1">
|
||||
</versionRange>
|
||||
<prefs>
|
||||
</prefs>
|
||||
</emItem>
|
||||
<emItem blockID="i431" id="chinaescapeone@facebook.com">
|
||||
<versionRange minVersion="0" maxVersion="*" severity="3">
|
||||
@ -1544,6 +1576,12 @@
|
||||
</versionRange>
|
||||
<prefs>
|
||||
</prefs>
|
||||
</emItem>
|
||||
<emItem blockID="i852" id="6lIy@T.edu">
|
||||
<versionRange minVersion="0" maxVersion="*" severity="1">
|
||||
</versionRange>
|
||||
<prefs>
|
||||
</prefs>
|
||||
</emItem>
|
||||
<emItem blockID="i505" id="extacylife@a.com">
|
||||
<versionRange minVersion="0" maxVersion="*" severity="3">
|
||||
|
@ -415,3 +415,10 @@
|
||||
label="&inspectContextMenu.label;"
|
||||
accesskey="&inspectContextMenu.accesskey;"
|
||||
oncommand="gContextMenu.inspectNode();"/>
|
||||
<menuseparator id="context-media-eme-separator" hidden="true"/>
|
||||
<menuitem id="context-media-eme-learnmore"
|
||||
class="menuitem-iconic"
|
||||
hidden="true"
|
||||
label="&emeLearnMoreContextMenu.label;"
|
||||
accesskey="&emeLearnMoreContextMenu.accesskey;"
|
||||
onclick="gContextMenu.drmLearnMore(event);"/>
|
||||
|
@ -478,6 +478,8 @@ nsContextMenu.prototype = {
|
||||
var statsShowing = this.onVideo && this.target.mozMediaStatisticsShowing;
|
||||
this.showItem("context-video-showstats", this.onVideo && this.target.controls && !statsShowing);
|
||||
this.showItem("context-video-hidestats", this.onVideo && this.target.controls && statsShowing);
|
||||
this.showItem("context-media-eme-learnmore", this.onDRMMedia);
|
||||
this.showItem("context-media-eme-separator", this.onDRMMedia);
|
||||
|
||||
// Disable them when there isn't a valid media source loaded.
|
||||
if (onMedia) {
|
||||
@ -499,7 +501,7 @@ nsContextMenu.prototype = {
|
||||
this.setItemAttr("context-media-showcontrols", "disabled", hasError);
|
||||
this.setItemAttr("context-media-hidecontrols", "disabled", hasError);
|
||||
if (this.onVideo) {
|
||||
let canSaveSnapshot = this.target.readyState >= this.target.HAVE_CURRENT_DATA;
|
||||
let canSaveSnapshot = !this.onDRMMedia && this.target.readyState >= this.target.HAVE_CURRENT_DATA;
|
||||
this.setItemAttr("context-video-saveimage", "disabled", !canSaveSnapshot);
|
||||
this.setItemAttr("context-video-fullscreen", "disabled", hasError);
|
||||
this.setItemAttr("context-video-showstats", "disabled", hasError);
|
||||
@ -562,6 +564,7 @@ nsContextMenu.prototype = {
|
||||
this.onCanvas = false;
|
||||
this.onVideo = false;
|
||||
this.onAudio = false;
|
||||
this.onDRMMedia = false;
|
||||
this.onTextInput = false;
|
||||
this.onNumeric = false;
|
||||
this.onKeywordField = false;
|
||||
@ -640,6 +643,9 @@ nsContextMenu.prototype = {
|
||||
if (this.isMediaURLReusable(mediaURL)) {
|
||||
this.mediaURL = mediaURL;
|
||||
}
|
||||
if (this.target.isEncrypted) {
|
||||
this.onDRMMedia = true;
|
||||
}
|
||||
// Firefox always creates a HTMLVideoElement when loading an ogg file
|
||||
// directly. If the media is actually audio, be smarter and provide a
|
||||
// context menu with audio operations.
|
||||
@ -656,6 +662,9 @@ nsContextMenu.prototype = {
|
||||
if (this.isMediaURLReusable(mediaURL)) {
|
||||
this.mediaURL = mediaURL;
|
||||
}
|
||||
if (this.target.isEncrypted) {
|
||||
this.onDRMMedia = true;
|
||||
}
|
||||
}
|
||||
else if (editFlags & (SpellCheckHelper.INPUT | SpellCheckHelper.TEXTAREA)) {
|
||||
this.onTextInput = (editFlags & SpellCheckHelper.TEXTINPUT) !== 0;
|
||||
@ -1698,6 +1707,17 @@ nsContextMenu.prototype = {
|
||||
clipboard.copyString(this.mediaURL, document);
|
||||
},
|
||||
|
||||
drmLearnMore: function(aEvent) {
|
||||
let drmInfoURL = Services.urlFormatter.formatURLPref("app.support.baseURL") + "drm-content";
|
||||
let dest = whereToOpenLink(aEvent);
|
||||
// Don't ever want this to open in the same tab as it'll unload the
|
||||
// DRM'd video, which is going to be a bad idea in most cases.
|
||||
if (dest == "current") {
|
||||
dest = "tab";
|
||||
}
|
||||
openUILinkIn(drmInfoURL, dest);
|
||||
},
|
||||
|
||||
get imageURL() {
|
||||
if (this.onImage)
|
||||
return this.mediaURL;
|
||||
|
@ -61,6 +61,7 @@ support-files =
|
||||
head.js
|
||||
healthreport_testRemoteCommands.html
|
||||
moz.png
|
||||
navigating_window_with_download.html
|
||||
offlineQuotaNotification.cacheManifest
|
||||
offlineQuotaNotification.html
|
||||
page_style_sample.html
|
||||
@ -82,6 +83,8 @@ support-files =
|
||||
test_process_flags_chrome.html
|
||||
test_wyciwyg_copying.html
|
||||
title_test.svg
|
||||
unknownContentType_file.pif
|
||||
unknownContentType_file.pif^headers^
|
||||
video.ogg
|
||||
web_video.html
|
||||
web_video1.ogv
|
||||
@ -387,6 +390,8 @@ skip-if = buildapp == 'mulet'
|
||||
skip-if = buildapp == 'mulet' || e10s # Bug 933103 - mochitest's EventUtils.synthesizeMouse functions not e10s friendly
|
||||
[browser_save_private_link_perwindowpb.js]
|
||||
skip-if = buildapp == 'mulet' || e10s # e10s: Bug 933103 - mochitest's EventUtils.synthesizeMouse functions not e10s friendly
|
||||
[browser_save_link_when_window_navigates.js]
|
||||
skip-if = buildapp == 'mulet' || e10s # Bug 933103 - mochitest's EventUtils.synthesizeMouse functions not e10s friendly
|
||||
[browser_save_video.js]
|
||||
skip-if = buildapp == 'mulet' || e10s # Bug 1100698 - test uses synthesizeMouse and then does a load of other stuff that breaks in e10s
|
||||
[browser_save_video_frame.js]
|
||||
|
@ -0,0 +1,173 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
var MockFilePicker = SpecialPowers.MockFilePicker;
|
||||
MockFilePicker.init(window);
|
||||
|
||||
const SAVE_PER_SITE_PREF = "browser.download.lastDir.savePerSite";
|
||||
const ALWAYS_DOWNLOAD_DIR_PREF = "browser.download.useDownloadDir";
|
||||
const UCT_URI = "chrome://mozapps/content/downloads/unknownContentType.xul";
|
||||
|
||||
Cc["@mozilla.org/moz/jssubscript-loader;1"]
|
||||
.getService(Ci.mozIJSSubScriptLoader)
|
||||
.loadSubScript("chrome://mochitests/content/browser/toolkit/content/tests/browser/common/mockTransfer.js",
|
||||
this);
|
||||
|
||||
function createTemporarySaveDirectory() {
|
||||
var saveDir = Cc["@mozilla.org/file/directory_service;1"]
|
||||
.getService(Ci.nsIProperties)
|
||||
.get("TmpD", Ci.nsIFile);
|
||||
saveDir.append("testsavedir");
|
||||
if (!saveDir.exists()) {
|
||||
info("create testsavedir!");
|
||||
saveDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0755);
|
||||
}
|
||||
info("return from createTempSaveDir: " + saveDir.path);
|
||||
return saveDir;
|
||||
}
|
||||
|
||||
function triggerSave(aWindow, aCallback) {
|
||||
info("started triggerSave, persite downloads: " + (Services.prefs.getBoolPref(SAVE_PER_SITE_PREF) ? "on" : "off"));
|
||||
var fileName;
|
||||
let testBrowser = aWindow.gBrowser.selectedBrowser;
|
||||
let testURI = "http://mochi.test:8888/browser/browser/base/content/test/general/navigating_window_with_download.html";
|
||||
windowObserver.setCallback(onUCTDialog);
|
||||
testBrowser.loadURI(testURI);
|
||||
|
||||
// Create the folder the link will be saved into.
|
||||
var destDir = createTemporarySaveDirectory();
|
||||
var destFile = destDir.clone();
|
||||
|
||||
MockFilePicker.displayDirectory = destDir;
|
||||
MockFilePicker.showCallback = function(fp) {
|
||||
info("showCallback");
|
||||
fileName = fp.defaultString;
|
||||
info("fileName: " + fileName);
|
||||
destFile.append (fileName);
|
||||
MockFilePicker.returnFiles = [destFile];
|
||||
MockFilePicker.filterIndex = 1; // kSaveAsType_URL
|
||||
info("done showCallback");
|
||||
};
|
||||
|
||||
mockTransferCallback = function(downloadSuccess) {
|
||||
info("mockTransferCallback");
|
||||
onTransferComplete(aWindow, downloadSuccess, destDir);
|
||||
destDir.remove(true);
|
||||
ok(!destDir.exists(), "Destination dir should be removed");
|
||||
ok(!destFile.exists(), "Destination file should be removed");
|
||||
mockTransferCallback = null;
|
||||
info("done mockTransferCallback");
|
||||
}
|
||||
|
||||
function onUCTDialog(dialog) {
|
||||
function doLoad() {
|
||||
content.document.querySelector('iframe').remove();
|
||||
}
|
||||
testBrowser.messageManager.loadFrameScript("data:,(" + doLoad.toString() + ")()", false);
|
||||
executeSoon(continueDownloading);
|
||||
}
|
||||
|
||||
function continueDownloading() {
|
||||
let windows = Services.wm.getEnumerator("");
|
||||
while (windows.hasMoreElements()) {
|
||||
let win = windows.getNext();
|
||||
if (win.location && win.location.href == UCT_URI) {
|
||||
win.document.documentElement._fireButtonEvent("accept");
|
||||
win.close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
ok(false, "No Unknown Content Type dialog yet?");
|
||||
}
|
||||
|
||||
function onTransferComplete(aWindow, downloadSuccess, destDir) {
|
||||
ok(downloadSuccess, "Link should have been downloaded successfully");
|
||||
aWindow.close();
|
||||
|
||||
executeSoon(aCallback);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let windowObserver = {
|
||||
setCallback: function(aCallback) {
|
||||
if (this._callback) {
|
||||
ok(false, "Should only be dealing with one callback at a time.");
|
||||
}
|
||||
this._callback = aCallback;
|
||||
},
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
if (aTopic != "domwindowopened") {
|
||||
return;
|
||||
}
|
||||
|
||||
let win = aSubject.QueryInterface(Ci.nsIDOMEventTarget);
|
||||
|
||||
win.addEventListener("load", function onLoad(event) {
|
||||
win.removeEventListener("load", onLoad, false);
|
||||
|
||||
if (win.location == UCT_URI) {
|
||||
SimpleTest.executeSoon(function() {
|
||||
if (windowObserver._callback) {
|
||||
windowObserver._callback(win);
|
||||
delete windowObserver._callback;
|
||||
} else {
|
||||
ok(false, "Unexpected UCT dialog!");
|
||||
}
|
||||
});
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
};
|
||||
|
||||
Services.ww.registerNotification(windowObserver);
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
function testOnWindow(options, callback) {
|
||||
info("testOnWindow(" + options + ")");
|
||||
var win = OpenBrowserWindow(options);
|
||||
info("got " + win);
|
||||
whenDelayedStartupFinished(win, () => callback(win));
|
||||
}
|
||||
|
||||
function whenDelayedStartupFinished(aWindow, aCallback) {
|
||||
info("whenDelayedStartupFinished");
|
||||
Services.obs.addObserver(function observer(aSubject, aTopic) {
|
||||
info("whenDelayedStartupFinished, got topic: " + aTopic + ", got subject: " + aSubject + ", waiting for " + aWindow);
|
||||
if (aWindow == aSubject) {
|
||||
Services.obs.removeObserver(observer, aTopic);
|
||||
executeSoon(aCallback);
|
||||
info("whenDelayedStartupFinished found our window");
|
||||
}
|
||||
}, "browser-delayed-startup-finished", false);
|
||||
}
|
||||
|
||||
mockTransferRegisterer.register();
|
||||
|
||||
registerCleanupFunction(function () {
|
||||
info("Running the cleanup code");
|
||||
mockTransferRegisterer.unregister();
|
||||
MockFilePicker.cleanup();
|
||||
Services.ww.unregisterNotification(windowObserver);
|
||||
Services.prefs.clearUserPref(ALWAYS_DOWNLOAD_DIR_PREF);
|
||||
Services.prefs.clearUserPref(SAVE_PER_SITE_PREF);
|
||||
info("Finished running the cleanup code");
|
||||
});
|
||||
|
||||
Services.prefs.setBoolPref(ALWAYS_DOWNLOAD_DIR_PREF, false);
|
||||
testOnWindow(undefined, function(win) {
|
||||
let windowGonePromise = promiseWindowWillBeClosed(win);
|
||||
Services.prefs.setBoolPref(SAVE_PER_SITE_PREF, true);
|
||||
triggerSave(win, function() {
|
||||
windowGonePromise.then(function() {
|
||||
Services.prefs.setBoolPref(SAVE_PER_SITE_PREF, false);
|
||||
testOnWindow(undefined, function(win) {
|
||||
triggerSave(win, finish);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head><title>This window will navigate while you're downloading something</title></head>
|
||||
<body>
|
||||
<iframe src="http://mochi.test:8888/browser/browser/base/content/test/general/unknownContentType_file.pif"></iframe>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1 @@
|
||||
Dummy content for unknownContentType_dialog_layout_data.pif
|
@ -0,0 +1 @@
|
||||
Content-Type: application/octet-stream
|
@ -295,30 +295,54 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
* @param {Object} ratio Aspect ratio of the local camera stream
|
||||
*/
|
||||
updateLocalCameraPosition: function(ratio) {
|
||||
// The local stream is a quarter of the remote stream.
|
||||
var LOCAL_STREAM_SIZE = 0.25;
|
||||
// The local stream overlaps the remote stream by a quarter of the local stream.
|
||||
var LOCAL_STREAM_OVERLAP = 0.25;
|
||||
// The minimum size of video height/width allowed by the sdk css.
|
||||
var SDK_MIN_SIZE = 48;
|
||||
|
||||
var node = this._getElement(".local");
|
||||
var parent = node.offsetParent || this._getElement(".media");
|
||||
// The local camera view should be a sixth of the size of its offset parent
|
||||
// and positioned to overlap with the remote stream at a quarter of its width.
|
||||
var parentWidth = parent.offsetWidth;
|
||||
var targetWidth = parentWidth / 6;
|
||||
var targetWidth;
|
||||
|
||||
node.style.right = "auto";
|
||||
if (window.matchMedia && window.matchMedia("screen and (max-width:640px)").matches) {
|
||||
// For reduced screen widths, we just go for a fixed size and no overlap.
|
||||
targetWidth = 180;
|
||||
node.style.width = (targetWidth * ratio.width) + "px";
|
||||
node.style.height = (targetWidth * ratio.height) + "px";
|
||||
node.style.left = "auto";
|
||||
} else {
|
||||
// The local camera view should be a quarter of the size of the remote stream
|
||||
// and positioned to overlap with the remote stream at a quarter of its width.
|
||||
|
||||
// Now position the local camera view correctly with respect to the remote
|
||||
// video stream.
|
||||
var remoteVideoDimensions = this.getRemoteVideoDimensions();
|
||||
targetWidth = remoteVideoDimensions.streamWidth * LOCAL_STREAM_SIZE;
|
||||
|
||||
var realWidth = targetWidth * ratio.width;
|
||||
var realHeight = targetWidth * ratio.height;
|
||||
|
||||
// If we've hit the min size limits, then limit at the minimum.
|
||||
if (realWidth < SDK_MIN_SIZE) {
|
||||
realWidth = SDK_MIN_SIZE;
|
||||
realHeight = realWidth / ratio.width * ratio.height;
|
||||
}
|
||||
if (realHeight < SDK_MIN_SIZE) {
|
||||
realHeight = SDK_MIN_SIZE;
|
||||
realWidth = realHeight / ratio.height * ratio.width;
|
||||
}
|
||||
|
||||
var offsetX = (remoteVideoDimensions.streamWidth + remoteVideoDimensions.offsetX);
|
||||
// The horizontal offset of the stream, and the width of the resulting
|
||||
// pillarbox, is determined by the height exponent of the aspect ratio.
|
||||
// Therefore we multiply the width of the local camera view by the height
|
||||
// ratio.
|
||||
node.style.left = (offsetX - ((targetWidth * ratio.height) / 4)) + "px";
|
||||
node.style.left = (offsetX - (realWidth * LOCAL_STREAM_OVERLAP)) + "px";
|
||||
node.style.width = realWidth + "px";
|
||||
node.style.height = realHeight + "px";
|
||||
}
|
||||
node.style.width = (targetWidth * ratio.width) + "px";
|
||||
node.style.height = (targetWidth * ratio.height) + "px";
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -295,30 +295,54 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
* @param {Object} ratio Aspect ratio of the local camera stream
|
||||
*/
|
||||
updateLocalCameraPosition: function(ratio) {
|
||||
// The local stream is a quarter of the remote stream.
|
||||
var LOCAL_STREAM_SIZE = 0.25;
|
||||
// The local stream overlaps the remote stream by a quarter of the local stream.
|
||||
var LOCAL_STREAM_OVERLAP = 0.25;
|
||||
// The minimum size of video height/width allowed by the sdk css.
|
||||
var SDK_MIN_SIZE = 48;
|
||||
|
||||
var node = this._getElement(".local");
|
||||
var parent = node.offsetParent || this._getElement(".media");
|
||||
// The local camera view should be a sixth of the size of its offset parent
|
||||
// and positioned to overlap with the remote stream at a quarter of its width.
|
||||
var parentWidth = parent.offsetWidth;
|
||||
var targetWidth = parentWidth / 6;
|
||||
var targetWidth;
|
||||
|
||||
node.style.right = "auto";
|
||||
if (window.matchMedia && window.matchMedia("screen and (max-width:640px)").matches) {
|
||||
// For reduced screen widths, we just go for a fixed size and no overlap.
|
||||
targetWidth = 180;
|
||||
node.style.width = (targetWidth * ratio.width) + "px";
|
||||
node.style.height = (targetWidth * ratio.height) + "px";
|
||||
node.style.left = "auto";
|
||||
} else {
|
||||
// The local camera view should be a quarter of the size of the remote stream
|
||||
// and positioned to overlap with the remote stream at a quarter of its width.
|
||||
|
||||
// Now position the local camera view correctly with respect to the remote
|
||||
// video stream.
|
||||
var remoteVideoDimensions = this.getRemoteVideoDimensions();
|
||||
targetWidth = remoteVideoDimensions.streamWidth * LOCAL_STREAM_SIZE;
|
||||
|
||||
var realWidth = targetWidth * ratio.width;
|
||||
var realHeight = targetWidth * ratio.height;
|
||||
|
||||
// If we've hit the min size limits, then limit at the minimum.
|
||||
if (realWidth < SDK_MIN_SIZE) {
|
||||
realWidth = SDK_MIN_SIZE;
|
||||
realHeight = realWidth / ratio.width * ratio.height;
|
||||
}
|
||||
if (realHeight < SDK_MIN_SIZE) {
|
||||
realHeight = SDK_MIN_SIZE;
|
||||
realWidth = realHeight / ratio.height * ratio.width;
|
||||
}
|
||||
|
||||
var offsetX = (remoteVideoDimensions.streamWidth + remoteVideoDimensions.offsetX);
|
||||
// The horizontal offset of the stream, and the width of the resulting
|
||||
// pillarbox, is determined by the height exponent of the aspect ratio.
|
||||
// Therefore we multiply the width of the local camera view by the height
|
||||
// ratio.
|
||||
node.style.left = (offsetX - ((targetWidth * ratio.height) / 4)) + "px";
|
||||
node.style.left = (offsetX - (realWidth * LOCAL_STREAM_OVERLAP)) + "px";
|
||||
node.style.width = realWidth + "px";
|
||||
node.style.height = realHeight + "px";
|
||||
}
|
||||
node.style.width = (targetWidth * ratio.width) + "px";
|
||||
node.style.height = (targetWidth * ratio.height) + "px";
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -143,6 +143,108 @@ describe("loop.standaloneRoomViews", function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe("Local Stream Size Position", function() {
|
||||
var view, localElement;
|
||||
|
||||
beforeEach(function() {
|
||||
sandbox.stub(window, "matchMedia").returns({
|
||||
matches: false
|
||||
});
|
||||
view = mountTestComponent();
|
||||
localElement = view._getElement(".local");
|
||||
});
|
||||
|
||||
it("should be a quarter of the width of the main stream", function() {
|
||||
sandbox.stub(view, "getRemoteVideoDimensions").returns({
|
||||
streamWidth: 640,
|
||||
offsetX: 0
|
||||
});
|
||||
|
||||
view.updateLocalCameraPosition({
|
||||
width: 1,
|
||||
height: 0.75
|
||||
});
|
||||
|
||||
expect(localElement.style.width).eql("160px");
|
||||
expect(localElement.style.height).eql("120px");
|
||||
});
|
||||
|
||||
it("should be a quarter of the width reduced for aspect ratio", function() {
|
||||
sandbox.stub(view, "getRemoteVideoDimensions").returns({
|
||||
streamWidth: 640,
|
||||
offsetX: 0
|
||||
});
|
||||
|
||||
view.updateLocalCameraPosition({
|
||||
width: 0.75,
|
||||
height: 1
|
||||
});
|
||||
|
||||
expect(localElement.style.width).eql("120px");
|
||||
expect(localElement.style.height).eql("160px");
|
||||
});
|
||||
|
||||
it("should ensure the height is a minimum of 48px", function() {
|
||||
sandbox.stub(view, "getRemoteVideoDimensions").returns({
|
||||
streamWidth: 180,
|
||||
offsetX: 0
|
||||
});
|
||||
|
||||
view.updateLocalCameraPosition({
|
||||
width: 1,
|
||||
height: 0.75
|
||||
});
|
||||
|
||||
expect(localElement.style.width).eql("64px");
|
||||
expect(localElement.style.height).eql("48px");
|
||||
});
|
||||
|
||||
it("should ensure the width is a minimum of 48px", function() {
|
||||
sandbox.stub(view, "getRemoteVideoDimensions").returns({
|
||||
streamWidth: 180,
|
||||
offsetX: 0
|
||||
});
|
||||
|
||||
view.updateLocalCameraPosition({
|
||||
width: 0.75,
|
||||
height: 1
|
||||
});
|
||||
|
||||
expect(localElement.style.width).eql("48px");
|
||||
expect(localElement.style.height).eql("64px");
|
||||
});
|
||||
|
||||
it("should position the stream to overlap the main stream by a quarter", function() {
|
||||
sandbox.stub(view, "getRemoteVideoDimensions").returns({
|
||||
streamWidth: 640,
|
||||
offsetX: 0
|
||||
});
|
||||
|
||||
view.updateLocalCameraPosition({
|
||||
width: 1,
|
||||
height: 0.75
|
||||
});
|
||||
|
||||
expect(localElement.style.width).eql("160px");
|
||||
expect(localElement.style.left).eql("600px");
|
||||
});
|
||||
|
||||
it("should position the stream to overlap the main stream by a quarter when the aspect ratio is vertical", function() {
|
||||
sandbox.stub(view, "getRemoteVideoDimensions").returns({
|
||||
streamWidth: 640,
|
||||
offsetX: 0
|
||||
});
|
||||
|
||||
view.updateLocalCameraPosition({
|
||||
width: 0.75,
|
||||
height: 1
|
||||
});
|
||||
|
||||
expect(localElement.style.width).eql("120px");
|
||||
expect(localElement.style.left).eql("610px");
|
||||
});
|
||||
});
|
||||
|
||||
describe("#render", function() {
|
||||
var view;
|
||||
|
||||
|
@ -66,7 +66,9 @@
|
||||
</tree>
|
||||
|
||||
<hbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" class="button-container">
|
||||
<button class="primary" label="&welcomeback2.restoreButton;"
|
||||
<button class="primary"
|
||||
id="errorTryAgain"
|
||||
label="&welcomeback2.restoreButton;"
|
||||
accesskey="&welcomeback2.restoreButton.access;"
|
||||
oncommand="restoreSession();"/>
|
||||
</hbox>
|
||||
|
@ -138,6 +138,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "WebChannel",
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ReaderParent",
|
||||
"resource:///modules/ReaderParent.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AddonWatcher",
|
||||
"resource://gre/modules/AddonWatcher.jsm");
|
||||
|
||||
const PREF_PLUGINS_NOTIFYUSER = "plugins.update.notifyUser";
|
||||
const PREF_PLUGINS_UPDATEURL = "plugins.update.url";
|
||||
|
||||
@ -564,6 +567,76 @@ BrowserGlue.prototype = {
|
||||
this._distributionCustomizer.applyPrefDefaults();
|
||||
},
|
||||
|
||||
_notifySlowAddon: function BG_notifySlowAddon(addonId) {
|
||||
let addonCallback = function(addon) {
|
||||
if (!addon) {
|
||||
Cu.reportError("couldn't look up addon: " + addonId);
|
||||
return;
|
||||
}
|
||||
let win = RecentWindow.getMostRecentBrowserWindow();
|
||||
|
||||
if (!win) {
|
||||
return;
|
||||
}
|
||||
|
||||
let brandBundle = win.document.getElementById("bundle_brand");
|
||||
let brandShortName = brandBundle.getString("brandShortName");
|
||||
let message = win.gNavigatorBundle.getFormattedString("addonwatch.slow", [addon.name, brandShortName]);
|
||||
let notificationBox = win.document.getElementById("global-notificationbox");
|
||||
let notificationId = 'addon-slow:' + addonId;
|
||||
let notification = notificationBox.getNotificationWithValue(notificationId);
|
||||
if(notification) {
|
||||
notification.label = message;
|
||||
} else {
|
||||
let buttons = [
|
||||
{
|
||||
label: win.gNavigatorBundle.getFormattedString("addonwatch.disable.label", [addon.name]),
|
||||
accessKey: win.gNavigatorBundle.getString("addonwatch.disable.accesskey"),
|
||||
callback: function() {
|
||||
addon.userDisabled = true;
|
||||
if (addon.pendingOperations != addon.PENDING_NONE) {
|
||||
let restartMessage = win.gNavigatorBundle.getFormattedString("addonwatch.restart.message", [addon.name, brandShortName]);
|
||||
let restartButton = [
|
||||
{
|
||||
label: win.gNavigatorBundle.getFormattedString("addonwatch.restart.label", [brandShortName]),
|
||||
accessKey: win.gNavigatorBundle.getString("addonwatch.restart.accesskey"),
|
||||
callback: function() {
|
||||
let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"]
|
||||
.getService(Ci.nsIAppStartup);
|
||||
appStartup.quit(appStartup.eForceQuit | appStartup.eRestart);
|
||||
}
|
||||
}
|
||||
];
|
||||
const priority = notificationBox.PRIORITY_WARNING_MEDIUM;
|
||||
notificationBox.appendNotification(restartMessage, "restart-" + addonId, "",
|
||||
priority, restartButton);
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: win.gNavigatorBundle.getString("addonwatch.ignoreSession.label"),
|
||||
accessKey: win.gNavigatorBundle.getString("addonwatch.ignoreSession.accesskey"),
|
||||
callback: function() {
|
||||
AddonWatcher.ignoreAddonForSession(addonId);
|
||||
}
|
||||
},
|
||||
{
|
||||
label: win.gNavigatorBundle.getString("addonwatch.ignorePerm.label"),
|
||||
accessKey: win.gNavigatorBundle.getString("addonwatch.ignorePerm.accesskey"),
|
||||
callback: function() {
|
||||
AddonWatcher.ignoreAddonPermanently(addonId);
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
const priority = notificationBox.PRIORITY_WARNING_MEDIUM;
|
||||
notificationBox.appendNotification(message, notificationId, "",
|
||||
priority, buttons);
|
||||
}
|
||||
};
|
||||
AddonManager.getAddonByID(addonId, addonCallback);
|
||||
},
|
||||
|
||||
// runs on startup, before the first command line handler is invoked
|
||||
// (i.e. before the first window is opened)
|
||||
_finalUIStartup: function BG__finalUIStartup() {
|
||||
@ -612,6 +685,8 @@ BrowserGlue.prototype = {
|
||||
#endif
|
||||
|
||||
Services.obs.notifyObservers(null, "browser-ui-startup-complete", "");
|
||||
|
||||
AddonWatcher.init(this._notifySlowAddon);
|
||||
},
|
||||
|
||||
_checkForOldBuildUpdates: function () {
|
||||
|
@ -31,7 +31,7 @@
|
||||
<key key="&focusSearch2.key;" modifiers="accel" oncommand="gCookiesWindow.focusFilterBox();"/>
|
||||
</keyset>
|
||||
|
||||
<vbox flex="1" class="contentPane">
|
||||
<vbox flex="1" class="contentPane largeDialogContainer">
|
||||
<hbox align="center">
|
||||
<label accesskey="&filter.accesskey;" control="filter">&filter.label;</label>
|
||||
<textbox type="search" id="filter" flex="1"
|
||||
|
@ -22,6 +22,7 @@
|
||||
<script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/>
|
||||
|
||||
<prefpane id="FontsDialogPane"
|
||||
class="largeDialogContainer"
|
||||
helpTopic="prefs-fonts-and-colors">
|
||||
|
||||
<preferences id="fontPreferences">
|
||||
|
@ -37,6 +37,12 @@ let gSubDialog = {
|
||||
// Wait for the stylesheets injected during DOMContentLoaded to load before showing the dialog
|
||||
// otherwise there is a flicker of the stylesheet applying.
|
||||
this._frame.addEventListener("load", this._onLoad.bind(this));
|
||||
|
||||
chromeBrowser.addEventListener("unload", function(aEvent) {
|
||||
if (aEvent.target.location.href != "about:blank") {
|
||||
this.close();
|
||||
}
|
||||
}.bind(this), true);
|
||||
},
|
||||
|
||||
uninit: function() {
|
||||
@ -92,6 +98,8 @@ let gSubDialog = {
|
||||
// Clear the sizing attributes
|
||||
this._box.removeAttribute("width");
|
||||
this._box.removeAttribute("height");
|
||||
this._box.style.removeProperty("min-height");
|
||||
this._box.style.removeProperty("min-width");
|
||||
|
||||
setTimeout(() => {
|
||||
// Unload the dialog after the event listeners run so that the load of about:blank isn't
|
||||
@ -144,12 +152,42 @@ let gSubDialog = {
|
||||
// Do this on load to wait for the CSS to load and apply before calculating the size.
|
||||
let docEl = this._frame.contentDocument.documentElement;
|
||||
|
||||
// padding-bottom doesn't seem to be included in the scrollHeight of the document element in XUL
|
||||
// so add it ourselves.
|
||||
let paddingBottom = parseFloat(this._frame.contentWindow.getComputedStyle(docEl).paddingBottom);
|
||||
let groupBoxTitle = document.getAnonymousElementByAttribute(this._box, "class", "groupbox-title");
|
||||
let groupBoxTitleHeight = groupBoxTitle.clientHeight +
|
||||
parseFloat(getComputedStyle(groupBoxTitle).borderBottomWidth);
|
||||
|
||||
this._frame.style.width = docEl.style.width || docEl.scrollWidth + "px";
|
||||
this._frame.style.height = docEl.style.height || (docEl.scrollHeight + paddingBottom) + "px";
|
||||
let groupBoxBody = document.getAnonymousElementByAttribute(this._box, "class", "groupbox-body");
|
||||
let boxVerticalPadding = 2 * parseFloat(getComputedStyle(groupBoxBody).paddingTop);
|
||||
let boxHorizontalPadding = 2 * parseFloat(getComputedStyle(groupBoxBody).paddingLeft);
|
||||
let frameWidth = docEl.style.width || docEl.scrollWidth + "px";
|
||||
let frameHeight = docEl.style.height || docEl.scrollHeight + "px";
|
||||
let boxVerticalBorder = 2 * parseFloat(getComputedStyle(this._box).borderTopWidth);
|
||||
let boxHorizontalBorder = 2 * parseFloat(getComputedStyle(this._box).borderLeftWidth);
|
||||
|
||||
let frameRect = this._frame.getBoundingClientRect();
|
||||
let boxRect = this._box.getBoundingClientRect();
|
||||
let frameSizeDifference = (frameRect.top - boxRect.top) + (boxRect.bottom - frameRect.bottom);
|
||||
|
||||
// Now check if the frame height we calculated is possible at this window size,
|
||||
// accounting for titlebar, padding/border and some spacing.
|
||||
let maxHeight = window.innerHeight - frameSizeDifference - 30;
|
||||
if (frameHeight > maxHeight) {
|
||||
// If not, we should probably let the dialog scroll:
|
||||
frameHeight = maxHeight;
|
||||
let containers = this._frame.contentDocument.querySelectorAll('.largeDialogContainer');
|
||||
for (let container of containers) {
|
||||
container.classList.add("doScroll");
|
||||
}
|
||||
}
|
||||
|
||||
this._frame.style.width = frameWidth;
|
||||
this._frame.style.height = frameHeight;
|
||||
this._box.style.minHeight = "calc(" +
|
||||
(boxVerticalBorder + groupBoxTitleHeight + boxVerticalPadding) +
|
||||
"px + " + frameHeight + ")";
|
||||
this._box.style.minWidth = "calc(" +
|
||||
(boxHorizontalBorder + boxHorizontalPadding) +
|
||||
"px + " + frameWidth + ")";
|
||||
|
||||
this._overlay.style.visibility = "visible";
|
||||
this._frame.focus();
|
||||
|
@ -76,7 +76,7 @@ let gTests = [{
|
||||
dialog.document.documentElement.cancelDialog();
|
||||
|
||||
let closingEvent = yield closingPromise;
|
||||
ise(closingEvent.detail.button, "cancel", "closing event should indicate button was 'accept'");
|
||||
ise(closingEvent.detail.button, "cancel", "closing event should indicate button was 'cancel'");
|
||||
|
||||
yield deferredClose.promise;
|
||||
ise(rv.acceptCount, 0, "return value should NOT have been updated");
|
||||
@ -118,6 +118,26 @@ let gTests = [{
|
||||
ise(rv.acceptCount, 0, "return value should NOT have been updated");
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Check that 'back' navigation will close the dialog",
|
||||
run: function* () {
|
||||
let rv = { acceptCount: 0 };
|
||||
let deferredClose = Promise.defer();
|
||||
let dialogPromise = openAndLoadSubDialog(gDialogURL, null, rv,
|
||||
(aEvent) => dialogClosingCallback(deferredClose, aEvent));
|
||||
let dialog = yield dialogPromise;
|
||||
|
||||
// XXX Without the call to promiseDialogClosing the test causes
|
||||
// intermittent failures in browser_change_app_handler.js.
|
||||
let unusedClosingPromise = promiseDialogClosing(dialog);
|
||||
|
||||
info("cancelling the dialog");
|
||||
content.gSubDialog._frame.goBack();
|
||||
|
||||
yield deferredClose.promise;
|
||||
ise(rv.acceptCount, 0, "return value should NOT have been updated");
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Hitting escape in the dialog",
|
||||
run: function* () {
|
||||
|
@ -22,6 +22,7 @@
|
||||
<script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/>
|
||||
|
||||
<prefpane id="LanguagesDialogPane"
|
||||
class="largeDialogContainer"
|
||||
onpaneload="gLanguagesDialog.init();"
|
||||
helpTopic="prefs-languages">
|
||||
|
||||
|
@ -29,7 +29,7 @@
|
||||
<key key="&windowClose.key;" modifiers="accel" oncommand="window.close();"/>
|
||||
</keyset>
|
||||
|
||||
<vbox class="contentPane" flex="1">
|
||||
<vbox class="contentPane largeDialogContainer" flex="1">
|
||||
<description id="permissionsText" control="url"/>
|
||||
<separator class="thin"/>
|
||||
<label id="urlLabel" control="url" value="&address.label;" accesskey="&address.accesskey;"/>
|
||||
|
@ -82,8 +82,8 @@
|
||||
<caption label="&dataSection.label;"/>
|
||||
<grid flex="1">
|
||||
<columns>
|
||||
<column dialogWidth="&column.width2;"
|
||||
subdialogWidth="&inContentColumn.width;"/>
|
||||
<column dialogWidth="&sanitizePrefs2.column.width;"
|
||||
subdialogWidth="&sanitizePrefs2.inContent.column.width;"/>
|
||||
<column flex="1"/>
|
||||
</columns>
|
||||
<rows>
|
||||
|
@ -28,45 +28,47 @@
|
||||
<key key="&windowClose.key;" modifiers="accel" oncommand="window.close();"/>
|
||||
</keyset>
|
||||
|
||||
<vbox class="contentPane" flex="1">
|
||||
<label id="languagesLabel" control="permissionsTree">&noTranslationForLanguages.label;</label>
|
||||
<separator class="thin"/>
|
||||
<tree id="languagesTree" flex="1" style="height: 12em;"
|
||||
hidecolumnpicker="true"
|
||||
onkeypress="gTranslationExceptions.onLanguageKeyPress(event)"
|
||||
onselect="gTranslationExceptions.onLanguageSelected();">
|
||||
<treecols>
|
||||
<treecol id="languageCol" label="&treehead.languageName.label;" flex="1"/>
|
||||
</treecols>
|
||||
<treechildren/>
|
||||
</tree>
|
||||
</vbox>
|
||||
<hbox align="end">
|
||||
<hbox class="actionButtons" flex="1">
|
||||
<button id="removeLanguage" disabled="true"
|
||||
accesskey="&removeLanguage.accesskey;"
|
||||
icon="remove" label="&removeLanguage.label;"
|
||||
oncommand="gTranslationExceptions.onLanguageDeleted();"/>
|
||||
<button id="removeAllLanguages"
|
||||
icon="clear" label="&removeAllLanguages.label;"
|
||||
accesskey="&removeAllLanguages.accesskey;"
|
||||
oncommand="gTranslationExceptions.onAllLanguagesDeleted();"/>
|
||||
<spacer flex="1"/>
|
||||
<vbox class="largeDialogContainer">
|
||||
<vbox class="contentPane" flex="1">
|
||||
<label id="languagesLabel" control="permissionsTree">&noTranslationForLanguages.label;</label>
|
||||
<separator class="thin"/>
|
||||
<tree id="languagesTree" flex="1" style="height: 12em;"
|
||||
hidecolumnpicker="true"
|
||||
onkeypress="gTranslationExceptions.onLanguageKeyPress(event)"
|
||||
onselect="gTranslationExceptions.onLanguageSelected();">
|
||||
<treecols>
|
||||
<treecol id="languageCol" label="&treehead.languageName.label;" flex="1"/>
|
||||
</treecols>
|
||||
<treechildren/>
|
||||
</tree>
|
||||
</vbox>
|
||||
<hbox align="end">
|
||||
<hbox class="actionButtons" flex="1">
|
||||
<button id="removeLanguage" disabled="true"
|
||||
accesskey="&removeLanguage.accesskey;"
|
||||
icon="remove" label="&removeLanguage.label;"
|
||||
oncommand="gTranslationExceptions.onLanguageDeleted();"/>
|
||||
<button id="removeAllLanguages"
|
||||
icon="clear" label="&removeAllLanguages.label;"
|
||||
accesskey="&removeAllLanguages.accesskey;"
|
||||
oncommand="gTranslationExceptions.onAllLanguagesDeleted();"/>
|
||||
<spacer flex="1"/>
|
||||
</hbox>
|
||||
</hbox>
|
||||
</hbox>
|
||||
<separator/>
|
||||
<vbox class="contentPane" flex="1">
|
||||
<label id="languagesLabel" control="permissionsTree">&noTranslationForSites.label;</label>
|
||||
<separator class="thin"/>
|
||||
<tree id="sitesTree" flex="1" style="height: 12em;"
|
||||
hidecolumnpicker="true"
|
||||
onkeypress="gTranslationExceptions.onSiteKeyPress(event)"
|
||||
onselect="gTranslationExceptions.onSiteSelected();">
|
||||
<treecols>
|
||||
<treecol id="siteCol" label="&treehead.siteName.label;" flex="1"/>
|
||||
</treecols>
|
||||
<treechildren/>
|
||||
</tree>
|
||||
<separator/>
|
||||
<vbox class="contentPane" flex="1">
|
||||
<label id="languagesLabel" control="permissionsTree">&noTranslationForSites.label;</label>
|
||||
<separator class="thin"/>
|
||||
<tree id="sitesTree" flex="1" style="height: 12em;"
|
||||
hidecolumnpicker="true"
|
||||
onkeypress="gTranslationExceptions.onSiteKeyPress(event)"
|
||||
onselect="gTranslationExceptions.onSiteSelected();">
|
||||
<treecols>
|
||||
<treecol id="siteCol" label="&treehead.siteName.label;" flex="1"/>
|
||||
</treecols>
|
||||
<treechildren/>
|
||||
</tree>
|
||||
</vbox>
|
||||
</vbox>
|
||||
<hbox align="end">
|
||||
<hbox class="actionButtons" flex="1">
|
||||
|
@ -38,9 +38,9 @@ add_task(function* test_remove() {
|
||||
|
||||
is(hiddenOneOffs.length, 1,
|
||||
"hiddenOneOffs has the correct engine count post removal.");
|
||||
is(hiddenOneOffs.includes("FooDupe"), false,
|
||||
is(hiddenOneOffs.some(x => x == "FooDupe"), false,
|
||||
"Removed Engine is not in hiddenOneOffs after removal");
|
||||
is(hiddenOneOffs.includes("Foo"), true,
|
||||
is(hiddenOneOffs.some(x => x == "Foo"), true,
|
||||
"Current hidden engine is not affected by removal.");
|
||||
|
||||
info("Removing testEngine.xml");
|
||||
@ -61,9 +61,9 @@ add_task(function* test_add() {
|
||||
|
||||
is(hiddenOneOffs.length, 1,
|
||||
"hiddenOneOffs has the correct number of hidden engines present post add.");
|
||||
is(hiddenOneOffs.includes("FooDupe"), false,
|
||||
is(hiddenOneOffs.some(x => x == "FooDupe"), false,
|
||||
"Added engine is not present in hidden list.");
|
||||
is(hiddenOneOffs.includes("Foo"), true,
|
||||
is(hiddenOneOffs.some(x => x == "Foo"), true,
|
||||
"Adding an engine does not remove engines from hidden list.");
|
||||
});
|
||||
|
||||
|
@ -159,7 +159,12 @@ let openAnimationInspector = Task.async(function*() {
|
||||
let win = inspector.sidebar.getWindowForTab("animationinspector");
|
||||
let {AnimationsController, AnimationsPanel} = win;
|
||||
|
||||
yield AnimationsPanel.once(AnimationsPanel.PANEL_INITIALIZED);
|
||||
info("Waiting for the animation controller and panel to be ready");
|
||||
if (AnimationsPanel.initialized) {
|
||||
yield AnimationsPanel.initialized;
|
||||
} else {
|
||||
yield AnimationsPanel.once(AnimationsPanel.PANEL_INITIALIZED);
|
||||
}
|
||||
|
||||
return {
|
||||
toolbox: toolbox,
|
||||
|
@ -499,7 +499,7 @@
|
||||
<vbox class="security-info-section">
|
||||
<hbox id="security-protocol-version"
|
||||
class="tabpanel-summary-container"
|
||||
align="center">
|
||||
align="baseline">
|
||||
<label class="plain tabpanel-summary-label"
|
||||
value="&netmonitorUI.security.protocolVersion;"/>
|
||||
<label id="security-protocol-version-value"
|
||||
@ -512,7 +512,7 @@
|
||||
</hbox>
|
||||
<hbox id="security-ciphersuite"
|
||||
class="tabpanel-summary-container"
|
||||
align="center">
|
||||
align="baseline">
|
||||
<label class="plain tabpanel-summary-label"
|
||||
value="&netmonitorUI.security.cipherSuite;"/>
|
||||
<label id="security-ciphersuite-value"
|
||||
@ -532,7 +532,7 @@
|
||||
<vbox class="security-info-section">
|
||||
<hbox id="security-http-strict-transport-security"
|
||||
class="tabpanel-summary-container"
|
||||
align="center">
|
||||
align="baseline">
|
||||
<label class="plain tabpanel-summary-label"
|
||||
value="&netmonitorUI.security.hsts;"/>
|
||||
<label id="security-http-strict-transport-security-value"
|
||||
@ -542,7 +542,7 @@
|
||||
</hbox>
|
||||
<hbox id="security-public-key-pinning"
|
||||
class="tabpanel-summary-container"
|
||||
align="center">
|
||||
align="baseline">
|
||||
<label class="plain tabpanel-summary-label"
|
||||
value="&netmonitorUI.security.hpkp;"/>
|
||||
<label id="security-public-key-pinning-value"
|
||||
@ -563,7 +563,7 @@
|
||||
</vbox>
|
||||
<vbox class="security-info-section">
|
||||
<hbox class="tabpanel-summary-container"
|
||||
align="center">
|
||||
align="baseline">
|
||||
<label class="plain tabpanel-summary-label"
|
||||
value="&certmgr.certdetail.cn;:"/>
|
||||
<label id="security-cert-subject-cn"
|
||||
@ -572,7 +572,7 @@
|
||||
flex="1"/>
|
||||
</hbox>
|
||||
<hbox class="tabpanel-summary-container"
|
||||
align="center">
|
||||
align="baseline">
|
||||
<label class="plain tabpanel-summary-label"
|
||||
value="&certmgr.certdetail.o;:"/>
|
||||
<label id="security-cert-subject-o"
|
||||
@ -581,7 +581,7 @@
|
||||
flex="1"/>
|
||||
</hbox>
|
||||
<hbox class="tabpanel-summary-container"
|
||||
align="center">
|
||||
align="baseline">
|
||||
<label class="plain tabpanel-summary-label"
|
||||
value="&certmgr.certdetail.ou;:"/>
|
||||
<label id="security-cert-subject-ou"
|
||||
@ -596,7 +596,7 @@
|
||||
</vbox>
|
||||
<vbox class="security-info-section">
|
||||
<hbox class="tabpanel-summary-container"
|
||||
align="center">
|
||||
align="baseline">
|
||||
<label class="plain tabpanel-summary-label"
|
||||
value="&certmgr.certdetail.cn;:"/>
|
||||
<label id="security-cert-issuer-cn"
|
||||
@ -605,7 +605,7 @@
|
||||
flex="1"/>
|
||||
</hbox>
|
||||
<hbox class="tabpanel-summary-container"
|
||||
align="center">
|
||||
align="baseline">
|
||||
<label class="plain tabpanel-summary-label"
|
||||
value="&certmgr.certdetail.o;:"/>
|
||||
<label id="security-cert-issuer-o"
|
||||
@ -614,7 +614,7 @@
|
||||
flex="1"/>
|
||||
</hbox>
|
||||
<hbox class="tabpanel-summary-container"
|
||||
align="center">
|
||||
align="baseline">
|
||||
<label class="plain tabpanel-summary-label"
|
||||
value="&certmgr.certdetail.ou;:"/>
|
||||
<label id="security-cert-issuer-ou"
|
||||
@ -629,7 +629,7 @@
|
||||
</vbox>
|
||||
<vbox class="security-info-section">
|
||||
<hbox class="tabpanel-summary-container"
|
||||
align="center">
|
||||
align="baseline">
|
||||
<label class="plain tabpanel-summary-label"
|
||||
value="&certmgr.begins;:"/>
|
||||
<label id="security-cert-validity-begins"
|
||||
@ -638,7 +638,7 @@
|
||||
flex="1"/>
|
||||
</hbox>
|
||||
<hbox class="tabpanel-summary-container"
|
||||
align="center">
|
||||
align="baseline">
|
||||
<label class="plain tabpanel-summary-label"
|
||||
value="&certmgr.expires;:"/>
|
||||
<label id="security-cert-validity-expires"
|
||||
@ -653,7 +653,7 @@
|
||||
</vbox>
|
||||
<vbox class="security-info-section">
|
||||
<hbox class="tabpanel-summary-container"
|
||||
align="center">
|
||||
align="baseline">
|
||||
<label class="plain tabpanel-summary-label"
|
||||
value="&certmgr.certdetail.sha256fingerprint;:"/>
|
||||
<label id="security-cert-sha256-fingerprint"
|
||||
@ -662,7 +662,7 @@
|
||||
flex="1"/>
|
||||
</hbox>
|
||||
<hbox class="tabpanel-summary-container"
|
||||
align="center">
|
||||
align="baseline">
|
||||
<label class="plain tabpanel-summary-label"
|
||||
value="&certmgr.certdetail.sha1fingerprint;:"/>
|
||||
<label id="security-cert-sha1-fingerprint"
|
||||
|
@ -81,7 +81,11 @@ function memoryActorSupported (target) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !!target.getTrait("memoryActorAllocations");
|
||||
// We need to test that both the root actor has `memoryActorAllocations`,
|
||||
// which is in Gecko 38+, and also that the target has a memory actor. There
|
||||
// are scenarios, like addon debugging, where memoryActorAllocations is available,
|
||||
// but no memory actor (like addon debugging in Gecko 38+)
|
||||
return !!target.getTrait("memoryActorAllocations") && target.hasActor("memory");
|
||||
}
|
||||
exports.memoryActorSupported = Task.async(memoryActorSupported);
|
||||
|
||||
|
@ -40,7 +40,7 @@ support-files =
|
||||
[browser_perf-jump-to-debugger-01.js]
|
||||
[browser_perf-jump-to-debugger-02.js]
|
||||
[browser_perf-options-01.js]
|
||||
[browser_perf-options-02.js]
|
||||
# [browser_perf-options-02.js] bug 1133230
|
||||
[browser_perf-options-invert-call-tree-01.js]
|
||||
[browser_perf-options-invert-call-tree-02.js]
|
||||
[browser_perf-options-invert-flame-graph-01.js]
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
/**
|
||||
* Tests that the recording model is populated correctly when using timeline
|
||||
* and memory actor mocks.
|
||||
* and memory actor mocks, and the correct views are shown.
|
||||
*/
|
||||
|
||||
const WAIT_TIME = 1000;
|
||||
@ -14,15 +14,15 @@ let test = Task.async(function*() {
|
||||
TEST_MOCK_TIMELINE_ACTOR: true
|
||||
});
|
||||
Services.prefs.setBoolPref(MEMORY_PREF, true);
|
||||
let { EVENTS, gFront, PerformanceController, PerformanceView } = panel.panelWin;
|
||||
let { EVENTS, $, gFront, PerformanceController, PerformanceView, DetailsView, JsCallTreeView } = panel.panelWin;
|
||||
|
||||
let { memory: memoryMock, timeline: timelineMock } = gFront.getMocksInUse();
|
||||
ok(memoryMock, "memory should be mocked.");
|
||||
ok(timelineMock, "timeline should be mocked.");
|
||||
|
||||
yield startRecording(panel);
|
||||
yield startRecording(panel, { waitForOverview: false });
|
||||
busyWait(WAIT_TIME); // allow the profiler module to sample some cpu activity
|
||||
yield stopRecording(panel);
|
||||
yield stopRecording(panel, { waitForOverview: false });
|
||||
|
||||
let {
|
||||
label, duration, markers, frames, memory, ticks, allocations, profile
|
||||
@ -58,6 +58,23 @@ let test = Task.async(function*() {
|
||||
ok(sampleCount > 0,
|
||||
"At least some samples have been iterated over, checking for root nodes.");
|
||||
|
||||
is($("#overview-pane").hidden, true,
|
||||
"overview pane hidden when timeline mocked.");
|
||||
|
||||
is($("#select-waterfall-view").hidden, true,
|
||||
"waterfall view button hidden when timeline mocked");
|
||||
is($("#select-js-calltree-view").hidden, false,
|
||||
"jscalltree view button not hidden when timeline/memory mocked");
|
||||
is($("#select-js-flamegraph-view").hidden, true,
|
||||
"jsflamegraph view button hidden when timeline mocked");
|
||||
is($("#select-memory-calltree-view").hidden, true,
|
||||
"memorycalltree view button hidden when memory mocked");
|
||||
is($("#select-memory-flamegraph-view").hidden, true,
|
||||
"memoryflamegraph view button hidden when memory mocked");
|
||||
|
||||
ok(DetailsView.isViewSelected(JsCallTreeView),
|
||||
"JS Call Tree view selected by default when timeline/memory mocked.");
|
||||
|
||||
yield teardown(panel);
|
||||
finish();
|
||||
});
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
/**
|
||||
* Tests that the recording model is populated correctly when using timeline
|
||||
* and memory actor mocks.
|
||||
* and memory actor mocks, and that the correct button/overview displays are shown.
|
||||
*/
|
||||
|
||||
const WAIT_TIME = 1000;
|
||||
@ -13,7 +13,7 @@ let test = Task.async(function*() {
|
||||
TEST_MOCK_MEMORY_ACTOR: true
|
||||
});
|
||||
Services.prefs.setBoolPref(MEMORY_PREF, true);
|
||||
let { EVENTS, gFront, PerformanceController, PerformanceView } = panel.panelWin;
|
||||
let { EVENTS, $, gFront, PerformanceController, PerformanceView, DetailsView, WaterfallView } = panel.panelWin;
|
||||
|
||||
|
||||
let { memory: memoryMock, timeline: timelineMock } = gFront.getMocksInUse();
|
||||
@ -57,6 +57,23 @@ let test = Task.async(function*() {
|
||||
ok(sampleCount > 0,
|
||||
"At least some samples have been iterated over, checking for root nodes.");
|
||||
|
||||
is($("#overview-pane").hidden, false,
|
||||
"overview pane not hidden when only memory mocked.");
|
||||
|
||||
is($("#select-waterfall-view").hidden, false,
|
||||
"waterfall view button not hidden when memory mocked");
|
||||
is($("#select-js-calltree-view").hidden, false,
|
||||
"jscalltree view button not hidden when memory mocked");
|
||||
is($("#select-js-flamegraph-view").hidden, false,
|
||||
"jsflamegraph view button not hidden when memory mocked");
|
||||
is($("#select-memory-calltree-view").hidden, true,
|
||||
"memorycalltree view button hidden when memory mocked");
|
||||
is($("#select-memory-flamegraph-view").hidden, true,
|
||||
"memoryflamegraph view button hidden when memory mocked");
|
||||
|
||||
ok(DetailsView.isViewSelected(WaterfallView),
|
||||
"Waterfall view selected by default when memory mocked.");
|
||||
|
||||
yield teardown(panel);
|
||||
finish();
|
||||
});
|
||||
|
@ -266,7 +266,7 @@ function mousedown (win, button) {
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" }, button, win);
|
||||
}
|
||||
|
||||
function* startRecording(panel) {
|
||||
function* startRecording(panel, options={}) {
|
||||
let win = panel.panelWin;
|
||||
let clicked = panel.panelWin.PerformanceView.once(win.EVENTS.UI_START_RECORDING);
|
||||
let willStart = panel.panelWin.PerformanceController.once(win.EVENTS.RECORDING_WILL_START);
|
||||
@ -290,7 +290,7 @@ function* startRecording(panel) {
|
||||
let stateChanged = once(win.PerformanceView, win.EVENTS.UI_STATE_CHANGED);
|
||||
|
||||
yield hasStarted;
|
||||
let overviewRendered = once(win.OverviewView, win.EVENTS.OVERVIEW_RENDERED);
|
||||
let overviewRendered = options.waitForOverview ? once(win.OverviewView, win.EVENTS.OVERVIEW_RENDERED) : Promise.resolve();
|
||||
|
||||
yield stateChanged;
|
||||
yield overviewRendered;
|
||||
@ -304,7 +304,7 @@ function* startRecording(panel) {
|
||||
"The record button should not be locked.");
|
||||
}
|
||||
|
||||
function* stopRecording(panel) {
|
||||
function* stopRecording(panel, options={}) {
|
||||
let win = panel.panelWin;
|
||||
let clicked = panel.panelWin.PerformanceView.once(win.EVENTS.UI_STOP_RECORDING);
|
||||
let willStop = panel.panelWin.PerformanceController.once(win.EVENTS.RECORDING_WILL_STOP);
|
||||
@ -328,7 +328,7 @@ function* stopRecording(panel) {
|
||||
let stateChanged = once(win.PerformanceView, win.EVENTS.UI_STATE_CHANGED);
|
||||
|
||||
yield hasStopped;
|
||||
let overviewRendered = once(win.OverviewView, win.EVENTS.OVERVIEW_RENDERED);
|
||||
let overviewRendered = options.waitForOverview ? once(win.OverviewView, win.EVENTS.OVERVIEW_RENDERED) : Promise.resolve();
|
||||
|
||||
yield stateChanged;
|
||||
yield overviewRendered;
|
||||
|
@ -3,8 +3,6 @@
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
const DEFAULT_DETAILS_SUBVIEW = "waterfall";
|
||||
|
||||
/**
|
||||
* Details view containing profiler call tree and markers waterfall. Manages
|
||||
* subviews and toggles visibility between them.
|
||||
@ -14,11 +12,11 @@ let DetailsView = {
|
||||
* Name to node+object mapping of subviews.
|
||||
*/
|
||||
components: {
|
||||
"waterfall": { id: "waterfall-view", view: WaterfallView },
|
||||
"waterfall": { id: "waterfall-view", view: WaterfallView, requires: ["timeline"] },
|
||||
"js-calltree": { id: "js-calltree-view", view: JsCallTreeView },
|
||||
"js-flamegraph": { id: "js-flamegraph-view", view: JsFlameGraphView },
|
||||
"memory-calltree": { id: "memory-calltree-view", view: MemoryCallTreeView, pref: "enable-memory" },
|
||||
"memory-flamegraph": { id: "memory-flamegraph-view", view: MemoryFlameGraphView, pref: "enable-memory" }
|
||||
"js-flamegraph": { id: "js-flamegraph-view", view: JsFlameGraphView, requires: ["timeline"] },
|
||||
"memory-calltree": { id: "memory-calltree-view", view: MemoryCallTreeView, requires: ["memory"], pref: "enable-memory" },
|
||||
"memory-flamegraph": { id: "memory-flamegraph-view", view: MemoryFlameGraphView, requires: ["memory", "timeline"], pref: "enable-memory" }
|
||||
},
|
||||
|
||||
/**
|
||||
@ -36,7 +34,7 @@ let DetailsView = {
|
||||
button.addEventListener("command", this._onViewToggle);
|
||||
}
|
||||
|
||||
yield this.selectView(DEFAULT_DETAILS_SUBVIEW);
|
||||
yield this.selectDefaultView();
|
||||
yield this.setAvailableViews();
|
||||
|
||||
PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
|
||||
@ -62,22 +60,27 @@ let DetailsView = {
|
||||
}),
|
||||
|
||||
/**
|
||||
* Sets the possible views based off of prefs by hiding/showing the
|
||||
* Sets the possible views based off of prefs and server actor support by hiding/showing the
|
||||
* buttons that select them and going to default view if currently selected.
|
||||
* Called when a preference changes in `devtools.performance.ui.`.
|
||||
*/
|
||||
setAvailableViews: Task.async(function* () {
|
||||
for (let [name, { view, pref }] of Iterator(this.components)) {
|
||||
let mocks = gFront.getMocksInUse();
|
||||
for (let [name, { view, pref, requires }] of Iterator(this.components)) {
|
||||
let recording = PerformanceController.getCurrentRecording();
|
||||
|
||||
let isRecorded = recording && !recording.isRecording();
|
||||
// View is enabled view prefs
|
||||
let isEnabled = !pref || PerformanceController.getPref(pref);
|
||||
$(`toolbarbutton[data-view=${name}]`).hidden = !isRecorded || !isEnabled;
|
||||
// View is supported by the server actor, and the requried actor is not being mocked
|
||||
let isSupported = !requires || requires.every(r => !mocks[r]);
|
||||
|
||||
$(`toolbarbutton[data-view=${name}]`).hidden = !isRecorded || !(isEnabled && isSupported);
|
||||
|
||||
// If the view is currently selected and not enabled, go back to the
|
||||
// default view.
|
||||
if (!isEnabled && this.isViewSelected(view)) {
|
||||
yield this.selectView(DEFAULT_DETAILS_SUBVIEW);
|
||||
yield this.selectDefaultView();
|
||||
}
|
||||
}
|
||||
}),
|
||||
@ -106,6 +109,22 @@ let DetailsView = {
|
||||
this.emit(EVENTS.DETAILS_VIEW_SELECTED, viewName);
|
||||
}),
|
||||
|
||||
/**
|
||||
* Selects a default view based off of protocol support
|
||||
* and preferences enabled.
|
||||
*/
|
||||
selectDefaultView: function () {
|
||||
let { timeline: mockTimeline } = gFront.getMocksInUse();
|
||||
// If timelines are mocked, the first view available is the js-calltree.
|
||||
if (mockTimeline) {
|
||||
return this.selectView("js-calltree");
|
||||
} else {
|
||||
// In every other scenario with preferences and mocks, waterfall will
|
||||
// be the default view.
|
||||
return this.selectView("waterfall");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if the provided view is currently selected.
|
||||
*
|
||||
|
@ -26,6 +26,9 @@ let OverviewView = {
|
||||
* Sets up the view with event binding.
|
||||
*/
|
||||
initialize: function () {
|
||||
if (gFront.getMocksInUse().timeline) {
|
||||
this.disable();
|
||||
}
|
||||
this._onRecordingWillStart = this._onRecordingWillStart.bind(this);
|
||||
this._onRecordingStarted = this._onRecordingStarted.bind(this);
|
||||
this._onRecordingWillStop = this._onRecordingWillStop.bind(this);
|
||||
@ -60,6 +63,25 @@ let OverviewView = {
|
||||
PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
|
||||
},
|
||||
|
||||
/**
|
||||
* Disabled in the event we're using a Timeline mock, so we'll have no
|
||||
* markers, ticks or memory data to show, so just block rendering and hide
|
||||
* the panel.
|
||||
*/
|
||||
disable: function () {
|
||||
this._disabled = true;
|
||||
$("#overview-pane").hidden = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the disabled status.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
isDisabled: function () {
|
||||
return this._disabled;
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the time interval selection for all graphs in this overview.
|
||||
*
|
||||
@ -67,6 +89,10 @@ let OverviewView = {
|
||||
* The { starTime, endTime }, in milliseconds.
|
||||
*/
|
||||
setTimeInterval: function(interval, options = {}) {
|
||||
if (this.isDisabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let recording = PerformanceController.getCurrentRecording();
|
||||
if (recording == null) {
|
||||
throw new Error("A recording should be available in order to set the selection.");
|
||||
@ -83,10 +109,15 @@ let OverviewView = {
|
||||
* Gets the time interval selection for all graphs in this overview.
|
||||
*
|
||||
* @return object
|
||||
* The { starTime, endTime }, in milliseconds.
|
||||
* The { startTime, endTime }, in milliseconds.
|
||||
*/
|
||||
getTimeInterval: function() {
|
||||
let recording = PerformanceController.getCurrentRecording();
|
||||
|
||||
if (this.isDisabled()) {
|
||||
return { startTime: 0, endTime: recording.getDuration() };
|
||||
}
|
||||
|
||||
if (recording == null) {
|
||||
throw new Error("A recording should be available in order to get the selection.");
|
||||
}
|
||||
@ -172,6 +203,9 @@ let OverviewView = {
|
||||
* The fps graph resolution. @see Graphs.jsm
|
||||
*/
|
||||
render: Task.async(function *(resolution) {
|
||||
if (this.isDisabled()) {
|
||||
return;
|
||||
}
|
||||
let recording = PerformanceController.getCurrentRecording();
|
||||
let duration = recording.getDuration();
|
||||
let markers = recording.getMarkers();
|
||||
|
@ -5,9 +5,9 @@
|
||||
const {Cu, Ci} = require("chrome");
|
||||
const {Devices} = Cu.import("resource://gre/modules/devtools/Devices.jsm");
|
||||
const {Services} = Cu.import("resource://gre/modules/Services.jsm");
|
||||
const {Simulator} = Cu.import("resource://gre/modules/devtools/Simulator.jsm");
|
||||
const {ConnectionManager, Connection} = require("devtools/client/connection-manager");
|
||||
const {Connection} = require("devtools/client/connection-manager");
|
||||
const {DebuggerServer} = require("resource://gre/modules/devtools/dbg-server.jsm");
|
||||
const {Simulators} = require("devtools/webide/simulators");
|
||||
const discovery = require("devtools/toolkit/discovery/discovery");
|
||||
const EventEmitter = require("devtools/toolkit/event-emitter");
|
||||
const promise = require("promise");
|
||||
@ -193,14 +193,12 @@ let SimulatorScanner = {
|
||||
|
||||
enable() {
|
||||
this._updateRuntimes = this._updateRuntimes.bind(this);
|
||||
Simulator.on("register", this._updateRuntimes);
|
||||
Simulator.on("unregister", this._updateRuntimes);
|
||||
Simulators.on("updated", this._updateRuntimes);
|
||||
this._updateRuntimes();
|
||||
},
|
||||
|
||||
disable() {
|
||||
Simulator.off("register", this._updateRuntimes);
|
||||
Simulator.off("unregister", this._updateRuntimes);
|
||||
Simulators.off("updated", this._updateRuntimes);
|
||||
},
|
||||
|
||||
_emitUpdated() {
|
||||
@ -208,11 +206,13 @@ let SimulatorScanner = {
|
||||
},
|
||||
|
||||
_updateRuntimes() {
|
||||
this._runtimes = [];
|
||||
for (let name of Simulator.availableNames()) {
|
||||
this._runtimes.push(new SimulatorRuntime(name));
|
||||
}
|
||||
this._emitUpdated();
|
||||
Simulators.getAll().then(simulators => {
|
||||
this._runtimes = [];
|
||||
for (let simulator of simulators) {
|
||||
this._runtimes.push(new SimulatorRuntime(simulator));
|
||||
}
|
||||
this._emitUpdated();
|
||||
});
|
||||
},
|
||||
|
||||
scan() {
|
||||
@ -542,28 +542,26 @@ WiFiRuntime.prototype = {
|
||||
// For testing use only
|
||||
exports._WiFiRuntime = WiFiRuntime;
|
||||
|
||||
function SimulatorRuntime(name) {
|
||||
this.name = name;
|
||||
function SimulatorRuntime(simulator) {
|
||||
this.simulator = simulator;
|
||||
}
|
||||
|
||||
SimulatorRuntime.prototype = {
|
||||
type: RuntimeTypes.SIMULATOR,
|
||||
connect: function(connection) {
|
||||
let port = ConnectionManager.getFreeTCPPort();
|
||||
let simulator = Simulator.getByName(this.name);
|
||||
if (!simulator || !simulator.launch) {
|
||||
return promise.reject(new Error("Can't find simulator: " + this.name));
|
||||
}
|
||||
return simulator.launch({port: port}).then(() => {
|
||||
return this.simulator.launch().then(port => {
|
||||
connection.host = "localhost";
|
||||
connection.port = port;
|
||||
connection.keepConnecting = true;
|
||||
connection.once(Connection.Events.DISCONNECTED, simulator.close);
|
||||
connection.once(Connection.Events.DISCONNECTED, e => this.simulator.kill());
|
||||
connection.connect();
|
||||
});
|
||||
},
|
||||
get id() {
|
||||
return this.name;
|
||||
return this.simulator.id;
|
||||
},
|
||||
get name() {
|
||||
return this.simulator.name;
|
||||
},
|
||||
};
|
||||
|
||||
|
272
browser/devtools/webide/modules/simulator-process.js
Normal file
@ -0,0 +1,272 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const { Cc, Ci, Cu } = require("chrome");
|
||||
|
||||
const Environment = require("sdk/system/environment").env;
|
||||
const Subprocess = require("sdk/system/child_process/subprocess");
|
||||
const { EventEmitter } = Cu.import("resource://gre/modules/devtools/event-emitter.js", {});
|
||||
const { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
|
||||
const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
|
||||
|
||||
let platform = Services.appShell.hiddenDOMWindow.navigator.platform;
|
||||
let OS = "";
|
||||
if (platform.indexOf("Win") != -1) {
|
||||
OS = "win32";
|
||||
} else if (platform.indexOf("Mac") != -1) {
|
||||
OS = "mac64";
|
||||
} else if (platform.indexOf("Linux") != -1) {
|
||||
if (platform.indexOf("x86_64") != -1) {
|
||||
OS = "linux64";
|
||||
} else {
|
||||
OS = "linux32";
|
||||
}
|
||||
}
|
||||
|
||||
function SimulatorProcess() {}
|
||||
SimulatorProcess.prototype = {
|
||||
|
||||
// Check if B2G is running.
|
||||
get isRunning() !!this.process,
|
||||
|
||||
// Start the process and connect the debugger client.
|
||||
run() {
|
||||
|
||||
// Resolve B2G binary.
|
||||
let b2g = this.b2gBinary;
|
||||
if (!b2g || !b2g.exists()) {
|
||||
throw Error("B2G executable not found.");
|
||||
}
|
||||
|
||||
this.once("stdout", function () {
|
||||
if (OS == "mac64") {
|
||||
console.debug("WORKAROUND run osascript to show b2g-desktop window on OS=='mac64'");
|
||||
// Escape double quotes and escape characters for use in AppleScript.
|
||||
let path = b2g.path.replace(/\\/g, "\\\\").replace(/\"/g, '\\"');
|
||||
|
||||
Subprocess.call({
|
||||
command: "/usr/bin/osascript",
|
||||
arguments: ["-e", 'tell application "' + path + '" to activate'],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this.on("stdout", (e, data) => this.log(e, data.trim()));
|
||||
this.on("stderr", (e, data) => this.log(e, data.trim()));
|
||||
|
||||
let environment;
|
||||
if (OS.indexOf("linux") > -1) {
|
||||
environment = ["TMPDIR=" + Services.dirsvc.get("TmpD", Ci.nsIFile).path];
|
||||
if ("DISPLAY" in Environment) {
|
||||
environment.push("DISPLAY=" + Environment.DISPLAY);
|
||||
}
|
||||
}
|
||||
|
||||
// Spawn a B2G instance.
|
||||
this.process = Subprocess.call({
|
||||
command: b2g,
|
||||
arguments: this.args,
|
||||
environment: environment,
|
||||
stdout: data => this.emit("stdout", data),
|
||||
stderr: data => this.emit("stderr", data),
|
||||
// On B2G instance exit, reset tracked process, remote debugger port and
|
||||
// shuttingDown flag, then finally emit an exit event.
|
||||
done: result => {
|
||||
console.log("B2G terminated with " + result.exitCode);
|
||||
this.process = null;
|
||||
this.emit("exit", result.exitCode);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// Request a B2G instance kill.
|
||||
kill() {
|
||||
let deferred = promise.defer();
|
||||
if (this.process) {
|
||||
this.once("exit", (e, exitCode) => {
|
||||
this.shuttingDown = false;
|
||||
deferred.resolve(exitCode);
|
||||
});
|
||||
if (!this.shuttingDown) {
|
||||
this.shuttingDown = true;
|
||||
this.emit("kill", null);
|
||||
this.process.kill();
|
||||
}
|
||||
return deferred.promise;
|
||||
} else {
|
||||
return promise.resolve(undefined);
|
||||
}
|
||||
},
|
||||
|
||||
// Maybe log output messages.
|
||||
log(level, message) {
|
||||
if (!Services.prefs.getBoolPref("devtools.webide.logSimulatorOutput")) {
|
||||
return;
|
||||
}
|
||||
if (level === "stderr" || level === "error") {
|
||||
console.error(message);
|
||||
return;
|
||||
}
|
||||
console.log(message);
|
||||
},
|
||||
|
||||
// Compute B2G CLI arguments.
|
||||
get args() {
|
||||
let args = [];
|
||||
|
||||
let gaia = this.gaiaProfile;
|
||||
if (!gaia || !gaia.exists()) {
|
||||
throw Error("Gaia profile directory not found.");
|
||||
}
|
||||
args.push("-profile", gaia.path);
|
||||
|
||||
args.push("-start-debugger-server", "" + this.options.port);
|
||||
|
||||
// Ignore eventual zombie instances of b2g that are left over.
|
||||
args.push("-no-remote");
|
||||
|
||||
return args;
|
||||
},
|
||||
};
|
||||
|
||||
EventEmitter.decorate(SimulatorProcess.prototype);
|
||||
|
||||
|
||||
function CustomSimulatorProcess(options) {
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
let CSPp = CustomSimulatorProcess.prototype = Object.create(SimulatorProcess.prototype);
|
||||
|
||||
// Compute B2G binary file handle.
|
||||
Object.defineProperty(CSPp, "b2gBinary", {
|
||||
get: function() {
|
||||
let file = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsILocalFile);
|
||||
file.initWithPath(this.options.b2gBinary);
|
||||
return file;
|
||||
}
|
||||
});
|
||||
|
||||
// Compute Gaia profile file handle.
|
||||
Object.defineProperty(CSPp, "gaiaProfile", {
|
||||
get: function() {
|
||||
let file = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsILocalFile);
|
||||
file.initWithPath(this.options.gaiaProfile);
|
||||
return file;
|
||||
}
|
||||
});
|
||||
|
||||
exports.CustomSimulatorProcess = CustomSimulatorProcess;
|
||||
|
||||
|
||||
function AddonSimulatorProcess(addon, options) {
|
||||
this.addon = addon;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
let ASPp = AddonSimulatorProcess.prototype = Object.create(SimulatorProcess.prototype);
|
||||
|
||||
// Compute B2G binary file handle.
|
||||
Object.defineProperty(ASPp, "b2gBinary", {
|
||||
get: function() {
|
||||
let file;
|
||||
try {
|
||||
let pref = "extensions." + this.addon.id + ".customRuntime";
|
||||
file = Services.prefs.getComplexValue(pref, Ci.nsIFile);
|
||||
} catch(e) {}
|
||||
|
||||
if (!file) {
|
||||
file = this.addon.getResourceURI().QueryInterface(Ci.nsIFileURL).file;
|
||||
file.append("b2g");
|
||||
let binaries = {
|
||||
win32: "b2g-bin.exe",
|
||||
mac64: "B2G.app/Contents/MacOS/b2g-bin",
|
||||
linux32: "b2g-bin",
|
||||
linux64: "b2g-bin",
|
||||
};
|
||||
binaries[OS].split("/").forEach(node => file.append(node));
|
||||
}
|
||||
return file;
|
||||
}
|
||||
});
|
||||
|
||||
// Compute Gaia profile file handle.
|
||||
Object.defineProperty(ASPp, "gaiaProfile", {
|
||||
get: function() {
|
||||
let file;
|
||||
try {
|
||||
let pref = "extensions." + this.addon.id + ".gaiaProfile";
|
||||
file = Services.prefs.getComplexValue(pref, Ci.nsIFile);
|
||||
} catch(e) {}
|
||||
|
||||
if (!file) {
|
||||
file = this.addon.getResourceURI().QueryInterface(Ci.nsIFileURL).file;
|
||||
file.append("profile");
|
||||
}
|
||||
return file;
|
||||
}
|
||||
});
|
||||
|
||||
exports.AddonSimulatorProcess = AddonSimulatorProcess;
|
||||
|
||||
|
||||
function OldAddonSimulatorProcess(addon, options) {
|
||||
this.addon = addon;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
let OASPp = OldAddonSimulatorProcess.prototype = Object.create(AddonSimulatorProcess.prototype);
|
||||
|
||||
// Compute B2G binary file handle.
|
||||
Object.defineProperty(OASPp, "b2gBinary", {
|
||||
get: function() {
|
||||
let file;
|
||||
try {
|
||||
let pref = "extensions." + this.addon.id + ".customRuntime";
|
||||
file = Services.prefs.getComplexValue(pref, Ci.nsIFile);
|
||||
} catch(e) {}
|
||||
|
||||
if (!file) {
|
||||
file = this.addon.getResourceURI().QueryInterface(Ci.nsIFileURL).file;
|
||||
let version = this.addon.name.match(/\d+\.\d+/)[0].replace(/\./, "_");
|
||||
file.append("resources");
|
||||
file.append("fxos_" + version + "_simulator");
|
||||
file.append("data");
|
||||
file.append(OS == "linux32" ? "linux" : OS);
|
||||
let binaries = {
|
||||
win32: "b2g/b2g-bin.exe",
|
||||
mac64: "B2G.app/Contents/MacOS/b2g-bin",
|
||||
linux32: "b2g/b2g-bin",
|
||||
linux64: "b2g/b2g-bin",
|
||||
};
|
||||
binaries[OS].split("/").forEach(node => file.append(node));
|
||||
}
|
||||
return file;
|
||||
}
|
||||
});
|
||||
|
||||
// Compute B2G CLI arguments.
|
||||
Object.defineProperty(OASPp, "args", {
|
||||
get: function() {
|
||||
let args = [];
|
||||
|
||||
let gaia = this.gaiaProfile;
|
||||
if (!gaia || !gaia.exists()) {
|
||||
throw Error("Gaia profile directory not found.");
|
||||
}
|
||||
args.push("-profile", gaia.path);
|
||||
|
||||
args.push("-dbgport", "" + this.options.port);
|
||||
|
||||
// Ignore eventual zombie instances of b2g that are left over.
|
||||
args.push("-no-remote");
|
||||
|
||||
return args;
|
||||
}
|
||||
});
|
||||
|
||||
exports.OldAddonSimulatorProcess = OldAddonSimulatorProcess;
|
96
browser/devtools/webide/modules/simulators.js
Normal file
@ -0,0 +1,96 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const { Cu } = require("chrome");
|
||||
const { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm");
|
||||
const { EventEmitter } = Cu.import("resource://gre/modules/devtools/event-emitter.js");
|
||||
const { ConnectionManager } = require("devtools/client/connection-manager");
|
||||
const { AddonSimulatorProcess, OldAddonSimulatorProcess } = require("devtools/webide/simulator-process");
|
||||
const promise = require("promise");
|
||||
|
||||
const SimulatorRegExp = new RegExp(Services.prefs.getCharPref("devtools.webide.simulatorAddonRegExp"));
|
||||
|
||||
let Simulators = {
|
||||
// TODO (Bug 1090949) Don't generate this list from installed simulator
|
||||
// addons, but instead implement a persistent list of user-configured
|
||||
// simulators.
|
||||
getAll() {
|
||||
let deferred = promise.defer();
|
||||
AddonManager.getAllAddons(addons => {
|
||||
let simulators = [];
|
||||
for (let addon of addons) {
|
||||
if (SimulatorRegExp.exec(addon.id)) {
|
||||
simulators.push(new Simulator(addon));
|
||||
}
|
||||
}
|
||||
// Sort simulators alphabetically by name.
|
||||
simulators.sort((a, b) => {
|
||||
return a.name.toLowerCase().localeCompare(b.name.toLowerCase())
|
||||
});
|
||||
deferred.resolve(simulators);
|
||||
});
|
||||
return deferred.promise;
|
||||
},
|
||||
}
|
||||
EventEmitter.decorate(Simulators);
|
||||
exports.Simulators = Simulators;
|
||||
|
||||
function update() {
|
||||
Simulators.emit("updated");
|
||||
}
|
||||
AddonManager.addAddonListener({
|
||||
onEnabled: update,
|
||||
onDisabled: update,
|
||||
onInstalled: update,
|
||||
onUninstalled: update
|
||||
});
|
||||
|
||||
|
||||
function Simulator(addon) {
|
||||
this.addon = addon;
|
||||
}
|
||||
|
||||
Simulator.prototype = {
|
||||
launch() {
|
||||
// Close already opened simulation.
|
||||
if (this.process) {
|
||||
return this.kill().then(this.launch.bind(this));
|
||||
}
|
||||
|
||||
let options = {
|
||||
port: ConnectionManager.getFreeTCPPort()
|
||||
};
|
||||
|
||||
if (this.version <= "1.3") {
|
||||
// Support older simulator addons.
|
||||
this.process = new OldAddonSimulatorProcess(this.addon, options);
|
||||
} else {
|
||||
this.process = new AddonSimulatorProcess(this.addon, options);
|
||||
}
|
||||
this.process.run();
|
||||
|
||||
return promise.resolve(options.port);
|
||||
},
|
||||
|
||||
kill() {
|
||||
let process = this.process;
|
||||
if (!process) {
|
||||
return promise.resolve();
|
||||
}
|
||||
this.process = null;
|
||||
return process.kill();
|
||||
},
|
||||
|
||||
get id() {
|
||||
return this.addon.id;
|
||||
},
|
||||
|
||||
get name() {
|
||||
return this.addon.name.replace(" Simulator", "");
|
||||
},
|
||||
|
||||
get version() {
|
||||
return this.name.match(/\d+\.\d+/)[0];
|
||||
},
|
||||
};
|
@ -20,6 +20,8 @@ EXTRA_JS_MODULES.devtools.webide += [
|
||||
'modules/config-view.js',
|
||||
'modules/remote-resources.js',
|
||||
'modules/runtimes.js',
|
||||
'modules/simulator-process.js',
|
||||
'modules/simulators.js',
|
||||
'modules/tab-store.js',
|
||||
'modules/utils.js'
|
||||
]
|
||||
|
@ -13,6 +13,7 @@ pref("devtools.webide.enableLocalRuntime", false);
|
||||
pref("devtools.webide.addonsURL", "https://ftp.mozilla.org/pub/mozilla.org/labs/fxos-simulator/index.json");
|
||||
pref("devtools.webide.simulatorAddonsURL", "https://ftp.mozilla.org/pub/mozilla.org/labs/fxos-simulator/#VERSION#/#OS#/fxos_#SLASHED_VERSION#_simulator-#OS#-latest.xpi");
|
||||
pref("devtools.webide.simulatorAddonID", "fxos_#SLASHED_VERSION#_simulator@mozilla.org");
|
||||
pref("devtools.webide.simulatorAddonRegExp", "fxos_(.*)_simulator@mozilla\.org$");
|
||||
pref("devtools.webide.adbAddonURL", "https://ftp.mozilla.org/pub/mozilla.org/labs/fxos-simulator/adb-helper/#OS#/adbhelper-#OS#-latest.xpi");
|
||||
pref("devtools.webide.adbAddonID", "adbhelper@mozilla.org");
|
||||
pref("devtools.webide.adaptersAddonURL", "https://ftp.mozilla.org/pub/mozilla.org/labs/fxdt-adapters/#OS#/fxdt-adapters-#OS#-latest.xpi");
|
||||
@ -20,6 +21,7 @@ pref("devtools.webide.adaptersAddonID", "fxdevtools-adapters@mozilla.org");
|
||||
pref("devtools.webide.monitorWebSocketURL", "ws://localhost:9000");
|
||||
pref("devtools.webide.lastConnectedRuntime", "");
|
||||
pref("devtools.webide.lastSelectedProject", "");
|
||||
pref("devtools.webide.logSimulatorOutput", false);
|
||||
pref("devtools.webide.widget.autoinstall", true);
|
||||
#ifdef MOZ_DEV_EDITION
|
||||
pref("devtools.webide.widget.enabled", true);
|
||||
|
@ -837,3 +837,6 @@ just addresses the organization to follow, e.g. "This site is run by " -->
|
||||
<!ENTITY processHang.terminatePlugin.accessKey "P">
|
||||
<!ENTITY processHang.terminateProcess.label "Kill Web Process">
|
||||
<!ENTITY processHang.terminateProcess.accessKey "K">
|
||||
|
||||
<!ENTITY emeLearnMoreContextMenu.label "Learn more about DRM…">
|
||||
<!ENTITY emeLearnMoreContextMenu.accesskey "D">
|
||||
|
@ -40,6 +40,17 @@ addonDownloadRestart=Restart Download;Restart Downloads
|
||||
addonDownloadRestart.accessKey=R
|
||||
addonDownloadCancelTooltip=Cancel
|
||||
|
||||
addonwatch.slow=%S might be making %S run slowly
|
||||
addonwatch.disable.label=Disable %S
|
||||
addonwatch.disable.accesskey=D
|
||||
addonwatch.ignoreSession.label=Ignore for now
|
||||
addonwatch.ignoreSession.accesskey=I
|
||||
addonwatch.ignorePerm.label=Ignore permanently
|
||||
addonwatch.ignorePerm.accesskey=p
|
||||
addonwatch.restart.message=To disable %S you must restart %S
|
||||
addonwatch.restart.label=Restart %s
|
||||
addonwatch.restart.accesskey=R
|
||||
|
||||
# LOCALIZATION NOTE (addonsInstalled, addonsInstalledNeedsRestart):
|
||||
# Semicolon-separated list of plural forms. See:
|
||||
# http://developer.mozilla.org/en/docs/Localization_and_Plurals
|
||||
|
@ -669,6 +669,7 @@ this.BrowserUITelemetry = {
|
||||
"spell-add-dictionaries-main", "spell-dictionaries",
|
||||
"spell-dictionaries-menu", "spell-add-dictionaries",
|
||||
"bidi-text-direction-toggle", "bidi-page-direction-toggle", "inspect",
|
||||
"media-eme-learn-more"
|
||||
]),
|
||||
|
||||
_contextMenuInteractions: {},
|
||||
|
@ -11,7 +11,7 @@
|
||||
treechildren::-moz-tree-image(icon),
|
||||
treechildren::-moz-tree-image(noicon) {
|
||||
padding-right: 2px;
|
||||
margin: 0px 2px;
|
||||
margin: 0 2px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
@ -20,3 +20,32 @@
|
||||
#tabList[available] {
|
||||
display: -moz-box;
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-image(icon),
|
||||
treechildren::-moz-tree-image(noicon) {
|
||||
padding-right: 2px;
|
||||
margin: 0 2px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-image(noicon) {
|
||||
list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
|
||||
}
|
||||
treechildren::-moz-tree-image(container, noicon) {
|
||||
list-style-image: url("chrome://browser/skin/aboutSessionRestore-window-icon.png");
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-image(checked) {
|
||||
list-style-image: url("chrome://global/skin/in-content/check.svg#check");
|
||||
}
|
||||
treechildren::-moz-tree-image(checked, selected) {
|
||||
list-style-image: url("chrome://global/skin/in-content/check.svg#check-inverted");
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-image(partial) {
|
||||
list-style-image: url("chrome://global/skin/in-content/check-partial.svg#check-partial");
|
||||
}
|
||||
treechildren::-moz-tree-image(partial, selected) {
|
||||
list-style-image: url("chrome://global/skin/in-content/check-partial.svg#check-partial-inverted");
|
||||
}
|
@ -79,3 +79,7 @@
|
||||
height: 16px;
|
||||
margin: 7px;
|
||||
}
|
||||
|
||||
#context-media-eme-learnmore {
|
||||
list-style-image: url("chrome://browser/skin/drm-icon.svg#chains");
|
||||
}
|
||||
|
@ -350,6 +350,11 @@ description > html|a {
|
||||
width: 66ch;
|
||||
}
|
||||
|
||||
.largeDialogContainer.doScroll {
|
||||
overflow-y: auto;
|
||||
-moz-box-flex: 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* End Dialog
|
||||
*/
|
||||
|
@ -1,16 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="22 22 16 16" enable-background="new 22 22 16 16" xml:space="preserve">
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="30" y1="23" x2="30" y2="37">
|
||||
<stop offset="0" style="stop-color:#E63B2E"/>
|
||||
<stop offset="1" style="stop-color:#C33931"/>
|
||||
</linearGradient>
|
||||
<circle fill="url(#SVGID_1_)" cx="30" cy="30" r="7"/>
|
||||
<g>
|
||||
<path fill="#FFFFFF" d="M31.03,33.304c0,0.6-0.479,1.092-1.091,1.092c-0.6,0-1.079-0.492-1.079-1.092
|
||||
c0-0.588,0.479-1.079,1.079-1.079C30.551,32.225,31.03,32.716,31.03,33.304z M29.171,31.133l-0.24-5.253h2.015l-0.24,5.253H29.171z
|
||||
"/>
|
||||
</g>
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="22 22 16 16">
|
||||
<linearGradient id="gradient" gradientUnits="userSpaceOnUse" x1="30" y1="23" x2="30" y2="37">
|
||||
<stop offset="0" style="stop-color: #e63b2e"/>
|
||||
<stop offset="1" style="stop-color: #c33931"/>
|
||||
</linearGradient>
|
||||
<circle fill="url(#gradient)" cx="30" cy="30" r="7"/>
|
||||
<path fill="#fff" d="M31.03,33.304c0,0.6-0.479,1.092-1.091,1.092c-0.6,0-1.079-0.492-1.079-1.092 c0-0.588,0.479-1.079,1.079-1.079C30.551,32.225,31.03,32.716,31.03,33.304z M29.171,31.133l-0.24-5.253h2.015l-0.24,5.253H29.171z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 970 B After Width: | Height: | Size: 675 B |
@ -708,6 +708,7 @@ GK_ATOM(onconfigurationchange, "onconfigurationchange")
|
||||
GK_ATOM(onconnect, "onconnect")
|
||||
GK_ATOM(onconnected, "onconnected")
|
||||
GK_ATOM(onconnecting, "onconnecting")
|
||||
GK_ATOM(onconnectionstatechanged, "onconnectionstatechanged")
|
||||
GK_ATOM(oncontextmenu, "oncontextmenu")
|
||||
GK_ATOM(oncopy, "oncopy")
|
||||
GK_ATOM(oncurrentchannelchanged, "oncurrentchannelchanged")
|
||||
|
@ -152,6 +152,10 @@ DOMInterfaces = {
|
||||
'nativeType': 'mozilla::dom::bluetooth::BluetoothDiscoveryHandle',
|
||||
},
|
||||
|
||||
'BluetoothGatt': {
|
||||
'nativeType': 'mozilla::dom::bluetooth::BluetoothGatt',
|
||||
},
|
||||
|
||||
'BluetoothManager': {
|
||||
'nativeType': 'mozilla::dom::bluetooth::BluetoothManager',
|
||||
},
|
||||
|
@ -196,6 +196,12 @@ extern bool gBluetoothDebugFlag;
|
||||
*/
|
||||
#define REQUEST_MEDIA_PLAYSTATUS_ID "requestmediaplaystatus"
|
||||
|
||||
/**
|
||||
* When a remote BLE device gets connected / disconnected, we'll dispatch an
|
||||
* event
|
||||
*/
|
||||
#define GATT_CONNECTION_STATE_CHANGED_ID "connectionstatechanged"
|
||||
|
||||
// Bluetooth address format: xx:xx:xx:xx:xx:xx (or xx_xx_xx_xx_xx_xx)
|
||||
#define BLUETOOTH_ADDRESS_LENGTH 17
|
||||
#define BLUETOOTH_ADDRESS_NONE "00:00:00:00:00:00"
|
||||
|
@ -4,15 +4,16 @@
|
||||
* 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 "BluetoothClassOfDevice.h"
|
||||
#include "BluetoothDevice.h"
|
||||
#include "BluetoothReplyRunnable.h"
|
||||
#include "BluetoothService.h"
|
||||
#include "BluetoothUtils.h"
|
||||
|
||||
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
|
||||
#include "mozilla/dom/BluetoothAttributeEvent.h"
|
||||
#include "mozilla/dom/BluetoothDevice2Binding.h"
|
||||
#include "mozilla/dom/bluetooth/BluetoothClassOfDevice.h"
|
||||
#include "mozilla/dom/bluetooth/BluetoothDevice.h"
|
||||
#include "mozilla/dom/bluetooth/BluetoothGatt.h"
|
||||
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
|
||||
using namespace mozilla;
|
||||
@ -20,7 +21,10 @@ using namespace mozilla::dom;
|
||||
|
||||
USING_BLUETOOTH_NAMESPACE
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(BluetoothDevice, DOMEventTargetHelper, mCod)
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(BluetoothDevice,
|
||||
DOMEventTargetHelper,
|
||||
mCod,
|
||||
mGatt)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(BluetoothDevice)
|
||||
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
||||
|
||||
@ -302,6 +306,19 @@ BluetoothDevice::DispatchAttributeEvent(const nsTArray<nsString>& aTypes)
|
||||
DispatchTrustedEvent(event);
|
||||
}
|
||||
|
||||
BluetoothGatt*
|
||||
BluetoothDevice::GetGatt()
|
||||
{
|
||||
NS_ENSURE_TRUE(mType == BluetoothDeviceType::Le ||
|
||||
mType == BluetoothDeviceType::Dual,
|
||||
nullptr);
|
||||
if (!mGatt) {
|
||||
mGatt = new BluetoothGatt(GetOwner(), mAddress);
|
||||
}
|
||||
|
||||
return mGatt;
|
||||
}
|
||||
|
||||
JSObject*
|
||||
BluetoothDevice::WrapObject(JSContext* aContext)
|
||||
{
|
||||
|
@ -10,7 +10,7 @@
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "mozilla/dom/BluetoothDevice2Binding.h"
|
||||
#include "BluetoothCommon.h"
|
||||
#include "mozilla/dom/bluetooth/BluetoothCommon.h"
|
||||
#include "nsString.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
@ -23,6 +23,7 @@ namespace dom {
|
||||
BEGIN_BLUETOOTH_NAMESPACE
|
||||
|
||||
class BluetoothClassOfDevice;
|
||||
class BluetoothGatt;
|
||||
class BluetoothNamedValue;
|
||||
class BluetoothValue;
|
||||
class BluetoothSignal;
|
||||
@ -69,6 +70,8 @@ public:
|
||||
return mType;
|
||||
}
|
||||
|
||||
BluetoothGatt* GetGatt();
|
||||
|
||||
/****************************************************************************
|
||||
* Event Handlers
|
||||
***************************************************************************/
|
||||
@ -173,6 +176,11 @@ private:
|
||||
* Type of this device. Can be unknown/classic/le/dual.
|
||||
*/
|
||||
BluetoothDeviceType mType;
|
||||
|
||||
/**
|
||||
* GATT client object to interact with the remote device.
|
||||
*/
|
||||
nsRefPtr<BluetoothGatt> mGatt;
|
||||
};
|
||||
|
||||
END_BLUETOOTH_NAMESPACE
|
||||
|
217
dom/bluetooth2/BluetoothGatt.cpp
Normal file
@ -0,0 +1,217 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
|
||||
#include "BluetoothReplyRunnable.h"
|
||||
#include "BluetoothService.h"
|
||||
#include "BluetoothUtils.h"
|
||||
#include "mozilla/dom/bluetooth/BluetoothGatt.h"
|
||||
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
|
||||
#include "mozilla/dom/BluetoothGattBinding.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "nsIUUIDGenerator.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
USING_BLUETOOTH_NAMESPACE
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(BluetoothGatt)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(BluetoothGatt,
|
||||
DOMEventTargetHelper)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(BluetoothGatt,
|
||||
DOMEventTargetHelper)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(BluetoothGatt)
|
||||
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(BluetoothGatt, DOMEventTargetHelper)
|
||||
NS_IMPL_RELEASE_INHERITED(BluetoothGatt, DOMEventTargetHelper)
|
||||
|
||||
BluetoothGatt::BluetoothGatt(nsPIDOMWindow* aWindow,
|
||||
const nsAString& aDeviceAddr)
|
||||
: DOMEventTargetHelper(aWindow)
|
||||
, mAppUuid(EmptyString())
|
||||
, mClientIf(0)
|
||||
, mConnectionState(BluetoothConnectionState::Disconnected)
|
||||
, mDeviceAddr(aDeviceAddr)
|
||||
{
|
||||
MOZ_ASSERT(aWindow);
|
||||
MOZ_ASSERT(!mDeviceAddr.IsEmpty());
|
||||
}
|
||||
|
||||
BluetoothGatt::~BluetoothGatt()
|
||||
{
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
// bs can be null on shutdown, where destruction might happen.
|
||||
NS_ENSURE_TRUE_VOID(bs);
|
||||
|
||||
if (mClientIf > 0) {
|
||||
nsRefPtr<BluetoothVoidReplyRunnable> result =
|
||||
new BluetoothVoidReplyRunnable(nullptr);
|
||||
bs->UnregisterGattClientInternal(mClientIf, result);
|
||||
}
|
||||
|
||||
bs->UnregisterBluetoothSignalHandler(mAppUuid, this);
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothGatt::GenerateUuid(nsAString &aUuidString)
|
||||
{
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIUUIDGenerator> uuidGenerator =
|
||||
do_GetService("@mozilla.org/uuid-generator;1", &rv);
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
|
||||
nsID uuid;
|
||||
rv = uuidGenerator->GenerateUUIDInPlace(&uuid);
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
|
||||
// Build a string in {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} format
|
||||
char uuidBuffer[NSID_LENGTH];
|
||||
uuid.ToProvidedString(uuidBuffer);
|
||||
NS_ConvertASCIItoUTF16 uuidString(uuidBuffer);
|
||||
|
||||
// Remove {} and the null terminator
|
||||
aUuidString.Assign(Substring(uuidString, 1, NSID_LENGTH - 3));
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothGatt::DisconnectFromOwner()
|
||||
{
|
||||
DOMEventTargetHelper::DisconnectFromOwner();
|
||||
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
NS_ENSURE_TRUE_VOID(bs);
|
||||
|
||||
if (mClientIf > 0) {
|
||||
nsRefPtr<BluetoothVoidReplyRunnable> result =
|
||||
new BluetoothVoidReplyRunnable(nullptr);
|
||||
bs->UnregisterGattClientInternal(mClientIf, result);
|
||||
}
|
||||
|
||||
bs->UnregisterBluetoothSignalHandler(mAppUuid, this);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
BluetoothGatt::Connect(ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
|
||||
if (!global) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<Promise> promise = Promise::Create(global, aRv);
|
||||
NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
|
||||
|
||||
BT_ENSURE_TRUE_REJECT(
|
||||
mConnectionState == BluetoothConnectionState::Disconnected,
|
||||
NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
BT_ENSURE_TRUE_REJECT(bs, NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
if (mAppUuid.IsEmpty()) {
|
||||
GenerateUuid(mAppUuid);
|
||||
BT_ENSURE_TRUE_REJECT(!mAppUuid.IsEmpty(),
|
||||
NS_ERROR_DOM_OPERATION_ERR);
|
||||
bs->RegisterBluetoothSignalHandler(mAppUuid, this);
|
||||
}
|
||||
|
||||
UpdateConnectionState(BluetoothConnectionState::Connecting);
|
||||
nsRefPtr<BluetoothReplyRunnable> result =
|
||||
new BluetoothVoidReplyRunnable(nullptr /* DOMRequest */,
|
||||
promise,
|
||||
NS_LITERAL_STRING("ConnectGattClient"));
|
||||
bs->ConnectGattClientInternal(mAppUuid,
|
||||
mDeviceAddr,
|
||||
result);
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
BluetoothGatt::Disconnect(ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
|
||||
if (!global) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<Promise> promise = Promise::Create(global, aRv);
|
||||
NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
|
||||
|
||||
BT_ENSURE_TRUE_REJECT(
|
||||
mConnectionState == BluetoothConnectionState::Connected,
|
||||
NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
BT_ENSURE_TRUE_REJECT(bs, NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
UpdateConnectionState(BluetoothConnectionState::Disconnecting);
|
||||
nsRefPtr<BluetoothReplyRunnable> result =
|
||||
new BluetoothVoidReplyRunnable(nullptr /* DOMRequest */,
|
||||
promise,
|
||||
NS_LITERAL_STRING("DisconnectGattClient"));
|
||||
bs->DisconnectGattClientInternal(mAppUuid, mDeviceAddr, result);
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothGatt::UpdateConnectionState(BluetoothConnectionState aState)
|
||||
{
|
||||
BT_API2_LOGR("GATT connection state changes to: %d", int(aState));
|
||||
mConnectionState = aState;
|
||||
|
||||
// Dispatch connectionstatechanged event to application
|
||||
nsCOMPtr<nsIDOMEvent> event;
|
||||
nsresult rv = NS_NewDOMEvent(getter_AddRefs(event), this, nullptr, nullptr);
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
|
||||
rv = event->InitEvent(NS_LITERAL_STRING(GATT_CONNECTION_STATE_CHANGED_ID),
|
||||
false,
|
||||
false);
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
|
||||
DispatchTrustedEvent(event);
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothGatt::Notify(const BluetoothSignal& aData)
|
||||
{
|
||||
BT_LOGD("[D] %s", NS_ConvertUTF16toUTF8(aData.name()).get());
|
||||
|
||||
BluetoothValue v = aData.value();
|
||||
if (aData.name().EqualsLiteral("ClientRegistered")) {
|
||||
MOZ_ASSERT(v.type() == BluetoothValue::Tuint32_t);
|
||||
mClientIf = v.get_uint32_t();
|
||||
} else if (aData.name().EqualsLiteral("ClientUnregistered")) {
|
||||
mClientIf = 0;
|
||||
} else if (aData.name().EqualsLiteral(GATT_CONNECTION_STATE_CHANGED_ID)) {
|
||||
MOZ_ASSERT(v.type() == BluetoothValue::Tbool);
|
||||
|
||||
BluetoothConnectionState state =
|
||||
v.get_bool() ? BluetoothConnectionState::Connected
|
||||
: BluetoothConnectionState::Disconnected;
|
||||
UpdateConnectionState(state);
|
||||
} else {
|
||||
BT_WARNING("Not handling GATT signal: %s",
|
||||
NS_ConvertUTF16toUTF8(aData.name()).get());
|
||||
}
|
||||
}
|
||||
|
||||
JSObject*
|
||||
BluetoothGatt::WrapObject(JSContext* aContext)
|
||||
{
|
||||
return BluetoothGattBinding::Wrap(aContext, this);
|
||||
}
|
116
dom/bluetooth2/BluetoothGatt.h
Normal file
@ -0,0 +1,116 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_bluetooth_bluetoothgatt_h__
|
||||
#define mozilla_dom_bluetooth_bluetoothgatt_h__
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "mozilla/dom/BluetoothGattBinding.h"
|
||||
#include "mozilla/dom/bluetooth/BluetoothCommon.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
class Promise;
|
||||
}
|
||||
}
|
||||
|
||||
BEGIN_BLUETOOTH_NAMESPACE
|
||||
|
||||
class BluetoothReplyRunnable;
|
||||
class BluetoothService;
|
||||
class BluetoothSignal;
|
||||
class BluetoothValue;
|
||||
|
||||
class BluetoothGatt MOZ_FINAL : public DOMEventTargetHelper
|
||||
, public BluetoothSignalObserver
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(BluetoothGatt, DOMEventTargetHelper)
|
||||
|
||||
/****************************************************************************
|
||||
* Attribute Getters
|
||||
***************************************************************************/
|
||||
BluetoothConnectionState ConnectionState() const
|
||||
{
|
||||
return mConnectionState;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Event Handlers
|
||||
***************************************************************************/
|
||||
IMPL_EVENT_HANDLER(connectionstatechanged);
|
||||
|
||||
/****************************************************************************
|
||||
* Methods (Web API Implementation)
|
||||
***************************************************************************/
|
||||
already_AddRefed<Promise> Connect(ErrorResult& aRv);
|
||||
already_AddRefed<Promise> Disconnect(ErrorResult& aRv);
|
||||
|
||||
/****************************************************************************
|
||||
* Others
|
||||
***************************************************************************/
|
||||
void Notify(const BluetoothSignal& aParam); // BluetoothSignalObserver
|
||||
|
||||
nsPIDOMWindow* GetParentObject() const
|
||||
{
|
||||
return GetOwner();
|
||||
}
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
|
||||
virtual void DisconnectFromOwner() MOZ_OVERRIDE;
|
||||
|
||||
BluetoothGatt(nsPIDOMWindow* aOwner,
|
||||
const nsAString& aDeviceAddr);
|
||||
|
||||
private:
|
||||
~BluetoothGatt();
|
||||
|
||||
/**
|
||||
* Update mConnectionState to aState and fire
|
||||
* connectionstatechanged event to the application.
|
||||
*
|
||||
* @param aState [in] New connection state
|
||||
*/
|
||||
void UpdateConnectionState(BluetoothConnectionState aState);
|
||||
|
||||
/**
|
||||
* Generate a random uuid.
|
||||
*
|
||||
* @param aUuidString [out] String to store the generated uuid.
|
||||
*/
|
||||
void GenerateUuid(nsAString &aUuidString);
|
||||
|
||||
/****************************************************************************
|
||||
* Variables
|
||||
***************************************************************************/
|
||||
/**
|
||||
* Random generated UUID of this GATT client.
|
||||
*/
|
||||
nsString mAppUuid;
|
||||
|
||||
/**
|
||||
* Id of the GATT client interface given by bluetooth stack.
|
||||
* 0 if the client is not registered yet, nonzero otherwise.
|
||||
*/
|
||||
int mClientIf;
|
||||
|
||||
/**
|
||||
* Connection state of this remote device.
|
||||
*/
|
||||
BluetoothConnectionState mConnectionState;
|
||||
|
||||
/**
|
||||
* Address of the remote device.
|
||||
*/
|
||||
nsString mDeviceAddr;
|
||||
};
|
||||
|
||||
END_BLUETOOTH_NAMESPACE
|
||||
|
||||
#endif
|
@ -307,6 +307,31 @@ public:
|
||||
SendInputMessage(const nsAString& aDeviceAddresses,
|
||||
const nsAString& aMessage) = 0;
|
||||
|
||||
/**
|
||||
* Connect to a remote GATT server. (platform specific implementation)
|
||||
*/
|
||||
virtual void
|
||||
ConnectGattClientInternal(const nsAString& aAppUuid,
|
||||
const nsAString& aDeviceAddress,
|
||||
BluetoothReplyRunnable* aRunnable) = 0;
|
||||
|
||||
/**
|
||||
* Disconnect GATT client from a remote GATT server.
|
||||
* (platform specific implementation)
|
||||
*/
|
||||
virtual void
|
||||
DisconnectGattClientInternal(const nsAString& aAppUuid,
|
||||
const nsAString& aDeviceAddress,
|
||||
BluetoothReplyRunnable* aRunnable) = 0;
|
||||
|
||||
/**
|
||||
* Unregister a GATT client. (platform specific implementation)
|
||||
*/
|
||||
virtual void
|
||||
UnregisterGattClientInternal(int aClientIf,
|
||||
BluetoothReplyRunnable* aRunnable) = 0;
|
||||
|
||||
|
||||
bool
|
||||
IsEnabled() const
|
||||
{
|
||||
|
@ -40,6 +40,30 @@ UuidToString(const BluetoothUuid& aUuid, nsAString& aString)
|
||||
aString.AssignLiteral(uuidStr);
|
||||
}
|
||||
|
||||
void
|
||||
StringToUuid(const char* aString, BluetoothUuid& aUuid)
|
||||
{
|
||||
uint32_t uuid0, uuid4;
|
||||
uint16_t uuid1, uuid2, uuid3, uuid5;
|
||||
|
||||
sscanf(aString, "%08x-%04hx-%04hx-%04hx-%08x%04hx",
|
||||
&uuid0, &uuid1, &uuid2, &uuid3, &uuid4, &uuid5);
|
||||
|
||||
uuid0 = htonl(uuid0);
|
||||
uuid1 = htons(uuid1);
|
||||
uuid2 = htons(uuid2);
|
||||
uuid3 = htons(uuid3);
|
||||
uuid4 = htonl(uuid4);
|
||||
uuid5 = htons(uuid5);
|
||||
|
||||
memcpy(&aUuid.mUuid[0], &uuid0, 4);
|
||||
memcpy(&aUuid.mUuid[4], &uuid1, 2);
|
||||
memcpy(&aUuid.mUuid[6], &uuid2, 2);
|
||||
memcpy(&aUuid.mUuid[8], &uuid3, 2);
|
||||
memcpy(&aUuid.mUuid[10], &uuid4, 4);
|
||||
memcpy(&aUuid.mUuid[14], &uuid5, 2);
|
||||
}
|
||||
|
||||
bool
|
||||
SetJsObject(JSContext* aContext,
|
||||
const BluetoothValue& aValue,
|
||||
|
@ -19,6 +19,14 @@ class BluetoothReplyRunnable;
|
||||
void
|
||||
UuidToString(const BluetoothUuid& aUuid, nsAString& aString);
|
||||
|
||||
/**
|
||||
* Convert xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxx uuid string to BluetoothUuid object.
|
||||
* This utility function is used by gecko internal only to convert uuid string
|
||||
* created by gecko back to BluetoothUuid representation.
|
||||
*/
|
||||
void
|
||||
StringToUuid(const char* aString, BluetoothUuid& aUuid);
|
||||
|
||||
bool
|
||||
SetJsObject(JSContext* aContext,
|
||||
const BluetoothValue& aValue,
|
||||
|
@ -5,16 +5,28 @@
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "BluetoothGattManager.h"
|
||||
#include "BluetoothCommon.h"
|
||||
#include "BluetoothUtils.h"
|
||||
#include "BluetoothInterface.h"
|
||||
|
||||
#include "BluetoothCommon.h"
|
||||
#include "BluetoothInterface.h"
|
||||
#include "BluetoothReplyRunnable.h"
|
||||
#include "BluetoothService.h"
|
||||
#include "BluetoothUtils.h"
|
||||
#include "MainThreadUtils.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "MainThreadUtils.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
#define ENSURE_GATT_CLIENT_INTF_IS_READY_VOID(runnable) \
|
||||
do { \
|
||||
if (!sBluetoothGattInterface) { \
|
||||
NS_NAMED_LITERAL_STRING(errorStr, \
|
||||
"BluetoothGattClientInterface is not ready"); \
|
||||
DispatchBluetoothReply(runnable, BluetoothValue(), errorStr); \
|
||||
return; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
using namespace mozilla;
|
||||
USING_BLUETOOTH_NAMESPACE
|
||||
|
||||
@ -26,9 +38,58 @@ namespace {
|
||||
|
||||
bool BluetoothGattManager::mInShutdown = false;
|
||||
|
||||
/*
|
||||
* Static functions
|
||||
*/
|
||||
class BluetoothGattClient;
|
||||
static StaticAutoPtr<nsTArray<nsRefPtr<BluetoothGattClient> > > sClients;
|
||||
|
||||
class BluetoothGattClient MOZ_FINAL : public nsISupports
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
BluetoothGattClient(const nsAString& aAppUuid, const nsAString& aDeviceAddr)
|
||||
: mAppUuid(aAppUuid)
|
||||
, mDeviceAddr(aDeviceAddr)
|
||||
, mClientIf(0)
|
||||
, mConnId(0)
|
||||
{ }
|
||||
|
||||
~BluetoothGattClient()
|
||||
{
|
||||
mConnectRunnable = nullptr;
|
||||
mDisconnectRunnable = nullptr;
|
||||
mUnregisterClientRunnable = nullptr;
|
||||
}
|
||||
|
||||
nsString mAppUuid;
|
||||
nsString mDeviceAddr;
|
||||
int mClientIf;
|
||||
int mConnId;
|
||||
nsRefPtr<BluetoothReplyRunnable> mConnectRunnable;
|
||||
nsRefPtr<BluetoothReplyRunnable> mDisconnectRunnable;
|
||||
nsRefPtr<BluetoothReplyRunnable> mUnregisterClientRunnable;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS0(BluetoothGattClient)
|
||||
|
||||
class UuidComparator
|
||||
{
|
||||
public:
|
||||
bool Equals(const nsRefPtr<BluetoothGattClient>& aClient,
|
||||
const nsAString& aAppUuid) const
|
||||
{
|
||||
return aClient->mAppUuid.Equals(aAppUuid);
|
||||
}
|
||||
};
|
||||
|
||||
class ClientIfComparator
|
||||
{
|
||||
public:
|
||||
bool Equals(const nsRefPtr<BluetoothGattClient>& aClient,
|
||||
int aClientIf) const
|
||||
{
|
||||
return aClient->mClientIf == aClientIf;
|
||||
}
|
||||
};
|
||||
|
||||
BluetoothGattManager*
|
||||
BluetoothGattManager::Get()
|
||||
@ -103,6 +164,10 @@ BluetoothGattManager::InitGattInterface(BluetoothProfileResultHandler* aRes)
|
||||
sBluetoothGattInterface->GetBluetoothGattClientInterface();
|
||||
NS_ENSURE_TRUE_VOID(sBluetoothGattClientInterface);
|
||||
|
||||
if (!sClients) {
|
||||
sClients = new nsTArray<nsRefPtr<BluetoothGattClient> >;
|
||||
}
|
||||
|
||||
BluetoothGattManager* gattManager = BluetoothGattManager::Get();
|
||||
sBluetoothGattInterface->Init(gattManager,
|
||||
new InitGattResultHandler(aRes));
|
||||
@ -129,6 +194,8 @@ public:
|
||||
{
|
||||
sBluetoothGattClientInterface = nullptr;
|
||||
sBluetoothGattInterface = nullptr;
|
||||
sClients = nullptr;
|
||||
|
||||
if (mRes) {
|
||||
mRes->Deinit();
|
||||
}
|
||||
@ -176,6 +243,265 @@ BluetoothGattManager::DeinitGattInterface(BluetoothProfileResultHandler* aRes)
|
||||
}
|
||||
}
|
||||
|
||||
class BluetoothGattManager::RegisterClientResultHandler MOZ_FINAL
|
||||
: public BluetoothGattClientResultHandler
|
||||
{
|
||||
public:
|
||||
RegisterClientResultHandler(BluetoothGattClient* aClient)
|
||||
: mClient(aClient)
|
||||
{
|
||||
MOZ_ASSERT(mClient);
|
||||
}
|
||||
|
||||
void OnError(BluetoothStatus aStatus) MOZ_OVERRIDE
|
||||
{
|
||||
BT_WARNING("BluetoothGattClientInterface::RegisterClient failed: %d",
|
||||
(int)aStatus);
|
||||
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
NS_ENSURE_TRUE_VOID(bs);
|
||||
|
||||
// Notify BluetoothGatt for client disconnected
|
||||
BluetoothSignal signal(
|
||||
NS_LITERAL_STRING(GATT_CONNECTION_STATE_CHANGED_ID),
|
||||
mClient->mAppUuid,
|
||||
BluetoothValue(false)); // Disconnected
|
||||
bs->DistributeSignal(signal);
|
||||
|
||||
// Reject the connect request
|
||||
if (mClient->mConnectRunnable) {
|
||||
NS_NAMED_LITERAL_STRING(errorStr, "Register GATT client failed");
|
||||
DispatchBluetoothReply(mClient->mConnectRunnable,
|
||||
BluetoothValue(),
|
||||
errorStr);
|
||||
mClient->mConnectRunnable = nullptr;
|
||||
}
|
||||
|
||||
sClients->RemoveElement(mClient);
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<BluetoothGattClient> mClient;
|
||||
};
|
||||
|
||||
class BluetoothGattManager::UnregisterClientResultHandler MOZ_FINAL
|
||||
: public BluetoothGattClientResultHandler
|
||||
{
|
||||
public:
|
||||
UnregisterClientResultHandler(BluetoothGattClient* aClient)
|
||||
: mClient(aClient)
|
||||
{
|
||||
MOZ_ASSERT(mClient);
|
||||
}
|
||||
|
||||
void UnregisterClient() MOZ_OVERRIDE
|
||||
{
|
||||
MOZ_ASSERT(mClient->mUnregisterClientRunnable);
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
NS_ENSURE_TRUE_VOID(bs);
|
||||
|
||||
// Notify BluetoothGatt to clear the clientIf
|
||||
BluetoothSignal signal(
|
||||
NS_LITERAL_STRING("ClientUnregistered"),
|
||||
mClient->mAppUuid,
|
||||
BluetoothValue(true));
|
||||
bs->DistributeSignal(signal);
|
||||
|
||||
// Resolve the unregister request
|
||||
DispatchBluetoothReply(mClient->mUnregisterClientRunnable,
|
||||
BluetoothValue(true),
|
||||
EmptyString());
|
||||
mClient->mUnregisterClientRunnable = nullptr;
|
||||
|
||||
sClients->RemoveElement(mClient);
|
||||
}
|
||||
|
||||
void OnError(BluetoothStatus aStatus) MOZ_OVERRIDE
|
||||
{
|
||||
BT_WARNING("BluetoothGattClientInterface::UnregisterClient failed: %d",
|
||||
(int)aStatus);
|
||||
MOZ_ASSERT(mClient->mUnregisterClientRunnable);
|
||||
|
||||
// Reject the unregister request
|
||||
NS_NAMED_LITERAL_STRING(errorStr, "Unregister GATT client failed");
|
||||
DispatchBluetoothReply(mClient->mUnregisterClientRunnable,
|
||||
BluetoothValue(),
|
||||
errorStr);
|
||||
mClient->mUnregisterClientRunnable = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<BluetoothGattClient> mClient;
|
||||
};
|
||||
|
||||
void
|
||||
BluetoothGattManager::UnregisterClient(int aClientIf,
|
||||
BluetoothReplyRunnable* aRunnable)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aRunnable);
|
||||
|
||||
ENSURE_GATT_CLIENT_INTF_IS_READY_VOID(aRunnable);
|
||||
|
||||
size_t index = sClients->IndexOf(aClientIf, 0 /* Start */,
|
||||
ClientIfComparator());
|
||||
|
||||
// Reject the unregister request if the client is not found
|
||||
if (index == sClients->NoIndex) {
|
||||
NS_NAMED_LITERAL_STRING(errorStr, "Unregister GATT client failed");
|
||||
DispatchBluetoothReply(aRunnable,
|
||||
BluetoothValue(),
|
||||
errorStr);
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
|
||||
client->mUnregisterClientRunnable = aRunnable;
|
||||
|
||||
sBluetoothGattClientInterface->UnregisterClient(
|
||||
aClientIf,
|
||||
new UnregisterClientResultHandler(client));
|
||||
}
|
||||
|
||||
class BluetoothGattManager::ConnectResultHandler MOZ_FINAL
|
||||
: public BluetoothGattClientResultHandler
|
||||
{
|
||||
public:
|
||||
ConnectResultHandler(BluetoothGattClient* aClient)
|
||||
: mClient(aClient)
|
||||
{
|
||||
MOZ_ASSERT(mClient);
|
||||
}
|
||||
|
||||
void OnError(BluetoothStatus aStatus) MOZ_OVERRIDE
|
||||
{
|
||||
BT_WARNING("BluetoothGattClientInterface::Connect failed: %d",
|
||||
(int)aStatus);
|
||||
MOZ_ASSERT(mClient->mConnectRunnable);
|
||||
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
NS_ENSURE_TRUE_VOID(bs);
|
||||
|
||||
// Notify BluetoothGatt for client disconnected
|
||||
BluetoothSignal signal(
|
||||
NS_LITERAL_STRING(GATT_CONNECTION_STATE_CHANGED_ID),
|
||||
mClient->mAppUuid,
|
||||
BluetoothValue(false)); // Disconnected
|
||||
bs->DistributeSignal(signal);
|
||||
|
||||
// Reject the connect request
|
||||
NS_NAMED_LITERAL_STRING(errorStr, "Connect failed");
|
||||
DispatchBluetoothReply(mClient->mConnectRunnable,
|
||||
BluetoothValue(),
|
||||
errorStr);
|
||||
mClient->mConnectRunnable = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<BluetoothGattClient> mClient;
|
||||
};
|
||||
|
||||
void
|
||||
BluetoothGattManager::Connect(const nsAString& aAppUuid,
|
||||
const nsAString& aDeviceAddr,
|
||||
BluetoothReplyRunnable* aRunnable)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aRunnable);
|
||||
|
||||
ENSURE_GATT_CLIENT_INTF_IS_READY_VOID(aRunnable);
|
||||
|
||||
size_t index = sClients->IndexOf(aAppUuid, 0 /* Start */, UuidComparator());
|
||||
if (index == sClients->NoIndex) {
|
||||
index = sClients->Length();
|
||||
sClients->AppendElement(new BluetoothGattClient(aAppUuid, aDeviceAddr));
|
||||
}
|
||||
|
||||
nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
|
||||
client->mConnectRunnable = aRunnable;
|
||||
|
||||
if (client->mClientIf > 0) {
|
||||
sBluetoothGattClientInterface->Connect(client->mClientIf,
|
||||
aDeviceAddr,
|
||||
true, // direct connect
|
||||
new ConnectResultHandler(client));
|
||||
} else {
|
||||
BluetoothUuid uuid;
|
||||
StringToUuid(NS_ConvertUTF16toUTF8(aAppUuid).get(), uuid);
|
||||
|
||||
// connect will be proceeded after client registered
|
||||
sBluetoothGattClientInterface->RegisterClient(
|
||||
uuid, new RegisterClientResultHandler(client));
|
||||
}
|
||||
}
|
||||
|
||||
class BluetoothGattManager::DisconnectResultHandler MOZ_FINAL
|
||||
: public BluetoothGattClientResultHandler
|
||||
{
|
||||
public:
|
||||
DisconnectResultHandler(BluetoothGattClient* aClient)
|
||||
: mClient(aClient)
|
||||
{
|
||||
MOZ_ASSERT(mClient);
|
||||
}
|
||||
|
||||
void OnError(BluetoothStatus aStatus) MOZ_OVERRIDE
|
||||
{
|
||||
BT_WARNING("BluetoothGattClientInterface::Disconnect failed: %d",
|
||||
(int)aStatus);
|
||||
MOZ_ASSERT(mClient->mDisconnectRunnable);
|
||||
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
NS_ENSURE_TRUE_VOID(bs);
|
||||
|
||||
// Notify BluetoothGatt that the client remains connected
|
||||
BluetoothSignal signal(
|
||||
NS_LITERAL_STRING(GATT_CONNECTION_STATE_CHANGED_ID),
|
||||
mClient->mAppUuid,
|
||||
BluetoothValue(true)); // Connected
|
||||
bs->DistributeSignal(signal);
|
||||
|
||||
// Reject the disconnect request
|
||||
NS_NAMED_LITERAL_STRING(errorStr, "Disconnect failed");
|
||||
DispatchBluetoothReply(mClient->mDisconnectRunnable,
|
||||
BluetoothValue(),
|
||||
errorStr);
|
||||
mClient->mDisconnectRunnable = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<BluetoothGattClient> mClient;
|
||||
};
|
||||
|
||||
void
|
||||
BluetoothGattManager::Disconnect(const nsAString& aAppUuid,
|
||||
const nsAString& aDeviceAddr,
|
||||
BluetoothReplyRunnable* aRunnable)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aRunnable);
|
||||
|
||||
ENSURE_GATT_CLIENT_INTF_IS_READY_VOID(aRunnable);
|
||||
|
||||
size_t index = sClients->IndexOf(aAppUuid, 0 /* Start */, UuidComparator());
|
||||
|
||||
// Reject the disconnect request if the client is not found
|
||||
if (index == sClients->NoIndex) {
|
||||
NS_NAMED_LITERAL_STRING(errorStr, "Disconnect failed");
|
||||
DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr);
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
|
||||
client->mDisconnectRunnable = aRunnable;
|
||||
|
||||
sBluetoothGattClientInterface->Disconnect(
|
||||
client->mClientIf,
|
||||
aDeviceAddr,
|
||||
client->mConnId,
|
||||
new DisconnectResultHandler(client));
|
||||
}
|
||||
|
||||
//
|
||||
// Notification Handlers
|
||||
//
|
||||
@ -183,7 +509,60 @@ void
|
||||
BluetoothGattManager::RegisterClientNotification(int aStatus,
|
||||
int aClientIf,
|
||||
const BluetoothUuid& aAppUuid)
|
||||
{ }
|
||||
{
|
||||
BT_API2_LOGR("Client Registered, clientIf = %d", aClientIf);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsString uuid;
|
||||
UuidToString(aAppUuid, uuid);
|
||||
|
||||
size_t index = sClients->IndexOf(uuid, 0 /* Start */, UuidComparator());
|
||||
NS_ENSURE_TRUE_VOID(index != sClients->NoIndex);
|
||||
nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
|
||||
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
NS_ENSURE_TRUE_VOID(bs);
|
||||
|
||||
if (aStatus) { // operation failed
|
||||
BT_API2_LOGR(
|
||||
"RegisterClient failed, clientIf = %d, status = %d, appUuid = %s",
|
||||
aClientIf, aStatus, NS_ConvertUTF16toUTF8(uuid).get());
|
||||
|
||||
// Notify BluetoothGatt for client disconnected
|
||||
BluetoothSignal signal(
|
||||
NS_LITERAL_STRING(GATT_CONNECTION_STATE_CHANGED_ID),
|
||||
uuid, BluetoothValue(false)); // Disconnected
|
||||
bs->DistributeSignal(signal);
|
||||
|
||||
// Reject the connect request
|
||||
if (client->mConnectRunnable) {
|
||||
NS_NAMED_LITERAL_STRING(errorStr,
|
||||
"Connect failed due to registration failed");
|
||||
DispatchBluetoothReply(client->mConnectRunnable,
|
||||
BluetoothValue(),
|
||||
errorStr);
|
||||
client->mConnectRunnable = nullptr;
|
||||
}
|
||||
|
||||
sClients->RemoveElement(client);
|
||||
return;
|
||||
}
|
||||
|
||||
client->mClientIf = aClientIf;
|
||||
|
||||
// Notify BluetoothGatt to update the clientIf
|
||||
BluetoothSignal signal(
|
||||
NS_LITERAL_STRING("ClientRegistered"),
|
||||
uuid, BluetoothValue(uint32_t(aClientIf)));
|
||||
bs->DistributeSignal(signal);
|
||||
|
||||
// Client just registered, proceed remaining connect request.
|
||||
if (client->mConnectRunnable) {
|
||||
sBluetoothGattClientInterface->Connect(
|
||||
aClientIf, client->mDeviceAddr, true /* direct connect */,
|
||||
new ConnectResultHandler(client));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothGattManager::ScanResultNotification(
|
||||
@ -195,15 +574,114 @@ void
|
||||
BluetoothGattManager::ConnectNotification(int aConnId,
|
||||
int aStatus,
|
||||
int aClientIf,
|
||||
const nsAString& aBdAddr)
|
||||
{ }
|
||||
const nsAString& aDeviceAddr)
|
||||
{
|
||||
BT_API2_LOGR();
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
NS_ENSURE_TRUE_VOID(bs);
|
||||
|
||||
size_t index = sClients->IndexOf(aClientIf, 0 /* Start */,
|
||||
ClientIfComparator());
|
||||
NS_ENSURE_TRUE_VOID(index != sClients->NoIndex);
|
||||
nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
|
||||
|
||||
if (aStatus) { // operation failed
|
||||
BT_API2_LOGR("Connect failed, clientIf = %d, connId = %d, status = %d",
|
||||
aClientIf, aConnId, aStatus);
|
||||
|
||||
// Notify BluetoothGatt that the client remains disconnected
|
||||
BluetoothSignal signal(
|
||||
NS_LITERAL_STRING(GATT_CONNECTION_STATE_CHANGED_ID),
|
||||
client->mAppUuid,
|
||||
BluetoothValue(false)); // Disconnected
|
||||
bs->DistributeSignal(signal);
|
||||
|
||||
// Reject the connect request
|
||||
if (client->mConnectRunnable) {
|
||||
NS_NAMED_LITERAL_STRING(errorStr, "Connect failed");
|
||||
DispatchBluetoothReply(client->mConnectRunnable,
|
||||
BluetoothValue(),
|
||||
errorStr);
|
||||
client->mConnectRunnable = nullptr;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
client->mConnId = aConnId;
|
||||
|
||||
// Notify BluetoothGatt for client connected
|
||||
BluetoothSignal signal(
|
||||
NS_LITERAL_STRING(GATT_CONNECTION_STATE_CHANGED_ID),
|
||||
client->mAppUuid,
|
||||
BluetoothValue(true)); // Connected
|
||||
bs->DistributeSignal(signal);
|
||||
|
||||
// Resolve the connect request
|
||||
if (client->mConnectRunnable) {
|
||||
DispatchBluetoothReply(client->mConnectRunnable,
|
||||
BluetoothValue(true),
|
||||
EmptyString());
|
||||
client->mConnectRunnable = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothGattManager::DisconnectNotification(int aConnId,
|
||||
int aStatus,
|
||||
int aClientIf,
|
||||
const nsAString& aBdAddr)
|
||||
{ }
|
||||
const nsAString& aDeviceAddr)
|
||||
{
|
||||
BT_API2_LOGR();
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
NS_ENSURE_TRUE_VOID(bs);
|
||||
|
||||
size_t index = sClients->IndexOf(aClientIf, 0 /* Start */,
|
||||
ClientIfComparator());
|
||||
NS_ENSURE_TRUE_VOID(index != sClients->NoIndex);
|
||||
nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
|
||||
|
||||
if (aStatus) { // operation failed
|
||||
// Notify BluetoothGatt that the client remains connected
|
||||
BluetoothSignal signal(
|
||||
NS_LITERAL_STRING(GATT_CONNECTION_STATE_CHANGED_ID),
|
||||
client->mAppUuid,
|
||||
BluetoothValue(true)); // Connected
|
||||
bs->DistributeSignal(signal);
|
||||
|
||||
// Reject the disconnect request
|
||||
if (client->mDisconnectRunnable) {
|
||||
NS_NAMED_LITERAL_STRING(errorStr, "Disconnect failed");
|
||||
DispatchBluetoothReply(client->mDisconnectRunnable,
|
||||
BluetoothValue(),
|
||||
errorStr);
|
||||
client->mDisconnectRunnable = nullptr;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
client->mConnId = 0;
|
||||
|
||||
// Notify BluetoothGatt for client disconnected
|
||||
BluetoothSignal signal(
|
||||
NS_LITERAL_STRING(GATT_CONNECTION_STATE_CHANGED_ID),
|
||||
client->mAppUuid,
|
||||
BluetoothValue(false)); // Disconnected
|
||||
bs->DistributeSignal(signal);
|
||||
|
||||
// Resolve the disconnect request
|
||||
if (client->mDisconnectRunnable) {
|
||||
DispatchBluetoothReply(client->mDisconnectRunnable,
|
||||
BluetoothValue(true),
|
||||
EmptyString());
|
||||
client->mDisconnectRunnable = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothGattManager::SearchCompleteNotification(int aConnId, int aStatus)
|
||||
|
@ -12,6 +12,9 @@
|
||||
#include "BluetoothProfileManagerBase.h"
|
||||
|
||||
BEGIN_BLUETOOTH_NAMESPACE
|
||||
|
||||
class BluetoothReplyRunnable;
|
||||
|
||||
class BluetoothGattManager MOZ_FINAL : public nsIObserver
|
||||
, public BluetoothGattNotificationHandler
|
||||
{
|
||||
@ -24,10 +27,25 @@ public:
|
||||
static void DeinitGattInterface(BluetoothProfileResultHandler* aRes);
|
||||
virtual ~BluetoothGattManager();
|
||||
|
||||
void Connect(const nsAString& aAppUuid,
|
||||
const nsAString& aDeviceAddr,
|
||||
BluetoothReplyRunnable* aRunnable);
|
||||
|
||||
void Disconnect(const nsAString& aAppUuid,
|
||||
const nsAString& aDeviceAddr,
|
||||
BluetoothReplyRunnable* aRunnable);
|
||||
|
||||
void UnregisterClient(int aClientIf,
|
||||
BluetoothReplyRunnable* aRunnable);
|
||||
|
||||
private:
|
||||
class CleanupResultHandler;
|
||||
class CleanupResultHandlerRunnable;
|
||||
class InitGattResultHandler;
|
||||
class RegisterClientResultHandler;
|
||||
class UnregisterClientResultHandler;
|
||||
class ConnectResultHandler;
|
||||
class DisconnectResultHandler;
|
||||
|
||||
BluetoothGattManager();
|
||||
|
||||
|
@ -55,6 +55,15 @@
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define ENSURE_GATT_MGR_IS_READY_VOID(gatt, runnable) \
|
||||
do { \
|
||||
if (!gatt) { \
|
||||
NS_NAMED_LITERAL_STRING(replyError, "GattManager is not ready"); \
|
||||
DispatchBluetoothReply(runnable, BluetoothValue(), replyError); \
|
||||
return; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::ipc;
|
||||
USING_BLUETOOTH_NAMESPACE
|
||||
@ -1107,6 +1116,54 @@ BluetoothServiceBluedroid::ToggleCalls(BluetoothReplyRunnable* aRunnable)
|
||||
{
|
||||
}
|
||||
|
||||
//
|
||||
// GATT Client
|
||||
//
|
||||
|
||||
void
|
||||
BluetoothServiceBluedroid::ConnectGattClientInternal(
|
||||
const nsAString& aAppUuid, const nsAString& aDeviceAddress,
|
||||
BluetoothReplyRunnable* aRunnable)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
ENSURE_BLUETOOTH_IS_READY_VOID(aRunnable);
|
||||
|
||||
BluetoothGattManager* gatt = BluetoothGattManager::Get();
|
||||
ENSURE_GATT_MGR_IS_READY_VOID(gatt, aRunnable);
|
||||
|
||||
gatt->Connect(aAppUuid, aDeviceAddress, aRunnable);
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothServiceBluedroid::DisconnectGattClientInternal(
|
||||
const nsAString& aAppUuid, const nsAString& aDeviceAddress,
|
||||
BluetoothReplyRunnable* aRunnable)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
ENSURE_BLUETOOTH_IS_READY_VOID(aRunnable);
|
||||
|
||||
BluetoothGattManager* gatt = BluetoothGattManager::Get();
|
||||
ENSURE_GATT_MGR_IS_READY_VOID(gatt, aRunnable);
|
||||
|
||||
gatt->Disconnect(aAppUuid, aDeviceAddress, aRunnable);
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothServiceBluedroid::UnregisterGattClientInternal(
|
||||
int aClientIf, BluetoothReplyRunnable* aRunnable)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
ENSURE_BLUETOOTH_IS_READY_VOID(aRunnable);
|
||||
|
||||
BluetoothGattManager* gatt = BluetoothGattManager::Get();
|
||||
ENSURE_GATT_MGR_IS_READY_VOID(gatt, aRunnable);
|
||||
|
||||
gatt->UnregisterClient(aClientIf, aRunnable);
|
||||
}
|
||||
|
||||
//
|
||||
// Bluetooth notifications
|
||||
//
|
||||
|
@ -170,6 +170,24 @@ public:
|
||||
SendInputMessage(const nsAString& aDeviceAddresses,
|
||||
const nsAString& aMessage) MOZ_OVERRIDE;
|
||||
|
||||
//
|
||||
// GATT Client
|
||||
//
|
||||
|
||||
virtual void
|
||||
ConnectGattClientInternal(const nsAString& aAppUuid,
|
||||
const nsAString& aDeviceAddress,
|
||||
BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
|
||||
|
||||
virtual void
|
||||
DisconnectGattClientInternal(const nsAString& aAppUuid,
|
||||
const nsAString& aDeviceAddress,
|
||||
BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
|
||||
|
||||
virtual void
|
||||
UnregisterGattClientInternal(int aClientIf,
|
||||
BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
|
||||
|
||||
//
|
||||
// Bluetooth notifications
|
||||
//
|
||||
|
@ -4266,3 +4266,23 @@ BluetoothDBusService::UpdateNotification(ControlEventId aEventId,
|
||||
Task* task = new UpdateNotificationTask(deviceAddress, aEventId, aData);
|
||||
DispatchToDBusThread(task);
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothDBusService::ConnectGattClientInternal(
|
||||
const nsAString& aAppUuid, const nsAString& aDeviceAddress,
|
||||
BluetoothReplyRunnable* aRunnable)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothDBusService::DisconnectGattClientInternal(
|
||||
const nsAString& aAppUuid, const nsAString& aDeviceAddress,
|
||||
BluetoothReplyRunnable* aRunnable)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothDBusService::UnregisterGattClientInternal(
|
||||
int aClientIf, BluetoothReplyRunnable* aRunnable)
|
||||
{
|
||||
}
|
||||
|
@ -184,6 +184,20 @@ public:
|
||||
virtual nsresult
|
||||
SendInputMessage(const nsAString& aDeviceAddresses,
|
||||
const nsAString& aMessage) MOZ_OVERRIDE;
|
||||
|
||||
virtual void
|
||||
ConnectGattClientInternal(const nsAString& aAppUuid,
|
||||
const nsAString& aDeviceAddress,
|
||||
BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
|
||||
|
||||
virtual void
|
||||
DisconnectGattClientInternal(const nsAString& aAppUuid,
|
||||
const nsAString& aDeviceAddress,
|
||||
BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
|
||||
virtual void
|
||||
UnregisterGattClientInternal(int aClientIf,
|
||||
BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
nsresult SendGetPropertyMessage(const nsAString& aPath,
|
||||
const char* aInterface,
|
||||
|
@ -250,6 +250,12 @@ BluetoothParent::RecvPBluetoothRequestConstructor(
|
||||
return actor->DoRequest(aRequest.get_SendMetaDataRequest());
|
||||
case Request::TSendPlayStatusRequest:
|
||||
return actor->DoRequest(aRequest.get_SendPlayStatusRequest());
|
||||
case Request::TConnectGattClientRequest:
|
||||
return actor->DoRequest(aRequest.get_ConnectGattClientRequest());
|
||||
case Request::TDisconnectGattClientRequest:
|
||||
return actor->DoRequest(aRequest.get_DisconnectGattClientRequest());
|
||||
case Request::TUnregisterGattClientRequest:
|
||||
return actor->DoRequest(aRequest.get_UnregisterGattClientRequest());
|
||||
default:
|
||||
MOZ_CRASH("Unknown type!");
|
||||
}
|
||||
@ -684,3 +690,41 @@ BluetoothRequestParent::DoRequest(const SendPlayStatusRequest& aRequest)
|
||||
mReplyRunnable.get());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BluetoothRequestParent::DoRequest(const ConnectGattClientRequest& aRequest)
|
||||
{
|
||||
MOZ_ASSERT(mService);
|
||||
MOZ_ASSERT(mRequestType == Request::TConnectGattClientRequest);
|
||||
|
||||
mService->ConnectGattClientInternal(aRequest.appUuid(),
|
||||
aRequest.deviceAddress(),
|
||||
mReplyRunnable.get());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BluetoothRequestParent::DoRequest(const DisconnectGattClientRequest& aRequest)
|
||||
{
|
||||
MOZ_ASSERT(mService);
|
||||
MOZ_ASSERT(mRequestType == Request::TDisconnectGattClientRequest);
|
||||
|
||||
mService->DisconnectGattClientInternal(aRequest.appUuid(),
|
||||
aRequest.deviceAddress(),
|
||||
mReplyRunnable.get());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BluetoothRequestParent::DoRequest(const UnregisterGattClientRequest& aRequest)
|
||||
{
|
||||
MOZ_ASSERT(mService);
|
||||
MOZ_ASSERT(mRequestType == Request::TUnregisterGattClientRequest);
|
||||
|
||||
mService->UnregisterGattClientInternal(aRequest.clientIf(),
|
||||
mReplyRunnable.get());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -216,6 +216,15 @@ protected:
|
||||
|
||||
bool
|
||||
DoRequest(const SendPlayStatusRequest& aRequest);
|
||||
|
||||
bool
|
||||
DoRequest(const ConnectGattClientRequest& aRequest);
|
||||
|
||||
bool
|
||||
DoRequest(const DisconnectGattClientRequest& aRequest);
|
||||
|
||||
bool
|
||||
DoRequest(const UnregisterGattClientRequest& aRequest);
|
||||
};
|
||||
|
||||
END_BLUETOOTH_NAMESPACE
|
||||
|
@ -141,7 +141,7 @@ BluetoothServiceChildProcess::GetPairedDevicePropertiesInternal(
|
||||
|
||||
nsresult
|
||||
BluetoothServiceChildProcess::FetchUuidsInternal(
|
||||
const nsAString& aDeviceAddress, BluetoothReplyRunnable* aRunnable)
|
||||
const nsAString& aDeviceAddress, BluetoothReplyRunnable* aRunnable)
|
||||
{
|
||||
SendRequest(aRunnable, FetchUuidsRequest(nsString(aDeviceAddress)));
|
||||
return NS_OK;
|
||||
@ -379,6 +379,31 @@ BluetoothServiceChildProcess::SendPlayStatus(int64_t aDuration,
|
||||
nsString(aPlayStatus)));
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothServiceChildProcess::ConnectGattClientInternal(
|
||||
const nsAString& aAppUuid, const nsAString& aDeviceAddress,
|
||||
BluetoothReplyRunnable* aRunnable)
|
||||
{
|
||||
SendRequest(aRunnable, ConnectGattClientRequest(nsString(aAppUuid),
|
||||
nsString(aDeviceAddress)));
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothServiceChildProcess::DisconnectGattClientInternal(
|
||||
const nsAString& aAppUuid, const nsAString& aDeviceAddress,
|
||||
BluetoothReplyRunnable* aRunnable)
|
||||
{
|
||||
SendRequest(aRunnable,
|
||||
DisconnectGattClientRequest(nsString(aAppUuid), nsString(aDeviceAddress)));
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothServiceChildProcess::UnregisterGattClientInternal(
|
||||
int aClientIf, BluetoothReplyRunnable* aRunnable)
|
||||
{
|
||||
SendRequest(aRunnable, UnregisterGattClientRequest(aClientIf));
|
||||
}
|
||||
|
||||
nsresult
|
||||
BluetoothServiceChildProcess::HandleStartup()
|
||||
{
|
||||
|
@ -192,6 +192,20 @@ public:
|
||||
SendInputMessage(const nsAString& aDeviceAddresses,
|
||||
const nsAString& aMessage) MOZ_OVERRIDE;
|
||||
|
||||
virtual void
|
||||
ConnectGattClientInternal(const nsAString& aAppUuid,
|
||||
const nsAString& aDeviceAddress,
|
||||
BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
|
||||
|
||||
virtual void
|
||||
DisconnectGattClientInternal(const nsAString& aAppUuid,
|
||||
const nsAString& aDeviceAddress,
|
||||
BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
|
||||
|
||||
virtual void
|
||||
UnregisterGattClientInternal(int aClientIf,
|
||||
BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
|
||||
|
||||
protected:
|
||||
BluetoothServiceChildProcess();
|
||||
virtual ~BluetoothServiceChildProcess();
|
||||
|
@ -176,6 +176,23 @@ struct SendPlayStatusRequest
|
||||
nsString playStatus;
|
||||
};
|
||||
|
||||
struct ConnectGattClientRequest
|
||||
{
|
||||
nsString appUuid;
|
||||
nsString deviceAddress;
|
||||
};
|
||||
|
||||
struct DisconnectGattClientRequest
|
||||
{
|
||||
nsString appUuid;
|
||||
nsString deviceAddress;
|
||||
};
|
||||
|
||||
struct UnregisterGattClientRequest
|
||||
{
|
||||
int clientIf;
|
||||
};
|
||||
|
||||
union Request
|
||||
{
|
||||
GetAdaptersRequest;
|
||||
@ -208,6 +225,9 @@ union Request
|
||||
ToggleCallsRequest;
|
||||
SendMetaDataRequest;
|
||||
SendPlayStatusRequest;
|
||||
ConnectGattClientRequest;
|
||||
DisconnectGattClientRequest;
|
||||
UnregisterGattClientRequest;
|
||||
};
|
||||
|
||||
protocol PBluetooth
|
||||
|
@ -10,6 +10,7 @@ if CONFIG['MOZ_B2G_BT']:
|
||||
'BluetoothClassOfDevice.cpp',
|
||||
'BluetoothDevice.cpp',
|
||||
'BluetoothDiscoveryHandle.cpp',
|
||||
'BluetoothGatt.cpp',
|
||||
'BluetoothHidManager.cpp',
|
||||
'BluetoothInterface.cpp',
|
||||
'BluetoothManager.cpp',
|
||||
@ -122,6 +123,7 @@ EXPORTS.mozilla.dom.bluetooth += [
|
||||
'BluetoothCommon.h',
|
||||
'BluetoothDevice.h',
|
||||
'BluetoothDiscoveryHandle.h',
|
||||
'BluetoothGatt.h',
|
||||
'BluetoothManager.h',
|
||||
'BluetoothPairingHandle.h',
|
||||
'BluetoothPairingListener.h',
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Mozilla Foundation
|
||||
* Copyright (C) 2012-2015 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -40,14 +40,15 @@ DEF_GONK_RECORDER_PROFILE(CAMCORDER_QUALITY_1080P, "1080p")
|
||||
* profiles may have more than one resolution, depending on the camera.
|
||||
*/
|
||||
|
||||
DEF_GONK_RECORDER_PROFILE_DETECT("fwvga", 864, 480)
|
||||
DEF_GONK_RECORDER_PROFILE_DETECT("fwvga", 854, 480)
|
||||
DEF_GONK_RECORDER_PROFILE_DETECT("wvga", 800, 480)
|
||||
DEF_GONK_RECORDER_PROFILE_DETECT("wvga", 768, 480)
|
||||
DEF_GONK_RECORDER_PROFILE_DETECT("vga", 640, 480)
|
||||
DEF_GONK_RECORDER_PROFILE_DETECT("hvga", 480, 320)
|
||||
DEF_GONK_RECORDER_PROFILE_DETECT("wqvga", 400, 240)
|
||||
DEF_GONK_RECORDER_PROFILE_DETECT("qvga", 320, 240)
|
||||
DEF_GONK_RECORDER_PROFILE_DETECT("4kuhd", 3840, 2160)
|
||||
DEF_GONK_RECORDER_PROFILE_DETECT("fwvga", 864, 480)
|
||||
DEF_GONK_RECORDER_PROFILE_DETECT("fwvga", 854, 480)
|
||||
DEF_GONK_RECORDER_PROFILE_DETECT("wvga", 800, 480)
|
||||
DEF_GONK_RECORDER_PROFILE_DETECT("wvga", 768, 480)
|
||||
DEF_GONK_RECORDER_PROFILE_DETECT("vga", 640, 480)
|
||||
DEF_GONK_RECORDER_PROFILE_DETECT("hvga", 480, 320)
|
||||
DEF_GONK_RECORDER_PROFILE_DETECT("wqvga", 400, 240)
|
||||
DEF_GONK_RECORDER_PROFILE_DETECT("qvga", 320, 240)
|
||||
|
||||
#undef DEF_GONK_RECORDER_PROFILE
|
||||
#undef DEF_GONK_RECORDER_PROFILE_DETECT
|
||||
|
@ -600,7 +600,7 @@ bool OmxDecoder::ReadVideo(VideoFrame *aFrame, int64_t aTimeUs,
|
||||
// For some codecs, the length of first decoded frame after seek is 0.
|
||||
// Need to ignore it and continue to find the next one
|
||||
if (mVideoBuffer->range_length() == 0) {
|
||||
ReleaseVideoBuffer();
|
||||
PostReleaseVideoBuffer(mVideoBuffer, FenceHandle());
|
||||
findNextBuffer = true;
|
||||
}
|
||||
}
|
||||
|
@ -178,8 +178,10 @@ XPCOMUtils.defineLazyGetter(this, "gMessageManager", function () {
|
||||
},
|
||||
|
||||
setFocusApp: function setFocusApp(id, isFocus) {
|
||||
// if calling setNFCFocus(true) on the same browser-element, ignore.
|
||||
if (isFocus && (id == this.focusApp)) {
|
||||
// if calling setNFCFocus(true) on the browser-element which is already
|
||||
// focused, or calling setNFCFocus(false) on the browser-element which has
|
||||
// lost focus already, ignore.
|
||||
if (isFocus == (id == this.focusApp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -314,7 +314,7 @@ typedef enum {
|
||||
AUDIO_DEVICE_IN_DEFAULT),
|
||||
AUDIO_DEVICE_IN_ALL_SCO = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET,
|
||||
} audio_devices_t;
|
||||
#else
|
||||
#elif ANDROID_VERSION < 21
|
||||
enum {
|
||||
AUDIO_DEVICE_NONE = 0x0,
|
||||
/* reserved bits */
|
||||
@ -415,6 +415,133 @@ enum {
|
||||
AUDIO_DEVICE_IN_ALL_SCO = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET,
|
||||
};
|
||||
|
||||
typedef uint32_t audio_devices_t;
|
||||
#else
|
||||
enum {
|
||||
AUDIO_DEVICE_NONE = 0x0,
|
||||
/* reserved bits */
|
||||
AUDIO_DEVICE_BIT_IN = 0x80000000,
|
||||
AUDIO_DEVICE_BIT_DEFAULT = 0x40000000,
|
||||
/* output devices */
|
||||
AUDIO_DEVICE_OUT_EARPIECE = 0x1,
|
||||
AUDIO_DEVICE_OUT_SPEAKER = 0x2,
|
||||
AUDIO_DEVICE_OUT_WIRED_HEADSET = 0x4,
|
||||
AUDIO_DEVICE_OUT_WIRED_HEADPHONE = 0x8,
|
||||
AUDIO_DEVICE_OUT_BLUETOOTH_SCO = 0x10,
|
||||
AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET = 0x20,
|
||||
AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT = 0x40,
|
||||
AUDIO_DEVICE_OUT_BLUETOOTH_A2DP = 0x80,
|
||||
AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100,
|
||||
AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200,
|
||||
AUDIO_DEVICE_OUT_AUX_DIGITAL = 0x400,
|
||||
AUDIO_DEVICE_OUT_HDMI = AUDIO_DEVICE_OUT_AUX_DIGITAL,
|
||||
/* uses an analog connection (multiplexed over the USB connector pins for instance) */
|
||||
AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET = 0x800,
|
||||
AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET = 0x1000,
|
||||
/* USB accessory mode: your Android device is a USB device and the dock is a USB host */
|
||||
AUDIO_DEVICE_OUT_USB_ACCESSORY = 0x2000,
|
||||
/* USB host mode: your Android device is a USB host and the dock is a USB device */
|
||||
AUDIO_DEVICE_OUT_USB_DEVICE = 0x4000,
|
||||
AUDIO_DEVICE_OUT_REMOTE_SUBMIX = 0x8000,
|
||||
/* Telephony voice TX path */
|
||||
AUDIO_DEVICE_OUT_TELEPHONY_TX = 0x10000,
|
||||
/* Analog jack with line impedance detected */
|
||||
AUDIO_DEVICE_OUT_LINE = 0x20000,
|
||||
/* HDMI Audio Return Channel */
|
||||
AUDIO_DEVICE_OUT_HDMI_ARC = 0x40000,
|
||||
/* S/PDIF out */
|
||||
AUDIO_DEVICE_OUT_SPDIF = 0x80000,
|
||||
/* FM transmitter out */
|
||||
AUDIO_DEVICE_OUT_FM = 0x100000,
|
||||
/* Line out for av devices */
|
||||
AUDIO_DEVICE_OUT_AUX_LINE = 0x200000,
|
||||
/* limited-output speaker device for acoustic safety */
|
||||
AUDIO_DEVICE_OUT_SPEAKER_SAFE = 0x400000,
|
||||
AUDIO_DEVICE_OUT_DEFAULT = AUDIO_DEVICE_BIT_DEFAULT,
|
||||
AUDIO_DEVICE_OUT_ALL = (AUDIO_DEVICE_OUT_EARPIECE |
|
||||
AUDIO_DEVICE_OUT_SPEAKER |
|
||||
AUDIO_DEVICE_OUT_WIRED_HEADSET |
|
||||
AUDIO_DEVICE_OUT_WIRED_HEADPHONE |
|
||||
AUDIO_DEVICE_OUT_BLUETOOTH_SCO |
|
||||
AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET |
|
||||
AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT |
|
||||
AUDIO_DEVICE_OUT_BLUETOOTH_A2DP |
|
||||
AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
|
||||
AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER |
|
||||
AUDIO_DEVICE_OUT_AUX_DIGITAL |
|
||||
AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET |
|
||||
AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET |
|
||||
AUDIO_DEVICE_OUT_USB_ACCESSORY |
|
||||
AUDIO_DEVICE_OUT_USB_DEVICE |
|
||||
AUDIO_DEVICE_OUT_REMOTE_SUBMIX |
|
||||
AUDIO_DEVICE_OUT_TELEPHONY_TX |
|
||||
AUDIO_DEVICE_OUT_LINE |
|
||||
AUDIO_DEVICE_OUT_HDMI_ARC |
|
||||
AUDIO_DEVICE_OUT_SPDIF |
|
||||
AUDIO_DEVICE_OUT_FM |
|
||||
AUDIO_DEVICE_OUT_AUX_LINE |
|
||||
AUDIO_DEVICE_OUT_SPEAKER_SAFE |
|
||||
AUDIO_DEVICE_OUT_DEFAULT),
|
||||
AUDIO_DEVICE_OUT_ALL_A2DP = (AUDIO_DEVICE_OUT_BLUETOOTH_A2DP |
|
||||
AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
|
||||
AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER),
|
||||
AUDIO_DEVICE_OUT_ALL_SCO = (AUDIO_DEVICE_OUT_BLUETOOTH_SCO |
|
||||
AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET |
|
||||
AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT),
|
||||
AUDIO_DEVICE_OUT_ALL_USB = (AUDIO_DEVICE_OUT_USB_ACCESSORY |
|
||||
AUDIO_DEVICE_OUT_USB_DEVICE),
|
||||
/* input devices */
|
||||
AUDIO_DEVICE_IN_COMMUNICATION = AUDIO_DEVICE_BIT_IN | 0x1,
|
||||
AUDIO_DEVICE_IN_AMBIENT = AUDIO_DEVICE_BIT_IN | 0x2,
|
||||
AUDIO_DEVICE_IN_BUILTIN_MIC = AUDIO_DEVICE_BIT_IN | 0x4,
|
||||
AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET = AUDIO_DEVICE_BIT_IN | 0x8,
|
||||
AUDIO_DEVICE_IN_WIRED_HEADSET = AUDIO_DEVICE_BIT_IN | 0x10,
|
||||
AUDIO_DEVICE_IN_AUX_DIGITAL = AUDIO_DEVICE_BIT_IN | 0x20,
|
||||
AUDIO_DEVICE_IN_HDMI = AUDIO_DEVICE_IN_AUX_DIGITAL,
|
||||
/* Telephony voice RX path */
|
||||
AUDIO_DEVICE_IN_VOICE_CALL = AUDIO_DEVICE_BIT_IN | 0x40,
|
||||
AUDIO_DEVICE_IN_BACK_MIC = AUDIO_DEVICE_BIT_IN | 0x80,
|
||||
AUDIO_DEVICE_IN_REMOTE_SUBMIX = AUDIO_DEVICE_BIT_IN | 0x100,
|
||||
AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET = AUDIO_DEVICE_BIT_IN | 0x200,
|
||||
AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET = AUDIO_DEVICE_BIT_IN | 0x400,
|
||||
AUDIO_DEVICE_IN_USB_ACCESSORY = AUDIO_DEVICE_BIT_IN | 0x800,
|
||||
AUDIO_DEVICE_IN_USB_DEVICE = AUDIO_DEVICE_BIT_IN | 0x1000,
|
||||
/* FM tuner input */
|
||||
AUDIO_DEVICE_IN_FM_TUNER = AUDIO_DEVICE_BIT_IN | 0x2000,
|
||||
/* TV tuner input */
|
||||
AUDIO_DEVICE_IN_TV_TUNER = AUDIO_DEVICE_BIT_IN | 0x4000,
|
||||
/* Analog jack with line impedance detected */
|
||||
AUDIO_DEVICE_IN_LINE = AUDIO_DEVICE_BIT_IN | 0x8000,
|
||||
/* S/PDIF in */
|
||||
AUDIO_DEVICE_IN_SPDIF = AUDIO_DEVICE_BIT_IN | 0x10000,
|
||||
AUDIO_DEVICE_IN_BLUETOOTH_A2DP = AUDIO_DEVICE_BIT_IN | 0x20000,
|
||||
AUDIO_DEVICE_IN_LOOPBACK = AUDIO_DEVICE_BIT_IN | 0x40000,
|
||||
AUDIO_DEVICE_IN_DEFAULT = AUDIO_DEVICE_BIT_IN | AUDIO_DEVICE_BIT_DEFAULT,
|
||||
AUDIO_DEVICE_IN_ALL = (AUDIO_DEVICE_IN_COMMUNICATION |
|
||||
AUDIO_DEVICE_IN_AMBIENT |
|
||||
AUDIO_DEVICE_IN_BUILTIN_MIC |
|
||||
AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET |
|
||||
AUDIO_DEVICE_IN_WIRED_HEADSET |
|
||||
AUDIO_DEVICE_IN_AUX_DIGITAL |
|
||||
AUDIO_DEVICE_IN_VOICE_CALL |
|
||||
AUDIO_DEVICE_IN_BACK_MIC |
|
||||
AUDIO_DEVICE_IN_REMOTE_SUBMIX |
|
||||
AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET |
|
||||
AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET |
|
||||
AUDIO_DEVICE_IN_USB_ACCESSORY |
|
||||
AUDIO_DEVICE_IN_USB_DEVICE |
|
||||
AUDIO_DEVICE_IN_FM_TUNER |
|
||||
AUDIO_DEVICE_IN_TV_TUNER |
|
||||
AUDIO_DEVICE_IN_LINE |
|
||||
AUDIO_DEVICE_IN_SPDIF |
|
||||
AUDIO_DEVICE_IN_BLUETOOTH_A2DP |
|
||||
AUDIO_DEVICE_IN_LOOPBACK |
|
||||
AUDIO_DEVICE_IN_DEFAULT),
|
||||
AUDIO_DEVICE_IN_ALL_SCO = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET,
|
||||
AUDIO_DEVICE_IN_ALL_USB = (AUDIO_DEVICE_IN_USB_ACCESSORY |
|
||||
AUDIO_DEVICE_IN_USB_DEVICE),
|
||||
};
|
||||
|
||||
typedef uint32_t audio_devices_t;
|
||||
#endif
|
||||
|
||||
|
@ -465,6 +465,8 @@ this.NETWORK_CREG_TECH_EHRPD = 13;
|
||||
this.NETWORK_CREG_TECH_LTE = 14;
|
||||
this.NETWORK_CREG_TECH_HSPAP = 15;
|
||||
this.NETWORK_CREG_TECH_GSM = 16;
|
||||
this.NETWORK_CREG_TECH_DCHSPAP_1 = 18; // Some devices reports as 18
|
||||
this.NETWORK_CREG_TECH_DCHSPAP_2 = 19; // Some others report it as 19
|
||||
|
||||
this.CELL_INFO_TYPE_GSM = 1;
|
||||
this.CELL_INFO_TYPE_CDMA = 2;
|
||||
@ -2913,7 +2915,10 @@ this.GECKO_RADIO_TECH = [
|
||||
"ehrpd",
|
||||
"lte",
|
||||
"hspa+",
|
||||
"gsm"
|
||||
"gsm",
|
||||
null,
|
||||
"hspa+", // DC-HSPA+
|
||||
"hspa+"
|
||||
];
|
||||
|
||||
this.GECKO_VOICEMAIL_MESSAGE_COUNT_UNKNOWN = -1;
|
||||
|
@ -4226,6 +4226,8 @@ RilObject.prototype = {
|
||||
case NETWORK_CREG_TECH_LTE:
|
||||
case NETWORK_CREG_TECH_HSPAP:
|
||||
case NETWORK_CREG_TECH_GSM:
|
||||
case NETWORK_CREG_TECH_DCHSPAP_1:
|
||||
case NETWORK_CREG_TECH_DCHSPAP_2:
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -6194,6 +6196,8 @@ RilObject.prototype[REQUEST_GET_NEIGHBORING_CELL_IDS] = function REQUEST_GET_NEI
|
||||
case NETWORK_CREG_TECH_HSUPA:
|
||||
case NETWORK_CREG_TECH_HSPA:
|
||||
case NETWORK_CREG_TECH_HSPAP:
|
||||
case NETWORK_CREG_TECH_DCHSPAP_1:
|
||||
case NETWORK_CREG_TECH_DCHSPAP_2:
|
||||
cellId.wcdmaPsc = this.parseInt(cid, -1, 16);
|
||||
break;
|
||||
}
|
||||
|
@ -13,6 +13,12 @@ interface BluetoothDevice : EventTarget
|
||||
readonly attribute boolean paired;
|
||||
readonly attribute BluetoothDeviceType type;
|
||||
|
||||
/**
|
||||
* Retrieve the BluetoothGatt interface to interact with remote BLE devices.
|
||||
* This attribute is null if the device type is not dual or le.
|
||||
*/
|
||||
readonly attribute BluetoothGatt? gatt;
|
||||
|
||||
[Cached, Pure]
|
||||
readonly attribute sequence<DOMString> uuids;
|
||||
|
||||
|
38
dom/webidl/BluetoothGatt.webidl
Normal file
@ -0,0 +1,38 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
[CheckPermissions="bluetooth"]
|
||||
interface BluetoothGatt : EventTarget
|
||||
{
|
||||
readonly attribute BluetoothConnectionState connectionState;
|
||||
|
||||
// Fired when attribute connectionState changed
|
||||
attribute EventHandler onconnectionstatechanged;
|
||||
|
||||
/**
|
||||
* Connect/Disconnect to the remote BLE device if the connectionState is
|
||||
* disconnected/connected. Otherwise, the Promise will be rejected directly.
|
||||
*
|
||||
* If current connectionState is disconnected/connected,
|
||||
* 1) connectionState change to connecting/disconnecting along with a
|
||||
* connectionstatechanged event.
|
||||
* 2) connectionState change to connected/disconnected if the operation
|
||||
* succeeds. Otherwise, change to disconnected/connected.
|
||||
* 3) Promise is resolved or rejected according to the operation result.
|
||||
*/
|
||||
[NewObject]
|
||||
Promise<void> connect();
|
||||
[NewObject]
|
||||
Promise<void> disconnect();
|
||||
};
|
||||
|
||||
enum BluetoothConnectionState
|
||||
{
|
||||
"disconnected",
|
||||
"disconnecting",
|
||||
"connected",
|
||||
"connecting"
|
||||
};
|
@ -632,6 +632,7 @@ if CONFIG['MOZ_B2G_BT']:
|
||||
'BluetoothClassOfDevice.webidl',
|
||||
'BluetoothDevice2.webidl',
|
||||
'BluetoothDiscoveryHandle.webidl',
|
||||
'BluetoothGatt.webidl',
|
||||
'BluetoothManager2.webidl',
|
||||
'BluetoothPairingHandle.webidl',
|
||||
'BluetoothPairingListener.webidl',
|
||||
|
@ -37,6 +37,7 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop spec
|
||||
[test_bug639338.xhtml]
|
||||
[test_bug790265.xhtml]
|
||||
[test_bug821850.html]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[test_bug844783.html]
|
||||
[test_bug872273.xhtml]
|
||||
[test_bug946815.html]
|
||||
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 3.0 KiB |
@ -9,6 +9,6 @@ support-files =
|
||||
[test_private_window_from_content.html]
|
||||
# Next two tests are disabled in e10s because of bug 989501.
|
||||
[test_window_open_position_constraint.html]
|
||||
skip-if = toolkit == 'android' || e10s
|
||||
skip-if = toolkit == 'android' || e10s || buildapp == 'mulet'
|
||||
[test_window_open_units.html]
|
||||
skip-if = toolkit == 'android' || e10s
|
||||
skip-if = toolkit == 'android' || e10s || buildapp == 'mulet'
|
||||
|