mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 19:35:51 +00:00
Merge mozilla-central to mozilla-inbound
This commit is contained in:
commit
f1d2ae53bc
@ -360,6 +360,7 @@ pref("browser.dom.window.dump.enabled", false);
|
||||
|
||||
// Default Content Security Policy to apply to privileged and certified apps
|
||||
pref("security.apps.privileged.CSP.default", "default-src *; script-src 'self'; object-src 'none'; style-src 'self' 'unsafe-inline'");
|
||||
// If you change this CSP, make sure to update the fast path in nsCSPService.cpp
|
||||
pref("security.apps.certified.CSP.default", "default-src *; script-src 'self'; object-src 'none'; style-src 'self'");
|
||||
|
||||
// Temporarily force-enable GL compositing. This is default-disabled
|
||||
@ -388,9 +389,6 @@ pref("dom.ipc.browser_frames.oop_by_default", false);
|
||||
|
||||
// SMS/MMS
|
||||
pref("dom.sms.enabled", true);
|
||||
pref("dom.sms.strict7BitEncoding", false); // Disabled by default.
|
||||
pref("dom.sms.requestStatusReport", true); // Enabled by default.
|
||||
pref("dom.mms.requestStatusReport", true); // Enabled by default.
|
||||
|
||||
//The waiting time in network manager.
|
||||
pref("network.gonk.ms-release-mms-connection", 30000);
|
||||
@ -434,7 +432,6 @@ pref("services.push.udp.wakeupEnabled", true);
|
||||
// NetworkStats
|
||||
#ifdef MOZ_B2G_RIL
|
||||
pref("dom.mozNetworkStats.enabled", true);
|
||||
pref("ril.cellbroadcast.disabled", false);
|
||||
pref("dom.webapps.firstRunWithSIM", true);
|
||||
#endif
|
||||
|
||||
@ -730,10 +727,6 @@ pref("font.size.inflation.disabledInMasterProcess", true);
|
||||
// consumption when applications are sent to the background.
|
||||
pref("memory.free_dirty_pages", true);
|
||||
|
||||
// UAProfile settings
|
||||
pref("wap.UAProf.url", "");
|
||||
pref("wap.UAProf.tagname", "x-wap-profile");
|
||||
|
||||
pref("layout.imagevisibility.enabled", false);
|
||||
pref("layout.imagevisibility.numscrollportwidths", 1);
|
||||
pref("layout.imagevisibility.numscrollportheights", 1);
|
||||
@ -824,6 +817,19 @@ pref("gfx.canvas.azure.accelerated", true);
|
||||
// Enable Telephony API
|
||||
pref("dom.telephony.enabled", true);
|
||||
|
||||
// Cell Broadcast API
|
||||
pref("dom.cellbroadcast.enabled", true);
|
||||
pref("ril.cellbroadcast.disabled", false);
|
||||
|
||||
// ICC API
|
||||
pref("dom.icc.enabled", true);
|
||||
|
||||
// Mobile Connection API
|
||||
pref("dom.mobileconnection.enabled", true);
|
||||
|
||||
// Voice Mail API
|
||||
pref("dom.voicemail.enabled", true);
|
||||
|
||||
// The url of the page used to display network error details.
|
||||
pref("b2g.neterror.url", "app://system.gaiamobile.org/net_error.html");
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
{
|
||||
"revision": "563d1aa93586165246ab2ab9d40566a598f56387",
|
||||
"revision": "154bb18c48ff06e41fb7ba24d8f72d520919646f",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
@ -20,8 +20,7 @@
|
||||
],
|
||||
"env": {
|
||||
"VARIANT": "user",
|
||||
"MOZILLA_OFFICIAL": "1",
|
||||
"B2GUPDATER": "1"
|
||||
"MOZILLA_OFFICIAL": "1"
|
||||
},
|
||||
"b2g_manifest": "hamachi.xml",
|
||||
"b2g_manifest_branch": "master",
|
||||
|
@ -20,7 +20,6 @@
|
||||
"env": {
|
||||
"VARIANT": "user",
|
||||
"MOZILLA_OFFICIAL": "1",
|
||||
"B2GUPDATER": "1",
|
||||
"ANDROIDFS_DIR": "{workdir}/helix-ics"
|
||||
},
|
||||
"b2g_manifest": "helix.xml",
|
||||
|
@ -20,8 +20,7 @@
|
||||
],
|
||||
"env": {
|
||||
"VARIANT": "user",
|
||||
"MOZILLA_OFFICIAL": "1",
|
||||
"B2GUPDATER": "1"
|
||||
"MOZILLA_OFFICIAL": "1"
|
||||
},
|
||||
"b2g_manifest": "inari.xml",
|
||||
"b2g_manifest_branch": "master",
|
||||
|
@ -20,8 +20,7 @@
|
||||
],
|
||||
"env": {
|
||||
"VARIANT": "user",
|
||||
"MOZILLA_OFFICIAL": "1",
|
||||
"B2GUPDATER": "1"
|
||||
"MOZILLA_OFFICIAL": "1"
|
||||
},
|
||||
"b2g_manifest": "leo.xml",
|
||||
"b2g_manifest_branch": "master",
|
||||
|
@ -20,8 +20,7 @@
|
||||
],
|
||||
"env": {
|
||||
"VARIANT": "user",
|
||||
"MOZILLA_OFFICIAL": "1",
|
||||
"B2GUPDATER": "1"
|
||||
"MOZILLA_OFFICIAL": "1"
|
||||
},
|
||||
"b2g_manifest": "nexus-4.xml",
|
||||
"b2g_manifest_branch": "master",
|
||||
|
@ -22,8 +22,7 @@
|
||||
],
|
||||
"env": {
|
||||
"VARIANT": "user",
|
||||
"MOZILLA_OFFICIAL": "1",
|
||||
"B2GUPDATER": "1"
|
||||
"MOZILLA_OFFICIAL": "1"
|
||||
},
|
||||
"gecko_l10n_root": "http://hg.mozilla.org/l10n-central",
|
||||
"gaia": {
|
||||
|
@ -233,6 +233,12 @@ pref("xpinstall.whitelist.add.180", "marketplace.firefox.com");
|
||||
|
||||
pref("lightweightThemes.update.enabled", true);
|
||||
|
||||
// UI tour experience.
|
||||
pref("browser.uitour.enabled", true);
|
||||
pref("browser.uitour.themeOrigin", "https://addons.mozilla.org/%LOCALE%/firefox/themes/");
|
||||
pref("browser.uitour.pinnedTabUrl", "https://support.mozilla.org/%LOCALE%/kb/pinned-tabs-keep-favorite-websites-open");
|
||||
pref("browser.uitour.whitelist.add.260", "www.mozilla.org,support.mozilla.org");
|
||||
|
||||
pref("keyword.enabled", true);
|
||||
|
||||
pref("general.useragent.locale", "@AB_CD@");
|
||||
@ -660,6 +666,8 @@ pref("plugins.update.notifyUser", false);
|
||||
|
||||
pref("plugins.click_to_play", true);
|
||||
|
||||
pref("plugins.clickToActivateInfo.url", "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/clicktoplay");
|
||||
|
||||
// let all plugins except Flash default to click-to-play
|
||||
pref("plugin.default.state", 1);
|
||||
pref("plugin.state.flash", 2);
|
||||
|
@ -704,6 +704,9 @@ var gPluginHandler = {
|
||||
else if (pluginInfo.blocklistState != Ci.nsIBlocklistService.STATE_NOT_BLOCKED) {
|
||||
url = Services.blocklist.getPluginBlocklistURL(pluginInfo.pluginTag);
|
||||
}
|
||||
else {
|
||||
url = Services.urlFormatter.formatURLPref("plugins.clickToActivateInfo.url");
|
||||
}
|
||||
pluginInfo.detailsLink = url;
|
||||
|
||||
centerActions.push(pluginInfo);
|
||||
|
@ -22,6 +22,7 @@
|
||||
<window id="main-window"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
onload="gBrowserInit.onLoad()" onunload="gBrowserInit.onUnload()" onclose="return WindowIsClosing();"
|
||||
title="&mainWindow.title;@PRE_RELEASE_SUFFIX@"
|
||||
@ -183,6 +184,22 @@
|
||||
</hbox>
|
||||
</panel>
|
||||
|
||||
<!-- UI tour experience -->
|
||||
<panel id="UITourTooltip"
|
||||
type="arrow"
|
||||
hidden="true"
|
||||
consumeoutsideclicks="false"
|
||||
noautofocus="true"
|
||||
align="start"
|
||||
orient="vertical"
|
||||
role="alert">
|
||||
<label id="UITourTooltipTitle" flex="1"/>
|
||||
<description id="UITourTooltipDescription" flex="1"/>
|
||||
</panel>
|
||||
<html:div id="UITourHighlightContainer" style="position:relative">
|
||||
<html:div id="UITourHighlight"></html:div>
|
||||
</html:div>
|
||||
|
||||
<panel id="socialActivatedNotification"
|
||||
type="arrow"
|
||||
hidden="true"
|
||||
|
@ -15,6 +15,8 @@ XPCOMUtils.defineLazyModuleGetter(this,
|
||||
"InsecurePasswordUtils", "resource://gre/modules/InsecurePasswordUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
|
||||
"resource://gre/modules/PrivateBrowsingUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UITour",
|
||||
"resource:///modules/UITour.jsm");
|
||||
|
||||
// Creates a new nsIURI object.
|
||||
function makeURI(uri, originCharset, baseURI) {
|
||||
@ -49,6 +51,15 @@ if (Services.prefs.getBoolPref("browser.tabs.remote")) {
|
||||
addEventListener("blur", function(event) {
|
||||
LoginManagerContent.onUsernameInput(event);
|
||||
});
|
||||
|
||||
addEventListener("mozUITour", function(event) {
|
||||
if (!Services.prefs.getBoolPref("browser.uitour.enabled"))
|
||||
return;
|
||||
|
||||
let handled = UITour.onPageEvent(event);
|
||||
if (handled)
|
||||
addEventListener("pagehide", UITour);
|
||||
}, false, true);
|
||||
}
|
||||
|
||||
let AboutHomeListener = {
|
||||
|
@ -472,6 +472,7 @@ BrowserGlue.prototype = {
|
||||
ShumwayUtils.init();
|
||||
webrtcUI.init();
|
||||
AboutHome.init();
|
||||
SessionStore.init();
|
||||
|
||||
if (Services.prefs.getBoolPref("browser.tabs.remote"))
|
||||
ContentClick.init();
|
||||
@ -612,7 +613,6 @@ BrowserGlue.prototype = {
|
||||
}
|
||||
#endif
|
||||
|
||||
SessionStore.init(aWindow);
|
||||
this._trackSlowStartup();
|
||||
|
||||
// Offer to reset a user's profile if it hasn't been used for 60 days.
|
||||
|
@ -156,8 +156,8 @@ this.SessionStore = {
|
||||
SessionStoreInternal.canRestoreLastSession = val;
|
||||
},
|
||||
|
||||
init: function ss_init(aWindow) {
|
||||
SessionStoreInternal.init(aWindow);
|
||||
init: function ss_init() {
|
||||
SessionStoreInternal.init();
|
||||
},
|
||||
|
||||
getBrowserState: function ss_getBrowserState() {
|
||||
@ -368,15 +368,11 @@ let SessionStoreInternal = {
|
||||
/**
|
||||
* Initialize the sessionstore service.
|
||||
*/
|
||||
init: function (aWindow) {
|
||||
init: function () {
|
||||
if (this._initialized) {
|
||||
throw new Error("SessionStore.init() must only be called once!");
|
||||
}
|
||||
|
||||
if (!aWindow) {
|
||||
throw new Error("SessionStore.init() must be called with a valid window.");
|
||||
}
|
||||
|
||||
this._disabledForMultiProcess = Services.prefs.getBoolPref("browser.tabs.remote");
|
||||
if (this._disabledForMultiProcess) {
|
||||
this._deferredInitialized.resolve();
|
||||
@ -390,20 +386,6 @@ let SessionStoreInternal = {
|
||||
|
||||
this._initPrefs();
|
||||
this._initialized = true;
|
||||
|
||||
// Wait until nsISessionStartup has finished reading the session data.
|
||||
gSessionStartup.onceInitialized.then(() => {
|
||||
// Parse session data and start restoring.
|
||||
let initialState = this.initSession();
|
||||
|
||||
// Start tracking the given (initial) browser window.
|
||||
if (!aWindow.closed) {
|
||||
this.onLoad(aWindow, initialState);
|
||||
}
|
||||
|
||||
// Let everyone know we're done.
|
||||
this._deferredInitialized.resolve();
|
||||
}, Cu.reportError);
|
||||
},
|
||||
|
||||
initSession: function ssi_initSession() {
|
||||
@ -489,7 +471,6 @@ let SessionStoreInternal = {
|
||||
this._prefBranch.setBoolPref("sessionstore.resume_session_once", false);
|
||||
|
||||
this._performUpgradeBackup();
|
||||
this._sessionInitialized = true;
|
||||
|
||||
return state;
|
||||
},
|
||||
@ -876,7 +857,34 @@ let SessionStoreInternal = {
|
||||
onOpen: function ssi_onOpen(aWindow) {
|
||||
let onload = () => {
|
||||
aWindow.removeEventListener("load", onload);
|
||||
this.onLoad(aWindow);
|
||||
|
||||
if (this._sessionInitialized) {
|
||||
this.onLoad(aWindow);
|
||||
return;
|
||||
}
|
||||
|
||||
// We can't call this.onLoad since initialization
|
||||
// hasn't completed, so we'll wait until it is done.
|
||||
// Even if additional windows are opened and wait
|
||||
// for initialization as well, the first opened
|
||||
// window should execute first, and this.onLoad
|
||||
// will be called with the initialState.
|
||||
gSessionStartup.onceInitialized.then(() => {
|
||||
if (aWindow.closed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._sessionInitialized) {
|
||||
this.onLoad(aWindow);
|
||||
} else {
|
||||
let initialState = this.initSession();
|
||||
this._sessionInitialized = true;
|
||||
this.onLoad(aWindow, initialState);
|
||||
|
||||
// Let everyone know we're done.
|
||||
this._deferredInitialized.resolve();
|
||||
}
|
||||
}, Cu.reportError);
|
||||
};
|
||||
|
||||
aWindow.addEventListener("load", onload);
|
||||
|
@ -1537,6 +1537,7 @@ NetworkDetailsView.prototype = {
|
||||
}));
|
||||
this._json = new VariablesView($("#response-content-json"),
|
||||
Heritage.extend(GENERIC_VARIABLES_VIEW_SETTINGS, {
|
||||
onlyEnumVisible: true,
|
||||
searchPlaceholder: L10N.getStr("jsonFilterText")
|
||||
}));
|
||||
VariablesViewController.attach(this._json);
|
||||
@ -1874,7 +1875,7 @@ NetworkDetailsView.prototype = {
|
||||
let sanitizedJSON = aString.replace(jsonpRegex, "");
|
||||
let callbackPadding = aString.match(jsonpRegex);
|
||||
|
||||
// Make sure this is an valid JSON object first. If so, nicely display
|
||||
// Make sure this is a valid JSON object first. If so, nicely display
|
||||
// the parsing results in a variables view. Otherwise, simply show
|
||||
// the contents as plain text.
|
||||
try {
|
||||
|
@ -610,66 +610,23 @@ var Scratchpad = {
|
||||
deferred.resolve(aError);
|
||||
}
|
||||
else {
|
||||
let reject = aReason => deferred.reject(aReason);
|
||||
let objectClient = new ObjectClient(this.debuggerClient, aError);
|
||||
|
||||
// Because properties on Error objects are lazily added, this roundabout
|
||||
// way of getting all the properties is required, rather than simply
|
||||
// using getPrototypeAndProperties. See bug 724768.
|
||||
let names = ["message", "stack", "fileName", "lineNumber"];
|
||||
let promises = names.map(aName => {
|
||||
let deferred = promise.defer();
|
||||
|
||||
objectClient.getProperty(aName, aResponse => {
|
||||
if (aResponse.error) {
|
||||
deferred.reject(aResponse);
|
||||
}
|
||||
else {
|
||||
deferred.resolve({
|
||||
name: aName,
|
||||
descriptor: aResponse.descriptor
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
});
|
||||
|
||||
{
|
||||
// We also need to use getPrototypeAndProperties to retrieve any
|
||||
// safeGetterValues in case this is a DOM error.
|
||||
let deferred = promise.defer();
|
||||
objectClient.getPrototypeAndProperties(aResponse => {
|
||||
if (aResponse.error) {
|
||||
deferred.reject(aResponse);
|
||||
}
|
||||
else {
|
||||
deferred.resolve(aResponse);
|
||||
}
|
||||
});
|
||||
promises.push(deferred.promise);
|
||||
}
|
||||
|
||||
promise.all(promises).then(aProperties => {
|
||||
let error = {};
|
||||
let safeGetters;
|
||||
|
||||
// Combine all the property descriptor/getter values into one object.
|
||||
for (let property of aProperties) {
|
||||
if (property.descriptor) {
|
||||
error[property.name] = property.descriptor.value;
|
||||
}
|
||||
else if (property.safeGetterValues) {
|
||||
safeGetters = property.safeGetterValues;
|
||||
}
|
||||
objectClient.getPrototypeAndProperties(aResponse => {
|
||||
if (aResponse.error) {
|
||||
deferred.reject(aResponse);
|
||||
return;
|
||||
}
|
||||
|
||||
if (safeGetters) {
|
||||
for (let key of Object.keys(safeGetters)) {
|
||||
if (!error.hasOwnProperty(key)) {
|
||||
error[key] = safeGetters[key].getterValue;
|
||||
}
|
||||
}
|
||||
let { ownProperties, safeGetterValues } = aResponse;
|
||||
let error = Object.create(null);
|
||||
|
||||
// Combine all the property descriptor/getter values into one object.
|
||||
for (let key of Object.keys(safeGetterValues)) {
|
||||
error[key] = safeGetterValues[key].getterValue;
|
||||
}
|
||||
|
||||
for (let key of Object.keys(ownProperties)) {
|
||||
error[key] = ownProperties[key].value;
|
||||
}
|
||||
|
||||
// Assemble the best possible stack we can given the properties we have.
|
||||
@ -693,23 +650,23 @@ var Scratchpad = {
|
||||
deferred.resolve(error.message + stack);
|
||||
}
|
||||
else {
|
||||
objectClient.getDisplayString(aResult => {
|
||||
if (aResult.error) {
|
||||
deferred.reject(aResult);
|
||||
objectClient.getDisplayString(aResponse => {
|
||||
if (aResponse.error) {
|
||||
deferred.reject(aResponse);
|
||||
}
|
||||
else if (aResult.displayString.type == "null") {
|
||||
deferred.resolve(stack);
|
||||
else if (typeof aResponse.displayString == "string") {
|
||||
deferred.resolve(aResponse.displayString + stack);
|
||||
}
|
||||
else {
|
||||
deferred.resolve(aResult.displayString + stack);
|
||||
deferred.resolve(stack);
|
||||
}
|
||||
}, reject);
|
||||
});
|
||||
}
|
||||
}, reject);
|
||||
});
|
||||
}
|
||||
|
||||
return deferred.promise.then(aMessage => {
|
||||
console.log(aMessage);
|
||||
console.error(aMessage);
|
||||
this.writeAsComment("Exception: " + aMessage);
|
||||
});
|
||||
},
|
||||
|
@ -43,6 +43,7 @@ function testColorUtils() {
|
||||
let color = new colorUtils.CssColor(authored);
|
||||
|
||||
// Check all values.
|
||||
info("Checking values for " + authored);
|
||||
is(color.name, name, "color.name === name");
|
||||
is(color.hex, hex, "color.hex === hex");
|
||||
is(color.hsl, hsl, "color.hsl === hsl");
|
||||
@ -291,14 +292,18 @@ function getTestData() {
|
||||
{authored: "whitesmoke", name: "whitesmoke", hex: "#F5F5F5", hsl: "hsl(0, 0%, 96%)", rgb: "rgb(245, 245, 245)"},
|
||||
{authored: "yellow", name: "yellow", hex: "#FF0", hsl: "hsl(60, 100%, 50%)", rgb: "rgb(255, 255, 0)"},
|
||||
{authored: "yellowgreen", name: "yellowgreen", hex: "#9ACD32", hsl: "hsl(79.742, 61%, 50%)", rgb: "rgb(154, 205, 50)"},
|
||||
{authored: "transparent", name: "transparent", hex: "transparent", hsl: "transparent", rgb: "transparent"},
|
||||
{authored: "rgba(0, 0, 0, 0)", name: "transparent", hex: "transparent", hsl: "transparent", rgb: "transparent"},
|
||||
{authored: "hsla(0, 0%, 0%, 0)", name: "transparent", hex: "transparent", hsl: "transparent", rgb: "transparent"},
|
||||
{authored: "rgba(0, 0, 0, 0)", name: "rgba(0, 0, 0, 0)", hex: "rgba(0, 0, 0, 0)", hsl: "hsla(0, 0%, 0%, 0)", rgb: "rgba(0, 0, 0, 0)"},
|
||||
{authored: "hsla(0, 0%, 0%, 0)", name: "rgba(0, 0, 0, 0)", hex: "rgba(0, 0, 0, 0)", hsl: "hsla(0, 0%, 0%, 0)", rgb: "rgba(0, 0, 0, 0)"},
|
||||
{authored: "rgba(50, 60, 70, 0.5)", name: "rgba(50, 60, 70, 0.5)", hex: "rgba(50, 60, 70, 0.5)", hsl: "hsla(210, 17%, 24%, 0.5)", rgb: "rgba(50, 60, 70, 0.5)"},
|
||||
{authored: "rgba(0, 0, 0, 0.3)", name: "rgba(0, 0, 0, 0.3)", hex: "rgba(0, 0, 0, 0.3)", hsl: "hsla(0, 0%, 0%, 0.3)", rgb: "rgba(0, 0, 0, 0.3)"},
|
||||
{authored: "rgba(255, 255, 255, 0.6)", name: "rgba(255, 255, 255, 0.6)", hex: "rgba(255, 255, 255, 0.6)", hsl: "hsla(0, 0%, 100%, 0.6)", rgb: "rgba(255, 255, 255, 0.6)"},
|
||||
{authored: "rgba(127, 89, 45, 1)", name: "#7F592D", hex: "#7F592D", hsl: "hsl(32.195, 48%, 34%)", rgb: "rgb(127, 89, 45)"},
|
||||
{authored: "hsla(19.304, 56%, 40%, 1)", name: "#9F512C", hex: "#9F512C", hsl: "hsl(19.304, 57%, 40%)", rgb: "rgb(159, 81, 44)"},
|
||||
{authored: "invalidColor", name: "", hex: "", hsl: "", rgb: ""}
|
||||
{authored: "currentcolor", name: "currentcolor", hex: "currentcolor", hsl: "currentcolor", rgb: "currentcolor"},
|
||||
{authored: "inherit", name: "inherit", hex: "inherit", hsl: "inherit", rgb: "inherit"},
|
||||
{authored: "initial", name: "initial", hex: "initial", hsl: "initial", rgb: "initial"},
|
||||
{authored: "invalidColor", name: "", hex: "", hsl: "", rgb: ""},
|
||||
{authored: "transparent", name: "transparent", hex: "transparent", hsl: "transparent", rgb: "transparent"},
|
||||
{authored: "unset", name: "unset", hex: "unset", hsl: "unset", rgb: "unset"}
|
||||
];
|
||||
}
|
||||
|
@ -461,7 +461,7 @@ VariablesViewController.prototype = {
|
||||
scope.expanded = true;
|
||||
scope.locked = true;
|
||||
|
||||
let variable = scope.addItem();
|
||||
let variable = scope.addItem("", { enumerable: true });
|
||||
let expanded;
|
||||
|
||||
if (aOptions.objectActor) {
|
||||
|
426
browser/modules/UITour.jsm
Normal file
426
browser/modules/UITour.jsm
Normal file
@ -0,0 +1,426 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["UITour"];
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager",
|
||||
"resource://gre/modules/LightweightThemeManager.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PermissionsUtils",
|
||||
"resource://gre/modules/PermissionsUtils.jsm");
|
||||
|
||||
|
||||
const UITOUR_PERMISSION = "uitour";
|
||||
const PREF_PERM_BRANCH = "browser.uitour.";
|
||||
|
||||
|
||||
this.UITour = {
|
||||
originTabs: new WeakMap(),
|
||||
pinnedTabs: new WeakMap(),
|
||||
urlbarCapture: new WeakMap(),
|
||||
|
||||
highlightEffects: ["wobble", "zoom", "color"],
|
||||
targets: new Map([
|
||||
["backforward", "#unified-back-forward-button"],
|
||||
["appmenu", "#appmenu-button"],
|
||||
["home", "#home-button"],
|
||||
["urlbar", "#urlbar"],
|
||||
["bookmarks", "#bookmarks-menu-button"],
|
||||
["search", "#searchbar"],
|
||||
["searchprovider", function UITour_target_searchprovider(aDocument) {
|
||||
let searchbar = aDocument.getElementById("searchbar");
|
||||
return aDocument.getAnonymousElementByAttribute(searchbar,
|
||||
"anonid",
|
||||
"searchbar-engine-button");
|
||||
}],
|
||||
]),
|
||||
|
||||
onPageEvent: function(aEvent) {
|
||||
let contentDocument = null;
|
||||
if (aEvent.target instanceof Ci.nsIDOMHTMLDocument)
|
||||
contentDocument = aEvent.target;
|
||||
else if (aEvent.target instanceof Ci.nsIDOMHTMLElement)
|
||||
contentDocument = aEvent.target.ownerDocument;
|
||||
else
|
||||
return false;
|
||||
|
||||
// Ignore events if they're not from a trusted origin.
|
||||
if (!this.ensureTrustedOrigin(contentDocument))
|
||||
return false;
|
||||
|
||||
if (typeof aEvent.detail != "object")
|
||||
return false;
|
||||
|
||||
let action = aEvent.detail.action;
|
||||
if (typeof action != "string" || !action)
|
||||
return false;
|
||||
|
||||
let data = aEvent.detail.data;
|
||||
if (typeof data != "object")
|
||||
return false;
|
||||
|
||||
let window = this.getChromeWindow(contentDocument);
|
||||
|
||||
switch (action) {
|
||||
case "showHighlight": {
|
||||
let target = this.getTarget(window, data.target);
|
||||
if (!target)
|
||||
return false;
|
||||
this.showHighlight(target);
|
||||
break;
|
||||
}
|
||||
|
||||
case "hideHighlight": {
|
||||
this.hideHighlight(window);
|
||||
break;
|
||||
}
|
||||
|
||||
case "showInfo": {
|
||||
let target = this.getTarget(window, data.target, true);
|
||||
if (!target)
|
||||
return false;
|
||||
this.showInfo(target, data.title, data.text);
|
||||
break;
|
||||
}
|
||||
|
||||
case "hideInfo": {
|
||||
this.hideInfo(window);
|
||||
break;
|
||||
}
|
||||
|
||||
case "previewTheme": {
|
||||
this.previewTheme(data.theme);
|
||||
break;
|
||||
}
|
||||
|
||||
case "resetTheme": {
|
||||
this.resetTheme();
|
||||
break;
|
||||
}
|
||||
|
||||
case "addPinnedTab": {
|
||||
this.ensurePinnedTab(window, true);
|
||||
break;
|
||||
}
|
||||
|
||||
case "removePinnedTab": {
|
||||
this.removePinnedTab(window);
|
||||
break;
|
||||
}
|
||||
|
||||
case "showMenu": {
|
||||
this.showMenu(window, data.name);
|
||||
break;
|
||||
}
|
||||
|
||||
case "startUrlbarCapture": {
|
||||
if (typeof data.text != "string" || !data.text ||
|
||||
typeof data.url != "string" || !data.url) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let uri = null;
|
||||
try {
|
||||
uri = Services.io.newURI(data.url, null, null);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let secman = Services.scriptSecurityManager;
|
||||
let principal = contentDocument.nodePrincipal;
|
||||
let flags = secman.DISALLOW_INHERIT_PRINCIPAL;
|
||||
try {
|
||||
secman.checkLoadURIWithPrincipal(principal, uri, flags);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.startUrlbarCapture(window, data.text, data.url);
|
||||
break;
|
||||
}
|
||||
|
||||
case "endUrlbarCapture": {
|
||||
this.endUrlbarCapture(window);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let tab = window.gBrowser._getTabForContentWindow(contentDocument.defaultView);
|
||||
if (!this.originTabs.has(window))
|
||||
this.originTabs.set(window, new Set());
|
||||
this.originTabs.get(window).add(tab);
|
||||
|
||||
tab.addEventListener("TabClose", this);
|
||||
window.gBrowser.tabContainer.addEventListener("TabSelect", this);
|
||||
window.addEventListener("SSWindowClosing", this);
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
handleEvent: function(aEvent) {
|
||||
switch (aEvent.type) {
|
||||
case "pagehide": {
|
||||
let window = this.getChromeWindow(aEvent.target);
|
||||
this.teardownTour(window);
|
||||
break;
|
||||
}
|
||||
|
||||
case "TabClose": {
|
||||
let window = aEvent.target.ownerDocument.defaultView;
|
||||
this.teardownTour(window);
|
||||
break;
|
||||
}
|
||||
|
||||
case "TabSelect": {
|
||||
let window = aEvent.target.ownerDocument.defaultView;
|
||||
let pinnedTab = this.pinnedTabs.get(window);
|
||||
if (pinnedTab && pinnedTab.tab == window.gBrowser.selectedTab)
|
||||
break;
|
||||
let originTabs = this.originTabs.get(window);
|
||||
if (originTabs && originTabs.has(window.gBrowser.selectedTab))
|
||||
break;
|
||||
|
||||
this.teardownTour(window);
|
||||
break;
|
||||
}
|
||||
|
||||
case "SSWindowClosing": {
|
||||
let window = aEvent.target;
|
||||
this.teardownTour(window, true);
|
||||
break;
|
||||
}
|
||||
|
||||
case "input": {
|
||||
if (aEvent.target.id == "urlbar") {
|
||||
let window = aEvent.target.ownerDocument.defaultView;
|
||||
this.handleUrlbarInput(window);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
teardownTour: function(aWindow, aWindowClosing = false) {
|
||||
aWindow.gBrowser.tabContainer.removeEventListener("TabSelect", this);
|
||||
aWindow.removeEventListener("SSWindowClosing", this);
|
||||
|
||||
let originTabs = this.originTabs.get(aWindow);
|
||||
if (originTabs) {
|
||||
for (let tab of originTabs)
|
||||
tab.removeEventListener("TabClose", this);
|
||||
}
|
||||
this.originTabs.delete(aWindow);
|
||||
|
||||
if (!aWindowClosing) {
|
||||
this.hideHighlight(aWindow);
|
||||
this.hideInfo(aWindow);
|
||||
}
|
||||
|
||||
this.endUrlbarCapture(aWindow);
|
||||
this.removePinnedTab(aWindow);
|
||||
this.resetTheme();
|
||||
},
|
||||
|
||||
getChromeWindow: function(aContentDocument) {
|
||||
return aContentDocument.defaultView
|
||||
.window
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShellTreeItem)
|
||||
.rootTreeItem
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindow)
|
||||
.wrappedJSObject;
|
||||
},
|
||||
|
||||
importPermissions: function() {
|
||||
try {
|
||||
PermissionsUtils.importFromPrefs(PREF_PERM_BRANCH, UITOUR_PERMISSION);
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
},
|
||||
|
||||
ensureTrustedOrigin: function(aDocument) {
|
||||
if (aDocument.defaultView.top != aDocument.defaultView)
|
||||
return false;
|
||||
|
||||
let uri = aDocument.documentURIObject;
|
||||
|
||||
if (uri.schemeIs("chrome"))
|
||||
return true;
|
||||
|
||||
if (!uri.schemeIs("https"))
|
||||
return false;
|
||||
|
||||
this.importPermissions();
|
||||
let permission = Services.perms.testPermission(uri, UITOUR_PERMISSION);
|
||||
return permission == Services.perms.ALLOW_ACTION;
|
||||
},
|
||||
|
||||
getTarget: function(aWindow, aTargetName, aSticky = false) {
|
||||
if (typeof aTargetName != "string" || !aTargetName)
|
||||
return null;
|
||||
|
||||
if (aTargetName == "pinnedtab")
|
||||
return this.ensurePinnedTab(aWindow, aSticky);
|
||||
|
||||
let targetQuery = this.targets.get(aTargetName);
|
||||
if (!targetQuery)
|
||||
return null;
|
||||
|
||||
if (typeof targetQuery == "function")
|
||||
return targetQuery(aWindow.document);
|
||||
|
||||
return aWindow.document.querySelector(targetQuery);
|
||||
},
|
||||
|
||||
previewTheme: function(aTheme) {
|
||||
let origin = Services.prefs.getCharPref("browser.uitour.themeOrigin");
|
||||
let data = LightweightThemeManager.parseTheme(aTheme, origin);
|
||||
if (data)
|
||||
LightweightThemeManager.previewTheme(data);
|
||||
},
|
||||
|
||||
resetTheme: function() {
|
||||
LightweightThemeManager.resetPreview();
|
||||
},
|
||||
|
||||
ensurePinnedTab: function(aWindow, aSticky = false) {
|
||||
let tabInfo = this.pinnedTabs.get(aWindow);
|
||||
|
||||
if (tabInfo) {
|
||||
tabInfo.sticky = tabInfo.sticky || aSticky;
|
||||
} else {
|
||||
let url = Services.urlFormatter.formatURLPref("browser.uitour.pinnedTabUrl");
|
||||
|
||||
let tab = aWindow.gBrowser.addTab(url);
|
||||
aWindow.gBrowser.pinTab(tab);
|
||||
tab.addEventListener("TabClose", () => {
|
||||
this.pinnedTabs.delete(aWindow);
|
||||
});
|
||||
|
||||
tabInfo = {
|
||||
tab: tab,
|
||||
sticky: aSticky
|
||||
};
|
||||
this.pinnedTabs.set(aWindow, tabInfo);
|
||||
}
|
||||
|
||||
return tabInfo.tab;
|
||||
},
|
||||
|
||||
removePinnedTab: function(aWindow) {
|
||||
let tabInfo = this.pinnedTabs.get(aWindow);
|
||||
if (tabInfo)
|
||||
aWindow.gBrowser.removeTab(tabInfo.tab);
|
||||
},
|
||||
|
||||
showHighlight: function(aTarget) {
|
||||
let highlighter = aTarget.ownerDocument.getElementById("UITourHighlight");
|
||||
|
||||
let randomEffect = Math.floor(Math.random() * this.highlightEffects.length);
|
||||
if (randomEffect == this.highlightEffects.length)
|
||||
randomEffect--; // On the order of 1 in 2^62 chance of this happening.
|
||||
highlighter.setAttribute("active", this.highlightEffects[randomEffect]);
|
||||
|
||||
let targetRect = aTarget.getBoundingClientRect();
|
||||
|
||||
highlighter.style.height = targetRect.height + "px";
|
||||
highlighter.style.width = targetRect.width + "px";
|
||||
|
||||
let highlighterRect = highlighter.getBoundingClientRect();
|
||||
|
||||
let top = targetRect.top + (targetRect.height / 2) - (highlighterRect.height / 2);
|
||||
highlighter.style.top = top + "px";
|
||||
let left = targetRect.left + (targetRect.width / 2) - (highlighterRect.width / 2);
|
||||
highlighter.style.left = left + "px";
|
||||
},
|
||||
|
||||
hideHighlight: function(aWindow) {
|
||||
let tabData = this.pinnedTabs.get(aWindow);
|
||||
if (tabData && !tabData.sticky)
|
||||
this.removePinnedTab(aWindow);
|
||||
|
||||
let highlighter = aWindow.document.getElementById("UITourHighlight");
|
||||
highlighter.removeAttribute("active");
|
||||
},
|
||||
|
||||
showInfo: function(aAnchor, aTitle, aDescription) {
|
||||
aAnchor.focus();
|
||||
|
||||
let document = aAnchor.ownerDocument;
|
||||
let tooltip = document.getElementById("UITourTooltip");
|
||||
let tooltipTitle = document.getElementById("UITourTooltipTitle");
|
||||
let tooltipDesc = document.getElementById("UITourTooltipDescription");
|
||||
|
||||
tooltip.hidePopup();
|
||||
|
||||
tooltipTitle.textContent = aTitle;
|
||||
tooltipDesc.textContent = aDescription;
|
||||
|
||||
let alignment = "bottomcenter topright";
|
||||
let anchorRect = aAnchor.getBoundingClientRect();
|
||||
|
||||
tooltip.hidden = false;
|
||||
tooltip.openPopup(aAnchor, alignment);
|
||||
},
|
||||
|
||||
hideInfo: function(aWindow) {
|
||||
let tooltip = aWindow.document.getElementById("UITourTooltip");
|
||||
tooltip.hidePopup();
|
||||
},
|
||||
|
||||
showMenu: function(aWindow, aMenuName) {
|
||||
function openMenuButton(aId) {
|
||||
let menuBtn = aWindow.document.getElementById(aId);
|
||||
if (menuBtn && menuBtn.boxObject)
|
||||
menuBtn.boxObject.QueryInterface(Ci.nsIMenuBoxObject).openMenu(true);
|
||||
}
|
||||
|
||||
if (aMenuName == "appmenu")
|
||||
openMenuButton("appmenu-button");
|
||||
else if (aMenuName == "bookmarks")
|
||||
openMenuButton("bookmarks-menu-button");
|
||||
},
|
||||
|
||||
startUrlbarCapture: function(aWindow, aExpectedText, aUrl) {
|
||||
let urlbar = aWindow.document.getElementById("urlbar");
|
||||
this.urlbarCapture.set(aWindow, {
|
||||
expected: aExpectedText.toLocaleLowerCase(),
|
||||
url: aUrl
|
||||
});
|
||||
urlbar.addEventListener("input", this);
|
||||
},
|
||||
|
||||
endUrlbarCapture: function(aWindow) {
|
||||
let urlbar = aWindow.document.getElementById("urlbar");
|
||||
urlbar.removeEventListener("input", this);
|
||||
this.urlbarCapture.delete(aWindow);
|
||||
},
|
||||
|
||||
handleUrlbarInput: function(aWindow) {
|
||||
if (!this.urlbarCapture.has(aWindow))
|
||||
return;
|
||||
|
||||
let urlbar = aWindow.document.getElementById("urlbar");
|
||||
|
||||
let {expected, url} = this.urlbarCapture.get(aWindow);
|
||||
|
||||
if (urlbar.value.toLocaleLowerCase().localeCompare(expected) != 0)
|
||||
return;
|
||||
|
||||
urlbar.handleRevert();
|
||||
|
||||
let tab = aWindow.gBrowser.addTab(url, {
|
||||
owner: aWindow.gBrowser.selectedTab,
|
||||
relatedToCurrent: true
|
||||
});
|
||||
aWindow.gBrowser.selectedTab = tab;
|
||||
},
|
||||
};
|
@ -15,6 +15,7 @@ EXTRA_JS_MODULES += [
|
||||
'SitePermissions.jsm',
|
||||
'Social.jsm',
|
||||
'TabCrashReporter.jsm',
|
||||
'UITour.jsm',
|
||||
'offlineAppCache.jsm',
|
||||
'openLocationLastURL.jsm',
|
||||
'webappsUI.jsm',
|
||||
|
@ -1,3 +1,5 @@
|
||||
[DEFAULT]
|
||||
|
||||
[browser_NetworkPrioritizer.js]
|
||||
[browser_UITour.js]
|
||||
support-files = uitour.*
|
212
browser/modules/test/browser_UITour.js
Normal file
212
browser/modules/test/browser_UITour.js
Normal file
@ -0,0 +1,212 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
let gTestTab;
|
||||
let gContentAPI;
|
||||
|
||||
Components.utils.import("resource:///modules/UITour.jsm");
|
||||
|
||||
function is_hidden(element) {
|
||||
var style = element.ownerDocument.defaultView.getComputedStyle(element, "");
|
||||
if (style.display == "none")
|
||||
return true;
|
||||
if (style.visibility != "visible")
|
||||
return true;
|
||||
|
||||
// Hiding a parent element will hide all its children
|
||||
if (element.parentNode != element.ownerDocument)
|
||||
return is_hidden(element.parentNode);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function is_element_visible(element, msg) {
|
||||
isnot(element, null, "Element should not be null, when checking visibility");
|
||||
ok(!is_hidden(element), msg);
|
||||
}
|
||||
|
||||
function is_element_hidden(element, msg) {
|
||||
isnot(element, null, "Element should not be null, when checking visibility");
|
||||
ok(is_hidden(element), msg);
|
||||
}
|
||||
|
||||
function loadTestPage(callback, untrustedHost = false) {
|
||||
if (gTestTab)
|
||||
gBrowser.removeTab(gTestTab);
|
||||
|
||||
let url = getRootDirectory(gTestPath) + "uitour.html";
|
||||
if (untrustedHost)
|
||||
url = url.replace("chrome://mochitests/content/", "http://example.com/");
|
||||
|
||||
gTestTab = gBrowser.addTab(url);
|
||||
gBrowser.selectedTab = gTestTab;
|
||||
|
||||
gTestTab.linkedBrowser.addEventListener("load", function onLoad() {
|
||||
gTestTab.linkedBrowser.removeEventListener("load", onLoad);
|
||||
|
||||
let contentWindow = Components.utils.waiveXrays(gTestTab.linkedBrowser.contentDocument.defaultView);
|
||||
gContentAPI = contentWindow.Mozilla.UITour;
|
||||
|
||||
waitForFocus(callback, contentWindow);
|
||||
}, true);
|
||||
}
|
||||
|
||||
function test() {
|
||||
Services.prefs.setBoolPref("browser.uitour.enabled", true);
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
delete window.UITour;
|
||||
delete window.gContentAPI;
|
||||
if (gTestTab)
|
||||
gBrowser.removeTab(gTestTab);
|
||||
delete window.gTestTab;
|
||||
Services.prefs.clearUserPref("browser.uitour.enabled", true);
|
||||
});
|
||||
|
||||
function done() {
|
||||
if (gTestTab)
|
||||
gBrowser.removeTab(gTestTab);
|
||||
gTestTab = null;
|
||||
|
||||
let highlight = document.getElementById("UITourHighlight");
|
||||
is_element_hidden(highlight, "Highlight should be hidden after UITour tab is closed");
|
||||
|
||||
let popup = document.getElementById("UITourTooltip");
|
||||
isnot(["hidding","closed"].indexOf(popup.state), -1, "Popup should be closed/hidding after UITour tab is closed");
|
||||
|
||||
is(UITour.pinnedTabs.get(window), null, "Any pinned tab should be closed after UITour tab is closed");
|
||||
|
||||
executeSoon(nextTest);
|
||||
}
|
||||
|
||||
function nextTest() {
|
||||
if (tests.length == 0) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
let test = tests.shift();
|
||||
|
||||
loadTestPage(function() {
|
||||
test(done);
|
||||
});
|
||||
}
|
||||
nextTest();
|
||||
}
|
||||
|
||||
let tests = [
|
||||
function test_disabled(done) {
|
||||
Services.prefs.setBoolPref("browser.uitour.enabled", false);
|
||||
|
||||
let highlight = document.getElementById("UITourHighlight");
|
||||
is_element_hidden(highlight, "Highlight should initially be hidden");
|
||||
|
||||
gContentAPI.showHighlight("urlbar");
|
||||
is_element_hidden(highlight, "Highlight should not be shown when feature is disabled");
|
||||
|
||||
Services.prefs.setBoolPref("browser.uitour.enabled", true);
|
||||
done();
|
||||
},
|
||||
function test_untrusted_host(done) {
|
||||
loadTestPage(function() {
|
||||
let highlight = document.getElementById("UITourHighlight");
|
||||
is_element_hidden(highlight, "Highlight should initially be hidden");
|
||||
|
||||
gContentAPI.showHighlight("urlbar");
|
||||
is_element_hidden(highlight, "Highlight should not be shown on a untrusted domain");
|
||||
|
||||
done();
|
||||
}, true);
|
||||
},
|
||||
function test_highlight(done) {
|
||||
let highlight = document.getElementById("UITourHighlight");
|
||||
is_element_hidden(highlight, "Highlight should initially be hidden");
|
||||
|
||||
gContentAPI.showHighlight("urlbar");
|
||||
is_element_visible(highlight, "Highlight should be shown after showHighlight()");
|
||||
|
||||
gContentAPI.hideHighlight();
|
||||
is_element_hidden(highlight, "Highlight should be hidden after hideHighlight()");
|
||||
|
||||
gContentAPI.showHighlight("urlbar");
|
||||
is_element_visible(highlight, "Highlight should be shown after showHighlight()");
|
||||
gContentAPI.showHighlight("backforward");
|
||||
is_element_visible(highlight, "Highlight should be shown after showHighlight()");
|
||||
|
||||
done();
|
||||
},
|
||||
function test_info_1(done) {
|
||||
let popup = document.getElementById("UITourTooltip");
|
||||
let title = document.getElementById("UITourTooltipTitle");
|
||||
let desc = document.getElementById("UITourTooltipDescription");
|
||||
popup.addEventListener("popupshown", function onPopupShown() {
|
||||
popup.removeEventListener("popupshown", onPopupShown);
|
||||
is(popup.popupBoxObject.anchorNode, document.getElementById("urlbar"), "Popup should be anchored to the urlbar");
|
||||
is(title.textContent, "test title", "Popup should have correct title");
|
||||
is(desc.textContent, "test text", "Popup should have correct description text");
|
||||
|
||||
popup.addEventListener("popuphidden", function onPopupHidden() {
|
||||
popup.removeEventListener("popuphidden", onPopupHidden);
|
||||
|
||||
popup.addEventListener("popupshown", function onPopupShown() {
|
||||
popup.removeEventListener("popupshown", onPopupShown);
|
||||
done();
|
||||
});
|
||||
|
||||
gContentAPI.showInfo("urlbar", "test title", "test text");
|
||||
|
||||
});
|
||||
gContentAPI.hideInfo();
|
||||
});
|
||||
|
||||
gContentAPI.showInfo("urlbar", "test title", "test text");
|
||||
},
|
||||
function test_info_2(done) {
|
||||
let popup = document.getElementById("UITourTooltip");
|
||||
let title = document.getElementById("UITourTooltipTitle");
|
||||
let desc = document.getElementById("UITourTooltipDescription");
|
||||
popup.addEventListener("popupshown", function onPopupShown() {
|
||||
popup.removeEventListener("popupshown", onPopupShown);
|
||||
is(popup.popupBoxObject.anchorNode, document.getElementById("urlbar"), "Popup should be anchored to the urlbar");
|
||||
is(title.textContent, "urlbar title", "Popup should have correct title");
|
||||
is(desc.textContent, "urlbar text", "Popup should have correct description text");
|
||||
|
||||
gContentAPI.showInfo("search", "search title", "search text");
|
||||
executeSoon(function() {
|
||||
is(popup.popupBoxObject.anchorNode, document.getElementById("searchbar"), "Popup should be anchored to the searchbar");
|
||||
is(title.textContent, "search title", "Popup should have correct title");
|
||||
is(desc.textContent, "search text", "Popup should have correct description text");
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
gContentAPI.showInfo("urlbar", "urlbar title", "urlbar text");
|
||||
},
|
||||
function test_pinnedTab(done) {
|
||||
is(UITour.pinnedTabs.get(window), null, "Should not already have a pinned tab");
|
||||
|
||||
gContentAPI.addPinnedTab();
|
||||
let tabInfo = UITour.pinnedTabs.get(window);
|
||||
isnot(tabInfo, null, "Should have recorded data about a pinned tab after addPinnedTab()");
|
||||
isnot(tabInfo.tab, null, "Should have added a pinned tab after addPinnedTab()");
|
||||
is(tabInfo.tab.pinned, true, "Tab should be marked as pinned");
|
||||
|
||||
let tab = tabInfo.tab;
|
||||
|
||||
gContentAPI.removePinnedTab();
|
||||
isnot(gBrowser.tabs[0], tab, "First tab should not be the pinned tab");
|
||||
let tabInfo = UITour.pinnedTabs.get(window);
|
||||
is(tabInfo, null, "Should not have any data about the removed pinned tab after removePinnedTab()");
|
||||
|
||||
gContentAPI.addPinnedTab();
|
||||
gContentAPI.addPinnedTab();
|
||||
gContentAPI.addPinnedTab();
|
||||
is(gBrowser.tabs[1].pinned, false, "After multiple calls of addPinnedTab, should still only have one pinned tab");
|
||||
|
||||
done();
|
||||
},
|
||||
];
|
15
browser/modules/test/uitour.html
Normal file
15
browser/modules/test/uitour.html
Normal file
@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>UITour test</title>
|
||||
<script type="application/javascript" src="uitour.js">
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>UITour tests</h1>
|
||||
<p>Because Firefox is...</p>
|
||||
<p>Never gonna let you down</p>
|
||||
<p>Never gonna give you up</p>
|
||||
</body>
|
||||
</html>
|
115
browser/modules/test/uitour.js
Normal file
115
browser/modules/test/uitour.js
Normal file
@ -0,0 +1,115 @@
|
||||
/* 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/. */
|
||||
|
||||
// Copied from the proposed JS library for Bedrock (ie, www.mozilla.org).
|
||||
|
||||
// create namespace
|
||||
if (typeof Mozilla == 'undefined') {
|
||||
var Mozilla = {};
|
||||
}
|
||||
|
||||
(function($) {
|
||||
'use strict';
|
||||
|
||||
// create namespace
|
||||
if (typeof Mozilla.UITour == 'undefined') {
|
||||
Mozilla.UITour = {};
|
||||
}
|
||||
|
||||
var themeIntervalId = null;
|
||||
function _stopCyclingThemes() {
|
||||
if (themeIntervalId) {
|
||||
clearInterval(themeIntervalId);
|
||||
themeIntervalId = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function _sendEvent(action, data) {
|
||||
var event = new CustomEvent('mozUITour', {
|
||||
bubbles: true,
|
||||
detail: {
|
||||
action: action,
|
||||
data: data || {}
|
||||
}
|
||||
});
|
||||
console.log("Sending mozUITour event: ", event);
|
||||
document.dispatchEvent(event);
|
||||
}
|
||||
|
||||
Mozilla.UITour.DEFAULT_THEME_CYCLE_DELAY = 10 * 1000;
|
||||
|
||||
Mozilla.UITour.showHighlight = function(target) {
|
||||
_sendEvent('showHighlight', {
|
||||
target: target
|
||||
});
|
||||
};
|
||||
|
||||
Mozilla.UITour.hideHighlight = function() {
|
||||
_sendEvent('hideHighlight');
|
||||
};
|
||||
|
||||
Mozilla.UITour.showInfo = function(target, title, text) {
|
||||
_sendEvent('showInfo', {
|
||||
target: target,
|
||||
title: title,
|
||||
text: text
|
||||
});
|
||||
};
|
||||
|
||||
Mozilla.UITour.hideInfo = function() {
|
||||
_sendEvent('hideInfo');
|
||||
};
|
||||
|
||||
Mozilla.UITour.previewTheme = function(theme) {
|
||||
_stopCyclingThemes();
|
||||
|
||||
_sendEvent('previewTheme', {
|
||||
theme: JSON.stringify(theme)
|
||||
});
|
||||
};
|
||||
|
||||
Mozilla.UITour.resetTheme = function() {
|
||||
_stopCyclingThemes();
|
||||
|
||||
_sendEvent('resetTheme');
|
||||
};
|
||||
|
||||
Mozilla.UITour.cycleThemes = function(themes, delay, callback) {
|
||||
_stopCyclingThemes();
|
||||
|
||||
if (!delay) {
|
||||
delay = Mozilla.UITour.DEFAULT_THEME_CYCLE_DELAY;
|
||||
}
|
||||
|
||||
function nextTheme() {
|
||||
var theme = themes.shift();
|
||||
themes.push(theme);
|
||||
|
||||
_sendEvent('previewTheme', {
|
||||
theme: JSON.stringify(theme),
|
||||
state: true
|
||||
});
|
||||
|
||||
callback(theme);
|
||||
}
|
||||
|
||||
themeIntervalId = setInterval(nextTheme, delay);
|
||||
nextTheme();
|
||||
};
|
||||
|
||||
Mozilla.UITour.addPinnedTab = function() {
|
||||
_sendEvent('addPinnedTab');
|
||||
};
|
||||
|
||||
Mozilla.UITour.removePinnedTab = function() {
|
||||
_sendEvent('removePinnedTab');
|
||||
};
|
||||
|
||||
Mozilla.UITour.showMenu = function(name) {
|
||||
_sendEvent('showMenu', {
|
||||
name: name
|
||||
});
|
||||
};
|
||||
})();
|
@ -2113,6 +2113,90 @@ toolbar[mode="text"] toolbarbutton.chevron > .toolbarbutton-icon {
|
||||
-moz-margin-end: 2px;
|
||||
}
|
||||
|
||||
/* UI Tour */
|
||||
|
||||
@keyframes uitour-wobble {
|
||||
from {
|
||||
transform: rotate(0deg) translateX(2px) rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg) translateX(2px) rotate(-360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes uitour-zoom {
|
||||
from {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
to {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes uitour-color {
|
||||
from {
|
||||
border-color: #5B9CD9;
|
||||
}
|
||||
50% {
|
||||
border-color: #FF0000;
|
||||
}
|
||||
to {
|
||||
border-color: #5B9CD9;
|
||||
}
|
||||
}
|
||||
|
||||
html|div#UITourHighlight {
|
||||
display: none;
|
||||
position: absolute;
|
||||
min-height: 32px;
|
||||
min-width: 32px;
|
||||
display: none;
|
||||
border: 2px #5B9CD9 solid;
|
||||
box-shadow: 0 0 2px #5B9CD9, inset 0 0 1px #5B9CD9;
|
||||
border-radius: 20px;
|
||||
z-index: 10000000000;
|
||||
}
|
||||
|
||||
html|div#UITourHighlight[active] {
|
||||
display: block;
|
||||
animation-delay: 2s;
|
||||
animation-timing-function: linear;
|
||||
animation-iteration-count: infinite;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
html|div#UITourHighlight[active="wobble"] {
|
||||
animation-name: uitour-wobble;
|
||||
animation-duration: 1s;
|
||||
}
|
||||
html|div#UITourHighlight[active="zoom"] {
|
||||
animation-name: uitour-zoom;
|
||||
animation-duration: 1s;
|
||||
}
|
||||
html|div#UITourHighlight[active="color"] {
|
||||
animation-name: uitour-color;
|
||||
animation-duration: 2s;
|
||||
}
|
||||
|
||||
#UITourTooltip {
|
||||
max-width: 20em;
|
||||
}
|
||||
|
||||
#UITourTooltipTitle {
|
||||
font-weight: bold;
|
||||
font-size: 130%;
|
||||
margin: 0 0 5px 0;
|
||||
}
|
||||
|
||||
#UITourTooltipDescription {
|
||||
max-width: 20em;
|
||||
}
|
||||
|
||||
/* Social toolbar item */
|
||||
|
||||
#social-provider-button {
|
||||
-moz-image-region: rect(0, 16px, 16px, 0);
|
||||
list-style-image: url(chrome://browser/skin/social/services-16.png);
|
||||
|
@ -3728,6 +3728,88 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
|
||||
border-radius: 1px;
|
||||
}
|
||||
|
||||
/* UI Tour */
|
||||
|
||||
@keyframes uitour-wobble {
|
||||
from {
|
||||
transform: rotate(0deg) translateX(2px) rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg) translateX(2px) rotate(-360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes uitour-zoom {
|
||||
from {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
to {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes uitour-color {
|
||||
from {
|
||||
border-color: #5B9CD9;
|
||||
}
|
||||
50% {
|
||||
border-color: #FF0000;
|
||||
}
|
||||
to {
|
||||
border-color: #5B9CD9;
|
||||
}
|
||||
}
|
||||
|
||||
html|div#UITourHighlight {
|
||||
display: none;
|
||||
position: absolute;
|
||||
min-height: 32px;
|
||||
min-width: 32px;
|
||||
display: none;
|
||||
border: 2px #5B9CD9 solid;
|
||||
box-shadow: 0 0 2px #5B9CD9, inset 0 0 1px #5B9CD9;
|
||||
border-radius: 20px;
|
||||
z-index: 10000000000;
|
||||
}
|
||||
|
||||
html|div#UITourHighlight[active] {
|
||||
display: block;
|
||||
animation-delay: 2s;
|
||||
animation-timing-function: linear;
|
||||
animation-iteration-count: infinite;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
html|div#UITourHighlight[active="wobble"] {
|
||||
animation-name: uitour-wobble;
|
||||
animation-duration: 1s;
|
||||
}
|
||||
html|div#UITourHighlight[active="zoom"] {
|
||||
animation-name: uitour-zoom;
|
||||
animation-duration: 1s;
|
||||
}
|
||||
html|div#UITourHighlight[active="color"] {
|
||||
animation-name: uitour-color;
|
||||
animation-duration: 2s;
|
||||
}
|
||||
|
||||
#UITourTooltip {
|
||||
max-width: 20em;
|
||||
}
|
||||
|
||||
#UITourTooltipTitle {
|
||||
font-weight: bold;
|
||||
font-size: 130%;
|
||||
margin: 0 0 5px 0;
|
||||
}
|
||||
|
||||
#UITourTooltipDescription {
|
||||
max-width: 20em;
|
||||
}
|
||||
|
||||
/* === social toolbar button === */
|
||||
|
||||
#social-toolbar-item > .toolbarbutton-1 {
|
||||
|
@ -2855,6 +2855,87 @@ toolbarbutton.bookmark-item[dragover="true"][open="true"] {
|
||||
-moz-margin-end: 5px;
|
||||
}
|
||||
|
||||
/* UI Tour */
|
||||
|
||||
@keyframes uitour-wobble {
|
||||
from {
|
||||
transform: rotate(0deg) translateX(2px) rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg) translateX(2px) rotate(-360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes uitour-zoom {
|
||||
from {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
to {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes uitour-color {
|
||||
from {
|
||||
border-color: #5B9CD9;
|
||||
}
|
||||
50% {
|
||||
border-color: #FF0000;
|
||||
}
|
||||
to {
|
||||
border-color: #5B9CD9;
|
||||
}
|
||||
}
|
||||
|
||||
html|div#UITourHighlight {
|
||||
display: none;
|
||||
position: absolute;
|
||||
min-height: 32px;
|
||||
min-width: 32px;
|
||||
display: none;
|
||||
border: 2px #5B9CD9 solid;
|
||||
box-shadow: 0 0 2px #5B9CD9, inset 0 0 1px #5B9CD9;
|
||||
border-radius: 20px;
|
||||
z-index: 10000000000;
|
||||
}
|
||||
|
||||
html|div#UITourHighlight[active] {
|
||||
display: block;
|
||||
animation-delay: 2s;
|
||||
animation-timing-function: linear;
|
||||
animation-iteration-count: infinite;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
html|div#UITourHighlight[active="wobble"] {
|
||||
animation-name: uitour-wobble;
|
||||
animation-duration: 1s;
|
||||
}
|
||||
html|div#UITourHighlight[active="zoom"] {
|
||||
animation-name: uitour-zoom;
|
||||
animation-duration: 1s;
|
||||
}
|
||||
html|div#UITourHighlight[active="color"] {
|
||||
animation-name: uitour-color;
|
||||
animation-duration: 2s;
|
||||
}
|
||||
|
||||
#UITourTooltip {
|
||||
}
|
||||
|
||||
#UITourTooltipTitle {
|
||||
font-weight: bold;
|
||||
font-size: 130%;
|
||||
margin: 0 0 5px 0;
|
||||
}
|
||||
|
||||
#UITourTooltipDescription {
|
||||
max-width: 20em;
|
||||
}
|
||||
|
||||
/* Social toolbar item */
|
||||
|
||||
#social-provider-button {
|
||||
|
11
configure.in
11
configure.in
@ -222,6 +222,7 @@ if test -n "$gonkdir" ; then
|
||||
15)
|
||||
GONK_INCLUDES="-I$gonkdir/frameworks/base/opengl/include -I$gonkdir/frameworks/base/native/include -I$gonkdir/frameworks/base/include -I$gonkdir/frameworks/base/services/camera -I$gonkdir/frameworks/base/include/media/stagefright -I$gonkdir/frameworks/base/include/media/stagefright/openmax -I$gonkdir/frameworks/base/media/libstagefright/rtsp -I$gonkdir/frameworks/base/media/libstagefright/include -I$gonkdir/external/dbus -I$gonkdir/external/bluetooth/bluez/lib -I$gonkdir/dalvik/libnativehelper/include/nativehelper"
|
||||
MOZ_B2G_BT=1
|
||||
MOZ_B2G_BT_BLUEZ=1
|
||||
MOZ_B2G_CAMERA=1
|
||||
MOZ_OMX_DECODER=1
|
||||
AC_SUBST(MOZ_OMX_DECODER)
|
||||
@ -230,9 +231,15 @@ if test -n "$gonkdir" ; then
|
||||
17|18)
|
||||
GONK_INCLUDES="-I$gonkdir/frameworks/native/include -I$gonkdir/frameworks/av/include -I$gonkdir/frameworks/av/include/media -I$gonkdir/frameworks/av/include/camera -I$gonkdir/frameworks/native/include/media/openmax -I$gonkdir/frameworks/av/media/libstagefright/include"
|
||||
if test -d "$gonkdir/external/bluetooth/bluez"; then
|
||||
GONK_INCLUDES+=" -I$gonkdir/external/dbus -I$gonkdir/external/bluetooth/bluez/lib"
|
||||
GONK_INCLUDES="$GONK_INCLUDES -I$gonkdir/external/dbus -I$gonkdir/external/bluetooth/bluez/lib"
|
||||
MOZ_B2G_BT=1
|
||||
MOZ_B2G_BT_BLUEZ=1
|
||||
fi
|
||||
if test -d "$gonkdir/external/bluetooth/bluedroid"; then
|
||||
MOZ_B2G_BT=1
|
||||
MOZ_B2G_BT_BLUEDROID=1
|
||||
fi
|
||||
|
||||
MOZ_B2G_CAMERA=1
|
||||
MOZ_OMX_DECODER=1
|
||||
AC_SUBST(MOZ_OMX_DECODER)
|
||||
@ -7288,6 +7295,8 @@ if test -n "$MOZ_B2G_BT"; then
|
||||
AC_DEFINE(MOZ_B2G_BT)
|
||||
fi
|
||||
AC_SUBST(MOZ_B2G_BT)
|
||||
AC_SUBST(MOZ_B2G_BT_BLUEZ)
|
||||
AC_SUBST(MOZ_B2G_BT_BLUEDROID)
|
||||
|
||||
dnl ========================================================
|
||||
dnl = Enable Pico Speech Synthesis (Gonk usually)
|
||||
|
@ -45,6 +45,7 @@ CSPService::CSPService()
|
||||
|
||||
CSPService::~CSPService()
|
||||
{
|
||||
mAppStatusCache.Clear();
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS2(CSPService, nsIContentPolicy, nsIChannelEventSink)
|
||||
@ -105,6 +106,55 @@ CSPService::ShouldLoad(uint32_t aContentType,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// ----- THIS IS A TEMPORARY FAST PATH FOR CERTIFIED APPS. -----
|
||||
// ----- PLEASE REMOVE ONCE bug 925004 LANDS. -----
|
||||
|
||||
// Cache the app status for this origin.
|
||||
uint16_t status = nsIPrincipal::APP_STATUS_NOT_INSTALLED;
|
||||
nsAutoCString contentOrigin;
|
||||
aContentLocation->GetPrePath(contentOrigin);
|
||||
if (aRequestPrincipal && !mAppStatusCache.Get(contentOrigin, &status)) {
|
||||
aRequestPrincipal->GetAppStatus(&status);
|
||||
mAppStatusCache.Put(contentOrigin, status);
|
||||
}
|
||||
|
||||
if (status == nsIPrincipal::APP_STATUS_CERTIFIED) {
|
||||
// The CSP for certified apps is :
|
||||
// "default-src *; script-src 'self'; object-src 'none'; style-src 'self'"
|
||||
// That means we can optimize for this case by:
|
||||
// - loading only same origin scripts and stylesheets.
|
||||
// - never loading objects.
|
||||
// - accepting everything else.
|
||||
|
||||
switch (aContentType) {
|
||||
case nsIContentPolicy::TYPE_SCRIPT:
|
||||
case nsIContentPolicy::TYPE_STYLESHEET:
|
||||
{
|
||||
nsAutoCString sourceOrigin;
|
||||
aRequestOrigin->GetPrePath(sourceOrigin);
|
||||
if (!sourceOrigin.Equals(contentOrigin)) {
|
||||
*aDecision = nsIContentPolicy::REJECT_SERVER;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case nsIContentPolicy::TYPE_OBJECT:
|
||||
*aDecision = nsIContentPolicy::REJECT_SERVER;
|
||||
break;
|
||||
|
||||
default:
|
||||
*aDecision = nsIContentPolicy::ACCEPT;
|
||||
}
|
||||
|
||||
// Only cache and return if we are successful. If not, we want the error
|
||||
// to be reported, and thus fallback to the slow path.
|
||||
if (*aDecision == nsIContentPolicy::ACCEPT) {
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// ----- END OF TEMPORARY FAST PATH FOR CERTIFIED APPS. -----
|
||||
|
||||
// find the principal of the document that initiated this request and see
|
||||
// if it has a CSP policy object
|
||||
nsCOMPtr<nsINode> node(do_QueryInterface(aRequestContext));
|
||||
|
@ -23,4 +23,7 @@ public:
|
||||
CSPService();
|
||||
virtual ~CSPService();
|
||||
static bool sCSPEnabled;
|
||||
private:
|
||||
// Maps origins to app status.
|
||||
nsDataHashtable<nsCStringHashKey, uint16_t> mAppStatusCache;
|
||||
};
|
||||
|
@ -85,7 +85,7 @@ void VideoFrameContainer::ClearCurrentFrame(bool aResetSize)
|
||||
kungFuDeathGrip = mImageContainer->LockCurrentImage();
|
||||
mImageContainer->UnlockCurrentImage();
|
||||
|
||||
mImageContainer->SetCurrentImage(nullptr);
|
||||
mImageContainer->ClearAllImages();
|
||||
mImageSizeChanged = aResetSize;
|
||||
}
|
||||
|
||||
|
@ -345,6 +345,10 @@ nsresult MediaOmxReader::Seek(int64_t aTarget, int64_t aStartTime, int64_t aEndT
|
||||
{
|
||||
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
|
||||
|
||||
VideoFrameContainer* container = mDecoder->GetVideoFrameContainer();
|
||||
if (container && container->GetImageContainer()) {
|
||||
container->GetImageContainer()->ClearAllImagesExceptFront();
|
||||
}
|
||||
mVideoQueue.Reset();
|
||||
mAudioQueue.Reset();
|
||||
|
||||
|
@ -1716,8 +1716,13 @@ Navigator::HasCameraSupport(JSContext* /* unused */, JSObject* aGlobal)
|
||||
bool
|
||||
Navigator::HasTelephonySupport(JSContext* /* unused */, JSObject* aGlobal)
|
||||
{
|
||||
// First of all, the general pref has to be turned on.
|
||||
bool enabled = false;
|
||||
Preferences::GetBool("dom.telephony.enabled", &enabled);
|
||||
NS_ENSURE_TRUE(enabled, false);
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(aGlobal);
|
||||
return win && Telephony::CheckPermission(win);
|
||||
return win && CheckPermission(win, "telephony");
|
||||
}
|
||||
|
||||
/* static */
|
||||
@ -1725,6 +1730,11 @@ bool
|
||||
Navigator::HasMobileConnectionSupport(JSContext* /* unused */,
|
||||
JSObject* aGlobal)
|
||||
{
|
||||
// First of all, the general pref has to be turned on.
|
||||
bool enabled = false;
|
||||
Preferences::GetBool("dom.mobileconnection.enabled", &enabled);
|
||||
NS_ENSURE_TRUE(enabled, false);
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(aGlobal);
|
||||
return win && (CheckPermission(win, "mobileconnection") ||
|
||||
CheckPermission(win, "mobilenetwork"));
|
||||
@ -1735,6 +1745,11 @@ bool
|
||||
Navigator::HasCellBroadcastSupport(JSContext* /* unused */,
|
||||
JSObject* aGlobal)
|
||||
{
|
||||
// First of all, the general pref has to be turned on.
|
||||
bool enabled = false;
|
||||
Preferences::GetBool("dom.cellbroadcast.enabled", &enabled);
|
||||
NS_ENSURE_TRUE(enabled, false);
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(aGlobal);
|
||||
return win && CheckPermission(win, "cellbroadcast");
|
||||
}
|
||||
@ -1744,6 +1759,11 @@ bool
|
||||
Navigator::HasVoicemailSupport(JSContext* /* unused */,
|
||||
JSObject* aGlobal)
|
||||
{
|
||||
// First of all, the general pref has to be turned on.
|
||||
bool enabled = false;
|
||||
Preferences::GetBool("dom.voicemail.enabled", &enabled);
|
||||
NS_ENSURE_TRUE(enabled, false);
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(aGlobal);
|
||||
return win && CheckPermission(win, "voicemail");
|
||||
}
|
||||
@ -1753,6 +1773,11 @@ bool
|
||||
Navigator::HasIccManagerSupport(JSContext* /* unused */,
|
||||
JSObject* aGlobal)
|
||||
{
|
||||
// First of all, the general pref has to be turned on.
|
||||
bool enabled = false;
|
||||
Preferences::GetBool("dom.icc.enabled", &enabled);
|
||||
NS_ENSURE_TRUE(enabled, false);
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(aGlobal);
|
||||
return win && CheckPermission(win, "mobileconnection");
|
||||
}
|
||||
|
@ -878,6 +878,75 @@ BluetoothAdapter::IsScoConnected(ErrorResult& aRv)
|
||||
return request.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<DOMRequest>
|
||||
BluetoothAdapter::AnswerWaitingCall(ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> win = GetOwner();
|
||||
if (!win) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<DOMRequest> request = new DOMRequest(win);
|
||||
nsRefPtr<BluetoothVoidReplyRunnable> results =
|
||||
new BluetoothVoidReplyRunnable(request);
|
||||
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
if (!bs) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
bs->AnswerWaitingCall(results);
|
||||
|
||||
return request.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<DOMRequest>
|
||||
BluetoothAdapter::IgnoreWaitingCall(ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> win = GetOwner();
|
||||
if (!win) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<DOMRequest> request = new DOMRequest(win);
|
||||
nsRefPtr<BluetoothVoidReplyRunnable> results =
|
||||
new BluetoothVoidReplyRunnable(request);
|
||||
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
if (!bs) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
bs->IgnoreWaitingCall(results);
|
||||
|
||||
return request.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<DOMRequest>
|
||||
BluetoothAdapter::ToggleCalls(ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> win = GetOwner();
|
||||
if (!win) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<DOMRequest> request = new DOMRequest(win);
|
||||
nsRefPtr<BluetoothVoidReplyRunnable> results =
|
||||
new BluetoothVoidReplyRunnable(request);
|
||||
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
if (!bs) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
bs->ToggleCalls(results);
|
||||
|
||||
return request.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<DOMRequest>
|
||||
BluetoothAdapter::SendMediaMetaData(const MediaMetaData& aMediaMetaData, ErrorResult& aRv)
|
||||
{
|
||||
|
@ -137,6 +137,10 @@ public:
|
||||
already_AddRefed<DOMRequest> DisconnectSco(ErrorResult& aRv);
|
||||
already_AddRefed<DOMRequest> IsScoConnected(ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<DOMRequest> AnswerWaitingCall(ErrorResult& aRv);
|
||||
already_AddRefed<DOMRequest> IgnoreWaitingCall(ErrorResult& aRv);
|
||||
already_AddRefed<DOMRequest> ToggleCalls(ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<DOMRequest>
|
||||
SendMediaMetaData(const MediaMetaData& aMediaMetaData, ErrorResult& aRv);
|
||||
already_AddRefed<DOMRequest>
|
||||
|
@ -142,22 +142,6 @@ static CINDItem sCINDItems[] = {
|
||||
{"roam", "0,1", 0, true}
|
||||
};
|
||||
|
||||
class mozilla::dom::bluetooth::Call {
|
||||
public:
|
||||
Call(uint16_t aState = nsITelephonyProvider::CALL_STATE_DISCONNECTED,
|
||||
bool aDirection = false,
|
||||
const nsAString& aNumber = EmptyString(),
|
||||
int aType = TOA_UNKNOWN)
|
||||
: mState(aState), mDirection(aDirection), mNumber(aNumber), mType(aType)
|
||||
{
|
||||
}
|
||||
|
||||
uint16_t mState;
|
||||
bool mDirection; // true: incoming call; false: outgoing call
|
||||
nsString mNumber;
|
||||
int mType;
|
||||
};
|
||||
|
||||
class BluetoothHfpManager::GetVolumeTask : public nsISettingsServiceCallback
|
||||
{
|
||||
public:
|
||||
@ -313,6 +297,32 @@ IsMandatoryIndicator(const CINDType aType) {
|
||||
(aType == CINDType::CALLSETUP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call
|
||||
*/
|
||||
Call::Call()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
void
|
||||
Call::Reset()
|
||||
{
|
||||
mState = nsITelephonyProvider::CALL_STATE_DISCONNECTED;
|
||||
mDirection = false;
|
||||
mNumber.Truncate();
|
||||
mType = TOA_UNKNOWN;
|
||||
}
|
||||
|
||||
bool
|
||||
Call::IsActive()
|
||||
{
|
||||
return (mState == nsITelephonyProvider::CALL_STATE_CONNECTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* BluetoothHfpManager
|
||||
*/
|
||||
BluetoothHfpManager::BluetoothHfpManager()
|
||||
{
|
||||
Reset();
|
||||
@ -326,6 +336,10 @@ BluetoothHfpManager::ResetCallArray()
|
||||
// index from RIL starts at 1.
|
||||
Call call;
|
||||
mCurrentCallArray.AppendElement(call);
|
||||
|
||||
if (mPhoneType == PhoneType::CDMA) {
|
||||
mCdmaSecondCall.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -550,6 +564,10 @@ BluetoothHfpManager::HandleVoiceConnectionChanged()
|
||||
connection->GetVoiceConnectionInfo(getter_AddRefs(voiceInfo));
|
||||
NS_ENSURE_TRUE_VOID(voiceInfo);
|
||||
|
||||
nsString type;
|
||||
voiceInfo->GetType(type);
|
||||
mPhoneType = GetPhoneType(type);
|
||||
|
||||
bool roaming;
|
||||
voiceInfo->GetRoaming(&roaming);
|
||||
UpdateCIND(CINDType::ROAM, roaming);
|
||||
@ -647,10 +665,15 @@ BluetoothHfpManager::ReceiveSocketData(BluetoothSocket* aSocket,
|
||||
// For more information, please refer to 4.34.1 "Bluetooth Defined AT
|
||||
// Capabilities" in Bluetooth hands-free profile 1.6
|
||||
if (msg.Find("AT+BRSF=") != -1) {
|
||||
uint32_t brsf = BRSF_BIT_THREE_WAY_CALLING |
|
||||
BRSF_BIT_ABILITY_TO_REJECT_CALL |
|
||||
uint32_t brsf = BRSF_BIT_ABILITY_TO_REJECT_CALL |
|
||||
BRSF_BIT_ENHANCED_CALL_STATUS;
|
||||
|
||||
// No support for three way calling in CDMA since
|
||||
// CDMA disallows to hang existing call for CHLD=1
|
||||
if (mPhoneType != PhoneType::CDMA) {
|
||||
brsf |= BRSF_BIT_THREE_WAY_CALLING;
|
||||
}
|
||||
|
||||
if (mBSIR) {
|
||||
brsf |= BRSF_BIT_IN_BAND_RING_TONE;
|
||||
}
|
||||
@ -1076,6 +1099,68 @@ BluetoothHfpManager::Disconnect(BluetoothProfileController* aController)
|
||||
mSocket = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothHfpManager::SendCCWA(const nsAString& aNumber, int aType)
|
||||
{
|
||||
if (mCCWA) {
|
||||
nsAutoCString ccwaMsg("+CCWA: \"");
|
||||
ccwaMsg.Append(NS_ConvertUTF16toUTF8(aNumber));
|
||||
ccwaMsg.AppendLiteral("\",");
|
||||
ccwaMsg.AppendInt(aType);
|
||||
SendLine(ccwaMsg.get());
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
BluetoothHfpManager::SendCLCC(const Call& aCall, int aIndex)
|
||||
{
|
||||
if (aCall.mState == nsITelephonyProvider::CALL_STATE_DISCONNECTED) {
|
||||
return true;
|
||||
}
|
||||
|
||||
nsAutoCString message("+CLCC: ");
|
||||
message.AppendInt(aIndex);
|
||||
message.AppendLiteral(",");
|
||||
message.AppendInt(aCall.mDirection);
|
||||
message.AppendLiteral(",");
|
||||
|
||||
int status = 0;
|
||||
switch (aCall.mState) {
|
||||
case nsITelephonyProvider::CALL_STATE_CONNECTED:
|
||||
if (mPhoneType == PhoneType::CDMA && aIndex == 1) {
|
||||
status = (mCdmaSecondCall.IsActive()) ? 1 : 0;
|
||||
}
|
||||
message.AppendInt(status);
|
||||
break;
|
||||
case nsITelephonyProvider::CALL_STATE_HELD:
|
||||
message.AppendInt(1);
|
||||
break;
|
||||
case nsITelephonyProvider::CALL_STATE_DIALING:
|
||||
message.AppendInt(2);
|
||||
break;
|
||||
case nsITelephonyProvider::CALL_STATE_ALERTING:
|
||||
message.AppendInt(3);
|
||||
break;
|
||||
case nsITelephonyProvider::CALL_STATE_INCOMING:
|
||||
if (!FindFirstCall(nsITelephonyProvider::CALL_STATE_CONNECTED)) {
|
||||
message.AppendInt(4);
|
||||
} else {
|
||||
message.AppendInt(5);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
BT_WARNING("Not handling call status for CLCC");
|
||||
break;
|
||||
}
|
||||
|
||||
message.AppendLiteral(",0,0,\"");
|
||||
message.Append(NS_ConvertUTF16toUTF8(aCall.mNumber));
|
||||
message.AppendLiteral("\",");
|
||||
message.AppendInt(aCall.mType);
|
||||
|
||||
return SendLine(message.get());
|
||||
}
|
||||
|
||||
bool
|
||||
BluetoothHfpManager::SendLine(const char* aMessage)
|
||||
{
|
||||
@ -1143,49 +1228,18 @@ BluetoothHfpManager::SendCommand(const char* aCommand, uint32_t aValue)
|
||||
} else if (!strcmp(aCommand, "+CLCC: ")) {
|
||||
bool rv = true;
|
||||
uint32_t callNumbers = mCurrentCallArray.Length();
|
||||
for (uint32_t i = 1; i < callNumbers; i++) {
|
||||
Call& call = mCurrentCallArray[i];
|
||||
if (call.mState == nsITelephonyProvider::CALL_STATE_DISCONNECTED) {
|
||||
continue;
|
||||
}
|
||||
|
||||
message.AssignLiteral("+CLCC: ");
|
||||
message.AppendInt(i);
|
||||
message.AppendLiteral(",");
|
||||
message.AppendInt(call.mDirection);
|
||||
message.AppendLiteral(",");
|
||||
|
||||
switch (call.mState) {
|
||||
case nsITelephonyProvider::CALL_STATE_CONNECTED:
|
||||
message.AppendInt(0);
|
||||
break;
|
||||
case nsITelephonyProvider::CALL_STATE_HELD:
|
||||
message.AppendInt(1);
|
||||
break;
|
||||
case nsITelephonyProvider::CALL_STATE_DIALING:
|
||||
message.AppendInt(2);
|
||||
break;
|
||||
case nsITelephonyProvider::CALL_STATE_ALERTING:
|
||||
message.AppendInt(3);
|
||||
break;
|
||||
case nsITelephonyProvider::CALL_STATE_INCOMING:
|
||||
if (!FindFirstCall(nsITelephonyProvider::CALL_STATE_CONNECTED)) {
|
||||
message.AppendInt(4);
|
||||
} else {
|
||||
message.AppendInt(5);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
BT_WARNING("Not handling call status for CLCC");
|
||||
break;
|
||||
}
|
||||
message.AppendLiteral(",0,0,\"");
|
||||
message.Append(NS_ConvertUTF16toUTF8(call.mNumber));
|
||||
message.AppendLiteral("\",");
|
||||
message.AppendInt(call.mType);
|
||||
|
||||
rv &= SendLine(message.get());
|
||||
uint32_t i;
|
||||
for (i = 1; i < callNumbers; i++) {
|
||||
rv &= SendCLCC(mCurrentCallArray[i], i);
|
||||
}
|
||||
|
||||
if (!mCdmaSecondCall.mNumber.IsEmpty()) {
|
||||
MOZ_ASSERT(mPhoneType == PhoneType::CDMA);
|
||||
MOZ_ASSERT(i == 2);
|
||||
|
||||
rv &= SendCLCC(mCdmaSecondCall, 2);
|
||||
}
|
||||
|
||||
return rv;
|
||||
} else {
|
||||
message.AppendInt(aValue);
|
||||
@ -1276,13 +1330,7 @@ BluetoothHfpManager::HandleCallStateChanged(uint32_t aCallIndex,
|
||||
break;
|
||||
case nsITelephonyProvider::CALL_STATE_INCOMING:
|
||||
if (FindFirstCall(nsITelephonyProvider::CALL_STATE_CONNECTED)) {
|
||||
if (mCCWA) {
|
||||
nsAutoCString ccwaMsg("+CCWA: \"");
|
||||
ccwaMsg.Append(NS_ConvertUTF16toUTF8(aNumber));
|
||||
ccwaMsg.AppendLiteral("\",");
|
||||
ccwaMsg.AppendInt(mCurrentCallArray[aCallIndex].mType);
|
||||
SendLine(ccwaMsg.get());
|
||||
}
|
||||
SendCCWA(aNumber, mCurrentCallArray[aCallIndex].mType);
|
||||
UpdateCIND(CINDType::CALLSETUP, CallSetupState::INCOMING, aSend);
|
||||
} else {
|
||||
// Start sending RING indicator to HF
|
||||
@ -1410,6 +1458,77 @@ BluetoothHfpManager::HandleCallStateChanged(uint32_t aCallIndex,
|
||||
}
|
||||
}
|
||||
|
||||
PhoneType
|
||||
BluetoothHfpManager::GetPhoneType(const nsAString& aType)
|
||||
{
|
||||
// FIXME: Query phone type from RIL after RIL implements new API (bug 912019)
|
||||
if (aType.EqualsLiteral("gsm") || aType.EqualsLiteral("gprs") ||
|
||||
aType.EqualsLiteral("edge") || aType.EqualsLiteral("umts") ||
|
||||
aType.EqualsLiteral("hspa") || aType.EqualsLiteral("hsdpa") ||
|
||||
aType.EqualsLiteral("hsupa") || aType.EqualsLiteral("hspa+")) {
|
||||
return PhoneType::GSM;
|
||||
} else if (aType.EqualsLiteral("is95a") || aType.EqualsLiteral("is95b") ||
|
||||
aType.EqualsLiteral("1xrtt") || aType.EqualsLiteral("evdo0") ||
|
||||
aType.EqualsLiteral("evdoa") || aType.EqualsLiteral("evdob")) {
|
||||
return PhoneType::CDMA;
|
||||
}
|
||||
|
||||
return PhoneType::NONE;
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothHfpManager::UpdateSecondNumber(const nsAString& aNumber)
|
||||
{
|
||||
MOZ_ASSERT(mPhoneType == PhoneType::CDMA);
|
||||
|
||||
// Always regard second call as incoming call since v1.2 RIL
|
||||
// doesn't support outgoing second call in CDMA.
|
||||
mCdmaSecondCall.mDirection = true;
|
||||
|
||||
mCdmaSecondCall.mNumber = aNumber;
|
||||
mCdmaSecondCall.mType = (aNumber[0] == '+') ? TOA_INTERNATIONAL :
|
||||
TOA_UNKNOWN;
|
||||
|
||||
SendCCWA(aNumber, mCdmaSecondCall.mType);
|
||||
UpdateCIND(CINDType::CALLSETUP, CallSetupState::INCOMING, true);
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothHfpManager::AnswerWaitingCall()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mPhoneType == PhoneType::CDMA);
|
||||
|
||||
// Pick up second call. First call is held now.
|
||||
mCdmaSecondCall.mState = nsITelephonyProvider::CALL_STATE_CONNECTED;
|
||||
UpdateCIND(CINDType::CALLSETUP, CallSetupState::NO_CALLSETUP, true);
|
||||
|
||||
sCINDItems[CINDType::CALLHELD].value = CallHeldState::ONHOLD_ACTIVE;
|
||||
SendCommand("+CIEV: ", CINDType::CALLHELD);
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothHfpManager::IgnoreWaitingCall()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mPhoneType == PhoneType::CDMA);
|
||||
|
||||
mCdmaSecondCall.Reset();
|
||||
UpdateCIND(CINDType::CALLSETUP, CallSetupState::NO_CALLSETUP, true);
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothHfpManager::ToggleCalls()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mPhoneType == PhoneType::CDMA);
|
||||
|
||||
// Toggle acitve and held calls
|
||||
mCdmaSecondCall.mState = (mCdmaSecondCall.IsActive()) ?
|
||||
nsITelephonyProvider::CALL_STATE_HELD :
|
||||
nsITelephonyProvider::CALL_STATE_CONNECTED;
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothHfpManager::OnSocketConnectSuccess(BluetoothSocket* aSocket)
|
||||
{
|
||||
|
@ -50,6 +50,24 @@ enum BluetoothCmeError {
|
||||
NETWORK_NOT_ALLOWED = 32
|
||||
};
|
||||
|
||||
enum PhoneType {
|
||||
NONE, // no connection
|
||||
GSM,
|
||||
CDMA
|
||||
};
|
||||
|
||||
class Call {
|
||||
public:
|
||||
Call();
|
||||
void Reset();
|
||||
bool IsActive();
|
||||
|
||||
uint16_t mState;
|
||||
bool mDirection; // true: incoming call; false: outgoing call
|
||||
nsString mNumber;
|
||||
int mType;
|
||||
};
|
||||
|
||||
class BluetoothHfpManager : public BluetoothSocketObserver
|
||||
, public BluetoothProfileManagerBase
|
||||
, public BatteryObserver
|
||||
@ -103,6 +121,12 @@ public:
|
||||
bool IsConnected();
|
||||
bool IsScoConnected();
|
||||
|
||||
// CDMA-specific functions
|
||||
void UpdateSecondNumber(const nsAString& aNumber);
|
||||
void AnswerWaitingCall();
|
||||
void IgnoreWaitingCall();
|
||||
void ToggleCalls();
|
||||
|
||||
private:
|
||||
class CloseScoTask;
|
||||
class GetVolumeTask;
|
||||
@ -125,10 +149,13 @@ private:
|
||||
void ResetCallArray();
|
||||
uint32_t FindFirstCall(uint16_t aState);
|
||||
uint32_t GetNumberOfCalls(uint16_t aState);
|
||||
PhoneType GetPhoneType(const nsAString& aType);
|
||||
|
||||
void NotifyConnectionStatusChanged(const nsAString& aType);
|
||||
void NotifyDialer(const nsAString& aCommand);
|
||||
|
||||
void SendCCWA(const nsAString& aNumber, int aType);
|
||||
bool SendCLCC(const Call& aCall, int aIndex);
|
||||
bool SendCommand(const char* aCommand, uint32_t aValue = 0);
|
||||
bool SendLine(const char* aMessage);
|
||||
void UpdateCIND(uint8_t aType, uint8_t aValue, bool aSend = true);
|
||||
@ -145,6 +172,7 @@ private:
|
||||
bool mCMER;
|
||||
bool mFirstCKPD;
|
||||
int mNetworkSelectionMode;
|
||||
PhoneType mPhoneType;
|
||||
bool mReceiveVgsFlag;
|
||||
bool mDialingRequestProcessed;
|
||||
nsString mDeviceAddress;
|
||||
@ -170,6 +198,9 @@ private:
|
||||
nsRefPtr<BluetoothSocket> mHeadsetSocket;
|
||||
nsRefPtr<BluetoothSocket> mScoSocket;
|
||||
SocketConnectionStatus mScoSocketStatus;
|
||||
|
||||
// CDMA-specific variable
|
||||
Call mCdmaSecondCall;
|
||||
};
|
||||
|
||||
END_BLUETOOTH_NAMESPACE
|
||||
|
@ -214,6 +214,9 @@ TelephonyListener::SupplementaryServiceNotification(int32_t aCallIndex,
|
||||
NS_IMETHODIMP
|
||||
TelephonyListener::NotifyCdmaCallWaiting(const nsAString& aNumber)
|
||||
{
|
||||
BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
|
||||
hfp->UpdateSecondNumber(aNumber);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,9 @@
|
||||
|
||||
#if defined(MOZ_B2G_BT)
|
||||
# if defined(MOZ_BLUETOOTH_GONK)
|
||||
# include "BluetoothGonkService.h"
|
||||
#ifndef MOZ_B2G_BT_BLUEDROID
|
||||
#include "BluetoothGonkService.h"
|
||||
#endif
|
||||
# elif defined(MOZ_BLUETOOTH_DBUS)
|
||||
# include "BluetoothDBusService.h"
|
||||
# else
|
||||
@ -304,9 +306,13 @@ BluetoothService::Create()
|
||||
#endif
|
||||
|
||||
#if defined(MOZ_BLUETOOTH_GONK)
|
||||
#ifndef MOZ_B2G_BT_BLUEDROID
|
||||
return new BluetoothGonkService();
|
||||
#endif
|
||||
#elif defined(MOZ_BLUETOOTH_DBUS)
|
||||
#ifdef MOZ_B2G_BT_BLUEZ
|
||||
return new BluetoothDBusService();
|
||||
#endif
|
||||
#endif
|
||||
BT_WARNING("No platform support for bluetooth!");
|
||||
return nullptr;
|
||||
|
@ -266,6 +266,15 @@ public:
|
||||
virtual void
|
||||
IsScoConnected(BluetoothReplyRunnable* aRunnable) = 0;
|
||||
|
||||
virtual void
|
||||
AnswerWaitingCall(BluetoothReplyRunnable* aRunnable) = 0;
|
||||
|
||||
virtual void
|
||||
IgnoreWaitingCall(BluetoothReplyRunnable* aRunnable) = 0;
|
||||
|
||||
virtual void
|
||||
ToggleCalls(BluetoothReplyRunnable* aRunnable) = 0;
|
||||
|
||||
virtual void
|
||||
SendMetaData(const nsAString& aTitle,
|
||||
const nsAString& aArtist,
|
||||
|
@ -27,11 +27,12 @@
|
||||
#include <errno.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#ifdef MOZ_B2G_BT_BLUEZ
|
||||
#include <bluetooth/bluetooth.h>
|
||||
#include <bluetooth/l2cap.h>
|
||||
#include <bluetooth/rfcomm.h>
|
||||
#include <bluetooth/sco.h>
|
||||
|
||||
#endif
|
||||
#include "BluetoothUnixSocketConnector.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
@ -43,6 +44,7 @@ static const int L2CAP_SO_SNDBUF = 400 * 1024; // 400 KB send buffer
|
||||
static const int L2CAP_SO_RCVBUF = 400 * 1024; // 400 KB receive buffer
|
||||
static const int L2CAP_MAX_MTU = 65000;
|
||||
|
||||
#ifdef MOZ_B2G_BT_BLUEZ
|
||||
static
|
||||
int get_bdaddr(const char *str, bdaddr_t *ba)
|
||||
{
|
||||
@ -62,6 +64,8 @@ void get_bdaddr_as_string(const bdaddr_t *ba, char *str) {
|
||||
b[5], b[4], b[3], b[2], b[1], b[0]);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
BluetoothUnixSocketConnector::BluetoothUnixSocketConnector(
|
||||
BluetoothSocketType aType,
|
||||
int aChannel,
|
||||
@ -76,6 +80,7 @@ BluetoothUnixSocketConnector::BluetoothUnixSocketConnector(
|
||||
bool
|
||||
BluetoothUnixSocketConnector::SetUp(int aFd)
|
||||
{
|
||||
#ifdef MOZ_B2G_BT_BLUEZ
|
||||
int lm = 0;
|
||||
int sndbuf, rcvbuf;
|
||||
|
||||
@ -157,7 +162,7 @@ BluetoothUnixSocketConnector::SetUp(int aFd)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -167,6 +172,7 @@ BluetoothUnixSocketConnector::Create()
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
int fd = -1;
|
||||
|
||||
#ifdef MOZ_B2G_BT_BLUEZ
|
||||
switch (mType) {
|
||||
case BluetoothSocketType::RFCOMM:
|
||||
fd = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
|
||||
@ -193,7 +199,7 @@ BluetoothUnixSocketConnector::Create()
|
||||
BT_WARNING("Could not set up socket!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif
|
||||
return fd;
|
||||
}
|
||||
|
||||
@ -203,6 +209,7 @@ BluetoothUnixSocketConnector::CreateAddr(bool aIsServer,
|
||||
sockaddr_any& aAddr,
|
||||
const char* aAddress)
|
||||
{
|
||||
#ifdef MOZ_B2G_BT_BLUEZ
|
||||
// Set to BDADDR_ANY, if it's not a server, we'll reset.
|
||||
bdaddr_t bd_address_obj = {{0, 0, 0, 0, 0, 0}};
|
||||
|
||||
@ -242,6 +249,7 @@ BluetoothUnixSocketConnector::CreateAddr(bool aIsServer,
|
||||
BT_WARNING("Socket type unknown!");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -249,6 +257,7 @@ void
|
||||
BluetoothUnixSocketConnector::GetSocketAddr(const sockaddr_any& aAddr,
|
||||
nsAString& aAddrStr)
|
||||
{
|
||||
#ifdef MOZ_B2G_BT_BLUEZ
|
||||
char addr[18];
|
||||
switch (mType) {
|
||||
case BluetoothSocketType::RFCOMM:
|
||||
@ -265,4 +274,5 @@ BluetoothUnixSocketConnector::GetSocketAddr(const sockaddr_any& aAddr,
|
||||
MOZ_CRASH("Socket should be either RFCOMM or SCO!");
|
||||
}
|
||||
aAddrStr.AssignASCII(addr);
|
||||
#endif
|
||||
}
|
||||
|
@ -36,6 +36,14 @@ DEFINES += -DMOZ_BLUETOOTH_DBUS
|
||||
endif
|
||||
endif
|
||||
|
||||
ifdef MOZ_B2G_BT_BLUEZ
|
||||
DEFINES += -DMOZ_B2G_BT_BLUEZ
|
||||
endif
|
||||
|
||||
ifdef MOZ_B2G_BT_BLUEDROID
|
||||
DEFINES += -DMOZ_B2G_BT_BLUEDROID
|
||||
endif
|
||||
|
||||
# Add VPATH to LOCAL_INCLUDES so we are going to include the correct backend
|
||||
# subdirectory.
|
||||
LOCAL_INCLUDES += $(VPATH:%=-I%)
|
||||
|
@ -226,6 +226,12 @@ BluetoothParent::RecvPBluetoothRequestConstructor(
|
||||
return actor->DoRequest(aRequest.get_DisconnectScoRequest());
|
||||
case Request::TIsScoConnectedRequest:
|
||||
return actor->DoRequest(aRequest.get_IsScoConnectedRequest());
|
||||
case Request::TAnswerWaitingCallRequest:
|
||||
return actor->DoRequest(aRequest.get_AnswerWaitingCallRequest());
|
||||
case Request::TIgnoreWaitingCallRequest:
|
||||
return actor->DoRequest(aRequest.get_IgnoreWaitingCallRequest());
|
||||
case Request::TToggleCallsRequest:
|
||||
return actor->DoRequest(aRequest.get_ToggleCallsRequest());
|
||||
case Request::TSendMetaDataRequest:
|
||||
return actor->DoRequest(aRequest.get_SendMetaDataRequest());
|
||||
case Request::TSendPlayStatusRequest:
|
||||
@ -575,6 +581,39 @@ BluetoothRequestParent::DoRequest(const IsScoConnectedRequest& aRequest)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BluetoothRequestParent::DoRequest(const AnswerWaitingCallRequest& aRequest)
|
||||
{
|
||||
MOZ_ASSERT(mService);
|
||||
MOZ_ASSERT(mRequestType == Request::TAnswerWaitingCallRequest);
|
||||
|
||||
mService->AnswerWaitingCall(mReplyRunnable.get());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BluetoothRequestParent::DoRequest(const IgnoreWaitingCallRequest& aRequest)
|
||||
{
|
||||
MOZ_ASSERT(mService);
|
||||
MOZ_ASSERT(mRequestType == Request::TAnswerWaitingCallRequest);
|
||||
|
||||
mService->IgnoreWaitingCall(mReplyRunnable.get());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BluetoothRequestParent::DoRequest(const ToggleCallsRequest& aRequest)
|
||||
{
|
||||
MOZ_ASSERT(mService);
|
||||
MOZ_ASSERT(mRequestType == Request::TAnswerWaitingCallRequest);
|
||||
|
||||
mService->ToggleCalls(mReplyRunnable.get());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BluetoothRequestParent::DoRequest(const SendMetaDataRequest& aRequest)
|
||||
{
|
||||
|
@ -190,6 +190,15 @@ protected:
|
||||
bool
|
||||
DoRequest(const IsScoConnectedRequest& aRequest);
|
||||
|
||||
bool
|
||||
DoRequest(const AnswerWaitingCallRequest& aRequest);
|
||||
|
||||
bool
|
||||
DoRequest(const IgnoreWaitingCallRequest& aRequest);
|
||||
|
||||
bool
|
||||
DoRequest(const ToggleCallsRequest& aRequest);
|
||||
|
||||
bool
|
||||
DoRequest(const SendMetaDataRequest& aRequest);
|
||||
|
||||
|
@ -328,6 +328,27 @@ BluetoothServiceChildProcess::IsScoConnected(BluetoothReplyRunnable* aRunnable)
|
||||
SendRequest(aRunnable, IsScoConnectedRequest());
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothServiceChildProcess::AnswerWaitingCall(
|
||||
BluetoothReplyRunnable* aRunnable)
|
||||
{
|
||||
SendRequest(aRunnable, AnswerWaitingCallRequest());
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothServiceChildProcess::IgnoreWaitingCall(
|
||||
BluetoothReplyRunnable* aRunnable)
|
||||
{
|
||||
SendRequest(aRunnable, IgnoreWaitingCallRequest());
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothServiceChildProcess::ToggleCalls(
|
||||
BluetoothReplyRunnable* aRunnable)
|
||||
{
|
||||
SendRequest(aRunnable, ToggleCallsRequest());
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothServiceChildProcess::SendMetaData(const nsAString& aTitle,
|
||||
const nsAString& aArtist,
|
||||
|
@ -149,6 +149,15 @@ public:
|
||||
virtual void
|
||||
IsScoConnected(BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
|
||||
|
||||
virtual void
|
||||
AnswerWaitingCall(BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
|
||||
|
||||
virtual void
|
||||
IgnoreWaitingCall(BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
|
||||
|
||||
virtual void
|
||||
ToggleCalls(BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
|
||||
|
||||
virtual void
|
||||
SendMetaData(const nsAString& aTitle,
|
||||
const nsAString& aArtist,
|
||||
|
@ -134,6 +134,18 @@ struct IsScoConnectedRequest
|
||||
{
|
||||
};
|
||||
|
||||
struct AnswerWaitingCallRequest
|
||||
{
|
||||
};
|
||||
|
||||
struct IgnoreWaitingCallRequest
|
||||
{
|
||||
};
|
||||
|
||||
struct ToggleCallsRequest
|
||||
{
|
||||
};
|
||||
|
||||
struct SendMetaDataRequest
|
||||
{
|
||||
nsString title;
|
||||
@ -175,6 +187,9 @@ union Request
|
||||
ConnectScoRequest;
|
||||
DisconnectScoRequest;
|
||||
IsScoConnectedRequest;
|
||||
AnswerWaitingCallRequest;
|
||||
IgnoreWaitingCallRequest;
|
||||
ToggleCallsRequest;
|
||||
SendMetaDataRequest;
|
||||
SendPlayStatusRequest;
|
||||
};
|
||||
|
@ -2631,6 +2631,39 @@ BluetoothDBusService::IsConnected(const uint16_t aServiceUuid)
|
||||
return profile->IsConnected();
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothDBusService::AnswerWaitingCall(BluetoothReplyRunnable* aRunnable)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
|
||||
hfp->AnswerWaitingCall();
|
||||
|
||||
DispatchBluetoothReply(aRunnable, BluetoothValue(true), EmptyString());
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothDBusService::IgnoreWaitingCall(BluetoothReplyRunnable* aRunnable)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
|
||||
hfp->IgnoreWaitingCall();
|
||||
|
||||
DispatchBluetoothReply(aRunnable, BluetoothValue(true), EmptyString());
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothDBusService::ToggleCalls(BluetoothReplyRunnable* aRunnable)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
|
||||
hfp->ToggleCalls();
|
||||
|
||||
DispatchBluetoothReply(aRunnable, BluetoothValue(true), EmptyString());
|
||||
}
|
||||
|
||||
class OnUpdateSdpRecordsRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
|
@ -24,6 +24,9 @@ BEGIN_BLUETOOTH_NAMESPACE
|
||||
class BluetoothDBusService : public BluetoothService
|
||||
{
|
||||
public:
|
||||
BluetoothDBusService();
|
||||
~BluetoothDBusService();
|
||||
|
||||
bool IsReady();
|
||||
|
||||
virtual nsresult StartInternal() MOZ_OVERRIDE;
|
||||
@ -132,6 +135,15 @@ public:
|
||||
virtual void
|
||||
IsScoConnected(BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
|
||||
|
||||
virtual void
|
||||
AnswerWaitingCall(BluetoothReplyRunnable* aRunnable);
|
||||
|
||||
virtual void
|
||||
IgnoreWaitingCall(BluetoothReplyRunnable* aRunnable);
|
||||
|
||||
virtual void
|
||||
ToggleCalls(BluetoothReplyRunnable* aRunnable);
|
||||
|
||||
virtual void
|
||||
SendMetaData(const nsAString& aTitle,
|
||||
const nsAString& aArtist,
|
||||
@ -159,11 +171,6 @@ public:
|
||||
virtual nsresult
|
||||
SendInputMessage(const nsAString& aDeviceAddresses,
|
||||
const nsAString& aMessage) MOZ_OVERRIDE;
|
||||
|
||||
protected:
|
||||
BluetoothDBusService();
|
||||
~BluetoothDBusService();
|
||||
|
||||
private:
|
||||
/**
|
||||
* For DBus Control method of "UpdateNotification", event id should be
|
||||
|
@ -52,7 +52,7 @@ if CONFIG['MOZ_B2G_BT']:
|
||||
'BluetoothRilListener.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk' and CONFIG['MOZ_B2G_BT_BLUEZ']:
|
||||
CPP_SOURCES += [
|
||||
'linux/BluetoothDBusService.cpp',
|
||||
'gonk/BluetoothGonkService.cpp',
|
||||
|
@ -180,7 +180,10 @@ function validateArrayField(data, createCb) {
|
||||
data = Array.isArray(data) ? data : [data];
|
||||
let filtered = [];
|
||||
for (let i = 0, n = data.length; i < n; ++i) {
|
||||
filtered.push(createCb(data[i]));
|
||||
let obj = data[i];
|
||||
if (obj && isVanillaObj(obj)) {
|
||||
filtered.push(createCb(obj));
|
||||
}
|
||||
}
|
||||
if (filtered.length === 0) {
|
||||
return undefined;
|
||||
|
@ -189,9 +189,15 @@ this.Keyboard = {
|
||||
|
||||
let browser = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
|
||||
// Chrome event, used also to render value selectors; that's why we need
|
||||
// the info about choices / min / max here as well...
|
||||
browser.shell.sendChromeEvent({
|
||||
type: 'inputmethod-contextchange',
|
||||
inputType: msg.data.type
|
||||
inputType: msg.data.type,
|
||||
value: msg.data.value,
|
||||
choices: JSON.stringify(msg.data.choices),
|
||||
min: msg.data.min,
|
||||
max: msg.data.max
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -42,7 +42,7 @@ interface nsIRilMobileMessageDatabaseService : nsIMobileMessageDatabaseService
|
||||
* - |delivery| DOMString: the delivery state of received message
|
||||
* - |deliveryStatus| DOMString Array: the delivery status of received message
|
||||
* - |receivers| DOMString Array: the phone numbers of receivers
|
||||
* - |msisdn| DOMString: [optional] my own phone number.
|
||||
* - |phoneNumber| DOMString: [optional] my own phone number.
|
||||
* - |transactionId| DOMString: the transaction ID from MMS PDU header.
|
||||
*
|
||||
* Note: |deliveryStatus| should only contain single string to specify
|
||||
|
@ -1232,9 +1232,24 @@ MmsService.prototype = {
|
||||
return config >= CONFIG_SEND_REPORT_DEFAULT_YES;
|
||||
},
|
||||
|
||||
getMsisdn: function getMsisdn() {
|
||||
/**
|
||||
* Get phone number from iccInfo.
|
||||
*
|
||||
* If the icc card is gsm card, the phone number is in msisdn.
|
||||
* @see nsIDOMMozGsmIccInfo
|
||||
*
|
||||
* Otherwise, the phone number is in mdn.
|
||||
* @see nsIDOMMozCdmaIccInfo
|
||||
*/
|
||||
getPhoneNumber: function getPhoneNumber() {
|
||||
let iccInfo = gRadioInterface.rilContext.iccInfo;
|
||||
let number = iccInfo ? iccInfo.msisdn : null;
|
||||
|
||||
if (!iccInfo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let number = (iccInfo instanceof Ci.nsIDOMMozGsmIccInfo)
|
||||
? iccInfo.msisdn : iccInfo.mdn;
|
||||
|
||||
// Workaround an xpconnect issue with undefined string objects.
|
||||
// See bug 808220
|
||||
@ -1285,7 +1300,7 @@ MmsService.prototype = {
|
||||
intermediate.sender = "anonymous";
|
||||
}
|
||||
intermediate.receivers = [];
|
||||
intermediate.msisdn = this.getMsisdn();
|
||||
intermediate.phoneNumber = this.getPhoneNumber();
|
||||
return intermediate;
|
||||
},
|
||||
|
||||
@ -1771,7 +1786,7 @@ MmsService.prototype = {
|
||||
aMessage["type"] = "mms";
|
||||
aMessage["timestamp"] = Date.now();
|
||||
aMessage["receivers"] = receivers;
|
||||
aMessage["sender"] = this.getMsisdn();
|
||||
aMessage["sender"] = this.getPhoneNumber();
|
||||
try {
|
||||
aMessage["deliveryStatusRequested"] =
|
||||
Services.prefs.getBoolPref("dom.mms.requestStatusReport");
|
||||
|
@ -1373,8 +1373,8 @@ MobileMessageDatabaseService.prototype = {
|
||||
if (receivers.length >= 2) {
|
||||
let isSuccess = false;
|
||||
let slicedReceivers = receivers.slice();
|
||||
if (aMessage.msisdn) {
|
||||
let found = slicedReceivers.indexOf(aMessage.msisdn);
|
||||
if (aMessage.phoneNumber) {
|
||||
let found = slicedReceivers.indexOf(aMessage.phoneNumber);
|
||||
if (found !== -1) {
|
||||
isSuccess = true;
|
||||
slicedReceivers.splice(found, 1);
|
||||
@ -1382,9 +1382,10 @@ MobileMessageDatabaseService.prototype = {
|
||||
}
|
||||
|
||||
if (!isSuccess) {
|
||||
// For some SIMs we cannot retrieve the vaild MSISDN (i.e. the user's
|
||||
// own phone number), so we cannot correcly exclude the user's own
|
||||
// number from the receivers, thus wrongly building the thread index.
|
||||
// For some SIMs we cannot retrieve the vaild MSISDN or MDN (i.e. the
|
||||
// user's own phone number), so we cannot correcly exclude the user's
|
||||
// own number from the receivers, thus wrongly building the thread
|
||||
// index.
|
||||
if (DEBUG) debug("Error! Cannot strip out user's own phone number!");
|
||||
}
|
||||
|
||||
|
@ -88,6 +88,8 @@ namespace system {
|
||||
|
||||
class AutoMounter;
|
||||
|
||||
static void SetAutoMounterStatus(int32_t aStatus);
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
inline const char* SwitchStateStr(const SwitchEvent& aEvent)
|
||||
@ -376,6 +378,7 @@ AutoMounter::UpdateState()
|
||||
LOG("UpdateState: umsAvail:%d umsEnabled:%d mode:%d usbCablePluggedIn:%d tryToShare:%d",
|
||||
umsAvail, umsEnabled, mMode, usbCablePluggedIn, tryToShare);
|
||||
|
||||
bool filesOpen = false;
|
||||
VolumeArray::index_type volIndex;
|
||||
VolumeArray::size_type numVolumes = VolumeManager::NumVolumes();
|
||||
for (volIndex = 0; volIndex < numVolumes; volIndex++) {
|
||||
@ -437,6 +440,7 @@ AutoMounter::UpdateState()
|
||||
PostDelayedTask(FROM_HERE,
|
||||
NewRunnableMethod(this, &AutoMounter::UpdateState),
|
||||
5000);
|
||||
filesOpen = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -481,6 +485,14 @@ AutoMounter::UpdateState()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int32_t status = AUTOMOUNTER_STATUS_DISABLED;
|
||||
if (filesOpen) {
|
||||
status = AUTOMOUNTER_STATUS_FILES_OPEN;
|
||||
} else if (enabled) {
|
||||
status = AUTOMOUNTER_STATUS_ENABLED;
|
||||
}
|
||||
SetAutoMounterStatus(status);
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
@ -595,7 +607,7 @@ InitVolumeConfig()
|
||||
return;
|
||||
}
|
||||
while(fgets(line, sizeof(line), fp)) {
|
||||
char *delim = " \t\n";
|
||||
const char *delim = " \t\n";
|
||||
n++;
|
||||
|
||||
if (line[0] == '#')
|
||||
@ -644,6 +656,24 @@ InitAutoMounter()
|
||||
sUsbCableObserver = new UsbCableObserver();
|
||||
}
|
||||
|
||||
int32_t
|
||||
GetAutoMounterStatus()
|
||||
{
|
||||
if (sAutoMounterSetting) {
|
||||
return sAutoMounterSetting->GetStatus();
|
||||
}
|
||||
return AUTOMOUNTER_STATUS_DISABLED;
|
||||
}
|
||||
|
||||
//static
|
||||
void
|
||||
SetAutoMounterStatus(int32_t aStatus)
|
||||
{
|
||||
if (sAutoMounterSetting) {
|
||||
sAutoMounterSetting->SetStatus(aStatus);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SetAutoMounterMode(int32_t aMode)
|
||||
{
|
||||
|
@ -17,6 +17,11 @@ namespace system {
|
||||
#define AUTOMOUNTER_ENABLE 1
|
||||
#define AUTOMOUNTER_DISABLE_WHEN_UNPLUGGED 2
|
||||
|
||||
// Automounter statuses
|
||||
#define AUTOMOUNTER_STATUS_DISABLED 0
|
||||
#define AUTOMOUNTER_STATUS_ENABLED 1
|
||||
#define AUTOMOUNTER_STATUS_FILES_OPEN 2
|
||||
|
||||
/**
|
||||
* Initialize the automounter. This causes some of the phone's
|
||||
* directories to show up on the host when the phone is plugged
|
||||
@ -38,6 +43,12 @@ InitAutoMounter();
|
||||
void
|
||||
SetAutoMounterMode(int32_t aMode);
|
||||
|
||||
/**
|
||||
* Reports the status of the automounter.
|
||||
*/
|
||||
int32_t
|
||||
GetAutoMounterStatus();
|
||||
|
||||
/**
|
||||
* Sets the sharing mode of an individual volume.
|
||||
*
|
||||
|
@ -26,6 +26,7 @@
|
||||
#define ERR(args...) __android_log_print(ANDROID_LOG_ERROR, "AutoMounterSetting" , ## args)
|
||||
|
||||
#define UMS_MODE "ums.mode"
|
||||
#define UMS_STATUS "ums.status"
|
||||
#define UMS_VOLUME_ENABLED_PREFIX "ums.volume."
|
||||
#define UMS_VOLUME_ENABLED_SUFFIX ".enabled"
|
||||
#define MOZSETTINGS_CHANGED "mozsettings-changed"
|
||||
@ -87,7 +88,10 @@ private:
|
||||
NS_IMPL_ISUPPORTS1(CheckVolumeSettingsCallback, nsISettingsServiceCallback)
|
||||
|
||||
AutoMounterSetting::AutoMounterSetting()
|
||||
: mStatus(AUTOMOUNTER_STATUS_DISABLED)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// Setup an observer to watch changes to the setting
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
@ -116,6 +120,7 @@ AutoMounterSetting::AutoMounterSetting()
|
||||
settingsService->CreateLock(getter_AddRefs(lock));
|
||||
nsCOMPtr<nsISettingsServiceCallback> callback = new SettingsServiceCallback();
|
||||
lock->Set(UMS_MODE, INT_TO_JSVAL(AUTOMOUNTER_DISABLE), callback, nullptr);
|
||||
lock->Set(UMS_STATUS, INT_TO_JSVAL(mStatus), nullptr, nullptr);
|
||||
}
|
||||
|
||||
AutoMounterSetting::~AutoMounterSetting()
|
||||
@ -129,6 +134,17 @@ AutoMounterSetting::~AutoMounterSetting()
|
||||
|
||||
NS_IMPL_ISUPPORTS1(AutoMounterSetting, nsIObserver)
|
||||
|
||||
const char *
|
||||
AutoMounterSetting::StatusStr(int32_t aStatus)
|
||||
{
|
||||
switch (aStatus) {
|
||||
case AUTOMOUNTER_STATUS_DISABLED: return "Disabled";
|
||||
case AUTOMOUNTER_STATUS_ENABLED: return "Enabled";
|
||||
case AUTOMOUNTER_STATUS_FILES_OPEN: return "FilesOpen";
|
||||
}
|
||||
return "??? Unknown ???";
|
||||
}
|
||||
|
||||
class CheckVolumeSettingsRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
@ -162,6 +178,39 @@ AutoMounterSetting::CheckVolumeSettings(const nsACString& aVolumeName)
|
||||
NS_DispatchToMainThread(new CheckVolumeSettingsRunnable(aVolumeName));
|
||||
}
|
||||
|
||||
class SetStatusRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
SetStatusRunnable(int32_t aStatus) : mStatus(aStatus) {}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsCOMPtr<nsISettingsService> settingsService =
|
||||
do_GetService("@mozilla.org/settingsService;1");
|
||||
NS_ENSURE_TRUE(settingsService, NS_ERROR_FAILURE);
|
||||
nsCOMPtr<nsISettingsServiceLock> lock;
|
||||
settingsService->CreateLock(getter_AddRefs(lock));
|
||||
lock->Set(UMS_STATUS, INT_TO_JSVAL(mStatus), nullptr, nullptr);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
int32_t mStatus;
|
||||
};
|
||||
|
||||
//static
|
||||
void
|
||||
AutoMounterSetting::SetStatus(int32_t aStatus)
|
||||
{
|
||||
if (aStatus != mStatus) {
|
||||
LOG("Changing status from '%s' to '%s'",
|
||||
StatusStr(mStatus), StatusStr(aStatus));
|
||||
mStatus = aStatus;
|
||||
NS_DispatchToMainThread(new SetStatusRunnable(aStatus));
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AutoMounterSetting::Observe(nsISupports* aSubject,
|
||||
const char* aTopic,
|
||||
|
@ -22,6 +22,13 @@ public:
|
||||
virtual ~AutoMounterSetting();
|
||||
|
||||
static void CheckVolumeSettings(const nsACString& aVolumeName);
|
||||
|
||||
int32_t GetStatus() { return mStatus; }
|
||||
void SetStatus(int32_t aStatus);
|
||||
const char *StatusStr(int32_t aStatus);
|
||||
|
||||
private:
|
||||
int32_t mStatus;
|
||||
};
|
||||
|
||||
} // namespace system
|
||||
|
@ -47,6 +47,10 @@ const RADIOINTERFACE_CID =
|
||||
Components.ID("{6a7c91f0-a2b3-4193-8562-8969296c0b54}");
|
||||
const RILNETWORKINTERFACE_CID =
|
||||
Components.ID("{3bdd52a9-3965-4130-b569-0ac5afed045e}");
|
||||
const GSMICCINFO_CID =
|
||||
Components.ID("{d90c4261-a99d-47bc-8b05-b057bb7e8f8a}");
|
||||
const CDMAICCINFO_CID =
|
||||
Components.ID("{39ba3c08-aacc-46d0-8c04-9b619c387061}");
|
||||
|
||||
const kNetworkInterfaceStateChangedTopic = "network-interface-state-changed";
|
||||
const kSmsReceivedObserverTopic = "sms-received";
|
||||
@ -463,6 +467,52 @@ XPCOMUtils.defineLazyGetter(this, "gMessageManager", function () {
|
||||
};
|
||||
});
|
||||
|
||||
function IccInfo() {}
|
||||
IccInfo.prototype = {
|
||||
iccType: null,
|
||||
iccid: null,
|
||||
mcc: null,
|
||||
mnc: null,
|
||||
spn: null,
|
||||
isDisplayNetworkNameRequired: null,
|
||||
isDisplaySpnRequired: null
|
||||
};
|
||||
|
||||
function GsmIccInfo() {}
|
||||
GsmIccInfo.prototype = {
|
||||
__proto__: IccInfo.prototype,
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMMozGsmIccInfo]),
|
||||
classID: GSMICCINFO_CID,
|
||||
classInfo: XPCOMUtils.generateCI({
|
||||
classID: GSMICCINFO_CID,
|
||||
classDescription: "MozGsmIccInfo",
|
||||
flags: Ci.nsIClassInfo.DOM_OBJECT,
|
||||
interfaces: [Ci.nsIDOMMozGsmIccInfo]
|
||||
}),
|
||||
|
||||
// nsIDOMMozGsmIccInfo
|
||||
|
||||
msisdn: null
|
||||
};
|
||||
|
||||
function CdmaIccInfo() {}
|
||||
CdmaIccInfo.prototype = {
|
||||
__proto__: IccInfo.prototype,
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMMozCdmaIccInfo]),
|
||||
classID: CDMAICCINFO_CID,
|
||||
classInfo: XPCOMUtils.generateCI({
|
||||
classID: CDMAICCINFO_CID,
|
||||
classDescription: "MozCdmaIccInfo",
|
||||
flags: Ci.nsIClassInfo.DOM_OBJECT,
|
||||
interfaces: [Ci.nsIDOMMozCdmaIccInfo]
|
||||
}),
|
||||
|
||||
// nsIDOMMozCdmaIccInfo
|
||||
|
||||
mdn: null,
|
||||
min: null
|
||||
};
|
||||
|
||||
function RadioInterfaceLayer() {
|
||||
gMessageManager.init(this);
|
||||
|
||||
@ -1049,9 +1099,27 @@ RadioInterface.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
getMsisdn: function getMsisdn() {
|
||||
/**
|
||||
* Get phone number from iccInfo.
|
||||
*
|
||||
* If the icc card is gsm card, the phone number is in msisdn.
|
||||
* @see nsIDOMMozGsmIccInfo
|
||||
*
|
||||
* Otherwise, the phone number is in mdn.
|
||||
* @see nsIDOMMozCdmaIccInfo
|
||||
*/
|
||||
getPhoneNumber: function getPhoneNumber() {
|
||||
let iccInfo = this.rilContext.iccInfo;
|
||||
let number = iccInfo ? iccInfo.msisdn : null;
|
||||
|
||||
if (!iccInfo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// After moving SMS code out of RadioInterfaceLayer, we could use
|
||||
// |iccInfo instanceof Ci.nsIDOMMozGsmIccInfo| here.
|
||||
// TODO: Bug 873351 - B2G SMS: move SMS code out of RadioInterfaceLayer to
|
||||
// SmsService
|
||||
let number = (iccInfo instanceof GsmIccInfo) ? iccInfo.msisdn : iccInfo.mdn;
|
||||
|
||||
// Workaround an xpconnect issue with undefined string objects.
|
||||
// See bug 808220
|
||||
@ -1744,7 +1812,7 @@ RadioInterface.prototype = {
|
||||
|
||||
message.type = "sms";
|
||||
message.sender = message.sender || null;
|
||||
message.receiver = this.getMsisdn();
|
||||
message.receiver = this.getPhoneNumber();
|
||||
message.body = message.fullBody = message.fullBody || null;
|
||||
message.timestamp = Date.now();
|
||||
|
||||
@ -1928,14 +1996,14 @@ RadioInterface.prototype = {
|
||||
setTimezoneByNitz: function setTimezoneByNitz(message) {
|
||||
// To set the sytem timezone. Note that we need to convert the time zone
|
||||
// value to a UTC repesentation string in the format of "UTC(+/-)hh:mm".
|
||||
// Ex, time zone -480 is "UTC-08:00"; time zone 630 is "UTC+10:30".
|
||||
// Ex, time zone -480 is "UTC+08:00"; time zone 630 is "UTC-10:30".
|
||||
//
|
||||
// We can unapply the DST correction if we want the raw time zone offset:
|
||||
// message.networkTimeZoneInMinutes -= message.networkDSTInMinutes;
|
||||
if (message.networkTimeZoneInMinutes != (new Date()).getTimezoneOffset()) {
|
||||
let absTimeZoneInMinutes = Math.abs(message.networkTimeZoneInMinutes);
|
||||
let timeZoneStr = "UTC";
|
||||
timeZoneStr += (message.networkTimeZoneInMinutes >= 0 ? "+" : "-");
|
||||
timeZoneStr += (message.networkTimeZoneInMinutes > 0 ? "-" : "+");
|
||||
timeZoneStr += ("0" + Math.floor(absTimeZoneInMinutes / 60)).slice(-2);
|
||||
timeZoneStr += ":";
|
||||
timeZoneStr += ("0" + absTimeZoneInMinutes % 60).slice(-2);
|
||||
@ -1991,12 +2059,27 @@ RadioInterface.prototype = {
|
||||
},
|
||||
|
||||
handleIccInfoChange: function handleIccInfoChange(message) {
|
||||
let oldIccInfo = this.rilContext.iccInfo;
|
||||
this.rilContext.iccInfo = message;
|
||||
let oldSpn = this.rilContext.iccInfo ? this.rilContext.iccInfo.spn : null;
|
||||
|
||||
if (!this.isInfoChanged(message, oldIccInfo)) {
|
||||
return;
|
||||
if (!message || !message.iccType) {
|
||||
// Card is not detected, clear iccInfo to null.
|
||||
this.rilContext.iccInfo = null;
|
||||
} else {
|
||||
if (!this.rilContext.iccInfo) {
|
||||
if (message.iccType === "ruim" || message.iccType === "csim") {
|
||||
this.rilContext.iccInfo = new CdmaIccInfo();
|
||||
} else {
|
||||
this.rilContext.iccInfo = new GsmIccInfo();
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.isInfoChanged(message, this.rilContext.iccInfo)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.updateInfo(message, this.rilContext.iccInfo);
|
||||
}
|
||||
|
||||
// RIL:IccInfoChanged corresponds to a DOM event that gets fired only
|
||||
// when iccInfo has changed.
|
||||
gMessageManager.sendIccMessage("RIL:IccInfoChanged",
|
||||
@ -2020,7 +2103,6 @@ RadioInterface.prototype = {
|
||||
}
|
||||
|
||||
// If spn becomes available, we should check roaming again.
|
||||
let oldSpn = oldIccInfo ? oldIccInfo.spn : null;
|
||||
if (!oldSpn && message.spn) {
|
||||
let voice = this.rilContext.voice;
|
||||
let data = this.rilContext.data;
|
||||
@ -2945,7 +3027,7 @@ RadioInterface.prototype = {
|
||||
|
||||
let sendingMessage = {
|
||||
type: "sms",
|
||||
sender: this.getMsisdn(),
|
||||
sender: this.getPhoneNumber(),
|
||||
receiver: number,
|
||||
body: message,
|
||||
deliveryStatusRequested: options.requestStatusReport,
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "xpcpublic.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsCxPusher.h"
|
||||
#include "nsPrintfCString.h"
|
||||
|
||||
#undef LOG
|
||||
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Time Zone Setting" , ## args)
|
||||
@ -61,9 +62,18 @@ public:
|
||||
// to make settings consistent with system. This usually happens
|
||||
// at the very first boot. After that, settings must have a value.
|
||||
if (aResult.isNull()) {
|
||||
// Get the current system timezone and convert it to a JS string.
|
||||
nsCString curTimezone = hal::GetTimezone();
|
||||
NS_ConvertUTF8toUTF16 utf16Str(curTimezone);
|
||||
// Get the current system time zone offset. Note that we need to
|
||||
// convert the value to a UTC representation in the format of
|
||||
// "UTC{+,-}hh:mm", so that the Gaia end can know how to interpret.
|
||||
// E.g., -480 is "UTC+08:00"; 630 is "UTC-10:30".
|
||||
int32_t timeZoneOffset = hal::GetTimezoneOffset();
|
||||
nsPrintfCString curTimeZone("UTC%+03d:%02d",
|
||||
-timeZoneOffset / 60,
|
||||
abs(timeZoneOffset) % 60);
|
||||
|
||||
// Convert it to a JS string.
|
||||
NS_ConvertUTF8toUTF16 utf16Str(curTimeZone);
|
||||
|
||||
JSString *jsStr = JS_NewUCStringCopyN(cx, utf16Str.get(), utf16Str.Length());
|
||||
|
||||
// Set the settings based on the current system timezone.
|
||||
@ -127,6 +137,7 @@ TimeZoneSettingObserver::TimeZoneSettingObserver()
|
||||
nsresult TimeZoneSettingObserver::SetTimeZone(const JS::Value &aValue, JSContext *aContext)
|
||||
{
|
||||
// Convert the JS value to a nsCString type.
|
||||
// The value should be a JS string like "America/Chicago" or "UTC-05:00".
|
||||
nsDependentJSString valueStr;
|
||||
if (!valueStr.init(aContext, aValue.toString())) {
|
||||
ERR("Failed to convert JS value to nsCString");
|
||||
@ -134,6 +145,18 @@ nsresult TimeZoneSettingObserver::SetTimeZone(const JS::Value &aValue, JSContext
|
||||
}
|
||||
NS_ConvertUTF16toUTF8 newTimezone(valueStr);
|
||||
|
||||
// Hal expects opposite sign from general notations,
|
||||
// so we need to flip it.
|
||||
if (newTimezone.Find(NS_LITERAL_CSTRING("UTC+")) == 0) {
|
||||
if (!newTimezone.SetCharAt('-', 3)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
} else if (newTimezone.Find(NS_LITERAL_CSTRING("UTC-")) == 0) {
|
||||
if (!newTimezone.SetCharAt('+', 3)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
// Set the timezone only when the system timezone is not identical.
|
||||
nsCString curTimezone = hal::GetTimezone();
|
||||
if (!curTimezone.Equals(newTimezone)) {
|
||||
@ -166,7 +189,8 @@ TimeZoneSettingObserver::Observe(nsISupports *aSubject,
|
||||
// so we need to carefully check if we have the one we're interested in.
|
||||
//
|
||||
// The string that we're interested in will be a JSON string that looks like:
|
||||
// {"key":"time.timezone","value":"America/Chicago"}
|
||||
// {"key":"time.timezone","value":"America/Chicago"} or
|
||||
// {"key":"time.timezone","value":"UTC-05:00"}
|
||||
|
||||
AutoSafeJSContext cx;
|
||||
|
||||
|
@ -719,25 +719,3 @@ Telephony::EnqueueEnumerationAck()
|
||||
NS_WARNING("Failed to dispatch to current thread!");
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool
|
||||
Telephony::CheckPermission(nsPIDOMWindow* aWindow)
|
||||
{
|
||||
MOZ_ASSERT(aWindow && aWindow->IsInnerWindow());
|
||||
|
||||
nsCOMPtr<nsIPermissionManager> permMgr =
|
||||
do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
|
||||
NS_ENSURE_TRUE(permMgr, false);
|
||||
|
||||
uint32_t permission;
|
||||
nsresult rv =
|
||||
permMgr->TestPermissionFromWindow(aWindow, "telephony", &permission);
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
|
||||
if (permission != nsIPermissionManager::ALLOW_ACTION) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -106,8 +106,6 @@ public:
|
||||
static already_AddRefed<Telephony>
|
||||
Create(nsPIDOMWindow* aOwner, ErrorResult& aRv);
|
||||
|
||||
static bool CheckPermission(nsPIDOMWindow* aOwner);
|
||||
|
||||
void
|
||||
AddCall(TelephonyCall* aCall)
|
||||
{
|
||||
|
@ -100,7 +100,7 @@ var interfaceNamesInGlobalScope =
|
||||
{name: "BluetoothStatusChangedEvent", b2g: true},
|
||||
{name: "BoxObject", xbl: true},
|
||||
{name: "BrowserFeedWriter", desktop: true},
|
||||
{name: "CallEvent", b2g: true},
|
||||
{name: "CallEvent", b2g: true, pref: "dom.telephony.enabled"},
|
||||
"CameraCapabilities",
|
||||
"CameraControl",
|
||||
"CameraManager",
|
||||
@ -109,7 +109,7 @@ var interfaceNamesInGlobalScope =
|
||||
"CanvasRenderingContext2D",
|
||||
"CaretPosition",
|
||||
"CDATASection",
|
||||
{name: "CFStateChangeEvent", b2g: true},
|
||||
{name: "CFStateChangeEvent", b2g: true, pref: "dom.mobileconnection.enabled"},
|
||||
"ChannelMergerNode",
|
||||
"ChannelSplitterNode",
|
||||
"CharacterData",
|
||||
@ -147,7 +147,7 @@ var interfaceNamesInGlobalScope =
|
||||
"CustomEvent",
|
||||
"DataChannel",
|
||||
"DataContainerEvent",
|
||||
{name: "DataErrorEvent", b2g: true},
|
||||
{name: "DataErrorEvent", b2g: true, pref: "dom.mobileconnection.enabled"},
|
||||
{name: "DataStore", b2g: true, release: false},
|
||||
{name: "DataStoreChangeEvent", b2g: true, release: false},
|
||||
{name: "DataStoreCursor", b2g: true, release: false},
|
||||
@ -277,7 +277,7 @@ var interfaceNamesInGlobalScope =
|
||||
"HTMLUListElement",
|
||||
"HTMLUnknownElement",
|
||||
"HTMLVideoElement",
|
||||
{name: "IccCardLockError", b2g: true},
|
||||
{name: "IccCardLockError", b2g: true, pref: "dom.icc.enabled"},
|
||||
"IDBCursor",
|
||||
"IDBCursorWithValue",
|
||||
"IDBDatabase",
|
||||
@ -317,24 +317,24 @@ var interfaceNamesInGlobalScope =
|
||||
"MouseScrollEvent",
|
||||
{name: "MozActivity", b2g: true},
|
||||
"MozApplicationEvent",
|
||||
{name: "MozCellBroadcast", b2g: true},
|
||||
{name: "MozCellBroadcastEvent", b2g: true},
|
||||
{name: "MozCellBroadcast", b2g: true, pref: "dom.cellbroadcast.enabled"},
|
||||
{name: "MozCellBroadcastEvent", b2g: true, pref: "dom.cellbroadcast.enabled"},
|
||||
"MozConnection",
|
||||
"mozContact",
|
||||
"MozContactChangeEvent",
|
||||
"MozCSSKeyframeRule",
|
||||
"MozCSSKeyframesRule",
|
||||
{name: "MozEmergencyCbModeEvent", b2g: true},
|
||||
{name: "MozIccManager", b2g: true},
|
||||
{name: "MozEmergencyCbModeEvent", b2g: true, pref: "dom.mobileconnection.enabled"},
|
||||
{name: "MozIccManager", b2g: true, pref: "dom.icc.enabled"},
|
||||
{name: "MozInputContext", b2g: true},
|
||||
{name: "MozInputMethodManager", b2g: true},
|
||||
"MozMmsEvent",
|
||||
"MozMmsMessage",
|
||||
{name: "MozMobileConnection", b2g: true},
|
||||
{name: "MozMobileConnection", b2g: true, pref: "dom.mobileconnection.enabled"},
|
||||
"MozMobileMessageManager",
|
||||
"MozMobileMessageThread",
|
||||
"MozNamedAttrMap",
|
||||
{name: "MozOtaStatusEvent", b2g: true},
|
||||
{name: "MozOtaStatusEvent", b2g: true, pref: "dom.mobileconnection.enabled"},
|
||||
"MozPowerManager",
|
||||
"mozRTCIceCandidate",
|
||||
"mozRTCPeerConnection",
|
||||
@ -344,10 +344,10 @@ var interfaceNamesInGlobalScope =
|
||||
"MozSmsFilter",
|
||||
"MozSmsMessage",
|
||||
"MozSmsSegmentInfo",
|
||||
{name: "MozStkCommandEvent", b2g: true},
|
||||
{name: "MozStkCommandEvent", b2g: true, pref: "dom.icc.enabled"},
|
||||
{name: "MozTimeManager", b2g: true},
|
||||
{name: "MozVoicemail", b2g: true},
|
||||
{name: "MozVoicemailEvent", b2g: true},
|
||||
{name: "MozVoicemail", b2g: true, pref: "dom.voicemail.enabled"},
|
||||
{name: "MozVoicemailEvent", b2g: true, pref: "dom.voicemail.enabled"},
|
||||
"MozWakeLock",
|
||||
{name: "MozWifiConnectionInfoEvent", b2g: true},
|
||||
{name: "MozWifiStatusChangeEvent", b2g: true},
|
||||
@ -531,9 +531,9 @@ var interfaceNamesInGlobalScope =
|
||||
"SVGViewElement",
|
||||
"SVGZoomAndPan",
|
||||
"SVGZoomEvent",
|
||||
{name: "Telephony", b2g: true},
|
||||
{name: "TelephonyCall", b2g: true},
|
||||
{name: "TelephonyCallGroup", b2g: true},
|
||||
{name: "Telephony", b2g: true, pref: "dom.telephony.enabled"},
|
||||
{name: "TelephonyCall", b2g: true, pref: "dom.telephony.enabled"},
|
||||
{name: "TelephonyCallGroup", b2g: true, pref: "dom.telephony.enabled"},
|
||||
"Text",
|
||||
"TextDecoder",
|
||||
"TextEncoder",
|
||||
@ -555,7 +555,7 @@ var interfaceNamesInGlobalScope =
|
||||
"URL",
|
||||
{name: "UserDataHandler", xbl: true},
|
||||
"UserProximityEvent",
|
||||
{name: "USSDReceivedEvent", b2g: true},
|
||||
{name: "USSDReceivedEvent", b2g: true, pref: "dom.mobileconnection.enabled"},
|
||||
"ValidityState",
|
||||
"VideoStreamTrack",
|
||||
"WaveShaperNode",
|
||||
@ -601,6 +601,7 @@ var interfaceNamesInGlobalScope =
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
|
||||
function createInterfaceMap(isXBLScope) {
|
||||
var prefs = SpecialPowers.Services.prefs;
|
||||
var version = SpecialPowers.Cc["@mozilla.org/xre/app-info;1"].getService(SpecialPowers.Ci.nsIXULAppInfo).version;
|
||||
var isNightly = version.endsWith("a1");
|
||||
var isRelease = !version.contains("a");
|
||||
@ -618,7 +619,8 @@ function createInterfaceMap(isXBLScope) {
|
||||
(entry.xbl === !isXBLScope) ||
|
||||
(entry.desktop === !isDesktop) ||
|
||||
(entry.b2g === !isB2G) ||
|
||||
(entry.release === !isRelease)) {
|
||||
(entry.release === !isRelease) ||
|
||||
(entry.pref && !prefs.getBoolPref(entry.pref))) {
|
||||
interfaceMap[entry.name] = false;
|
||||
} else {
|
||||
interfaceMap[entry.name] = shouldExpect;
|
||||
|
@ -11,6 +11,9 @@ Cu.import("resource://gre/modules/WspPduHelper.jsm", WSP);
|
||||
let WBXML = {};
|
||||
Cu.import("resource://gre/modules/WbxmlPduHelper.jsm", WBXML);
|
||||
|
||||
Cu.import("resource://services-crypto/utils.js");
|
||||
Cu.import("resource://services-common/utils.js");
|
||||
|
||||
// set to true to see debug messages
|
||||
let DEBUG = WBXML.DEBUG_ALL | false;
|
||||
|
||||
@ -83,6 +86,128 @@ this.PduHelper = {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* SEC type values
|
||||
*
|
||||
* @see WAP-183-ProvCont-20010724-A, clause 5.3
|
||||
*/
|
||||
const AUTH_SEC_TYPE = (function () {
|
||||
let names = {};
|
||||
function add(name, number) {
|
||||
names[number] = name;
|
||||
}
|
||||
|
||||
add("NETWPIN", 0);
|
||||
add("USERPIN", 1);
|
||||
add("USERNETWPIN", 2);
|
||||
add("USERPINMAC", 3);
|
||||
|
||||
return names;
|
||||
})();
|
||||
|
||||
this.Authenticator = {
|
||||
/**
|
||||
* Format IMSI string into GSM format
|
||||
*
|
||||
* @param imsi
|
||||
* IMSI string
|
||||
*
|
||||
* @return IMSI in GSM format as string object
|
||||
*/
|
||||
formatImsi: function formatImsi(imsi) {
|
||||
let parityByte = ((imsi.length & 1) ? 9 : 1);
|
||||
|
||||
// Make sure length of IMSI is 15 digits.
|
||||
// @see GSM 11.11, clause 10.2.2
|
||||
let i = 0;
|
||||
for (i = 15 - imsi.length; i > 0; i--) {
|
||||
imsi += "F";
|
||||
}
|
||||
|
||||
// char-by-char atoi
|
||||
let imsiValue = [];
|
||||
imsiValue.push(parityByte);
|
||||
for (i = 0; i < imsi.length; i++) {
|
||||
imsiValue.push(parseInt(imsi.substr(i, 1), 10));
|
||||
}
|
||||
|
||||
// encoded IMSI
|
||||
let imsiEncoded = "";
|
||||
for (i = 0; i < imsiValue.length; i += 2) {
|
||||
imsiEncoded += String.fromCharCode(imsiValue[i] | (imsiValue[i+1] << 4));
|
||||
}
|
||||
|
||||
return imsiEncoded;
|
||||
},
|
||||
|
||||
/**
|
||||
* Perform HMAC check
|
||||
*
|
||||
* @param wbxml
|
||||
* Uint8 typed array of raw WBXML data.
|
||||
* @param key
|
||||
* key string for HMAC check.
|
||||
* @param mac
|
||||
* Expected MAC value.
|
||||
*
|
||||
* @return true for valid, false for invalid.
|
||||
*/
|
||||
isValid: function isValid(wbxml, key, mac) {
|
||||
let hasher = CryptoUtils.makeHMACHasher(Ci.nsICryptoHMAC.SHA1,
|
||||
CryptoUtils.makeHMACKey(key));
|
||||
hasher.update(wbxml, wbxml.length);
|
||||
let result = CommonUtils.bytesAsHex(hasher.finish(false)).toUpperCase();
|
||||
return mac == result;
|
||||
},
|
||||
|
||||
/**
|
||||
* Perform HMAC authentication.
|
||||
*
|
||||
* @param wbxml
|
||||
* Uint8 typed array of raw WBXML data.
|
||||
* @param sec
|
||||
* Security method for HMAC check.
|
||||
* @param mac
|
||||
* Expected MAC value.
|
||||
* @param getNetworkPin
|
||||
* Callback function for getting network pin.
|
||||
*
|
||||
* @return true for valid, false for invalid.
|
||||
*/
|
||||
check: function check_hmac(wbxml, sec, mac, getNetworkPin) {
|
||||
// No security set.
|
||||
if (sec == null || !mac) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let authInfo = {
|
||||
pass: false,
|
||||
checked: false,
|
||||
sec: AUTH_SEC_TYPE[sec],
|
||||
mac: mac.toUpperCase(),
|
||||
dataLength: wbxml.length,
|
||||
data: wbxml
|
||||
};
|
||||
|
||||
switch (authInfo.sec) {
|
||||
case "NETWPIN":
|
||||
let key = getNetworkPin();
|
||||
authInfo.pass = this.isValid(wbxml, key, authInfo.mac);
|
||||
authInfo.checked = true;
|
||||
return authInfo;
|
||||
|
||||
case "USERPIN":
|
||||
case "USERPINMAC":
|
||||
// We can't check without USER PIN
|
||||
return authInfo;
|
||||
|
||||
case "USERNETWPIN":
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Tag tokens
|
||||
*
|
||||
@ -347,4 +472,6 @@ if (DEBUG) {
|
||||
this.EXPORTED_SYMBOLS = [
|
||||
// Parser
|
||||
"PduHelper",
|
||||
// HMAC Authenticator
|
||||
"Authenticator",
|
||||
];
|
||||
|
@ -37,6 +37,9 @@ XPCOMUtils.defineLazyGetter(this, "CP", function () {
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gSystemMessenger",
|
||||
"@mozilla.org/system-message-internal;1",
|
||||
"nsISystemMessagesInternal");
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gRIL",
|
||||
"@mozilla.org/ril;1",
|
||||
"nsIRadioInterfaceLayer");
|
||||
|
||||
/**
|
||||
* Helpers for WAP PDU processing.
|
||||
@ -91,6 +94,7 @@ this.WapPushManager = {
|
||||
*/
|
||||
let contentType = options.headers["content-type"].media;
|
||||
let msg;
|
||||
let authInfo = null;
|
||||
|
||||
if (contentType === "text/vnd.wap.si" ||
|
||||
contentType === "application/vnd.wap.sic") {
|
||||
@ -100,6 +104,18 @@ this.WapPushManager = {
|
||||
msg = SL.PduHelper.parse(data, contentType);
|
||||
} else if (contentType === "text/vnd.wap.connectivity-xml" ||
|
||||
contentType === "application/vnd.wap.connectivity-wbxml") {
|
||||
// Apply HMAC authentication on WBXML encoded CP message.
|
||||
if (contentType === "application/vnd.wap.connectivity-wbxml") {
|
||||
let params = options.headers["content-type"].params;
|
||||
let sec = params && params.sec;
|
||||
let mac = params && params.mac;
|
||||
authInfo = CP.Authenticator.check(data.array.subarray(data.offset),
|
||||
sec, mac, function getNetworkPin() {
|
||||
let imsi = gRIL.getRadioInterface(0).rilContext.imsi;
|
||||
return CP.Authenticator.formatImsi(imsi);
|
||||
});
|
||||
}
|
||||
|
||||
msg = CP.PduHelper.parse(data, contentType);
|
||||
} else {
|
||||
// Unsupported type, provide raw data.
|
||||
@ -118,7 +134,8 @@ this.WapPushManager = {
|
||||
gSystemMessenger.broadcastMessage("wappush-received", {
|
||||
sender: sender,
|
||||
contentType: msg.contentType,
|
||||
content: msg.content
|
||||
content: msg.content,
|
||||
authInfo: authInfo
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -16,6 +16,16 @@ function test_parser(rawDataArray, contentType, expectResult) {
|
||||
do_check_eq(msg.content, expectResult.content);
|
||||
}
|
||||
|
||||
function test_hmac(rawDataArray, mac, key, expectResult) {
|
||||
let authInfo = CP.Authenticator.check(rawDataArray, 0, mac, function getNetworkPin() {
|
||||
return key;
|
||||
});
|
||||
do_check_eq(authInfo.data, rawDataArray);
|
||||
do_check_eq(authInfo.dataLength, rawDataArray.length);
|
||||
do_check_eq(authInfo.checked, expectResult.checked);
|
||||
do_check_eq(authInfo.pass, expectResult.pass);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test data from OMA-TS-WAP_ProvCont-V1_1-2009 0421-C.pdf, clause 6.1
|
||||
*/
|
||||
@ -264,6 +274,75 @@ let wbxml_data_array = new Uint8Array([
|
||||
0x01
|
||||
]);
|
||||
|
||||
/*
|
||||
* Test data from CHT CP, with code page change
|
||||
*/
|
||||
let wbxml_code_page_data_array = new Uint8Array([
|
||||
0x03, 0x0B, 0x6A, 0x00, 0x45, 0xC6, 0x56, 0x01,
|
||||
0x87, 0x07, 0x06, 0x03, 0x43, 0x48, 0x54, 0x5F,
|
||||
0x65, 0x6D, 0x6F, 0x6D, 0x65, 0x00, 0x01, 0x01,
|
||||
0xC6, 0x00, 0x01, 0x55, 0x01, 0x87, 0x36, 0x00,
|
||||
0x00, 0x06, 0x03, 0x77, 0x32, 0x00, 0x01, 0x87,
|
||||
0x00, 0x01, 0x39, 0x00, 0x00, 0x06, 0x03, 0x57,
|
||||
0x50, 0x52, 0x4F, 0x58, 0x59, 0x00, 0x01, 0x87,
|
||||
0x07, 0x06, 0x03, 0x43, 0x48, 0x54, 0x5F, 0x65,
|
||||
0x6D, 0x6F, 0x6D, 0x65, 0x00, 0x01, 0xC6, 0x00,
|
||||
0x01, 0x59, 0x01, 0x87, 0x3A, 0x00, 0x00, 0x06,
|
||||
0x03, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F,
|
||||
0x77, 0x61, 0x70, 0x2E, 0x65, 0x6D, 0x6F, 0x6D,
|
||||
0x65, 0x2E, 0x6E, 0x65, 0x74, 0x2F, 0x00, 0x01,
|
||||
0x87, 0x07, 0x06, 0x03, 0x43, 0x48, 0x54, 0x5F,
|
||||
0x65, 0x6D, 0x6F, 0x6D, 0x65, 0x00, 0x01, 0x87,
|
||||
0x1C, 0x01, 0x01, 0x01, 0xC6, 0x00, 0x01, 0x55,
|
||||
0x01, 0x87, 0x36, 0x00, 0x00, 0x06, 0x03, 0x77,
|
||||
0x34, 0x00, 0x01, 0x87, 0x00, 0x01, 0x39, 0x00,
|
||||
0x00, 0x06, 0x03, 0x4D, 0x50, 0x52, 0x4F, 0x58,
|
||||
0x59, 0x00, 0x01, 0x87, 0x00, 0x01, 0x34, 0x00,
|
||||
0x00, 0x06, 0x03, 0x68, 0x74, 0x74, 0x70, 0x3A,
|
||||
0x2F, 0x2F, 0x6D, 0x6D, 0x73, 0x3A, 0x38, 0x30,
|
||||
0x30, 0x32, 0x00, 0x01, 0x01, 0xC6, 0x51, 0x01,
|
||||
0x87, 0x15, 0x06, 0x03, 0x57, 0x50, 0x52, 0x4F,
|
||||
0x58, 0x59, 0x00, 0x01, 0x87, 0x07, 0x06, 0x03,
|
||||
0x43, 0x48, 0x54, 0x5F, 0x65, 0x6D, 0x6F, 0x6D,
|
||||
0x65, 0x00, 0x01, 0x87, 0x1C, 0x06, 0x03, 0x68,
|
||||
0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x77, 0x61,
|
||||
0x70, 0x2E, 0x65, 0x6D, 0x6F, 0x6D, 0x65, 0x2E,
|
||||
0x6E, 0x65, 0x74, 0x2F, 0x00, 0x01, 0xC6, 0x52,
|
||||
0x01, 0x87, 0x2F, 0x06, 0x03, 0x50, 0x52, 0x4F,
|
||||
0x58, 0x59, 0x31, 0x00, 0x01, 0x87, 0x20, 0x06,
|
||||
0x03, 0x31, 0x30, 0x2E, 0x31, 0x2E, 0x31, 0x2E,
|
||||
0x31, 0x00, 0x01, 0x87, 0x21, 0x06, 0x85, 0x01,
|
||||
0x87, 0x22, 0x06, 0x03, 0x43, 0x48, 0x54, 0x5F,
|
||||
0x65, 0x6D, 0x6F, 0x6D, 0x65, 0x00, 0x01, 0xC6,
|
||||
0x53, 0x01, 0x87, 0x23, 0x06, 0x03, 0x38, 0x30,
|
||||
0x38, 0x30, 0x00, 0x01, 0x01, 0x01, 0x01, 0xC6,
|
||||
0x51, 0x01, 0x87, 0x15, 0x06, 0x03, 0x4D, 0x50,
|
||||
0x52, 0x4F, 0x58, 0x59, 0x00, 0x01, 0x87, 0x07,
|
||||
0x06, 0x03, 0x43, 0x48, 0x54, 0x5F, 0x4D, 0x4D,
|
||||
0x53, 0x00, 0x01, 0xC6, 0x52, 0x01, 0x87, 0x2F,
|
||||
0x06, 0x03, 0x50, 0x52, 0x4F, 0x58, 0x59, 0x32,
|
||||
0x00, 0x01, 0x87, 0x20, 0x06, 0x03, 0x31, 0x30,
|
||||
0x2E, 0x31, 0x2E, 0x31, 0x2E, 0x31, 0x00, 0x01,
|
||||
0x87, 0x21, 0x06, 0x85, 0x01, 0x87, 0x22, 0x06,
|
||||
0x03, 0x43, 0x48, 0x54, 0x5F, 0x4D, 0x4D, 0x53,
|
||||
0x00, 0x01, 0xC6, 0x53, 0x01, 0x87, 0x23, 0x06,
|
||||
0x03, 0x38, 0x30, 0x38, 0x30, 0x00, 0x01, 0x01,
|
||||
0x01, 0x01, 0xC6, 0x55, 0x01, 0x87, 0x11, 0x06,
|
||||
0x03, 0x43, 0x48, 0x54, 0x5F, 0x65, 0x6D, 0x6F,
|
||||
0x6D, 0x65, 0x00, 0x01, 0x87, 0x07, 0x06, 0x03,
|
||||
0x43, 0x48, 0x54, 0x5F, 0x65, 0x6D, 0x6F, 0x6D,
|
||||
0x65, 0x00, 0x01, 0x87, 0x10, 0x06, 0xAB, 0x01,
|
||||
0x87, 0x08, 0x06, 0x03, 0x65, 0x6D, 0x6F, 0x6D,
|
||||
0x65, 0x00, 0x01, 0x87, 0x09, 0x06, 0x89, 0x01,
|
||||
0x01, 0xC6, 0x55, 0x01, 0x87, 0x11, 0x06, 0x03,
|
||||
0x43, 0x48, 0x54, 0x5F, 0x4D, 0x4D, 0x53, 0x00,
|
||||
0x01, 0x87, 0x07, 0x06, 0x03, 0x43, 0x48, 0x54,
|
||||
0x5F, 0x4D, 0x4D, 0x53, 0x00, 0x01, 0x87, 0x10,
|
||||
0x06, 0xAB, 0x01, 0x87, 0x08, 0x06, 0x03, 0x65,
|
||||
0x6D, 0x6F, 0x6D, 0x65, 0x00, 0x01, 0x87, 0x09,
|
||||
0x06, 0x89, 0x01, 0x01, 0x01
|
||||
]);
|
||||
|
||||
/*
|
||||
* Test data from OMA-TS-WAP_ProvCont-V1_1-20090421-C.pdf, clause 6.1
|
||||
*/
|
||||
@ -313,121 +392,7 @@ let xml_body =
|
||||
"</characteristic>" +
|
||||
"</wap-provisioningdoc>";
|
||||
|
||||
/**
|
||||
* CP in plain text
|
||||
*
|
||||
* Test case from OMA-TS-WAP_ProvCont-V1_1-20090421-C.pdf, clause 6.1
|
||||
*/
|
||||
add_test(function test_cp_parse_plain_text() {
|
||||
test_parser(text_data_array, "text/vnd.wap.connectivity-xml", {
|
||||
contentType: "text/vnd.wap.connectivity-xml",
|
||||
content: xml_header + xml_body
|
||||
});
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
/**
|
||||
* CP compressed by WBXML
|
||||
*
|
||||
* Test case from OMA-TS-WAP_ProvCont-V1_1-20090421-C.pdf, Appendix C
|
||||
*/
|
||||
add_test(function test_cp_parse_wbxml() {
|
||||
test_parser(wbxml_data_array, "application/vnd.wap.connectivity-wbxml", {
|
||||
contentType: "text/vnd.wap.connectivity-xml",
|
||||
content: xml_body
|
||||
});
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
/**
|
||||
* CP compressed by WBXML with VENDORCONFIG
|
||||
*/
|
||||
add_test(function test_cp_parse_wbxml() {
|
||||
let wbxml_vendor_config_data_array = new Uint8Array([
|
||||
0x03, 0x0b, 0x6a, 0x05, 0x4e, 0x41, 0x50, 0x31,
|
||||
0x00, 0xC5, 0x46, 0x01, 0xc6, 0x57, 0x01, 0x01
|
||||
]);
|
||||
|
||||
test_parser(wbxml_vendor_config_data_array, "application/vnd.wap.connectivity-wbxml", {
|
||||
contentType: "application/vnd.wap.connectivity-wbxml",
|
||||
content: wbxml_vendor_config_data_array
|
||||
});
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
/**
|
||||
* CP compressed by WBXML with code page switch
|
||||
*/
|
||||
add_test(function test_cp_parse_wbxml_code_page() {
|
||||
let wbxml_code_page_data_array = new Uint8Array([
|
||||
0x03, 0x0B, 0x6A, 0x00, 0x45, 0xC6, 0x56, 0x01,
|
||||
0x87, 0x07, 0x06, 0x03, 0x43, 0x48, 0x54, 0x5F,
|
||||
0x65, 0x6D, 0x6F, 0x6D, 0x65, 0x00, 0x01, 0x01,
|
||||
0xC6, 0x00, 0x01, 0x55, 0x01, 0x87, 0x36, 0x00,
|
||||
0x00, 0x06, 0x03, 0x77, 0x32, 0x00, 0x01, 0x87,
|
||||
0x00, 0x01, 0x39, 0x00, 0x00, 0x06, 0x03, 0x57,
|
||||
0x50, 0x52, 0x4F, 0x58, 0x59, 0x00, 0x01, 0x87,
|
||||
0x07, 0x06, 0x03, 0x43, 0x48, 0x54, 0x5F, 0x65,
|
||||
0x6D, 0x6F, 0x6D, 0x65, 0x00, 0x01, 0xC6, 0x00,
|
||||
0x01, 0x59, 0x01, 0x87, 0x3A, 0x00, 0x00, 0x06,
|
||||
0x03, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F,
|
||||
0x77, 0x61, 0x70, 0x2E, 0x65, 0x6D, 0x6F, 0x6D,
|
||||
0x65, 0x2E, 0x6E, 0x65, 0x74, 0x2F, 0x00, 0x01,
|
||||
0x87, 0x07, 0x06, 0x03, 0x43, 0x48, 0x54, 0x5F,
|
||||
0x65, 0x6D, 0x6F, 0x6D, 0x65, 0x00, 0x01, 0x87,
|
||||
0x1C, 0x01, 0x01, 0x01, 0xC6, 0x00, 0x01, 0x55,
|
||||
0x01, 0x87, 0x36, 0x00, 0x00, 0x06, 0x03, 0x77,
|
||||
0x34, 0x00, 0x01, 0x87, 0x00, 0x01, 0x39, 0x00,
|
||||
0x00, 0x06, 0x03, 0x4D, 0x50, 0x52, 0x4F, 0x58,
|
||||
0x59, 0x00, 0x01, 0x87, 0x00, 0x01, 0x34, 0x00,
|
||||
0x00, 0x06, 0x03, 0x68, 0x74, 0x74, 0x70, 0x3A,
|
||||
0x2F, 0x2F, 0x6D, 0x6D, 0x73, 0x3A, 0x38, 0x30,
|
||||
0x30, 0x32, 0x00, 0x01, 0x01, 0xC6, 0x51, 0x01,
|
||||
0x87, 0x15, 0x06, 0x03, 0x57, 0x50, 0x52, 0x4F,
|
||||
0x58, 0x59, 0x00, 0x01, 0x87, 0x07, 0x06, 0x03,
|
||||
0x43, 0x48, 0x54, 0x5F, 0x65, 0x6D, 0x6F, 0x6D,
|
||||
0x65, 0x00, 0x01, 0x87, 0x1C, 0x06, 0x03, 0x68,
|
||||
0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x77, 0x61,
|
||||
0x70, 0x2E, 0x65, 0x6D, 0x6F, 0x6D, 0x65, 0x2E,
|
||||
0x6E, 0x65, 0x74, 0x2F, 0x00, 0x01, 0xC6, 0x52,
|
||||
0x01, 0x87, 0x2F, 0x06, 0x03, 0x50, 0x52, 0x4F,
|
||||
0x58, 0x59, 0x31, 0x00, 0x01, 0x87, 0x20, 0x06,
|
||||
0x03, 0x31, 0x30, 0x2E, 0x31, 0x2E, 0x31, 0x2E,
|
||||
0x31, 0x00, 0x01, 0x87, 0x21, 0x06, 0x85, 0x01,
|
||||
0x87, 0x22, 0x06, 0x03, 0x43, 0x48, 0x54, 0x5F,
|
||||
0x65, 0x6D, 0x6F, 0x6D, 0x65, 0x00, 0x01, 0xC6,
|
||||
0x53, 0x01, 0x87, 0x23, 0x06, 0x03, 0x38, 0x30,
|
||||
0x38, 0x30, 0x00, 0x01, 0x01, 0x01, 0x01, 0xC6,
|
||||
0x51, 0x01, 0x87, 0x15, 0x06, 0x03, 0x4D, 0x50,
|
||||
0x52, 0x4F, 0x58, 0x59, 0x00, 0x01, 0x87, 0x07,
|
||||
0x06, 0x03, 0x43, 0x48, 0x54, 0x5F, 0x4D, 0x4D,
|
||||
0x53, 0x00, 0x01, 0xC6, 0x52, 0x01, 0x87, 0x2F,
|
||||
0x06, 0x03, 0x50, 0x52, 0x4F, 0x58, 0x59, 0x32,
|
||||
0x00, 0x01, 0x87, 0x20, 0x06, 0x03, 0x31, 0x30,
|
||||
0x2E, 0x31, 0x2E, 0x31, 0x2E, 0x31, 0x00, 0x01,
|
||||
0x87, 0x21, 0x06, 0x85, 0x01, 0x87, 0x22, 0x06,
|
||||
0x03, 0x43, 0x48, 0x54, 0x5F, 0x4D, 0x4D, 0x53,
|
||||
0x00, 0x01, 0xC6, 0x53, 0x01, 0x87, 0x23, 0x06,
|
||||
0x03, 0x38, 0x30, 0x38, 0x30, 0x00, 0x01, 0x01,
|
||||
0x01, 0x01, 0xC6, 0x55, 0x01, 0x87, 0x11, 0x06,
|
||||
0x03, 0x43, 0x48, 0x54, 0x5F, 0x65, 0x6D, 0x6F,
|
||||
0x6D, 0x65, 0x00, 0x01, 0x87, 0x07, 0x06, 0x03,
|
||||
0x43, 0x48, 0x54, 0x5F, 0x65, 0x6D, 0x6F, 0x6D,
|
||||
0x65, 0x00, 0x01, 0x87, 0x10, 0x06, 0xAB, 0x01,
|
||||
0x87, 0x08, 0x06, 0x03, 0x65, 0x6D, 0x6F, 0x6D,
|
||||
0x65, 0x00, 0x01, 0x87, 0x09, 0x06, 0x89, 0x01,
|
||||
0x01, 0xC6, 0x55, 0x01, 0x87, 0x11, 0x06, 0x03,
|
||||
0x43, 0x48, 0x54, 0x5F, 0x4D, 0x4D, 0x53, 0x00,
|
||||
0x01, 0x87, 0x07, 0x06, 0x03, 0x43, 0x48, 0x54,
|
||||
0x5F, 0x4D, 0x4D, 0x53, 0x00, 0x01, 0x87, 0x10,
|
||||
0x06, 0xAB, 0x01, 0x87, 0x08, 0x06, 0x03, 0x65,
|
||||
0x6D, 0x6F, 0x6D, 0x65, 0x00, 0x01, 0x87, 0x09,
|
||||
0x06, 0x89, 0x01, 0x01, 0x01
|
||||
]);
|
||||
let wbxml_content =
|
||||
let wbxml_code_page_content =
|
||||
"<wap-provisioningdoc>" +
|
||||
"<characteristic type=\"BOOTSTRAP\">" +
|
||||
"<parm name=\"NAME\" value=\"CHT_emome\"/>" +
|
||||
@ -490,10 +455,90 @@ add_test(function test_cp_parse_wbxml_code_page() {
|
||||
"</characteristic>" +
|
||||
"</wap-provisioningdoc>";
|
||||
|
||||
test_parser(wbxml_code_page_data_array, "application/vnd.wap.connectivity-wbxml", {
|
||||
contentType: "text/vnd.wap.connectivity-xml",
|
||||
content: wbxml_content
|
||||
/**
|
||||
* CP in plain text
|
||||
*
|
||||
* Test case from OMA-TS-WAP_ProvCont-V1_1-20090421-C.pdf, clause 6.1
|
||||
*/
|
||||
add_test(function test_cp_parse_plain_text() {
|
||||
test_parser(text_data_array, "text/vnd.wap.connectivity-xml", {
|
||||
contentType: "text/vnd.wap.connectivity-xml",
|
||||
content: xml_header + xml_body
|
||||
});
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
/**
|
||||
* CP compressed by WBXML
|
||||
*
|
||||
* Test case from OMA-TS-WAP_ProvCont-V1_1-20090421-C.pdf, Appendix C
|
||||
*/
|
||||
add_test(function test_cp_parse_wbxml() {
|
||||
test_parser(wbxml_data_array, "application/vnd.wap.connectivity-wbxml", {
|
||||
contentType: "text/vnd.wap.connectivity-xml",
|
||||
content: xml_body
|
||||
});
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
/**
|
||||
* CP compressed by WBXML with VENDORCONFIG
|
||||
*/
|
||||
add_test(function test_cp_parse_wbxml() {
|
||||
let wbxml_vendor_config_data_array = new Uint8Array([
|
||||
0x03, 0x0b, 0x6a, 0x05, 0x4e, 0x41, 0x50, 0x31,
|
||||
0x00, 0xC5, 0x46, 0x01, 0xc6, 0x57, 0x01, 0x01
|
||||
]);
|
||||
|
||||
test_parser(wbxml_vendor_config_data_array, "application/vnd.wap.connectivity-wbxml", {
|
||||
contentType: "application/vnd.wap.connectivity-wbxml",
|
||||
content: wbxml_vendor_config_data_array
|
||||
});
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
/**
|
||||
* CP compressed by WBXML with code page switch
|
||||
*/
|
||||
add_test(function test_cp_parse_wbxml_code_page() {
|
||||
test_parser(wbxml_code_page_data_array, "application/vnd.wap.connectivity-wbxml", {
|
||||
contentType: "text/vnd.wap.connectivity-xml",
|
||||
content: wbxml_code_page_content
|
||||
});
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
/**
|
||||
* HMAC test
|
||||
*/
|
||||
add_test(function test_cp_hmac_userpin() {
|
||||
test_hmac(wbxml_code_page_data_array,
|
||||
"AA2DC41FC48AEEF3FED7351B1EE704461A8894D4",
|
||||
"0000",
|
||||
{
|
||||
checked: true,
|
||||
pass: true
|
||||
});
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_cp_hmac_networkpin() {
|
||||
let wbxml_empty_data_array = new Uint8Array([
|
||||
0x03, 0x0b, 0x6a, 0x00, 0x45, 0x01
|
||||
]);
|
||||
|
||||
test_hmac(wbxml_empty_data_array,
|
||||
"1AF545FE2823DC9347064450F90FF1BBF957E146",
|
||||
CP.Authenticator.formatImsi("466923103145252"),
|
||||
{
|
||||
checked: true,
|
||||
pass: true
|
||||
});
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
@ -129,6 +129,23 @@ interface BluetoothAdapter : EventTarget {
|
||||
[Creator, Throws]
|
||||
DOMRequest isScoConnected();
|
||||
|
||||
/**
|
||||
* Additional HFP methods to handle CDMA network.
|
||||
*
|
||||
* In GSM network we observe call operations from RIL call state changes;
|
||||
* however in CDMA network RIL call states do not change under some call
|
||||
* operations, so we need these additional methods to be informed of these
|
||||
* operations from dialer.
|
||||
*
|
||||
* For more information please refer to bug 912005 and 925638.
|
||||
*/
|
||||
[Creator, Throws]
|
||||
DOMRequest answerWaitingCall();
|
||||
[Creator, Throws]
|
||||
DOMRequest ignoreWaitingCall();
|
||||
[Creator, Throws]
|
||||
DOMRequest toggleCalls();
|
||||
|
||||
// AVRCP 1.3 methods
|
||||
[Creator,Throws]
|
||||
DOMRequest sendMediaMetaData(optional MediaMetaData mediaMetaData);
|
||||
|
@ -8,6 +8,7 @@
|
||||
* unacknowledged general CBS messages to be broadcast to all receivers within
|
||||
* a particular region.
|
||||
*/
|
||||
[Pref="dom.cellbroadcast.enabled"]
|
||||
interface MozCellBroadcast : EventTarget
|
||||
{
|
||||
/**
|
||||
|
@ -5,7 +5,7 @@
|
||||
*/
|
||||
interface MozCellBroadcastMessage;
|
||||
|
||||
[Constructor(DOMString type, optional MozCellBroadcastEventInit eventInitDict), HeaderFile="GeneratedEventClasses.h"]
|
||||
[Constructor(DOMString type, optional MozCellBroadcastEventInit eventInitDict), HeaderFile="GeneratedEventClasses.h", Pref="dom.cellbroadcast.enabled"]
|
||||
interface MozCellBroadcastEvent : Event
|
||||
{
|
||||
readonly attribute MozCellBroadcastMessage? message;
|
||||
|
@ -7,6 +7,7 @@
|
||||
// nsIDOMMozVoicemailStatus
|
||||
interface MozVoicemailStatus;
|
||||
|
||||
[Pref="dom.voicemail.enabled"]
|
||||
interface MozVoicemail : EventTarget
|
||||
{
|
||||
/**
|
||||
|
@ -5,7 +5,7 @@
|
||||
*/
|
||||
interface MozVoicemailStatus;
|
||||
|
||||
[Constructor(DOMString type, optional MozVoicemailEventInit eventInitDict), HeaderFile="GeneratedEventClasses.h"]
|
||||
[Constructor(DOMString type, optional MozVoicemailEventInit eventInitDict), HeaderFile="GeneratedEventClasses.h", Pref="dom.voicemail.enabled"]
|
||||
interface MozVoicemailEvent : Event
|
||||
{
|
||||
readonly attribute MozVoicemailStatus? status;
|
||||
|
@ -4,7 +4,7 @@
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
[Constructor(DOMString type, optional USSDReceivedEventInit eventInitDict), HeaderFile="GeneratedEventClasses.h"]
|
||||
[Constructor(DOMString type, optional USSDReceivedEventInit eventInitDict), HeaderFile="GeneratedEventClasses.h", Pref="dom.icc.enabled"]
|
||||
interface USSDReceivedEvent : Event
|
||||
{
|
||||
readonly attribute DOMString? message;
|
||||
|
@ -186,9 +186,8 @@ ImageContainer::ClearCurrentImage()
|
||||
void
|
||||
ImageContainer::SetCurrentImage(Image *aImage)
|
||||
{
|
||||
if (IsAsync() && !aImage) {
|
||||
// Let ImageClient to release all TextureClients.
|
||||
ImageBridgeChild::FlushImage(mImageClient, this);
|
||||
if (!aImage) {
|
||||
ClearAllImages();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -199,6 +198,27 @@ ImageContainer::SetCurrentImage(Image *aImage)
|
||||
SetCurrentImageInternal(aImage);
|
||||
}
|
||||
|
||||
void
|
||||
ImageContainer::ClearAllImages()
|
||||
{
|
||||
if (IsAsync()) {
|
||||
// Let ImageClient release all TextureClients.
|
||||
ImageBridgeChild::FlushAllImages(mImageClient, this, false);
|
||||
return;
|
||||
}
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
SetCurrentImageInternal(nullptr);
|
||||
}
|
||||
|
||||
void
|
||||
ImageContainer::ClearAllImagesExceptFront()
|
||||
{
|
||||
if (IsAsync()) {
|
||||
// Let ImageClient release all TextureClients except front one.
|
||||
ImageBridgeChild::FlushAllImages(mImageClient, this, true);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ImageContainer::SetCurrentImageInTransaction(Image *aImage)
|
||||
{
|
||||
|
@ -384,6 +384,17 @@ public:
|
||||
*/
|
||||
void SetCurrentImage(Image* aImage);
|
||||
|
||||
/**
|
||||
* Clear all images. Let ImageClient release all TextureClients.
|
||||
*/
|
||||
void ClearAllImages();
|
||||
|
||||
/**
|
||||
* Clear all images except current one.
|
||||
* Let ImageClient release all TextureClients except front one.
|
||||
*/
|
||||
void ClearAllImagesExceptFront();
|
||||
|
||||
/**
|
||||
* Clear the current image.
|
||||
* This function is expect to be called only from a CompositableClient
|
||||
|
@ -98,18 +98,18 @@ TextureInfo ImageClientSingle::GetTextureInfo() const
|
||||
}
|
||||
|
||||
void
|
||||
ImageClientSingle::FlushImage()
|
||||
ImageClientSingle::FlushAllImages(bool aExceptFront)
|
||||
{
|
||||
if (mFrontBuffer) {
|
||||
if (!aExceptFront && mFrontBuffer) {
|
||||
RemoveTextureClient(mFrontBuffer);
|
||||
mFrontBuffer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ImageClientBuffered::FlushImage()
|
||||
ImageClientBuffered::FlushAllImages(bool aExceptFront)
|
||||
{
|
||||
if (mFrontBuffer) {
|
||||
if (!aExceptFront && mFrontBuffer) {
|
||||
RemoveTextureClient(mFrontBuffer);
|
||||
mFrontBuffer = nullptr;
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ public:
|
||||
/**
|
||||
* Synchronously remove all the textures used by the image client.
|
||||
*/
|
||||
virtual void FlushImage() {}
|
||||
virtual void FlushAllImages(bool aExceptFront) {}
|
||||
|
||||
protected:
|
||||
ImageClient(CompositableForwarder* aFwd, CompositableType aType);
|
||||
@ -100,7 +100,7 @@ public:
|
||||
virtual already_AddRefed<Image> CreateImage(const uint32_t *aFormats,
|
||||
uint32_t aNumFormats) MOZ_OVERRIDE;
|
||||
|
||||
virtual void FlushImage() MOZ_OVERRIDE;
|
||||
virtual void FlushAllImages(bool aExceptFront) MOZ_OVERRIDE;
|
||||
|
||||
protected:
|
||||
RefPtr<TextureClient> mFrontBuffer;
|
||||
@ -123,7 +123,7 @@ public:
|
||||
|
||||
virtual void OnDetach() MOZ_OVERRIDE;
|
||||
|
||||
virtual void FlushImage() MOZ_OVERRIDE;
|
||||
virtual void FlushAllImages(bool aExceptFront) MOZ_OVERRIDE;
|
||||
|
||||
protected:
|
||||
RefPtr<TextureClient> mBackBuffer;
|
||||
|
@ -76,6 +76,9 @@ CompositableHost::RemoveTextureHost(uint64_t aTextureID)
|
||||
}
|
||||
it = it->GetNextSibling();
|
||||
}
|
||||
if (!mFirstTexture && mBackendData) {
|
||||
mBackendData->ClearData();
|
||||
}
|
||||
}
|
||||
|
||||
TextureHost*
|
||||
|
@ -72,6 +72,7 @@ public:
|
||||
MOZ_COUNT_DTOR(CompositableBackendSpecificData);
|
||||
}
|
||||
virtual void SetCompositor(Compositor* aCompositor) {}
|
||||
virtual void ClearData() {}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -422,9 +422,9 @@ void ImageBridgeChild::DispatchImageClientUpdate(ImageClient* aClient,
|
||||
nsRefPtr<ImageContainer> >(&UpdateImageClientNow, aClient, aContainer));
|
||||
}
|
||||
|
||||
static void FlushImageSync(ImageClient* aClient, ImageContainer* aContainer, ReentrantMonitor* aBarrier, bool* aDone)
|
||||
static void FlushAllImagesSync(ImageClient* aClient, ImageContainer* aContainer, bool aExceptFront, ReentrantMonitor* aBarrier, bool* aDone)
|
||||
{
|
||||
ImageBridgeChild::FlushImageNow(aClient, aContainer);
|
||||
ImageBridgeChild::FlushAllImagesNow(aClient, aContainer, aExceptFront);
|
||||
|
||||
ReentrantMonitorAutoEnter autoMon(*aBarrier);
|
||||
*aDone = true;
|
||||
@ -432,10 +432,10 @@ static void FlushImageSync(ImageClient* aClient, ImageContainer* aContainer, Ree
|
||||
}
|
||||
|
||||
//static
|
||||
void ImageBridgeChild::FlushImage(ImageClient* aClient, ImageContainer* aContainer)
|
||||
void ImageBridgeChild::FlushAllImages(ImageClient* aClient, ImageContainer* aContainer, bool aExceptFront)
|
||||
{
|
||||
if (InImageBridgeChildThread()) {
|
||||
FlushImageNow(aClient, aContainer);
|
||||
FlushAllImagesNow(aClient, aContainer, aExceptFront);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -445,7 +445,7 @@ void ImageBridgeChild::FlushImage(ImageClient* aClient, ImageContainer* aContain
|
||||
|
||||
sImageBridgeChildSingleton->GetMessageLoop()->PostTask(
|
||||
FROM_HERE,
|
||||
NewRunnableFunction(&FlushImageSync, aClient, aContainer, &barrier, &done));
|
||||
NewRunnableFunction(&FlushAllImagesSync, aClient, aContainer, aExceptFront, &barrier, &done));
|
||||
|
||||
// should stop the thread until the ImageClient has been created on
|
||||
// the other thread
|
||||
@ -455,14 +455,14 @@ void ImageBridgeChild::FlushImage(ImageClient* aClient, ImageContainer* aContain
|
||||
}
|
||||
|
||||
//static
|
||||
void ImageBridgeChild::FlushImageNow(ImageClient* aClient, ImageContainer* aContainer)
|
||||
void ImageBridgeChild::FlushAllImagesNow(ImageClient* aClient, ImageContainer* aContainer, bool aExceptFront)
|
||||
{
|
||||
MOZ_ASSERT(aClient);
|
||||
sImageBridgeChildSingleton->BeginTransaction();
|
||||
if (aContainer) {
|
||||
if (aContainer && !aExceptFront) {
|
||||
aContainer->ClearCurrentImage();
|
||||
}
|
||||
aClient->FlushImage();
|
||||
aClient->FlushAllImages(aExceptFront);
|
||||
aClient->OnTransaction();
|
||||
sImageBridgeChildSingleton->EndTransaction();
|
||||
aClient->FlushTexturesToRemoveCallbacks();
|
||||
|
@ -243,12 +243,12 @@ public:
|
||||
/**
|
||||
* Flush all Images sent to CompositableHost.
|
||||
*/
|
||||
static void FlushImage(ImageClient* aClient, ImageContainer* aContainer);
|
||||
static void FlushAllImages(ImageClient* aClient, ImageContainer* aContainer, bool aExceptFront);
|
||||
|
||||
/**
|
||||
* Must be called on the ImageBridgeChild's thread.
|
||||
*/
|
||||
static void FlushImageNow(ImageClient* aClient, ImageContainer* aContainer);
|
||||
static void FlushAllImagesNow(ImageClient* aClient, ImageContainer* aContainer, bool aExceptFront);
|
||||
|
||||
// CompositableForwarder
|
||||
|
||||
|
@ -186,6 +186,11 @@ void CompositableDataGonkOGL::SetCompositor(Compositor* aCompositor)
|
||||
mCompositor = static_cast<CompositorOGL*>(aCompositor);
|
||||
}
|
||||
|
||||
void CompositableDataGonkOGL::ClearData()
|
||||
{
|
||||
DeleteTextureIfPresent();
|
||||
}
|
||||
|
||||
GLuint CompositableDataGonkOGL::GetTexture()
|
||||
{
|
||||
if (!mTexture) {
|
||||
|
@ -70,6 +70,7 @@ public:
|
||||
virtual ~CompositableDataGonkOGL();
|
||||
|
||||
virtual void SetCompositor(Compositor* aCompositor) MOZ_OVERRIDE;
|
||||
virtual void ClearData() MOZ_OVERRIDE;
|
||||
GLuint GetTexture();
|
||||
void DeleteTextureIfPresent();
|
||||
gl::GLContext* gl() const;
|
||||
|
@ -503,6 +503,13 @@ SetTimezone(const nsCString& aTimezoneSpec)
|
||||
PROXY_IF_SANDBOXED(SetTimezone(aTimezoneSpec));
|
||||
}
|
||||
|
||||
int32_t
|
||||
GetTimezoneOffset()
|
||||
{
|
||||
AssertMainThread();
|
||||
RETURN_PROXY_IF_SANDBOXED(GetTimezoneOffset(), 0);
|
||||
}
|
||||
|
||||
nsCString
|
||||
GetTimezone()
|
||||
{
|
||||
|
@ -259,6 +259,12 @@ void SetTimezone(const nsCString& aTimezoneSpec);
|
||||
*/
|
||||
nsCString GetTimezone();
|
||||
|
||||
/**
|
||||
* Get timezone offset
|
||||
* returns the timezone offset relative to UTC in minutes (DST effect included)
|
||||
*/
|
||||
int32_t GetTimezoneOffset();
|
||||
|
||||
/**
|
||||
* Register observer for system clock changed notification.
|
||||
* @param aObserver The observer that should be added.
|
||||
|
@ -24,6 +24,12 @@ GetTimezone()
|
||||
return EmptyCString();
|
||||
}
|
||||
|
||||
int32_t
|
||||
GetTimezoneOffset()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
EnableSystemClockChangeNotifications()
|
||||
{
|
||||
|
@ -742,7 +742,7 @@ AdjustSystemClock(int64_t aDeltaMilliseconds)
|
||||
hal::NotifySystemClockChange(aDeltaMilliseconds);
|
||||
}
|
||||
|
||||
static int32_t
|
||||
int32_t
|
||||
GetTimezoneOffset()
|
||||
{
|
||||
PRExplodedTime prTime;
|
||||
|
@ -143,6 +143,8 @@ parent:
|
||||
SetTimezone(nsCString aTimezoneSpec);
|
||||
sync GetTimezone()
|
||||
returns (nsCString aTimezoneSpec);
|
||||
sync GetTimezoneOffset()
|
||||
returns (int32_t aTimezoneOffset);
|
||||
EnableSystemClockChangeNotifications();
|
||||
DisableSystemClockChangeNotifications();
|
||||
EnableSystemTimezoneChangeNotifications();
|
||||
|
@ -212,6 +212,14 @@ GetTimezone()
|
||||
return timezone;
|
||||
}
|
||||
|
||||
int32_t
|
||||
GetTimezoneOffset()
|
||||
{
|
||||
int32_t timezoneOffset;
|
||||
Hal()->SendGetTimezoneOffset(&timezoneOffset);
|
||||
return timezoneOffset;
|
||||
}
|
||||
|
||||
void
|
||||
EnableSystemClockChangeNotifications()
|
||||
{
|
||||
@ -684,6 +692,16 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool
|
||||
RecvGetTimezoneOffset(int32_t *aTimezoneOffset) MOZ_OVERRIDE
|
||||
{
|
||||
if (!AssertAppProcessPermission(this, "time")) {
|
||||
return false;
|
||||
}
|
||||
*aTimezoneOffset = hal::GetTimezoneOffset();
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool
|
||||
RecvEnableSystemClockChangeNotifications() MOZ_OVERRIDE
|
||||
{
|
||||
|
@ -14,7 +14,7 @@ DIRS += [
|
||||
if CONFIG['MOZ_B2G_RIL']:
|
||||
DIRS += ['ril']
|
||||
|
||||
if CONFIG['MOZ_B2G_BT']:
|
||||
if CONFIG['MOZ_B2G_BT_BLUEZ']:
|
||||
DIRS += ['dbus']
|
||||
|
||||
if CONFIG['MOZ_B2G_RIL'] or CONFIG['MOZ_B2G_BT']:
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
#include <netinet/in.h>
|
||||
#ifdef MOZ_B2G_BT
|
||||
#ifdef MOZ_B2G_BT_BLUEZ
|
||||
#include <bluetooth/bluetooth.h>
|
||||
#include <bluetooth/sco.h>
|
||||
#include <bluetooth/l2cap.h>
|
||||
@ -31,7 +31,7 @@ union sockaddr_any {
|
||||
sockaddr_un un;
|
||||
sockaddr_in in;
|
||||
sockaddr_in6 in6;
|
||||
#ifdef MOZ_B2G_BT
|
||||
#ifdef MOZ_B2G_BT_BLUEZ
|
||||
sockaddr_sco sco;
|
||||
sockaddr_rc rc;
|
||||
sockaddr_l2 l2;
|
||||
|
@ -127,6 +127,31 @@ HasMouseListener(nsIContent* aContent)
|
||||
elm->HasListenersFor(nsGkAtoms::onmouseup);
|
||||
}
|
||||
|
||||
static bool gTouchEventsRegistered = false;
|
||||
static int32_t gTouchEventsEnabled = 0;
|
||||
|
||||
static bool
|
||||
HasTouchListener(nsIContent* aContent)
|
||||
{
|
||||
nsEventListenerManager* elm = aContent->GetListenerManager(false);
|
||||
if (!elm) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!gTouchEventsRegistered) {
|
||||
Preferences::AddIntVarCache(&gTouchEventsEnabled,
|
||||
"dom.w3c_touch_events.enabled", gTouchEventsEnabled);
|
||||
gTouchEventsRegistered = true;
|
||||
}
|
||||
|
||||
if (!gTouchEventsEnabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return elm->HasListenersFor(nsGkAtoms::ontouchstart) ||
|
||||
elm->HasListenersFor(nsGkAtoms::ontouchend);
|
||||
}
|
||||
|
||||
static bool
|
||||
IsElementClickable(nsIFrame* aFrame, nsIAtom* stopAt = nullptr)
|
||||
{
|
||||
@ -138,7 +163,7 @@ IsElementClickable(nsIFrame* aFrame, nsIAtom* stopAt = nullptr)
|
||||
if (content->IsHTML() && stopAt && tag == stopAt) {
|
||||
break;
|
||||
}
|
||||
if (HasMouseListener(content)) {
|
||||
if (HasTouchListener(content) || HasMouseListener(content)) {
|
||||
return true;
|
||||
}
|
||||
if (content->IsHTML()) {
|
||||
|
@ -45,6 +45,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=780847
|
||||
|
||||
<div id="t8" contenteditable="true" class="target" hidden></div>
|
||||
|
||||
<div id="t9" class="target" ontouchend="x=1" hidden></div>
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
@ -203,6 +205,15 @@ function test3() {
|
||||
});
|
||||
setShowing("t8", false);
|
||||
|
||||
// Check that elements are touchable
|
||||
setShowing("t9", true);
|
||||
var rect = document.getElementById("t9").getBoundingClientRect();
|
||||
testMouseClick("t9", rect.left + 1, rect.top + 1, "t9", "div enabled with mouse input");
|
||||
testMouseClick("t9", rect.left + 1, rect.top + 1, "t9", "div enabled with touch input", {
|
||||
inputSource: SpecialPowers.Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH
|
||||
});
|
||||
setShowing("t9", false);
|
||||
|
||||
// Not yet tested:
|
||||
// -- visited link weight
|
||||
// -- "Closest" using Euclidean distance
|
||||
|
@ -1776,13 +1776,14 @@ abstract public class BrowserApp extends GeckoApp
|
||||
// Set attribute for the menu item in cache, if available
|
||||
if (mAddonMenuItemsCache != null && !mAddonMenuItemsCache.isEmpty()) {
|
||||
for (MenuItemInfo item : mAddonMenuItemsCache) {
|
||||
if (item.id == id) {
|
||||
item.checkable = options.optBoolean("checkable", item.checkable);
|
||||
item.checked = options.optBoolean("checked", item.checked);
|
||||
item.enabled = options.optBoolean("enabled", item.enabled);
|
||||
item.visible = options.optBoolean("visible", item.visible);
|
||||
break;
|
||||
}
|
||||
if (item.id == id) {
|
||||
item.label = options.optString("name", item.label);
|
||||
item.checkable = options.optBoolean("checkable", item.checkable);
|
||||
item.checked = options.optBoolean("checked", item.checked);
|
||||
item.enabled = options.optBoolean("enabled", item.enabled);
|
||||
item.visible = options.optBoolean("visible", item.visible);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1791,6 +1792,7 @@ abstract public class BrowserApp extends GeckoApp
|
||||
|
||||
MenuItem menuItem = mMenu.findItem(id);
|
||||
if (menuItem != null) {
|
||||
menuItem.setTitle(options.optString("name", menuItem.getTitle().toString()));
|
||||
menuItem.setCheckable(options.optBoolean("checkable", menuItem.isCheckable()));
|
||||
menuItem.setChecked(options.optBoolean("checked", menuItem.isChecked()));
|
||||
menuItem.setEnabled(options.optBoolean("enabled", menuItem.isEnabled()));
|
||||
|
@ -4170,7 +4170,7 @@ var BrowserEventHandler = {
|
||||
if (this._scrollableElement != null) {
|
||||
// Discard if it's the top-level scrollable, we let Java handle this
|
||||
let doc = BrowserApp.selectedBrowser.contentDocument;
|
||||
if (this._scrollableElement != doc.documentElement)
|
||||
if (this._scrollableElement != doc.body && this._scrollableElement != doc.documentElement)
|
||||
sendMessageToJava({ type: "Panning:Override" });
|
||||
}
|
||||
}
|
||||
@ -4257,6 +4257,7 @@ var BrowserEventHandler = {
|
||||
|
||||
let doc = BrowserApp.selectedBrowser.contentDocument;
|
||||
if (this._scrollableElement == null ||
|
||||
this._scrollableElement == doc.body ||
|
||||
this._scrollableElement == doc.documentElement) {
|
||||
sendMessageToJava({ type: "Panning:CancelOverride" });
|
||||
return;
|
||||
@ -4627,14 +4628,15 @@ var BrowserEventHandler = {
|
||||
let scrollable = false;
|
||||
while (elem) {
|
||||
/* Element is scrollable if its scroll-size exceeds its client size, and:
|
||||
* - It has overflow 'auto' or 'scroll', or
|
||||
* - It's a textarea or HTML node, or
|
||||
* - It has overflow 'auto' or 'scroll'
|
||||
* - It's a textarea
|
||||
* - It's an HTML/BODY node
|
||||
* - It's a select element showing multiple rows
|
||||
*/
|
||||
if (checkElem) {
|
||||
if ((elem.scrollTopMax > 0 || elem.scrollLeftMax > 0) &&
|
||||
(this._hasScrollableOverflow(elem) ||
|
||||
elem.mozMatchesSelector("html, textarea")) ||
|
||||
elem.mozMatchesSelector("html, body, textarea")) ||
|
||||
(elem instanceof HTMLSelectElement && (elem.size > 1 || elem.multiple))) {
|
||||
scrollable = true;
|
||||
break;
|
||||
|
@ -262,9 +262,9 @@ AlertDownloadProgressListener.prototype = {
|
||||
case Ci.nsIDownloadManager.DOWNLOAD_FINISHED: {
|
||||
Downloads.removeNotification(aDownload);
|
||||
if (aDownload.isPrivate) {
|
||||
let index = this._privateDownloads.indexOf(aDownload);
|
||||
let index = Downloads._privateDownloads.indexOf(aDownload);
|
||||
if (index != -1) {
|
||||
this._privateDownloads.splice(index, 1);
|
||||
Downloads._privateDownloads.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4281,6 +4281,7 @@ pref("dom.sms.enabled", false);
|
||||
// 7-bit default alphabet.
|
||||
pref("dom.sms.strict7BitEncoding", false);
|
||||
pref("dom.sms.requestStatusReport", true);
|
||||
pref("dom.mms.requestStatusReport", true);
|
||||
|
||||
// WebContacts
|
||||
pref("dom.mozContacts.enabled", false);
|
||||
@ -4397,7 +4398,7 @@ pref("dom.browserElement.maxScreenshotDelayMS", 2000);
|
||||
// Whether we should show the placeholder when the element is focused but empty.
|
||||
pref("dom.placeholder.show_on_focus", true);
|
||||
|
||||
// UAProfile settings
|
||||
// MMS UA Profile settings
|
||||
pref("wap.UAProf.url", "");
|
||||
pref("wap.UAProf.tagname", "x-wap-profile");
|
||||
|
||||
@ -4458,6 +4459,18 @@ pref("dom.datastore.enabled", false);
|
||||
// Telephony API
|
||||
pref("dom.telephony.enabled", false);
|
||||
|
||||
// Cell Broadcast API
|
||||
pref("dom.cellbroadcast.enabled", false);
|
||||
|
||||
// ICC API
|
||||
pref("dom.icc.enabled", false);
|
||||
|
||||
// Mobile Connection API
|
||||
pref("dom.mobileconnection.enabled", false);
|
||||
|
||||
// Voice Mail API
|
||||
pref("dom.voicemail.enabled", false);
|
||||
|
||||
// DOM Inter-App Communication API.
|
||||
pref("dom.inter-app-communication-api.enabled", false);
|
||||
|
||||
|
@ -956,7 +956,7 @@
|
||||
},
|
||||
|
||||
startFade : function (element, fadeIn, immediate) {
|
||||
if (element.className == "controlBar" && fadeIn) {
|
||||
if (element.classList.contains("controlBar") && fadeIn) {
|
||||
// Bug 493523, the scrubber doesn't call valueChanged while hidden,
|
||||
// so our dependent state (eg, timestamp in the thumb) will be stale.
|
||||
// As a workaround, update it manually when it first becomes unhidden.
|
||||
@ -975,11 +975,11 @@
|
||||
// when we remove the attribute.
|
||||
element.clientTop;
|
||||
element.removeAttribute("fadeout");
|
||||
if (element.className == "controlBar")
|
||||
if (element.classList.contains("controlBar"))
|
||||
this.controlsSpacer.removeAttribute("hideCursor");
|
||||
} else {
|
||||
element.setAttribute("fadeout", true);
|
||||
if (element.className == "controlBar" && !this.hasError() &&
|
||||
if (element.classList.contains("controlBar") && !this.hasError() &&
|
||||
document.mozFullScreenElement == this.video)
|
||||
this.controlsSpacer.setAttribute("hideCursor", true);
|
||||
|
||||
|
@ -28,6 +28,14 @@ const REGEX_HSL_3_TUPLE = /^\bhsl\(([\d.]+),\s*([\d.]+%),\s*([\d.]+%)\)$/i;
|
||||
*/
|
||||
const REGEX_ALL_COLORS = /#[0-9a-fA-F]{3}\b|#[0-9a-fA-F]{6}\b|hsl\(.*?\)|hsla\(.*?\)|rgba?\(.*?\)|\b[a-zA-Z-]+\b/g;
|
||||
|
||||
const SPECIALVALUES = new Set([
|
||||
"currentcolor",
|
||||
"initial",
|
||||
"inherit",
|
||||
"transparent",
|
||||
"unset"
|
||||
]);
|
||||
|
||||
let {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
|
||||
|
||||
/**
|
||||
@ -89,7 +97,7 @@ CssColor.prototype = {
|
||||
authored: null,
|
||||
|
||||
get hasAlpha() {
|
||||
if (!this.valid || this.transparent) {
|
||||
if (!this.valid) {
|
||||
return false;
|
||||
}
|
||||
return this._getRGBATuple().a !== 1;
|
||||
@ -99,28 +107,35 @@ CssColor.prototype = {
|
||||
return this._validateColor(this.authored);
|
||||
},
|
||||
|
||||
/**
|
||||
* Return true for all transparent values e.g. rgba(0, 0, 0, 0).
|
||||
*/
|
||||
get transparent() {
|
||||
try {
|
||||
let tuple = this._getRGBATuple();
|
||||
return tuple === "transparent";
|
||||
return !(tuple.r || tuple.g || tuple.b || tuple.a);
|
||||
} catch(e) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
get specialValue() {
|
||||
if (SPECIALVALUES.has(this.authored)) {
|
||||
return this.authored;
|
||||
}
|
||||
},
|
||||
|
||||
get name() {
|
||||
if (!this.valid) {
|
||||
return "";
|
||||
}
|
||||
if (this.authored === "transparent") {
|
||||
return "transparent";
|
||||
if (this.specialValue) {
|
||||
return this.specialValue;
|
||||
}
|
||||
|
||||
try {
|
||||
let tuple = this._getRGBATuple();
|
||||
|
||||
if (tuple === "transparent") {
|
||||
return "transparent";
|
||||
}
|
||||
if (tuple.a !== 1) {
|
||||
return this.rgb;
|
||||
}
|
||||
@ -135,12 +150,12 @@ CssColor.prototype = {
|
||||
if (!this.valid) {
|
||||
return "";
|
||||
}
|
||||
if (this.specialValue) {
|
||||
return this.specialValue;
|
||||
}
|
||||
if (this.hasAlpha) {
|
||||
return this.rgba;
|
||||
}
|
||||
if (this.transparent) {
|
||||
return "transparent";
|
||||
}
|
||||
|
||||
let hex = this.longHex;
|
||||
if (hex.charAt(1) == hex.charAt(2) &&
|
||||
@ -155,12 +170,12 @@ CssColor.prototype = {
|
||||
if (!this.valid) {
|
||||
return "";
|
||||
}
|
||||
if (this.specialValue) {
|
||||
return this.specialValue;
|
||||
}
|
||||
if (this.hasAlpha) {
|
||||
return this.rgba;
|
||||
}
|
||||
if (this.transparent) {
|
||||
return "transparent";
|
||||
}
|
||||
return this.rgb.replace(/\brgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)/gi, function(_, r, g, b) {
|
||||
return "#" + ((1 << 24) + (r << 16) + (g << 8) + (b << 0)).toString(16).substr(-6).toUpperCase();
|
||||
});
|
||||
@ -170,8 +185,8 @@ CssColor.prototype = {
|
||||
if (!this.valid) {
|
||||
return "";
|
||||
}
|
||||
if (this.transparent) {
|
||||
return "transparent";
|
||||
if (this.specialValue) {
|
||||
return this.specialValue;
|
||||
}
|
||||
if (!this.hasAlpha) {
|
||||
if (this.authored.startsWith("rgb(")) {
|
||||
@ -188,8 +203,8 @@ CssColor.prototype = {
|
||||
if (!this.valid) {
|
||||
return "";
|
||||
}
|
||||
if (this.transparent) {
|
||||
return "transparent";
|
||||
if (this.specialValue) {
|
||||
return this.specialValue;
|
||||
}
|
||||
if (this.authored.startsWith("rgba(")) {
|
||||
// The color is valid and begins with rgba(. Return the authored value.
|
||||
@ -206,8 +221,8 @@ CssColor.prototype = {
|
||||
if (!this.valid) {
|
||||
return "";
|
||||
}
|
||||
if (this.transparent) {
|
||||
return "transparent";
|
||||
if (this.specialValue) {
|
||||
return this.specialValue;
|
||||
}
|
||||
if (this.authored.startsWith("hsl(")) {
|
||||
// The color is valid and begins with hsl(. Return the authored value.
|
||||
@ -223,8 +238,8 @@ CssColor.prototype = {
|
||||
if (!this.valid) {
|
||||
return "";
|
||||
}
|
||||
if (this.transparent) {
|
||||
return "transparent";
|
||||
if (this.specialValue) {
|
||||
return this.specialValue;
|
||||
}
|
||||
if (this.authored.startsWith("hsla(")) {
|
||||
// The color is valid and begins with hsla(. Return the authored value.
|
||||
@ -290,7 +305,7 @@ CssColor.prototype = {
|
||||
let computed = win.getComputedStyle(span).color;
|
||||
|
||||
if (computed === "transparent") {
|
||||
return "transparent";
|
||||
return {r: 0, g: 0, b: 0, a: 0};
|
||||
}
|
||||
|
||||
let rgba = computed.match(REGEX_RGBA_4_TUPLE);
|
||||
|
@ -89,7 +89,7 @@ ifdef MOZ_B2G_RIL #{
|
||||
STATIC_LIBS += mozril_s
|
||||
endif #}
|
||||
|
||||
ifdef MOZ_B2G_BT #{
|
||||
ifdef MOZ_B2G_BT_BLUEZ #{
|
||||
STATIC_LIBS += mozdbus_s
|
||||
ifeq (gonk,$(MOZ_WIDGET_TOOLKIT))
|
||||
OS_LIBS += -ldbus
|
||||
|
Loading…
Reference in New Issue
Block a user