diff --git a/CLOBBER b/CLOBBER
index 221cfa2e536..79939c511a1 100644
--- a/CLOBBER
+++ b/CLOBBER
@@ -22,4 +22,6 @@
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
# don't change CLOBBER for WebIDL changes any more.
-bug 940506: removing idl files appears to break the build without a clobber
+Bug 1033481 - Use a .mozbuild file rather than a .mk in
+m/a/tests/background/junit3 -- doesn't clean the classes* directory
+when moving from in APK to in JAR building.
diff --git a/addon-sdk/source/lib/sdk/base64.js b/addon-sdk/source/lib/sdk/base64.js
index 5879a02ffcf..e96dd8534ea 100644
--- a/addon-sdk/source/lib/sdk/base64.js
+++ b/addon-sdk/source/lib/sdk/base64.js
@@ -11,7 +11,9 @@ module.metadata = {
const { Cu } = require("chrome");
// Passing an empty object as second argument to avoid scope's pollution
-const { atob, btoa } = Cu.import("resource://gre/modules/Services.jsm", {});
+// (devtools loader injects these symbols as global and prevent using
+// const here)
+var { atob, btoa } = Cu.import("resource://gre/modules/Services.jsm", {});
function isUTF8(charset) {
let type = typeof charset;
diff --git a/addon-sdk/source/lib/toolkit/loader.js b/addon-sdk/source/lib/toolkit/loader.js
index c73c1ad5bde..bda348c85db 100644
--- a/addon-sdk/source/lib/toolkit/loader.js
+++ b/addon-sdk/source/lib/toolkit/loader.js
@@ -283,17 +283,31 @@ const load = iced(function load(loader, module) {
}
});
- let sandbox = sandboxes[module.uri] = Sandbox({
- name: module.uri,
- prototype: create(globals, descriptors),
- wantXrays: false,
- wantGlobalProperties: module.id == "sdk/indexed-db" ? ["indexedDB"] : [],
- invisibleToDebugger: loader.invisibleToDebugger,
- metadata: {
- addonID: loader.id,
- URI: module.uri
- }
- });
+ let sandbox;
+ if (loader.sharedGlobalSandbox &&
+ loader.sharedGlobalBlacklist.indexOf(module.id) == -1) {
+ // Create a new object in this sandbox, that will be used as
+ // the scope object for this particular module
+ sandbox = new loader.sharedGlobalSandbox.Object();
+ // Inject all expected globals in the scope object
+ getOwnPropertyNames(globals).forEach(function(name) {
+ descriptors[name] = getOwnPropertyDescriptor(globals, name)
+ });
+ define(sandbox, descriptors);
+ } else {
+ sandbox = Sandbox({
+ name: module.uri,
+ prototype: create(globals, descriptors),
+ wantXrays: false,
+ wantGlobalProperties: module.id == "sdk/indexed-db" ? ["indexedDB"] : [],
+ invisibleToDebugger: loader.invisibleToDebugger,
+ metadata: {
+ addonID: loader.id,
+ URI: module.uri
+ }
+ });
+ }
+ sandboxes[module.uri] = sandbox;
try {
evaluate(sandbox, module.uri);
@@ -691,8 +705,8 @@ const Loader = iced(function Loader(options) {
});
let {
- modules, globals, resolve, paths, rootURI,
- manifest, requireMap, isNative, metadata
+ modules, globals, resolve, paths, rootURI, manifest, requireMap, isNative,
+ metadata, sharedGlobal, sharedGlobalBlacklist
} = override({
paths: {},
modules: {},
@@ -702,6 +716,7 @@ const Loader = iced(function Loader(options) {
resolve: options.isNative ?
exports.nodeResolve :
exports.resolve,
+ sharedGlobalBlacklist: ["sdk/indexed-db"]
}, options);
// We create an identity object that will be dispatched on an unload
@@ -738,6 +753,24 @@ const Loader = iced(function Loader(options) {
return result;
}, {});
+ let sharedGlobalSandbox;
+ if (sharedGlobal) {
+ // Create the unique sandbox we will be using for all modules,
+ // so that we prevent creating a new comportment per module.
+ // The side effect is that all modules will share the same
+ // global objects.
+ sharedGlobalSandbox = Sandbox({
+ name: "Addon-SDK",
+ wantXrays: false,
+ wantGlobalProperties: [],
+ invisibleToDebugger: options.invisibleToDebugger || false,
+ metadata: {
+ addonID: options.id,
+ URI: "Addon-SDK"
+ }
+ });
+ }
+
// Loader object is just a representation of a environment
// state. We freeze it and mark make it's properties non-enumerable
// as they are pure implementation detail that no one should rely upon.
@@ -748,6 +781,8 @@ const Loader = iced(function Loader(options) {
// Map of module objects indexed by module URIs.
modules: { enumerable: false, value: modules },
metadata: { enumerable: false, value: metadata },
+ sharedGlobalSandbox: { enumerable: false, value: sharedGlobalSandbox },
+ sharedGlobalBlacklist: { enumerable: false, value: sharedGlobalBlacklist },
// Map of module sandboxes indexed by module URIs.
sandboxes: { enumerable: false, value: {} },
resolve: { enumerable: false, value: resolve },
diff --git a/addon-sdk/source/test/test-loader.js b/addon-sdk/source/test/test-loader.js
index 4ddcc00883c..03ea1b3e0a1 100644
--- a/addon-sdk/source/test/test-loader.js
+++ b/addon-sdk/source/test/test-loader.js
@@ -343,4 +343,27 @@ exports['test console global by default'] = function (assert) {
function fakeConsole () {};
};
+exports['test shared globals'] = function(assert) {
+ let uri = root + '/fixtures/loader/cycles/';
+ let loader = Loader({ paths: { '': uri }, sharedGlobal: true,
+ sharedGlobalBlacklist: ['b'] });
+
+ let program = main(loader, 'main');
+
+ // As it is hard to verify what is the global of an object
+ // (due to wrappers) we check that we see the `foo` symbol
+ // being manually injected into the shared global object
+ loader.sharedGlobalSandbox.foo = true;
+
+ let m = loader.sandboxes[uri + 'main.js'];
+ let a = loader.sandboxes[uri + 'a.js'];
+ let b = loader.sandboxes[uri + 'b.js'];
+
+ assert.ok(Cu.getGlobalForObject(m).foo, "main is shared");
+ assert.ok(Cu.getGlobalForObject(a).foo, "a is shared");
+ assert.ok(!Cu.getGlobalForObject(b).foo, "b isn't shared");
+
+ unload(loader);
+}
+
require('test').run(exports);
diff --git a/b2g/chrome/content/shell.js b/b2g/chrome/content/shell.js
index ea94ceb40f0..f29de04e91e 100644
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -9,7 +9,6 @@ Cu.import('resource://gre/modules/SettingsChangeNotifier.jsm');
Cu.import('resource://gre/modules/DataStoreChangeNotifier.jsm');
Cu.import('resource://gre/modules/AlarmService.jsm');
Cu.import('resource://gre/modules/ActivitiesService.jsm');
-Cu.import('resource://gre/modules/PermissionPromptHelper.jsm');
Cu.import('resource://gre/modules/NotificationDB.jsm');
Cu.import('resource://gre/modules/Payment.jsm');
Cu.import("resource://gre/modules/AppsUtils.jsm");
diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml
index 0b16e2da563..5aba8f5eeb6 100644
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -19,13 +19,13 @@
-
+
-
+
diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml
index c2d47a92600..6bba9f9ba8b 100644
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -17,10 +17,10 @@
-
+
-
+
@@ -128,7 +128,7 @@
-
+
diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml
index c89c913fc7d..9942471605d 100644
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -15,7 +15,7 @@
-
+
@@ -23,7 +23,7 @@
-
+
diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml
index 0b16e2da563..5aba8f5eeb6 100644
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -19,13 +19,13 @@
-
+
-
+
diff --git a/b2g/config/flame/sources.xml b/b2g/config/flame/sources.xml
index b85ff724df6..cc384eaa598 100644
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -17,10 +17,10 @@
-
+
-
+
diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json
index 8655d50f8d3..1b751b151bd 100644
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -4,6 +4,6 @@
"remote": "",
"branch": ""
},
- "revision": "3e612bc9b3d79a3fa36d2f38af4202abb0ead68f",
+ "revision": "190172ac413ab6476a6d7df3999950ec756f96a4",
"repo_path": "/integration/gaia-central"
}
diff --git a/b2g/config/hamachi/sources.xml b/b2g/config/hamachi/sources.xml
index 332a3d657d7..2053ab0c8aa 100644
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -17,12 +17,12 @@
-
+
-
+
diff --git a/b2g/config/helix/sources.xml b/b2g/config/helix/sources.xml
index ecad1f8aeea..a04b3138050 100644
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -15,7 +15,7 @@
-
+
diff --git a/b2g/config/nexus-4/sources.xml b/b2g/config/nexus-4/sources.xml
index 78f96f25673..90a10f563ce 100644
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -17,10 +17,10 @@
-
+
-
+
diff --git a/b2g/config/wasabi/sources.xml b/b2g/config/wasabi/sources.xml
index 384a9ec720e..a55ec3feb2c 100644
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -17,12 +17,12 @@
-
+
-
+
diff --git a/browser/components/preferences/content.xul b/browser/components/preferences/content.xul
index 89ad253a4e8..fcddb544f3d 100644
--- a/browser/components/preferences/content.xul
+++ b/browser/components/preferences/content.xul
@@ -149,10 +149,11 @@
label="&translateWebPages.label;." accesskey="&translateWebPages.accesskey;"
onsyncfrompreference="return gContentPane.updateButtons('translateButton',
'browser.translation.detectLanguage');"/>
-
+
+
-
+
+
+
diff --git a/browser/devtools/app-manager/app-projects.js b/browser/devtools/app-manager/app-projects.js
index f1466c8f141..4d50fcce65d 100644
--- a/browser/devtools/app-manager/app-projects.js
+++ b/browser/devtools/app-manager/app-projects.js
@@ -14,14 +14,13 @@ const { indexedDB } = require("sdk/indexed-db");
* a unique `location` object.
*/
-const global = this;
const IDB = {
_db: null,
open: function () {
let deferred = promise.defer();
- let request = global.indexedDB.open("AppProjects", 5);
+ let request = indexedDB.open("AppProjects", 5);
request.onerror = function(event) {
deferred.reject("Unable to open AppProjects indexedDB. " +
"Error code: " + event.target.errorCode);
diff --git a/browser/devtools/framework/toolbox-options.js b/browser/devtools/framework/toolbox-options.js
index a0a604311c5..b125b0bc510 100644
--- a/browser/devtools/framework/toolbox-options.js
+++ b/browser/devtools/framework/toolbox-options.js
@@ -6,6 +6,7 @@
const {Cu, Cc, Ci} = require("chrome");
const Services = require("Services");
+const promise = require("promise");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "gDevTools", "resource:///modules/devtools/gDevTools.jsm");
diff --git a/browser/devtools/projecteditor/lib/shells.js b/browser/devtools/projecteditor/lib/shells.js
index 88d5b5eab68..de1e0fb4f6a 100644
--- a/browser/devtools/projecteditor/lib/shells.js
+++ b/browser/devtools/projecteditor/lib/shells.js
@@ -10,6 +10,7 @@ const { EventTarget } = require("sdk/event/target");
const { emit } = require("sdk/event/core");
const { EditorTypeForResource } = require("projecteditor/editors");
const NetworkHelper = require("devtools/toolkit/webconsole/network-helper");
+const promise = require("promise");
/**
* The Shell is the object that manages the editor for a single resource.
diff --git a/browser/devtools/shared/test/browser_graphs-06.js b/browser/devtools/shared/test/browser_graphs-06.js
index eb9bc53776c..5d28bcda033 100644
--- a/browser/devtools/shared/test/browser_graphs-06.js
+++ b/browser/devtools/shared/test/browser_graphs-06.js
@@ -65,6 +65,14 @@ function testGraph(graph) {
"The mapped selection's min value is correct (5).");
is(graph.getMappedSelection().max, max,
"The mapped selection's max value is correct (6).");
+
+ graph.setSelection({ start: graph.width + 100, end: -100 });
+ min = map(0, 0, graph.width, 112, 4180);
+ max = map(graph.width, 0, graph.width, 112, 4180);
+ is(graph.getMappedSelection().min, min,
+ "The mapped selection's min value is correct (7).");
+ is(graph.getMappedSelection().max, max,
+ "The mapped selection's max value is correct (8).");
}
/**
diff --git a/browser/devtools/shared/widgets/Graphs.jsm b/browser/devtools/shared/widgets/Graphs.jsm
index dce8f9b6e92..a80e1830169 100644
--- a/browser/devtools/shared/widgets/Graphs.jsm
+++ b/browser/devtools/shared/widgets/Graphs.jsm
@@ -136,9 +136,11 @@ GraphSelectionResizer.prototype = {
this.AbstractCanvasGraph = function(parent, name, sharpness) {
EventEmitter.decorate(this);
- this._ready = promise.defer();
this._parent = parent;
+ this._ready = promise.defer();
+
this._uid = "canvas-graph-" + Date.now();
+ this._renderTargets = new Map();
AbstractCanvasGraph.createIframe(GRAPH_SRC, parent, iframe => {
this._iframe = iframe;
@@ -231,7 +233,9 @@ AbstractCanvasGraph.prototype = {
this._data = null;
this._regions = null;
+ this._cachedBackgroundImage = null;
this._cachedGraphImage = null;
+ this._renderTargets.clear();
gCachedStripePattern.clear();
},
@@ -254,6 +258,14 @@ AbstractCanvasGraph.prototype = {
fixedWidth: null,
fixedHeight: null,
+ /**
+ * Optionally builds and caches a background image for this graph.
+ * Inheriting classes may override this method.
+ */
+ buildBackgroundImage: function() {
+ return null;
+ },
+
/**
* Builds and caches a graph image, based on the data source supplied
* in `setData`. The graph image is not rebuilt on each frame, but
@@ -278,6 +290,7 @@ AbstractCanvasGraph.prototype = {
*/
setData: function(data) {
this._data = data;
+ this._cachedBackgroundImage = this.buildBackgroundImage();
this._cachedGraphImage = this.buildGraphImage();
this._shouldRedraw = true;
},
@@ -392,7 +405,7 @@ AbstractCanvasGraph.prototype = {
*/
getMappedSelection: function(unpack = e => e.delta) {
if (!this.hasData() || !this.hasSelection()) {
- return { start: null, end: null };
+ return { min: null, max: null };
}
let selection = this.getSelection();
let totalTicks = this._data.length;
@@ -401,8 +414,9 @@ AbstractCanvasGraph.prototype = {
// The selection's start and end values are not guaranteed to be ascending.
// This can happen, for example, when click & dragging from right to left.
- let min = Math.min(selection.start, selection.end);
- let max = Math.max(selection.start, selection.end);
+ // Also make sure that the selection bounds fit inside the canvas bounds.
+ let min = Math.max(Math.min(selection.start, selection.end), 0);
+ let max = Math.min(Math.max(selection.start, selection.end), this._width);
min = map(min, 0, this._width, firstTick, lastTick);
max = map(max, 0, this._width, firstTick, lastTick);
@@ -564,10 +578,11 @@ AbstractCanvasGraph.prototype = {
this._width = this._canvas.width = bounds.width * this._pixelRatio;
this._height = this._canvas.height = bounds.height * this._pixelRatio;
- if (this._data) {
+ if (this.hasData()) {
+ this._cachedBackgroundImage = this.buildBackgroundImage();
this._cachedGraphImage = this.buildGraphImage();
}
- if (this._regions) {
+ if (this.hasRegions()) {
this._bakeRegions(this._regions, this._cachedGraphImage);
}
@@ -575,6 +590,38 @@ AbstractCanvasGraph.prototype = {
this.emit("refresh");
},
+ /**
+ * Gets a canvas with the specified name, for this graph.
+ *
+ * If it doesn't exist yet, it will be created, otherwise the cached instance
+ * will be cleared and returned.
+ *
+ * @param string name
+ * The canvas name.
+ * @param number width, height [optional]
+ * A custom width and height for the canvas. Defaults to this graph's
+ * container canvas width and height.
+ */
+ _getNamedCanvas: function(name, width = this._width, height = this._height) {
+ let cachedRenderTarget = this._renderTargets.get(name);
+ if (cachedRenderTarget) {
+ let { canvas, ctx } = cachedRenderTarget;
+ canvas.width = width;
+ canvas.height = height;
+ ctx.clearRect(0, 0, width, height);
+ return cachedRenderTarget;
+ }
+
+ let canvas = this._document.createElementNS(HTML_NS, "canvas");
+ let ctx = canvas.getContext("2d");
+ canvas.width = width;
+ canvas.height = height;
+
+ let renderTarget = { canvas: canvas, ctx: ctx };
+ this._renderTargets.set(name, renderTarget);
+ return renderTarget;
+ },
+
/**
* The contents of this graph are redrawn only when something changed,
* like the data source, or the selection bounds etc. This flag tracks
@@ -598,14 +645,16 @@ AbstractCanvasGraph.prototype = {
if (!this._shouldRedraw) {
return;
}
-
let ctx = this._ctx;
ctx.clearRect(0, 0, this._width, this._height);
- // Draw the graph underneath the cursor and selection.
- if (this.hasData()) {
+ if (this._cachedBackgroundImage) {
+ ctx.drawImage(this._cachedBackgroundImage, 0, 0, this._width, this._height);
+ }
+ if (this._cachedGraphImage) {
ctx.drawImage(this._cachedGraphImage, 0, 0, this._width, this._height);
}
+
if (this.hasCursor()) {
this._drawCliphead();
}
@@ -1009,7 +1058,7 @@ AbstractCanvasGraph.prototype = {
* Listener for the "resize" event on the graph's parent node.
*/
_onResize: function() {
- if (this._cachedGraphImage) {
+ if (this.hasData()) {
setNamedTimeout(this._uid, GRAPH_RESIZE_EVENTS_DRAIN, this.refresh);
}
}
@@ -1074,10 +1123,9 @@ LineGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
* @see AbstractCanvasGraph.prototype.buildGraphImage
*/
buildGraphImage: function() {
- let canvas = this._document.createElementNS(HTML_NS, "canvas");
- let ctx = canvas.getContext("2d");
- let width = canvas.width = this._width;
- let height = canvas.height = this._height;
+ let { canvas, ctx } = this._getNamedCanvas("line-graph-data");
+ let width = this._width;
+ let height = this._height;
let totalTicks = this._data.length;
let firstTick = this._data[0].delta;
@@ -1334,6 +1382,24 @@ BarGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
*/
minBlocksHeight: BAR_GRAPH_MIN_BLOCKS_HEIGHT,
+ /**
+ * Renders the graph's background.
+ * @see AbstractCanvasGraph.prototype.buildBackgroundImage
+ */
+ buildBackgroundImage: function() {
+ let { canvas, ctx } = this._getNamedCanvas("bar-graph-background");
+ let width = this._width;
+ let height = this._height;
+
+ let gradient = ctx.createLinearGradient(0, 0, 0, height);
+ gradient.addColorStop(0, BAR_GRAPH_BACKGROUND_GRADIENT_START);
+ gradient.addColorStop(1, BAR_GRAPH_BACKGROUND_GRADIENT_END);
+ ctx.fillStyle = gradient;
+ ctx.fillRect(0, 0, width, height);
+
+ return canvas;
+ },
+
/**
* Renders the graph on a canvas.
* @see AbstractCanvasGraph.prototype.buildGraphImage
@@ -1342,11 +1408,9 @@ BarGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
if (!this.format || !this.format.length) {
throw "The graph format traits are mandatory to style the data source.";
}
-
- let canvas = this._document.createElementNS(HTML_NS, "canvas");
- let ctx = canvas.getContext("2d");
- let width = canvas.width = this._width;
- let height = canvas.height = this._height;
+ let { canvas, ctx } = this._getNamedCanvas("bar-graph-data");
+ let width = this._width;
+ let height = this._height;
let totalTypes = this.format.length;
let totalTicks = this._data.length;
@@ -1364,14 +1428,6 @@ BarGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
minBarsWidth: minBarsWidth
}) * BAR_GRAPH_DAMPEN_VALUES;
- // Draw the background.
-
- let gradient = ctx.createLinearGradient(0, 0, 0, height);
- gradient.addColorStop(0, BAR_GRAPH_BACKGROUND_GRADIENT_START);
- gradient.addColorStop(1, BAR_GRAPH_BACKGROUND_GRADIENT_END);
- ctx.fillStyle = gradient;
- ctx.fillRect(0, 0, width, height);
-
// Draw the graph.
// Iterate over the blocks, then the bars, to draw all rectangles of
diff --git a/browser/devtools/webide/modules/runtimes.js b/browser/devtools/webide/modules/runtimes.js
index 3750afb774e..0330f9e0caa 100644
--- a/browser/devtools/webide/modules/runtimes.js
+++ b/browser/devtools/webide/modules/runtimes.js
@@ -9,6 +9,7 @@ const {Simulator} = Cu.import("resource://gre/modules/devtools/Simulator.jsm");
const {ConnectionManager, Connection} = require("devtools/client/connection-manager");
const {DebuggerServer} = require("resource://gre/modules/devtools/dbg-server.jsm");
const discovery = require("devtools/toolkit/discovery/discovery");
+const promise = require("promise");
const Strings = Services.strings.createBundle("chrome://webide/content/webide.properties");
diff --git a/browser/locales/en-US/chrome/browser/preferences/content.dtd b/browser/locales/en-US/chrome/browser/preferences/content.dtd
index a2ab3a153ea..5966084114a 100644
--- a/browser/locales/en-US/chrome/browser/preferences/content.dtd
+++ b/browser/locales/en-US/chrome/browser/preferences/content.dtd
@@ -32,3 +32,13 @@
+
+
+
+
diff --git a/browser/locales/en-US/chrome/browser/translation.dtd b/browser/locales/en-US/chrome/browser/translation.dtd
index 2a8ed0135b5..dbcd4ac05d3 100644
--- a/browser/locales/en-US/chrome/browser/translation.dtd
+++ b/browser/locales/en-US/chrome/browser/translation.dtd
@@ -56,3 +56,11 @@
+
+
+
+
diff --git a/browser/themes/osx/browser.css b/browser/themes/osx/browser.css
index ab3bf910bc6..47074f55d92 100644
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -3627,6 +3627,7 @@ notification[value="translation"] {
}
button.translate-infobar-element {
+ background: linear-gradient(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0.1)) repeat scroll 0% 0% padding-box transparent;
color: #333333;
border: 1px solid;
border-color: rgba(23, 51, 78, 0.15) rgba(23, 51, 78, 0.17) rgba(23, 51, 78, 0.2);
@@ -3650,6 +3651,7 @@ label.translate-infobar-element {
}
button.translate-infobar-element:hover {
+ background: #f0f0f0;
box-shadow: 0 1px 0 hsla(0,0%,100%,.1) inset, 0 0 0 1px hsla(0,0%,100%,.05) inset, 0 1px 0 hsla(210,54%,20%,.01), 0 0 4px hsla(206,100%,20%,.1);
}
@@ -3660,7 +3662,7 @@ button.translate-infobar-element:active {
button.translate-infobar-element[anonid="translate"] {
color: #ffffff;
- background-image: linear-gradient(#4cb1ff, #1793e5);
+ background: linear-gradient(#4cb1ff, #1793e5);
box-shadow: 0 1px 0 hsla(0,0%,100%,.2) inset, 0 0 0 1px hsla(0,0%,100%,.1) inset, 0 1px 0 hsla(210,54%,20%,.03);
border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.17) hsla(210,54%,20%,.2);
padding: 0 1.1em !important;;
@@ -3668,35 +3670,28 @@ button.translate-infobar-element[anonid="translate"] {
button.translate-infobar-element[anonid="translate"]:hover {
background-image: linear-gradient(#66bdff, #0d9eff);
-}
-
-button.translate-infobar-element[anonid="notNow"] {
- background: linear-gradient(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0.1)) repeat scroll 0% 0% padding-box transparent;
-}
-
-button.translate-infobar-element[anonid="notNow"]:hover {
- background: #f0f0f0;
+ box-shadow: 0 1px 0 hsla(0,0%,100%,.2) inset, 0 0 0 1px hsla(0,0%,100%,.1) inset, 0 1px 0 hsla(210,54%,20%,.03), 0 0 4px hsla(206,100%,20%,.2);
}
button.translate-infobar-element.options-menu-button {
-moz-padding-start: 0.5em !important;
- -moz-padding-end: 0.3em !important;
+ -moz-padding-end: 0em !important;
}
button.translate-infobar-element.options-menu-button > .button-box > .button-menu-dropmarker {
display: -moz-box;
- list-style-image: url("chrome://browser/skin/toolbarbutton-dropmarker.png");
+ list-style-image: url("chrome://global/skin/icons/glyph-dropdown.png");
padding: 0 !important;
margin: 0 !important;
}
-@media (min-resolution: 1.25dppx) {
+@media (min-resolution: 2dppx) {
button.translate-infobar-element.options-menu-button > .button-box > .button-menu-dropmarker {
- list-style-image: url("chrome://browser/skin/toolbarbutton-dropmarker@2x.png");
+ list-style-image: url("chrome://global/skin/icons/glyph-dropdown@2x.png");
}
button.translate-infobar-element.options-menu-button > .button-box > .button-menu-dropmarker > .dropmarker-icon {
- width: 7px;
+ width: 8px;
}
}
@@ -3709,6 +3704,7 @@ menulist.translate-infobar-element {
background-image: linear-gradient(#FFFFFF, rgba(255,255,255,0.1));
color: #333333;
padding: 0;
+ min-height: 22px !important;
}
menulist.translate-infobar-element > .menulist-label-box {
@@ -3719,7 +3715,8 @@ menulist.translate-infobar-element > .menulist-label-box {
}
menulist.translate-infobar-element:hover {
- background-image: linear-gradient(#FFFFFF, rgba(255,255,255,0.6));
+ background: #f0f0f0;
+ box-shadow: 0 1px 0 hsla(0,0%,100%,.1) inset, 0 0 0 1px hsla(0,0%,100%,.05) inset, 0 1px 0 hsla(210,54%,20%,.01), 0 0 4px hsla(206,100%,20%,.1);
}
menulist.translate-infobar-element[open="true"] {
@@ -3729,7 +3726,17 @@ menulist.translate-infobar-element[open="true"] {
menulist.translate-infobar-element > .menulist-dropmarker {
display: -moz-box;
- list-style-image: url("chrome://global/skin/icons/menulist-dropmarker.png");
+ list-style-image: url("chrome://global/skin/icons/glyph-dropdown.png");
+}
+
+@media (min-resolution: 2dppx) {
+ menulist.translate-infobar-element > .menulist-dropmarker {
+ list-style-image: url("chrome://global/skin/icons/glyph-dropdown@2x.png");
+ }
+
+ menulist.translate-infobar-element > .menulist-dropmarker > .dropmarker-icon {
+ width: 8px;
+ }
}
diff --git a/configure.in b/configure.in
index 4a81b36ab77..4fa32bf4d7d 100644
--- a/configure.in
+++ b/configure.in
@@ -3895,6 +3895,7 @@ MOZ_SAFE_BROWSING=
MOZ_HELP_VIEWER=
MOZ_SPELLCHECK=1
MOZ_ANDROID_OMTC=
+MOZ_NATIVE_CASTING=1
MOZ_TOOLKIT_SEARCH=1
MOZ_UI_LOCALE=en-US
MOZ_UNIVERSALCHARDET=1
@@ -7673,6 +7674,19 @@ else
OMNIJAR_NAME=omni.ja
fi
+dnl ========================================================
+dnl = --disable-native-casting
+dnl ========================================================
+
+MOZ_ARG_DISABLE_BOOL(native-casting,
+[ --disable-native-casting Disable native casting devices],
+ MOZ_NATIVE_CASTING=,
+ MOZ_NATIVE_CASTING=1)
+if test "$MOZ_NATIVE_CASTING"; then
+ AC_DEFINE(MOZ_NATIVE_CASTING)
+fi
+
+AC_SUBST(MOZ_NATIVE_CASTING)
AC_SUBST(OMNIJAR_NAME)
AC_SUBST(MOZ_OMNIJAR)
AC_SUBST(MOZ_PACKAGER_FORMAT)
diff --git a/dom/apps/src/PermissionsInstaller.jsm b/dom/apps/src/PermissionsInstaller.jsm
index e4c021c4fa4..4390c52c719 100644
--- a/dom/apps/src/PermissionsInstaller.jsm
+++ b/dom/apps/src/PermissionsInstaller.jsm
@@ -167,7 +167,7 @@ this.PermissionsInstaller = {
// If it's not a system update, then we should keep the prompt
// permissions that have been granted or denied previously.
permValue =
- PermissionSettingsModule.getPermission(permName,
+ PermissionSettingsModule.getPermission(expandedPermNames[idx],
aApp.manifestURL,
aApp.origin,
false);
diff --git a/dom/apps/src/PermissionsTable.jsm b/dom/apps/src/PermissionsTable.jsm
index 7f99651d0f4..87d0656d23e 100644
--- a/dom/apps/src/PermissionsTable.jsm
+++ b/dom/apps/src/PermissionsTable.jsm
@@ -163,9 +163,15 @@ this.PermissionsTable = { geolocation: {
},
attention: {
app: DENY_ACTION,
- privileged: ALLOW_ACTION,
+ privileged: DENY_ACTION,
certified: ALLOW_ACTION
},
+ "moz-attention": {
+ app: DENY_ACTION,
+ privileged: ALLOW_ACTION,
+ certified: ALLOW_ACTION,
+ substitute: ["attention"]
+ },
"webapps-manage": {
app: DENY_ACTION,
privileged: DENY_ACTION,
@@ -267,14 +273,26 @@ this.PermissionsTable = { geolocation: {
},
"audio-channel-telephony": {
app: DENY_ACTION,
- privileged: ALLOW_ACTION,
+ privileged: DENY_ACTION,
certified: ALLOW_ACTION
},
+ "moz-audio-channel-telephony": {
+ app: DENY_ACTION,
+ privileged: ALLOW_ACTION,
+ certified: ALLOW_ACTION,
+ substitute: ["audio-channel-telephony"]
+ },
"audio-channel-ringer": {
app: DENY_ACTION,
- privileged: ALLOW_ACTION,
+ privileged: DENY_ACTION,
certified: ALLOW_ACTION
},
+ "moz-audio-channel-ringer": {
+ app: DENY_ACTION,
+ privileged: ALLOW_ACTION,
+ certified: ALLOW_ACTION,
+ substitute: ["audio-channel-ringer"]
+ },
"audio-channel-publicnotification": {
app: DENY_ACTION,
privileged: DENY_ACTION,
@@ -340,6 +358,15 @@ this.PermissionsTable = { geolocation: {
app: DENY_ACTION,
privileged: PROMPT_ACTION,
certified: PROMPT_ACTION
+ },
+ // This permission doesn't actually grant access to
+ // anything. It exists only to check the correctness
+ // of web prompt composed permissions in tests.
+ "test-permission": {
+ app: PROMPT_ACTION,
+ privileged: PROMPT_ACTION,
+ certified: ALLOW_ACTION,
+ access: ["read", "write", "create"]
}
};
diff --git a/dom/apps/tests/file_packaged_app.template.webapp b/dom/apps/tests/file_packaged_app.template.webapp
index eb6ba12bd19..cd3c0e2de6a 100644
--- a/dom/apps/tests/file_packaged_app.template.webapp
+++ b/dom/apps/tests/file_packaged_app.template.webapp
@@ -8,6 +8,7 @@
"geolocation": {},
"audio-capture": {},
"video-capture": {},
+ "test-permission": {"access": "readonly"},
"downloads": {}
},
"launch_path": "tests/dom/apps/tests/file_packaged_app.sjs",
diff --git a/dom/apps/tests/test_packaged_app_update.html b/dom/apps/tests/test_packaged_app_update.html
index c26c2208ae2..3b1fc995775 100644
--- a/dom/apps/tests/test_packaged_app_update.html
+++ b/dom/apps/tests/test_packaged_app_update.html
@@ -95,11 +95,13 @@ var initialPermissionState = {
"geolocation": "prompt",
"audio-capture": "prompt",
"video-capture": "prompt",
+ "test-permission-read": "prompt",
"downloads": "deny"
}
var permissionsToSet = {
"geolocation": "allow",
+ "test-permission-read": "allow",
"audio-capture": "deny"
}
@@ -107,6 +109,7 @@ var permissionsToCheck = {
"geolocation": "allow",
"audio-capture": "deny",
"video-capture": "prompt",
+ "test-permission-read": "allow",
"downloads": "deny"
}
diff --git a/dom/base/nsContentPermissionHelper.cpp b/dom/base/nsContentPermissionHelper.cpp
index 5155841c5fe..98c35f49743 100644
--- a/dom/base/nsContentPermissionHelper.cpp
+++ b/dom/base/nsContentPermissionHelper.cpp
@@ -13,6 +13,7 @@
#include "mozilla/dom/PContentPermission.h"
#include "mozilla/dom/PermissionMessageUtils.h"
#include "mozilla/dom/PContentPermissionRequestParent.h"
+#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/TabParent.h"
#include "mozilla/unused.h"
#include "nsComponentManagerUtils.h"
@@ -358,3 +359,129 @@ nsContentPermissionRequestProxy::Allow(JS::HandleValue aChoices)
mParent = nullptr;
return NS_OK;
}
+
+// RemotePermissionRequest
+
+// static
+uint32_t
+RemotePermissionRequest::ConvertArrayToPermissionRequest(
+ nsIArray* aSrcArray,
+ nsTArray& aDesArray)
+{
+ uint32_t len = 0;
+ aSrcArray->GetLength(&len);
+ for (uint32_t i = 0; i < len; i++) {
+ nsCOMPtr cpt = do_QueryElementAt(aSrcArray, i);
+ nsAutoCString type;
+ nsAutoCString access;
+ cpt->GetType(type);
+ cpt->GetAccess(access);
+
+ nsCOMPtr optionArray;
+ cpt->GetOptions(getter_AddRefs(optionArray));
+ uint32_t optionsLength = 0;
+ if (optionArray) {
+ optionArray->GetLength(&optionsLength);
+ }
+ nsTArray options;
+ for (uint32_t j = 0; j < optionsLength; ++j) {
+ nsCOMPtr isupportsString = do_QueryElementAt(optionArray, j);
+ if (isupportsString) {
+ nsString option;
+ isupportsString->GetData(option);
+ options.AppendElement(option);
+ }
+ }
+
+ aDesArray.AppendElement(PermissionRequest(type, access, options));
+ }
+ return len;
+}
+
+NS_IMPL_ISUPPORTS(RemotePermissionRequest, nsIContentPermissionRequest)
+
+RemotePermissionRequest::RemotePermissionRequest(
+ nsIContentPermissionRequest* aRequest,
+ nsPIDOMWindow* aWindow)
+ : mRequest(aRequest)
+ , mWindow(aWindow)
+{
+}
+
+// nsIContentPermissionRequest methods
+NS_IMETHODIMP
+RemotePermissionRequest::GetTypes(nsIArray** aTypes)
+{
+ NS_ASSERTION(mRequest, "We need a request");
+ return mRequest->GetTypes(aTypes);
+}
+
+NS_IMETHODIMP
+RemotePermissionRequest::GetPrincipal(nsIPrincipal **aRequestingPrincipal)
+{
+ NS_ENSURE_ARG_POINTER(aRequestingPrincipal);
+
+ return mRequest->GetPrincipal(aRequestingPrincipal);
+}
+
+NS_IMETHODIMP
+RemotePermissionRequest::GetWindow(nsIDOMWindow** aRequestingWindow)
+{
+ NS_ENSURE_ARG_POINTER(aRequestingWindow);
+
+ return mRequest->GetWindow(aRequestingWindow);
+}
+
+NS_IMETHODIMP
+RemotePermissionRequest::GetElement(nsIDOMElement** aRequestingElement)
+{
+ NS_ENSURE_ARG_POINTER(aRequestingElement);
+ *aRequestingElement = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RemotePermissionRequest::Cancel()
+{
+ NS_ASSERTION(mRequest, "We need a request");
+ return mRequest->Cancel();
+}
+
+NS_IMETHODIMP
+RemotePermissionRequest::Allow(JS::HandleValue aChoices)
+{
+ NS_ASSERTION(mRequest, "We need a request");
+ return mRequest->Allow(aChoices);
+}
+
+// PCOMContentPermissionRequestChild
+bool
+RemotePermissionRequest::Recv__delete__(const bool& aAllow,
+ const nsTArray& aChoices)
+{
+ if (aAllow && mWindow->IsCurrentInnerWindow()) {
+ // Convert choices to a JS val if any.
+ // {"type1": "choice1", "type2": "choiceA"}
+ AutoJSAPI jsapi;
+ if (NS_WARN_IF(!jsapi.Init(mWindow))) {
+ return true; // This is not an IPC error.
+ }
+ JSContext* cx = jsapi.cx();
+ JS::Rooted obj(cx);
+ obj = JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr());
+ for (uint32_t i = 0; i < aChoices.Length(); ++i) {
+ const nsString& choice = aChoices[i].choice();
+ const nsCString& type = aChoices[i].type();
+ JS::Rooted jChoice(cx, JS_NewUCStringCopyN(cx, choice.get(), choice.Length()));
+ JS::Rooted vChoice(cx, StringValue(jChoice));
+ if (!JS_SetProperty(cx, obj, type.get(), vChoice)) {
+ return false;
+ }
+ }
+ JS::RootedValue val(cx, JS::ObjectValue(*obj));
+ (void) Allow(val);
+ } else {
+ (void) Cancel();
+ }
+ return true;
+}
diff --git a/dom/base/nsContentPermissionHelper.h b/dom/base/nsContentPermissionHelper.h
index b25e9a01974..cdf8d42cb5b 100644
--- a/dom/base/nsContentPermissionHelper.h
+++ b/dom/base/nsContentPermissionHelper.h
@@ -8,7 +8,9 @@
#include "nsIContentPermissionPrompt.h"
#include "nsTArray.h"
#include "nsIMutableArray.h"
+#include "PCOMContentPermissionRequestChild.h"
+class nsPIDOMWindow;
class nsContentPermissionRequestProxy;
// Forward declare IPC::Principal here which is defined in
@@ -83,4 +85,32 @@ class nsContentPermissionRequestProxy : public nsIContentPermissionRequest
nsTArray mPermissionRequests;
};
+/**
+ * RemotePermissionRequest will send a prompt ipdl request to b2g process.
+ */
+class RemotePermissionRequest : public nsIContentPermissionRequest
+ , public PCOMContentPermissionRequestChild
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSICONTENTPERMISSIONREQUEST
+
+ RemotePermissionRequest(nsIContentPermissionRequest* aRequest,
+ nsPIDOMWindow* aWindow);
+
+ // It will be called when prompt dismissed.
+ virtual bool Recv__delete__(const bool &aAllow,
+ const nsTArray& aChoices) MOZ_OVERRIDE;
+ virtual void IPDLRelease() MOZ_OVERRIDE { Release(); }
+
+ static uint32_t ConvertArrayToPermissionRequest(
+ nsIArray* aSrcArray,
+ nsTArray& aDesArray);
+private:
+ virtual ~RemotePermissionRequest() {}
+
+ nsCOMPtr mRequest;
+ nsCOMPtr mWindow;
+};
+
#endif // nsContentPermissionHelper_h
diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp
index 8042aad6118..0c5a439e22d 100644
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -71,6 +71,7 @@
#include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
#include "mozilla/dom/MutableFile.h"
#include "mozilla/dom/MutableFileBinding.h"
+#include "mozilla/dom/PermissionMessageUtils.h"
#include "mozilla/dom/quota/PersistenceType.h"
#include "mozilla/dom/quota/QuotaManager.h"
#include "nsDOMBlobBuilder.h"
@@ -89,6 +90,7 @@
#include "GeckoProfiler.h"
#include "mozilla/Preferences.h"
#include "nsIContentIterator.h"
+#include "nsContentPermissionHelper.h"
#ifdef XP_WIN
#undef GetClassName
@@ -3746,6 +3748,49 @@ nsDOMWindowUtils::XpconnectArgument(nsIDOMWindowUtils* aThis)
return NS_OK;
}
+NS_IMETHODIMP
+nsDOMWindowUtils::AskPermission(nsIContentPermissionRequest* aRequest)
+{
+ nsCOMPtr window = do_QueryReferent(mWindow);
+ nsRefPtr req =
+ new RemotePermissionRequest(aRequest, window->GetCurrentInnerWindow());
+
+ // for content process
+ if (XRE_GetProcessType() == GeckoProcessType_Content) {
+ MOZ_ASSERT(NS_IsMainThread()); // IPC can only be execute on main thread.
+
+ dom::TabChild* child = dom::TabChild::GetFrom(window->GetDocShell());
+ NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
+
+ nsCOMPtr typeArray;
+ nsresult rv = req->GetTypes(getter_AddRefs(typeArray));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsTArray permArray;
+ RemotePermissionRequest::ConvertArrayToPermissionRequest(typeArray, permArray);
+
+ nsCOMPtr principal;
+ rv = req->GetPrincipal(getter_AddRefs(principal));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ req->AddRef();
+ child->SendPContentPermissionRequestConstructor(req,
+ permArray,
+ IPC::Principal(principal));
+
+ req->Sendprompt();
+ return NS_OK;
+ }
+
+ // for chrome process
+ nsCOMPtr prompt =
+ do_GetService(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
+ if (prompt) {
+ prompt->Prompt(req);
+ }
+ return NS_OK;
+}
+
NS_INTERFACE_MAP_BEGIN(nsTranslationNodeList)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_ENTRY(nsITranslationNodeList)
diff --git a/dom/bluetooth2/tests/marionette/head.js b/dom/bluetooth2/tests/marionette/head.js
index eeb7071b95c..d73176f53b0 100644
--- a/dom/bluetooth2/tests/marionette/head.js
+++ b/dom/bluetooth2/tests/marionette/head.js
@@ -342,7 +342,7 @@ function setBluetoothEnabled(aEnabled) {
/**
* Wait for one named BluetoothManager event.
*
- * Resolve if that named event occurs. Never reject.
+ * Resolve if that named event occurs. Never reject.
*
* Fulfill params: the DOMEvent passed.
*
@@ -367,7 +367,7 @@ function waitForManagerEvent(aEventName) {
/**
* Wait for one named BluetoothAdapter event.
*
- * Resolve if that named event occurs. Never reject.
+ * Resolve if that named event occurs. Never reject.
*
* Fulfill params: the DOMEvent passed.
*
@@ -391,6 +391,118 @@ function waitForAdapterEvent(aAdapter, aEventName) {
return deferred.promise;
}
+/**
+ * Wait for 'onattributechanged' events for state changes of BluetoothAdapter
+ * with specified order.
+ *
+ * Resolve if those expected events occur in order. Never reject.
+ *
+ * Fulfill params: an array which contains every changed attributes during
+ * the waiting.
+ *
+ * @param aAdapter
+ * The BluetoothAdapter you want to use.
+ * @param aStateChangesInOrder
+ * An array which contains an expected order of BluetoothAdapterState.
+ * Example 1: [enabling, enabled]
+ * Example 2: [disabling, disabled]
+ *
+ * @return A deferred promise.
+ */
+function waitForAdapterStateChanged(aAdapter, aStateChangesInOrder) {
+ let deferred = Promise.defer();
+
+ let stateIndex = 0;
+ let prevStateIndex = 0;
+ let statesArray = [];
+ let changedAttrs = [];
+ aAdapter.onattributechanged = function(aEvent) {
+ for (let i in aEvent.attrs) {
+ changedAttrs.push(aEvent.attrs[i]);
+ switch (aEvent.attrs[i]) {
+ case "state":
+ log(" 'state' changed to " + aAdapter.state);
+
+ // Received state change order may differ from expected one even though
+ // state changes in expected order, because the value of state may change
+ // again before we receive prior 'onattributechanged' event.
+ //
+ // For example, expected state change order [A,B,C] may result in
+ // received ones:
+ // - [A,C,C] if state becomes C before we receive 2nd 'onattributechanged'
+ // - [B,B,C] if state becomes B before we receive 1st 'onattributechanged'
+ // - [C,C,C] if state becomes C before we receive 1st 'onattributechanged'
+ // - [A,B,C] if all 'onattributechanged' are received in perfect timing
+ //
+ // As a result, we ensure only following conditions instead of exactly
+ // matching received and expected state change order.
+ // - Received state change order never reverse expected one. For example,
+ // [B,A,C] should never occur with expected state change order [A,B,C].
+ // - The changed value of state in received state change order never
+ // appears later than that in expected one. For example, [A,A,C] should
+ // never occur with expected state change order [A,B,C].
+ let stateIndex = aStateChangesInOrder.indexOf(aAdapter.state);
+ if (stateIndex >= prevStateIndex && stateIndex + 1 > statesArray.length) {
+ statesArray.push(aAdapter.state);
+ prevStateIndex = stateIndex;
+
+ if (statesArray.length == aStateChangesInOrder.length) {
+ aAdapter.onattributechanged = null;
+ ok(true, "BluetoothAdapter event 'onattributechanged' got.");
+ deferred.resolve(changedAttrs);
+ }
+ } else {
+ ok(false, "The order of 'onattributechanged' events is unexpected.");
+ }
+
+ break;
+ case "name":
+ log(" 'name' changed to " + aAdapter.name);
+ if (aAdapter.state == "enabling") {
+ isnot(aAdapter.name, "", "adapter.name");
+ }
+ else if (aAdapter.state == "disabling") {
+ is(aAdapter.name, "", "adapter.name");
+ }
+ break;
+ case "address":
+ log(" 'address' changed to " + aAdapter.address);
+ if (aAdapter.state == "enabling") {
+ isnot(aAdapter.address, "", "adapter.address");
+ }
+ else if (aAdapter.state == "disabling") {
+ is(aAdapter.address, "", "adapter.address");
+ }
+ break;
+ case "discoverable":
+ log(" 'discoverable' changed to " + aAdapter.discoverable);
+ if (aAdapter.state == "enabling") {
+ is(aAdapter.discoverable, true, "adapter.discoverable");
+ }
+ else if (aAdapter.state == "disabling") {
+ is(aAdapter.discoverable, false, "adapter.discoverable");
+ }
+ break;
+ case "discovering":
+ log(" 'discovering' changed to " + aAdapter.discovering);
+ if (aAdapter.state == "enabling") {
+ is(aAdapter.discovering, true, "adapter.discovering");
+ }
+ else if (aAdapter.state == "disabling") {
+ is(aAdapter.discovering, false, "adapter.discovering");
+ }
+ break;
+ case "unknown":
+ default:
+ ok(false, "Unknown attribute '" + aEvent.attrs[i] + "' changed." );
+ break;
+ }
+ }
+ };
+
+ return deferred.promise;
+}
+
/**
* Flush permission settings and call |finish()|.
*/
@@ -417,7 +529,7 @@ function startBluetoothTestBase(aPermissions, aTestCaseMain) {
}
function startBluetoothTest(aReenable, aTestCaseMain) {
- startBluetoothTestBase(["settings-read", "settings-write"], function() {
+ startBluetoothTestBase([], function() {
let origEnabled, needEnable;
return Promise.resolve()
.then(function() {
diff --git a/dom/bluetooth2/tests/marionette/manifest.ini b/dom/bluetooth2/tests/marionette/manifest.ini
index e469d54782a..d3ed382f7f5 100644
--- a/dom/bluetooth2/tests/marionette/manifest.ini
+++ b/dom/bluetooth2/tests/marionette/manifest.ini
@@ -4,3 +4,4 @@ browser = false
qemu = false
[test_dom_BluetoothManager_API2.js]
+[test_dom_BluetoothAdapter_enable_API2.js]
diff --git a/dom/bluetooth2/tests/marionette/test_dom_BluetoothAdapter_enable_API2.js b/dom/bluetooth2/tests/marionette/test_dom_BluetoothAdapter_enable_API2.js
new file mode 100644
index 00000000000..9594cf1d264
--- /dev/null
+++ b/dom/bluetooth2/tests/marionette/test_dom_BluetoothAdapter_enable_API2.js
@@ -0,0 +1,76 @@
+/* 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/. */
+
+///////////////////////////////////////////////////////////////////////////////
+// Test Purpose:
+// To verify that enable/disable process of BluetoothAdapter is correct.
+//
+// Test Procedure:
+// [0] Set Bluetooth permission and enable default adapter.
+// [1] Disable Bluetooth and check the correctness of 'onattributechanged'.
+// [2] Enable Bluetooth and check the correctness of 'onattributechanged'.
+//
+// Test Coverage:
+// - BluetoothAdapter.enable()
+// - BluetoothAdapter.disable()
+// - BluetoothAdapter.onattributechanged()
+// - BluetoothAdapter.address
+// - BluetoothAdapter.state
+//
+///////////////////////////////////////////////////////////////////////////////
+
+MARIONETTE_TIMEOUT = 60000;
+MARIONETTE_HEAD_JS = 'head.js';
+
+startBluetoothTest(true, function testCaseMain(aAdapter) {
+ log("Checking adapter attributes ...");
+
+ is(aAdapter.state, "enabled", "adapter.state");
+ isnot(aAdapter.address, "", "adapter.address");
+
+ // Since adapter has just been re-enabled, these properties should be 'false'.
+ is(aAdapter.discovering, false, "adapter.discovering");
+ is(aAdapter.discoverable, false, "adapter.discoverable");
+ // TODO: Check the correctness of name and address if we use emulator.
+ // is(aAdapter.name, EMULATOR_NAME, "adapter.name");
+ // is(aAdapter.address, EMULATOR_ADDRESS, "adapter.address");
+
+ log(" adapter.address: " + aAdapter.address);
+ log(" adapter.name: " + aAdapter.name);
+
+ let originalAddr = aAdapter.address;
+ let originalName = aAdapter.name;
+
+ return Promise.resolve()
+ .then(function() {
+ log("[1] Disable Bluetooth and check the correctness of 'onattributechanged'");
+ let promises = [];
+ promises.push(waitForAdapterStateChanged(aAdapter, ["disabling", "disabled"]));
+ promises.push(aAdapter.disable());
+ return Promise.all(promises);
+ })
+ .then(function(aResults) {
+ isnot(aResults[0].indexOf("address"), -1, "Indicator of 'address' changed event");
+ if (originalName != "") {
+ isnot(aResults[0].indexOf("name"), -1, "Indicator of 'name' changed event");
+ }
+ is(aAdapter.address, "", "adapter.address");
+ is(aAdapter.name, "", "adapter.name");
+ })
+ .then(function() {
+ log("[2] Enable Bluetooth and check the correctness of 'onattributechanged'");
+ let promises = [];
+ promises.push(waitForAdapterStateChanged(aAdapter, ["enabling", "enabled"]));
+ promises.push(aAdapter.enable());
+ return Promise.all(promises);
+ })
+ .then(function(aResults) {
+ isnot(aResults[0].indexOf("address"), -1, "Indicator of 'address' changed event");
+ if (originalName != "") {
+ isnot(aResults[0].indexOf("name"), -1, "Indicator of 'name' changed event");
+ }
+ is(aAdapter.address, originalAddr, "adapter.address");
+ is(aAdapter.name, originalName, "adapter.name");
+ })
+});
diff --git a/dom/cellbroadcast/tests/marionette/head.js b/dom/cellbroadcast/tests/marionette/head.js
index 940701952fd..28bc99e84af 100644
--- a/dom/cellbroadcast/tests/marionette/head.js
+++ b/dom/cellbroadcast/tests/marionette/head.js
@@ -5,6 +5,151 @@ const {Cc: Cc, Ci: Ci, Cr: Cr, Cu: Cu} = SpecialPowers;
let Promise = Cu.import("resource://gre/modules/Promise.jsm").Promise;
+const PDU_DCS_CODING_GROUP_BITS = 0xF0;
+const PDU_DCS_MSG_CODING_7BITS_ALPHABET = 0x00;
+const PDU_DCS_MSG_CODING_8BITS_ALPHABET = 0x04;
+const PDU_DCS_MSG_CODING_16BITS_ALPHABET = 0x08;
+
+const PDU_DCS_MSG_CLASS_BITS = 0x03;
+const PDU_DCS_MSG_CLASS_NORMAL = 0xFF;
+const PDU_DCS_MSG_CLASS_0 = 0x00;
+const PDU_DCS_MSG_CLASS_ME_SPECIFIC = 0x01;
+const PDU_DCS_MSG_CLASS_SIM_SPECIFIC = 0x02;
+const PDU_DCS_MSG_CLASS_TE_SPECIFIC = 0x03;
+const PDU_DCS_MSG_CLASS_USER_1 = 0x04;
+const PDU_DCS_MSG_CLASS_USER_2 = 0x05;
+
+const GECKO_SMS_MESSAGE_CLASSES = {};
+GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_NORMAL] = "normal";
+GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_0] = "class-0";
+GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_ME_SPECIFIC] = "class-1";
+GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_SIM_SPECIFIC] = "class-2";
+GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_TE_SPECIFIC] = "class-3";
+GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_USER_1] = "user-1";
+GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_USER_2] = "user-2";
+
+const CB_MESSAGE_SIZE_GSM = 88;
+const CB_MESSAGE_SIZE_ETWS = 56;
+
+const CB_GSM_MESSAGEID_ETWS_BEGIN = 0x1100;
+const CB_GSM_MESSAGEID_ETWS_END = 0x1107;
+
+const CB_GSM_GEOGRAPHICAL_SCOPE_NAMES = [
+ "cell-immediate",
+ "plmn",
+ "location-area",
+ "cell"
+];
+
+const CB_ETWS_WARNING_TYPE_NAMES = [
+ "earthquake",
+ "tsunami",
+ "earthquake-tsunami",
+ "test",
+ "other"
+];
+
+const CB_DCS_LANG_GROUP_1 = [
+ "de", "en", "it", "fr", "es", "nl", "sv", "da", "pt", "fi",
+ "no", "el", "tr", "hu", "pl", null
+];
+const CB_DCS_LANG_GROUP_2 = [
+ "cs", "he", "ar", "ru", "is", null, null, null, null, null,
+ null, null, null, null, null, null
+];
+
+/**
+ * Compose input number into specified number of semi-octets.
+ *
+ * @param: aNum
+ * The number to be converted.
+ * @param: aNumSemiOctets
+ * Number of semi-octects to be composed to.
+ *
+ * @return The composed Hex String.
+ */
+function buildHexStr(aNum, aNumSemiOctets) {
+ let str = aNum.toString(16);
+ ok(str.length <= aNumSemiOctets);
+ while (str.length < aNumSemiOctets) {
+ str = "0" + str;
+ }
+ return str;
+}
+
+/**
+ * Helper function to decode the given DCS into encoding type, language,
+ * language indicator and message class.
+ *
+ * @param: aDcs
+ * The DCS to be decoded.
+ *
+ * @return [encoding, language, hasLanguageIndicator,
+ * GECKO_SMS_MESSAGE_CLASSES[messageClass]]
+ */
+function decodeGsmDataCodingScheme(aDcs) {
+ let language = null;
+ let hasLanguageIndicator = false;
+ let encoding = PDU_DCS_MSG_CODING_7BITS_ALPHABET;
+ let messageClass = PDU_DCS_MSG_CLASS_NORMAL;
+
+ switch (aDcs & PDU_DCS_CODING_GROUP_BITS) {
+ case 0x00: // 0000
+ language = CB_DCS_LANG_GROUP_1[aDcs & 0x0F];
+ break;
+
+ case 0x10: // 0001
+ switch (aDcs & 0x0F) {
+ case 0x00:
+ hasLanguageIndicator = true;
+ break;
+ case 0x01:
+ encoding = PDU_DCS_MSG_CODING_16BITS_ALPHABET;
+ hasLanguageIndicator = true;
+ break;
+ }
+ break;
+
+ case 0x20: // 0010
+ language = CB_DCS_LANG_GROUP_2[aDcs & 0x0F];
+ break;
+
+ case 0x40: // 01xx
+ case 0x50:
+ //case 0x60:
+ //case 0x70:
+ case 0x90: // 1001
+ encoding = (aDcs & 0x0C);
+ if (encoding == 0x0C) {
+ encoding = PDU_DCS_MSG_CODING_7BITS_ALPHABET;
+ }
+ messageClass = (aDcs & PDU_DCS_MSG_CLASS_BITS);
+ break;
+
+ case 0xF0:
+ encoding = (aDcs & 0x04) ? PDU_DCS_MSG_CODING_8BITS_ALPHABET
+ : PDU_DCS_MSG_CODING_7BITS_ALPHABET;
+ switch(aDcs & PDU_DCS_MSG_CLASS_BITS) {
+ case 0x01: messageClass = PDU_DCS_MSG_CLASS_USER_1; break;
+ case 0x02: messageClass = PDU_DCS_MSG_CLASS_USER_2; break;
+ case 0x03: messageClass = PDU_DCS_MSG_CLASS_TE_SPECIFIC; break;
+ }
+ break;
+
+ case 0x30: // 0011 (Reserved)
+ case 0x80: // 1000 (Reserved)
+ case 0xA0: // 1010..1100 (Reserved)
+ case 0xB0:
+ case 0xC0:
+ break;
+ default:
+ throw new Error("Unsupported CBS data coding scheme: " + aDcs);
+ }
+
+ return [encoding, language, hasLanguageIndicator,
+ GECKO_SMS_MESSAGE_CLASSES[messageClass]];
+}
+
/**
* Push required permissions and test if |navigator.mozCellBroadcast| exists.
* Resolve if it does, reject otherwise.
@@ -148,7 +293,7 @@ function sendMultipleRawCbsToEmulatorAndWait(aPdus) {
promises.push(sendRawCbsToEmulator(pdu));
}
- return Promise.all(promises);
+ return Promise.all(promises).then(aResults => aResults[0].message);
}
/**
diff --git a/dom/cellbroadcast/tests/marionette/test_cellbroadcast_etws.js b/dom/cellbroadcast/tests/marionette/test_cellbroadcast_etws.js
index f08daec368c..c1e62441a56 100644
--- a/dom/cellbroadcast/tests/marionette/test_cellbroadcast_etws.js
+++ b/dom/cellbroadcast/tests/marionette/test_cellbroadcast_etws.js
@@ -1,268 +1,215 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
-MARIONETTE_TIMEOUT = 10000;
+MARIONETTE_TIMEOUT = 20000;
+MARIONETTE_HEAD_JS = 'head.js';
-const CB_MESSAGE_SIZE_ETWS = 56;
-
-const CB_GSM_GEOGRAPHICAL_SCOPE_NAMES = [
- "cell-immediate",
- "plmn",
- "location-area",
- "cell"
-];
-
-const CB_ETWS_WARNING_TYPE_NAMES = [
- "earthquake",
- "tsunami",
- "earthquake-tsunami",
- "test",
- "other"
-];
-
-SpecialPowers.addPermission("cellbroadcast", true, document);
-SpecialPowers.addPermission("mobileconnection", true, document);
-
-let cbs = window.navigator.mozCellBroadcast;
-ok(cbs instanceof window.MozCellBroadcast,
- "mozCellBroadcast is instanceof " + cbs.constructor);
-
-let pendingEmulatorCmdCount = 0;
-function sendCellBroadcastMessage(pdu, callback) {
- pendingEmulatorCmdCount++;
-
- let cmd = "cbs pdu " + pdu;
- runEmulatorCmd(cmd, function(result) {
- pendingEmulatorCmdCount--;
-
- is(result[0], "OK", "Emulator response");
-
- if (callback) {
- window.setTimeout(callback, 0);
- }
- });
-}
-
-function buildHexStr(n, numSemiOctets) {
- let str = n.toString(16);
- ok(str.length <= numSemiOctets);
- while (str.length < numSemiOctets) {
- str = "0" + str;
- }
- return str;
-}
-
-function seq(end, begin) {
- let result = [];
- for (let i = begin || 0; i < end; i++) {
- result.push(i);
- }
- return result;
-}
-
-function repeat(func, array, oncomplete) {
- (function do_call(index) {
- let next = index < (array.length - 1) ? do_call.bind(null, index + 1) : oncomplete;
- func.apply(null, [array[index], next]);
- })(0);
-}
-
-function doTestHelper(pdu, nextTest, checkFunc) {
- cbs.addEventListener("received", function onreceived(event) {
- cbs.removeEventListener("received", onreceived);
-
- checkFunc(event.message);
-
- window.setTimeout(nextTest, 0);
- });
-
- if (Array.isArray(pdu)) {
- repeat(sendCellBroadcastMessage, pdu);
- } else {
- sendCellBroadcastMessage(pdu);
- }
-}
-
-/**
- * Tests receiving Cell Broadcast messages, event instance type, all attributes
- * of CellBroadcastMessage exist.
- */
-function testEtwsMessageAttributes() {
- log("Test ETWS Primary Notification message attributes");
-
- cbs.addEventListener("received", function onreceived(event) {
- cbs.removeEventListener("received", onreceived);
-
- // Bug 838542: following check throws an exception and fails this case.
- // ok(event instanceof MozCellBroadcastEvent,
- // "event is instanceof " + event.constructor)
- ok(event, "event is valid");
-
- let message = event.message;
- ok(message, "event.message is valid");
+function testReceiving_ETWS_MessageAttributes() {
+ log("Test receiving ETWS Primary Notification - Message Attributes");
+ let verifyCBMessage = (aMessage) => {
// Attributes other than `language` and `body` should always be assigned.
- ok(message.gsmGeographicalScope != null, "message.gsmGeographicalScope");
- ok(message.messageCode != null, "message.messageCode");
- ok(message.messageId != null, "message.messageId");
- ok('language' in message, "message.language");
- ok(message.language == null, "message.language");
- ok('body' in message, "message.body");
- ok(message.body == null, "message.body");
- is(message.messageClass, "normal", "message.messageClass");
- ok(message.timestamp != null, "message.timestamp");
- ok(message.etws != null, "message.etws");
- ok(message.etws.warningType != null, "message.etws.warningType");
- ok(message.etws.emergencyUserAlert != null,
- "message.etws.emergencyUserAlert");
- ok(message.etws.popup != null, "message.etws.popup");
- ok(message.cdmaServiceCategory != null, "message.cdmaServiceCategory");
-
- window.setTimeout(testReceiving_ETWS_GeographicalScope, 0);
- });
+ ok(aMessage.gsmGeographicalScope != null, "aMessage.gsmGeographicalScope");
+ ok(aMessage.messageCode != null, "aMessage.messageCode");
+ ok(aMessage.messageId != null, "aMessage.messageId");
+ ok('language' in aMessage, "aMessage.language");
+ ok(aMessage.language == null, "aMessage.language");
+ ok('body' in aMessage, "aMessage.body");
+ ok(aMessage.body == null, "aMessage.body");
+ is(aMessage.messageClass, "normal", "aMessage.messageClass");
+ ok(aMessage.timestamp != null, "aMessage.timestamp");
+ ok(aMessage.etws != null, "aMessage.etws");
+ ok(aMessage.etws.warningType != null, "aMessage.etws.warningType");
+ ok(aMessage.etws.emergencyUserAlert != null,
+ "aMessage.etws.emergencyUserAlert");
+ ok(aMessage.etws.popup != null, "aMessage.etws.popup");
+ ok(aMessage.cdmaServiceCategory != null, "aMessage.cdmaServiceCategory");
+ };
// Here we use a simple ETWS message for test.
let pdu = buildHexStr(0, CB_MESSAGE_SIZE_ETWS * 2); // 6 octets
- sendCellBroadcastMessage(pdu);
+
+ return sendMultipleRawCbsToEmulatorAndWait([pdu])
+ .then((aMessage) => verifyCBMessage(aMessage));
}
function testReceiving_ETWS_GeographicalScope() {
log("Test receiving ETWS Primary Notification - Geographical Scope");
- function do_test(gs, nextTest) {
- // Here we use a simple ETWS message for test.
- let pdu = buildHexStr(((gs & 0x03) << 14), 4)
+ let promise = Promise.resolve();
+
+ let verifyCBMessage = (aMessage, aGsName) => {
+ is(aMessage.gsmGeographicalScope, aGsName,
+ "aMessage.gsmGeographicalScope");
+ };
+
+ CB_GSM_GEOGRAPHICAL_SCOPE_NAMES.forEach(function(aGsName, aIndex) {
+ let pdu = buildHexStr(((aIndex & 0x03) << 14), 4)
+ buildHexStr(0, (CB_MESSAGE_SIZE_ETWS - 2) * 2);
+ promise = promise
+ .then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
+ .then((aMessage) => verifyCBMessage(aMessage, aGsName));
+ });
- doTestHelper(pdu, nextTest, function(message) {
- is(message.gsmGeographicalScope, CB_GSM_GEOGRAPHICAL_SCOPE_NAMES[gs],
- "message.gsmGeographicalScope");
- });
- }
-
- repeat(do_test, seq(CB_GSM_GEOGRAPHICAL_SCOPE_NAMES.length),
- testReceiving_ETWS_MessageCode);
+ return promise;
}
function testReceiving_ETWS_MessageCode() {
log("Test receiving ETWS Primary Notification - Message Code");
+ let promise = Promise.resolve();
+
// Message Code has 10 bits, and is ORed into a 16 bits 'serial' number. Here
// we test every single bit to verify the operation doesn't go wrong.
- let messageCodesToTest = [
+ let messageCodes = [
0x000, 0x001, 0x002, 0x004, 0x008, 0x010, 0x020, 0x040,
0x080, 0x100, 0x200, 0x251
];
- function do_test(messageCode, nextTest) {
- let pdu = buildHexStr(((messageCode & 0x3FF) << 4), 4)
+ let verifyCBMessage = (aMessage, aMsgCode) => {
+ is(aMessage.messageCode, aMsgCode, "aMessage.messageCode");
+ };
+
+ messageCodes.forEach(function(aMsgCode) {
+ let pdu = buildHexStr(((aMsgCode & 0x3FF) << 4), 4)
+ buildHexStr(0, (CB_MESSAGE_SIZE_ETWS - 2) * 2);
+ promise = promise
+ .then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
+ .then((aMessage) => verifyCBMessage(aMessage, aMsgCode));
+ });
- doTestHelper(pdu, nextTest, function(message) {
- is(message.messageCode, messageCode, "message.messageCode");
- });
- }
-
- repeat(do_test, messageCodesToTest, testReceiving_ETWS_MessageId);
+ return promise;
}
function testReceiving_ETWS_MessageId() {
log("Test receiving ETWS Primary Notification - Message Identifier");
+ let promise = Promise.resolve();
+
// Message Identifier has 16 bits, but no bitwise operation is needed.
// Test some selected values only.
- let messageIdsToTest = [
- 0x0000, 0x0001, 0x0010, 0x0100, 0x1000, 0x1111, 0x8888, 0x8811
+ let messageIds = [
+ 0x0000, 0x0001, 0x0010, 0x0100, 0x1000, 0x1111, 0x8888, 0x8811,
];
- function do_test(messageId, nextTest) {
+ let verifyCBMessage = (aMessage, aMessageId) => {
+ is(aMessage.messageId, aMessageId, "aMessage.messageId");
+ };
+
+ messageIds.forEach(function(aMessageId) {
let pdu = buildHexStr(0, 4)
- + buildHexStr((messageId & 0xFFFF), 4)
+ + buildHexStr((aMessageId & 0xFFFF), 4)
+ buildHexStr(0, (CB_MESSAGE_SIZE_ETWS - 4) * 2);
+ promise = promise
+ .then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
+ .then((aMessage) => verifyCBMessage(aMessage, aMessageId));
+ });
- doTestHelper(pdu, nextTest, function(message) {
- is(message.messageId, messageId, "message.messageId");
- });
- }
-
- repeat(do_test, messageIdsToTest, testReceiving_ETWS_Timestamp);
+ return promise;
}
function testReceiving_ETWS_Timestamp() {
log("Test receiving ETWS Primary Notification - Timestamp");
- // Here we use a simple ETWS message for test.
- let pdu = buildHexStr(0, 12); // 6 octets
- doTestHelper(pdu, testReceiving_ETWS_WarningType, function(message) {
+ let verifyCBMessage = (aMessage) => {
// Cell Broadcast messages do not contain a timestamp field (however, ETWS
// does). We only check the timestamp doesn't go too far (60 seconds) here.
- let msMessage = message.timestamp;
+ let msMessage = aMessage.timestamp;
let msNow = Date.now();
- ok(Math.abs(msMessage - msNow) < (1000 * 60), "message.timestamp");
- });
+ ok(Math.abs(msMessage - msNow) < (1000 * 60), "aMessage.timestamp");
+ };
+
+ // Here we use a simple ETWS message for test.
+ let pdu = buildHexStr(0, 12); // 6 octets
+
+ return sendMultipleRawCbsToEmulatorAndWait([pdu])
+ .then((aMessage) => verifyCBMessage(aMessage));
}
function testReceiving_ETWS_WarningType() {
log("Test receiving ETWS Primary Notification - Warning Type");
+ let promise = Promise.resolve();
+
// Warning Type has 7 bits, and is ORed into a 16 bits 'WarningType' field.
// Here we test every single bit to verify the operation doesn't go wrong.
- let warningTypesToTest = [
+ let warningTypes = [
0x00, 0x01, 0x02, 0x03, 0x04, 0x08, 0x10, 0x20, 0x40, 0x41
];
- function do_test(warningType, nextTest) {
+ let verifyCBMessage = (aMessage, aWarningType) => {
+ ok(aMessage.etws, "aMessage.etws");
+ is(aMessage.etws.warningType, CB_ETWS_WARNING_TYPE_NAMES[aWarningType],
+ "aMessage.etws.warningType");
+ };
+
+ warningTypes.forEach(function(aWarningType) {
let pdu = buildHexStr(0, 8)
- + buildHexStr(((warningType & 0x7F) << 9), 4)
+ + buildHexStr(((aWarningType & 0x7F) << 9), 4)
+ buildHexStr(0, (CB_MESSAGE_SIZE_ETWS - 6) * 2);
-
- doTestHelper(pdu, nextTest, function(message) {
- ok(message.etws, "message.etws");
- is(message.etws.warningType, CB_ETWS_WARNING_TYPE_NAMES[warningType],
- "message.etws.warningType");
- });
- }
-
- repeat(do_test, warningTypesToTest, testReceiving_ETWS_EmergencyUserAlert);
-}
-
-function doTestEmergencyUserAlert_or_Popup(name, mask, nextTest) {
- let pdu = buildHexStr(0, 8)
- + buildHexStr(mask, 4)
- + buildHexStr(0, (CB_MESSAGE_SIZE_ETWS - 6) * 2);
- doTestHelper(pdu, nextTest, function(message) {
- ok(message.etws != null, "message.etws");
- is(message.etws[name], mask != 0, "message.etws." + name);
+ promise = promise
+ .then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
+ .then((aMessage) => verifyCBMessage(aMessage, aWarningType));
});
+
+ return promise;
}
function testReceiving_ETWS_EmergencyUserAlert() {
log("Test receiving ETWS Primary Notification - Emergency User Alert");
- repeat(doTestEmergencyUserAlert_or_Popup.bind(null, "emergencyUserAlert"),
- [0x100, 0x000], testReceiving_ETWS_Popup);
+ let promise = Promise.resolve();
+
+ let emergencyUserAlertMasks = [0x100, 0x000];
+
+ let verifyCBMessage = (aMessage, aMask) => {
+ ok(aMessage.etws != null, "aMessage.etws");
+ is(aMessage.etws.emergencyUserAlert, aMask != 0, "aMessage.etws.emergencyUserAlert");
+ };
+
+ emergencyUserAlertMasks.forEach(function(aMask) {
+ let pdu = buildHexStr(0, 8)
+ + buildHexStr(aMask, 4)
+ + buildHexStr(0, (CB_MESSAGE_SIZE_ETWS - 6) * 2);
+ promise = promise
+ .then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
+ .then((aMessage) => verifyCBMessage(aMessage, aMask));
+ });
+
+ return promise;
}
function testReceiving_ETWS_Popup() {
log("Test receiving ETWS Primary Notification - Popup");
- repeat(doTestEmergencyUserAlert_or_Popup.bind(null, "popup"),
- [0x80, 0x000], cleanUp);
+ let promise = Promise.resolve();
+
+ let popupMasks = [0x80, 0x000];
+
+ let verifyCBMessage = (aMessage, aMask) => {
+ ok(aMessage.etws != null, "aMessage.etws");
+ is(aMessage.etws.popup, aMask != 0, "aMessage.etws.popup");
+ };
+
+ popupMasks.forEach(function(aMask) {
+ let pdu = buildHexStr(0, 8)
+ + buildHexStr(aMask, 4)
+ + buildHexStr(0, (CB_MESSAGE_SIZE_ETWS - 6) * 2);
+ promise = promise
+ .then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
+ .then((aMessage) => verifyCBMessage(aMessage, aMask));
+ });
+
+ return promise;
}
-function cleanUp() {
- if (pendingEmulatorCmdCount > 0) {
- window.setTimeout(cleanUp, 100);
- return;
- }
-
- SpecialPowers.removePermission("mobileconnection", document);
- SpecialPowers.removePermission("cellbroadcast", true, document);
-
- finish();
-}
-
-waitFor(testEtwsMessageAttributes, function() {
- return navigator.mozMobileConnections[0].voice.connected;
+startTestCommon(function testCaseMain() {
+ return testReceiving_ETWS_MessageAttributes()
+ .then(() => testReceiving_ETWS_GeographicalScope())
+ .then(() => testReceiving_ETWS_MessageCode())
+ .then(() => testReceiving_ETWS_MessageId())
+ .then(() => testReceiving_ETWS_Timestamp())
+ .then(() => testReceiving_ETWS_WarningType())
+ .then(() => testReceiving_ETWS_EmergencyUserAlert())
+ .then(() => testReceiving_ETWS_Popup());
});
-
diff --git a/dom/cellbroadcast/tests/marionette/test_cellbroadcast_gsm.js b/dom/cellbroadcast/tests/marionette/test_cellbroadcast_gsm.js
index 9d9aec2b7c3..7cf94248c20 100644
--- a/dom/cellbroadcast/tests/marionette/test_cellbroadcast_gsm.js
+++ b/dom/cellbroadcast/tests/marionette/test_cellbroadcast_gsm.js
@@ -1,59 +1,8 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
-MARIONETTE_TIMEOUT = 30000;
-
-const PDU_DCS_CODING_GROUP_BITS = 0xF0;
-const PDU_DCS_MSG_CODING_7BITS_ALPHABET = 0x00;
-const PDU_DCS_MSG_CODING_8BITS_ALPHABET = 0x04;
-const PDU_DCS_MSG_CODING_16BITS_ALPHABET = 0x08;
-
-const PDU_DCS_MSG_CLASS_BITS = 0x03;
-const PDU_DCS_MSG_CLASS_NORMAL = 0xFF;
-const PDU_DCS_MSG_CLASS_0 = 0x00;
-const PDU_DCS_MSG_CLASS_ME_SPECIFIC = 0x01;
-const PDU_DCS_MSG_CLASS_SIM_SPECIFIC = 0x02;
-const PDU_DCS_MSG_CLASS_TE_SPECIFIC = 0x03;
-const PDU_DCS_MSG_CLASS_USER_1 = 0x04;
-const PDU_DCS_MSG_CLASS_USER_2 = 0x05;
-
-const GECKO_SMS_MESSAGE_CLASSES = {};
-GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_NORMAL] = "normal";
-GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_0] = "class-0";
-GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_ME_SPECIFIC] = "class-1";
-GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_SIM_SPECIFIC] = "class-2";
-GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_TE_SPECIFIC] = "class-3";
-GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_USER_1] = "user-1";
-GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_USER_2] = "user-2";
-
-const CB_MESSAGE_SIZE_GSM = 88;
-
-const CB_GSM_MESSAGEID_ETWS_BEGIN = 0x1100;
-const CB_GSM_MESSAGEID_ETWS_END = 0x1107;
-
-const CB_GSM_GEOGRAPHICAL_SCOPE_NAMES = [
- "cell-immediate",
- "plmn",
- "location-area",
- "cell"
-];
-
-const CB_ETWS_WARNING_TYPE_NAMES = [
- "earthquake",
- "tsunami",
- "earthquake-tsunami",
- "test",
- "other"
-];
-
-const CB_DCS_LANG_GROUP_1 = [
- "de", "en", "it", "fr", "es", "nl", "sv", "da", "pt", "fi",
- "no", "el", "tr", "hu", "pl", null
-];
-const CB_DCS_LANG_GROUP_2 = [
- "cs", "he", "ar", "ru", "is", null, null, null, null, null,
- null, null, null, null, null, null
-];
+MARIONETTE_TIMEOUT = 60000;
+MARIONETTE_HEAD_JS = 'head.js';
const CB_MAX_CONTENT_7BIT = Math.floor((CB_MESSAGE_SIZE_GSM - 6) * 8 / 7);
const CB_MAX_CONTENT_UCS2 = Math.floor((CB_MESSAGE_SIZE_GSM - 6) / 2);
@@ -70,412 +19,327 @@ const BODY_UCS2 = "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
+ "\u0000"; // 41 unicode chars.
const BODY_UCS2_IND = BODY_UCS2.substr(1);
-SpecialPowers.addPermission("cellbroadcast", true, document);
-SpecialPowers.addPermission("mobileconnection", true, document);
-
is(BODY_7BITS.length, CB_MAX_CONTENT_7BIT, "BODY_7BITS.length");
is(BODY_7BITS_IND.length, CB_MAX_CONTENT_7BIT - 3, "BODY_7BITS_IND.length");
is(BODY_UCS2.length, CB_MAX_CONTENT_UCS2, "BODY_UCS2.length");
-is(BODY_UCS2_IND.length, CB_MAX_CONTENT_UCS2 - 1, "BODY_UCS2_IND.length")
+is(BODY_UCS2_IND.length, CB_MAX_CONTENT_UCS2 - 1, "BODY_UCS2_IND.length");
-let cbs = window.navigator.mozCellBroadcast;
-ok(cbs instanceof window.MozCellBroadcast,
- "mozCellBroadcast is instanceof " + cbs.constructor);
-
-let pendingEmulatorCmdCount = 0;
-function sendCellBroadcastMessage(pdu, callback) {
- pendingEmulatorCmdCount++;
-
- let cmd = "cbs pdu " + pdu;
- runEmulatorCmd(cmd, function(result) {
- pendingEmulatorCmdCount--;
-
- is(result[0], "OK", "Emulator response");
-
- if (callback) {
- window.setTimeout(callback, 0);
- }
- });
-}
-
-function buildHexStr(n, numSemiOctets) {
- let str = n.toString(16);
- ok(str.length <= numSemiOctets);
- while (str.length < numSemiOctets) {
- str = "0" + str;
- }
- return str;
-}
-
-function seq(end, begin) {
- let result = [];
- for (let i = begin || 0; i < end; i++) {
- result.push(i);
- }
- return result;
-}
-
-function repeat(func, array, oncomplete) {
- (function do_call(index) {
- let next = index < (array.length - 1) ? do_call.bind(null, index + 1) : oncomplete;
- func.apply(null, [array[index], next]);
- })(0);
-}
-
-function doTestHelper(pdu, nextTest, checkFunc) {
- cbs.addEventListener("received", function onreceived(event) {
- cbs.removeEventListener("received", onreceived);
-
- checkFunc(event.message);
-
- window.setTimeout(nextTest, 0);
- });
-
- if (Array.isArray(pdu)) {
- repeat(sendCellBroadcastMessage, pdu);
- } else {
- sendCellBroadcastMessage(pdu);
- }
-}
-
-/**
- * Tests receiving Cell Broadcast messages, event instance type, all attributes
- * of CellBroadcastMessage exist.
- */
-function testGsmMessageAttributes() {
- log("Test GSM Cell Broadcast message attributes");
-
- cbs.addEventListener("received", function onreceived(event) {
- cbs.removeEventListener("received", onreceived);
-
- // Bug 838542: following check throws an exception and fails this case.
- // ok(event instanceof MozCellBroadcastEvent,
- // "event is instanceof " + event.constructor)
- ok(event, "event is valid");
-
- let message = event.message;
- ok(message, "event.message is valid");
+function testReceiving_GSM_MessageAttributes() {
+ log("Test receiving GSM Cell Broadcast - Message Attributes");
+ let verifyCBMessage = (aMessage) => {
// Attributes other than `language` and `body` should always be assigned.
- ok(message.gsmGeographicalScope != null, "message.gsmGeographicalScope");
- ok(message.messageCode != null, "message.messageCode");
- ok(message.messageId != null, "message.messageId");
- ok(message.language != null, "message.language");
- ok(message.body != null, "message.body");
- ok(message.messageClass != null, "message.messageClass");
- ok(message.timestamp != null, "message.timestamp");
- ok('etws' in message, "message.etws");
- if (message.etws) {
- ok('warningType' in message.etws, "message.etws.warningType");
- ok(message.etws.emergencyUserAlert != null, "message.etws.emergencyUserAlert");
- ok(message.etws.popup != null, "message.etws.popup");
+ ok(aMessage.gsmGeographicalScope != null, "aMessage.gsmGeographicalScope");
+ ok(aMessage.messageCode != null, "aMessage.messageCode");
+ ok(aMessage.messageId != null, "aMessage.messageId");
+ ok(aMessage.language != null, "aMessage.language");
+ ok(aMessage.body != null, "aMessage.body");
+ ok(aMessage.messageClass != null, "aMessage.messageClass");
+ ok(aMessage.timestamp != null, "aMessage.timestamp");
+ ok('etws' in aMessage, "aMessage.etws");
+ if (aMessage.etws) {
+ ok('warningType' in aMessage.etws, "aMessage.etws.warningType");
+ ok(aMessage.etws.emergencyUserAlert != null, "aMessage.etws.emergencyUserAlert");
+ ok(aMessage.etws.popup != null, "aMessage.etws.popup");
}
- ok(message.cdmaServiceCategory != null, "message.cdmaServiceCategory");
-
- window.setTimeout(testReceiving_GSM_GeographicalScope, 0);
- });
+ ok(aMessage.cdmaServiceCategory != null, "aMessage.cdmaServiceCategory");
+ };
// Here we use a simple GSM message for test.
let pdu = buildHexStr(0, CB_MESSAGE_SIZE_GSM * 2);
- sendCellBroadcastMessage(pdu);
+
+ return sendMultipleRawCbsToEmulatorAndWait([pdu])
+ .then((aMessage) => verifyCBMessage(aMessage));
}
function testReceiving_GSM_GeographicalScope() {
log("Test receiving GSM Cell Broadcast - Geographical Scope");
- function do_test(gs, nextTest) {
- let pdu = buildHexStr(((gs & 0x03) << 14), 4)
+ let promise = Promise.resolve();
+
+ let verifyCBMessage = (aMessage, aGsName) => {
+ is(aMessage.gsmGeographicalScope, aGsName,
+ "aMessage.gsmGeographicalScope");
+ };
+
+ CB_GSM_GEOGRAPHICAL_SCOPE_NAMES.forEach(function(aGsName, aIndex) {
+ let pdu = buildHexStr(((aIndex & 0x03) << 14), 4)
+ buildHexStr(0, (CB_MESSAGE_SIZE_GSM - 2) * 2);
+ promise = promise
+ .then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
+ .then((aMessage) => verifyCBMessage(aMessage, aGsName));
+ });
- doTestHelper(pdu, nextTest, function(message) {
- is(message.gsmGeographicalScope, CB_GSM_GEOGRAPHICAL_SCOPE_NAMES[gs],
- "message.gsmGeographicalScope");
- });
- }
-
- repeat(do_test, seq(CB_GSM_GEOGRAPHICAL_SCOPE_NAMES.length),
- testReceiving_GSM_MessageCode);
+ return promise;
}
function testReceiving_GSM_MessageCode() {
log("Test receiving GSM Cell Broadcast - Message Code");
+ let promise = Promise.resolve();
+
// Message Code has 10 bits, and is ORed into a 16 bits 'serial' number. Here
// we test every single bit to verify the operation doesn't go wrong.
- let messageCodesToTest = [
+ let messageCodes = [
0x000, 0x001, 0x002, 0x004, 0x008, 0x010, 0x020, 0x040,
0x080, 0x100, 0x200, 0x251
];
- function do_test(messageCode, nextTest) {
- let pdu = buildHexStr(((messageCode & 0x3FF) << 4), 4)
+ let verifyCBMessage = (aMessage, aMsgCode) => {
+ is(aMessage.messageCode, aMsgCode, "aMessage.messageCode");
+ };
+
+ messageCodes.forEach(function(aMsgCode) {
+ let pdu = buildHexStr(((aMsgCode & 0x3FF) << 4), 4)
+ buildHexStr(0, (CB_MESSAGE_SIZE_GSM - 2) * 2);
+ promise = promise
+ .then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
+ .then((aMessage) => verifyCBMessage(aMessage, aMsgCode));
+ });
- doTestHelper(pdu, nextTest, function(message) {
- is(message.messageCode, messageCode, "message.messageCode");
- });
- }
-
- repeat(do_test, messageCodesToTest, testReceiving_GSM_MessageId);
+ return promise;
}
function testReceiving_GSM_MessageId() {
log("Test receiving GSM Cell Broadcast - Message Identifier");
+ let promise = Promise.resolve();
+
// Message Identifier has 16 bits, but no bitwise operation is needed.
// Test some selected values only.
- let messageIdsToTest = [
+ let messageIds = [
0x0000, 0x0001, 0x0010, 0x0100, 0x1000, 0x1111, 0x8888, 0x8811,
];
- function do_test(messageId, nextTest) {
+ let verifyCBMessage = (aMessage, aMessageId) => {
+ is(aMessage.messageId, aMessageId, "aMessage.messageId");
+ ok(aMessage.etws == null, "aMessage.etws");
+ };
+
+ messageIds.forEach(function(aMessageId) {
let pdu = buildHexStr(0, 4)
- + buildHexStr((messageId & 0xFFFF), 4)
+ + buildHexStr((aMessageId & 0xFFFF), 4)
+ buildHexStr(0, (CB_MESSAGE_SIZE_GSM - 4) * 2);
+ promise = promise
+ .then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
+ .then((aMessage) => verifyCBMessage(aMessage, aMessageId));
+ });
- doTestHelper(pdu, nextTest, function(message) {
- is(message.messageId, messageId, "message.messageId");
- ok(message.etws == null, "message.etws");
- });
- }
-
- repeat(do_test, messageIdsToTest, testReceiving_GSM_Language_and_Body);
-}
-
-// Copied from GsmPDUHelper.readCbDataCodingScheme
-function decodeDataCodingScheme(dcs) {
- let language = null;
- let hasLanguageIndicator = false;
- let encoding = PDU_DCS_MSG_CODING_7BITS_ALPHABET;
- let messageClass = PDU_DCS_MSG_CLASS_NORMAL;
-
- switch (dcs & PDU_DCS_CODING_GROUP_BITS) {
- case 0x00: // 0000
- language = CB_DCS_LANG_GROUP_1[dcs & 0x0F];
- break;
-
- case 0x10: // 0001
- switch (dcs & 0x0F) {
- case 0x00:
- hasLanguageIndicator = true;
- break;
- case 0x01:
- encoding = PDU_DCS_MSG_CODING_16BITS_ALPHABET;
- hasLanguageIndicator = true;
- break;
- }
- break;
-
- case 0x20: // 0010
- language = CB_DCS_LANG_GROUP_2[dcs & 0x0F];
- break;
-
- case 0x40: // 01xx
- case 0x50:
- //case 0x60:
- //case 0x70:
- case 0x90: // 1001
- encoding = (dcs & 0x0C);
- if (encoding == 0x0C) {
- encoding = PDU_DCS_MSG_CODING_7BITS_ALPHABET;
- }
- messageClass = (dcs & PDU_DCS_MSG_CLASS_BITS);
- break;
-
- case 0xF0:
- encoding = (dcs & 0x04) ? PDU_DCS_MSG_CODING_8BITS_ALPHABET
- : PDU_DCS_MSG_CODING_7BITS_ALPHABET;
- switch(dcs & PDU_DCS_MSG_CLASS_BITS) {
- case 0x01: messageClass = PDU_DCS_MSG_CLASS_USER_1; break;
- case 0x02: messageClass = PDU_DCS_MSG_CLASS_USER_2; break;
- case 0x03: messageClass = PDU_DCS_MSG_CLASS_TE_SPECIFIC; break;
- }
- break;
-
- case 0x30: // 0011 (Reserved)
- case 0x80: // 1000 (Reserved)
- case 0xA0: // 1010..1100 (Reserved)
- case 0xB0:
- case 0xC0:
- break;
- default:
- throw new Error("Unsupported CBS data coding scheme: " + dcs);
- }
-
- return [encoding, language, hasLanguageIndicator,
- GECKO_SMS_MESSAGE_CLASSES[messageClass]];
+ return promise;
}
function testReceiving_GSM_Language_and_Body() {
log("Test receiving GSM Cell Broadcast - Language & Body");
- function do_test(dcs) {
- let encoding, language, indicator, messageClass;
+ let promise = Promise.resolve();
+
+ let testDcs = [];
+ dcs = 0;
+ while (dcs <= 0xFF) {
try {
- [encoding, language, indicator, messageClass] = decodeDataCodingScheme(dcs);
+ let dcsInfo = { dcs: dcs };
+ [ dcsInfo.encoding, dcsInfo.language,
+ dcsInfo.indicator, dcsInfo.messageClass ] = decodeGsmDataCodingScheme(dcs);
+ testDcs.push(dcsInfo);
} catch (e) {
// Unsupported coding group, skip.
- let nextGroup = (dcs & PDU_DCS_CODING_GROUP_BITS) + 0x10;
- window.setTimeout(do_test.bind(null, nextGroup), 0);
- return;
+ let dcs = (dcs & PDU_DCS_CODING_GROUP_BITS) + 0x10;
}
-
- let pdu = buildHexStr(0, 8)
- + buildHexStr(dcs, 2)
- + buildHexStr(0, (CB_MESSAGE_SIZE_GSM - 5) * 2);
-
- let nextTest = (dcs < 0xFF) ? do_test.bind(null, dcs + 1)
- : testReceiving_GSM_Timestamp;
- doTestHelper(pdu, nextTest, function(message) {
- if (language) {
- is(message.language, language, "message.language");
- } else if (indicator) {
- is(message.language, "@@", "message.language");
- } else {
- ok(message.language == null, "message.language");
- }
-
- switch (encoding) {
- case PDU_DCS_MSG_CODING_7BITS_ALPHABET:
- is(message.body, indicator ? BODY_7BITS_IND : BODY_7BITS, "message.body");
- break;
- case PDU_DCS_MSG_CODING_8BITS_ALPHABET:
- ok(message.body == null, "message.body");
- break;
- case PDU_DCS_MSG_CODING_16BITS_ALPHABET:
- is(message.body, indicator ? BODY_UCS2_IND : BODY_UCS2, "message.body");
- break;
- }
-
- is(message.messageClass, messageClass, "message.messageClass");
- });
+ dcs++;
}
- do_test(0);
+ let verifyCBMessage = (aMessage, aDcsInfo) => {
+ if (aDcsInfo.language) {
+ is(aMessage.language, aDcsInfo.language, "aMessage.language");
+ } else if (aDcsInfo.indicator) {
+ is(aMessage.language, "@@", "aMessage.language");
+ } else {
+ ok(aMessage.language == null, "aMessage.language");
+ }
+
+ switch (aDcsInfo.encoding) {
+ case PDU_DCS_MSG_CODING_7BITS_ALPHABET:
+ is(aMessage.body, aDcsInfo.indicator ? BODY_7BITS_IND : BODY_7BITS, "aMessage.body");
+ break;
+ case PDU_DCS_MSG_CODING_8BITS_ALPHABET:
+ ok(aMessage.body == null, "aMessage.body");
+ break;
+ case PDU_DCS_MSG_CODING_16BITS_ALPHABET:
+ is(aMessage.body, aDcsInfo.indicator ? BODY_UCS2_IND : BODY_UCS2, "aMessage.body");
+ break;
+ }
+
+ is(aMessage.messageClass, aDcsInfo.messageClass, "aMessage.messageClass");
+ };
+
+ testDcs.forEach(function(aDcsInfo) {
+ let pdu = buildHexStr(0, 8)
+ + buildHexStr(aDcsInfo.dcs, 2)
+ + buildHexStr(0, (CB_MESSAGE_SIZE_GSM - 5) * 2);
+ promise = promise
+ .then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
+ .then((aMessage) => verifyCBMessage(aMessage, aDcsInfo));
+ });
+
+ return promise;
}
function testReceiving_GSM_Timestamp() {
log("Test receiving GSM Cell Broadcast - Timestamp");
- let pdu = buildHexStr(0, CB_MESSAGE_SIZE_GSM * 2);
- doTestHelper(pdu, testReceiving_GSM_WarningType, function(message) {
+ let verifyCBMessage = (aMessage) => {
// Cell Broadcast messages do not contain a timestamp field (however, ETWS
// does). We only check the timestamp doesn't go too far (60 seconds) here.
- let msMessage = message.timestamp;
+ let msMessage = aMessage.timestamp;
let msNow = Date.now();
- ok(Math.abs(msMessage - msNow) < (1000 * 60), "message.timestamp");
- });
+ ok(Math.abs(msMessage - msNow) < (1000 * 60), "aMessage.timestamp");
+ };
+
+ let pdu = buildHexStr(0, CB_MESSAGE_SIZE_GSM * 2);
+
+ return sendMultipleRawCbsToEmulatorAndWait([pdu])
+ .then((aMessage) => verifyCBMessage(aMessage));
}
function testReceiving_GSM_WarningType() {
log("Test receiving GSM Cell Broadcast - Warning Type");
- let messageIdsToTest = [];
+ let promise = Promise.resolve();
+
+ let messageIds = [];
for (let i = CB_GSM_MESSAGEID_ETWS_BEGIN; i <= CB_GSM_MESSAGEID_ETWS_END; i++) {
- messageIdsToTest.push(i);
+ messageIds.push(i);
}
- function do_test(messageId, nextTest) {
+ let verifyCBMessage = (aMessage, aMessageId) => {
+ is(aMessage.messageId, aMessageId, "aMessage.messageId");
+ ok(aMessage.etws != null, "aMessage.etws");
+
+ let offset = aMessageId - CB_GSM_MESSAGEID_ETWS_BEGIN;
+ if (offset < CB_ETWS_WARNING_TYPE_NAMES.length) {
+ is(aMessage.etws.warningType, CB_ETWS_WARNING_TYPE_NAMES[offset],
+ "aMessage.etws.warningType");
+ } else {
+ ok(aMessage.etws.warningType == null, "aMessage.etws.warningType");
+ }
+ };
+
+ messageIds.forEach(function(aMessageId) {
let pdu = buildHexStr(0, 4)
- + buildHexStr((messageId & 0xFFFF), 4)
+ + buildHexStr((aMessageId & 0xFFFF), 4)
+ buildHexStr(0, (CB_MESSAGE_SIZE_GSM - 4) * 2);
-
- doTestHelper(pdu, nextTest, function(message) {
- is(message.messageId, messageId, "message.messageId");
- ok(message.etws != null, "message.etws");
-
- let offset = messageId - CB_GSM_MESSAGEID_ETWS_BEGIN;
- if (offset < CB_ETWS_WARNING_TYPE_NAMES.length) {
- is(message.etws.warningType, CB_ETWS_WARNING_TYPE_NAMES[offset],
- "message.etws.warningType");
- } else {
- ok(message.etws.warningType == null, "message.etws.warningType");
- }
- });
- }
-
- repeat(do_test, messageIdsToTest, testReceiving_GSM_EmergencyUserAlert);
-}
-
-function doTestEmergencyUserAlert_or_Popup(name, mask, nextTest) {
- let pdu = buildHexStr(mask, 4)
- + buildHexStr(CB_GSM_MESSAGEID_ETWS_BEGIN, 4)
- + buildHexStr(0, (CB_MESSAGE_SIZE_GSM - 4) * 2);
-
- doTestHelper(pdu, nextTest, function(message) {
- is(message.messageId, CB_GSM_MESSAGEID_ETWS_BEGIN, "message.messageId");
- ok(message.etws != null, "message.etws");
- is(message.etws[name], mask != 0, "message.etws." + name);
+ promise = promise
+ .then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
+ .then((aMessage) => verifyCBMessage(aMessage, aMessageId));
});
+
+ return promise;
}
function testReceiving_GSM_EmergencyUserAlert() {
log("Test receiving GSM Cell Broadcast - Emergency User Alert");
- repeat(doTestEmergencyUserAlert_or_Popup.bind(null, "emergencyUserAlert"),
- [0x2000, 0x0000], testReceiving_GSM_Popup);
+ let promise = Promise.resolve();
+
+ let emergencyUserAlertMasks = [0x2000, 0x0000];
+
+ let verifyCBMessage = (aMessage, aMask) => {
+ is(aMessage.messageId, CB_GSM_MESSAGEID_ETWS_BEGIN, "aMessage.messageId");
+ ok(aMessage.etws != null, "aMessage.etws");
+ is(aMessage.etws.emergencyUserAlert, aMask != 0, "aMessage.etws.emergencyUserAlert");
+ };
+
+ emergencyUserAlertMasks.forEach(function(aMask) {
+ let pdu = buildHexStr(aMask, 4)
+ + buildHexStr(CB_GSM_MESSAGEID_ETWS_BEGIN, 4)
+ + buildHexStr(0, (CB_MESSAGE_SIZE_GSM - 4) * 2);
+ promise = promise
+ .then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
+ .then((aMessage) => verifyCBMessage(aMessage, aMask));
+ });
+
+ return promise;
}
function testReceiving_GSM_Popup() {
log("Test receiving GSM Cell Broadcast - Popup");
- repeat(doTestEmergencyUserAlert_or_Popup.bind(null, "popup"),
- [0x1000, 0x0000], testReceiving_GSM_Multipart);
+ let promise = Promise.resolve();
+
+ let popupMasks = [0x1000, 0x0000];
+
+ let verifyCBMessage = (aMessage, aMask) => {
+ is(aMessage.messageId, CB_GSM_MESSAGEID_ETWS_BEGIN, "aMessage.messageId");
+ ok(aMessage.etws != null, "aMessage.etws");
+ is(aMessage.etws.popup, aMask != 0, "aMessage.etws.popup");
+ };
+
+ popupMasks.forEach(function(aMask) {
+ let pdu = buildHexStr(aMask, 4)
+ + buildHexStr(CB_GSM_MESSAGEID_ETWS_BEGIN, 4)
+ + buildHexStr(0, (CB_MESSAGE_SIZE_GSM - 4) * 2);
+ promise = promise
+ .then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
+ .then((aMessage) => verifyCBMessage(aMessage, aMask));
+ });
+
+ return promise;
}
function testReceiving_GSM_Multipart() {
log("Test receiving GSM Cell Broadcast - Multipart Messages");
- function do_test(numParts, nextTest) {
+ let promise = Promise.resolve();
+
+ // According to 9.4.1.2.4 Page Parameter in TS 23.041, the maximal Number of
+ // pages per CB message is 15.
+ let numParts = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
+
+ let verifyCBMessage = (aMessage, aNumParts) => {
+ is(aMessage.body.length, (aNumParts * CB_MAX_CONTENT_7BIT),
+ "aMessage.body");
+ };
+
+ numParts.forEach(function(aNumParts) {
let pdus = [];
- for (let i = 1; i <= numParts; i++) {
+ for (let i = 1; i <= aNumParts; i++) {
let pdu = buildHexStr(0, 10)
- + buildHexStr((i << 4) + numParts, 2)
+ + buildHexStr((i << 4) + aNumParts, 2)
+ buildHexStr(0, (CB_MESSAGE_SIZE_GSM - 6) * 2);
pdus.push(pdu);
}
+ promise = promise
+ .then(() => sendMultipleRawCbsToEmulatorAndWait(pdus))
+ .then((aMessage) => verifyCBMessage(aMessage, aNumParts));
+ });
- doTestHelper(pdus, nextTest, function(message) {
- is(message.body.length, (numParts * CB_MAX_CONTENT_7BIT),
- "message.body");
- });
- }
-
- repeat(do_test, seq(16, 1), testReceiving_GSM_ServiceCategory);
+ return promise;
}
function testReceiving_GSM_ServiceCategory() {
log("Test receiving GSM Cell Broadcast - Service Category");
- cbs.addEventListener("received", function onreceived(event) {
- cbs.removeEventListener("received", onreceived);
-
- let message = event.message;
-
+ let verifyCBMessage = (aMessage) => {
// Bug 910091
// "Service Category" is not defined in GSM. We should always get '0' here.
- is(message.cdmaServiceCategory, 0, "message.cdmaServiceCategory");
-
- window.setTimeout(cleanUp, 0);
- });
+ is(aMessage.cdmaServiceCategory, 0, "aMessage.cdmaServiceCategory");
+ };
let pdu = buildHexStr(0, CB_MESSAGE_SIZE_GSM * 2);
- sendCellBroadcastMessage(pdu);
+ return sendMultipleRawCbsToEmulatorAndWait([pdu])
+ .then((aMessage) => verifyCBMessage(aMessage));
}
-function cleanUp() {
- if (pendingEmulatorCmdCount > 0) {
- window.setTimeout(cleanUp, 100);
- return;
- }
-
- SpecialPowers.removePermission("mobileconnection", document);
- SpecialPowers.removePermission("cellbroadcast", true, document);
-
- finish();
-}
-
-waitFor(testGsmMessageAttributes, function() {
- return navigator.mozMobileConnections[0].voice.connected;
+startTestCommon(function testCaseMain() {
+ return testReceiving_GSM_MessageAttributes()
+ .then(() => testReceiving_GSM_GeographicalScope())
+ .then(() => testReceiving_GSM_MessageCode())
+ .then(() => testReceiving_GSM_MessageId())
+ .then(() => testReceiving_GSM_Language_and_Body())
+ .then(() => testReceiving_GSM_Timestamp())
+ .then(() => testReceiving_GSM_WarningType())
+ .then(() => testReceiving_GSM_EmergencyUserAlert())
+ .then(() => testReceiving_GSM_Popup())
+ .then(() => testReceiving_GSM_Multipart())
+ .then(() => testReceiving_GSM_ServiceCategory());
});
-
diff --git a/dom/cellbroadcast/tests/marionette/test_cellbroadcast_multi_sim.js b/dom/cellbroadcast/tests/marionette/test_cellbroadcast_multi_sim.js
index b89b54f7a13..e102045ecdf 100644
--- a/dom/cellbroadcast/tests/marionette/test_cellbroadcast_multi_sim.js
+++ b/dom/cellbroadcast/tests/marionette/test_cellbroadcast_multi_sim.js
@@ -1,19 +1,17 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
-MARIONETTE_TIMEOUT = 60000;
+MARIONETTE_TIMEOUT = 10000;
MARIONETTE_HEAD_JS = 'head.js';
const BODY_7BITS = "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
+ "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
+ "@@@@@@@@@@@@@"; // 93 ascii chars.
-const CB_PDU_SIZE = 88;
-function testReceivingMultiSIM() {
- let CB_PDU = "";
- while (CB_PDU.length < CB_PDU_SIZE * 2) {
- CB_PDU += "00";
- }
+function testReceiving_MultiSIM() {
+ log("Test receiving GSM Cell Broadcast - Multi-SIM");
+
+ let pdu = buildHexStr(0, CB_MESSAGE_SIZE_GSM * 2);
let verifyCBMessage = (aMessage, aServiceId) => {
log("Verify CB message received from serviceId: " + aServiceId);
@@ -22,13 +20,13 @@ function testReceivingMultiSIM() {
};
return selectModem(1)
- .then(() => sendMultipleRawCbsToEmulatorAndWait([CB_PDU]))
- .then((results) => verifyCBMessage(results[0].message, 1))
+ .then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
+ .then((aMessage) => verifyCBMessage(aMessage, 1))
.then(() => selectModem(0))
- .then(() => sendMultipleRawCbsToEmulatorAndWait([CB_PDU]))
- .then((results) => verifyCBMessage(results[0].message, 0));
+ .then(() => sendMultipleRawCbsToEmulatorAndWait([pdu]))
+ .then((aMessage) => verifyCBMessage(aMessage, 0));
}
startTestCommon(function testCaseMain() {
- return runIfMultiSIM(testReceivingMultiSIM);
+ return runIfMultiSIM(testReceiving_MultiSIM);
});
diff --git a/dom/contacts/ContactManager.js b/dom/contacts/ContactManager.js
index fbaaa027746..af3fd2c77f5 100644
--- a/dom/contacts/ContactManager.js
+++ b/dom/contacts/ContactManager.js
@@ -185,19 +185,6 @@ ContactManager.prototype = {
Services.DOMRequest.fireError(req.cursor, msg.errorMsg);
}
break;
- case "PermissionPromptHelper:AskPermission:OK":
- if (DEBUG) debug("id: " + msg.requestID);
- req = this.getRequest(msg.requestID);
- if (!req) {
- break;
- }
-
- if (msg.result == Ci.nsIPermissionManager.ALLOW_ACTION) {
- req.allow();
- } else {
- req.cancel();
- }
- break;
case "Contact:Changed":
// Fire oncontactchange event
if (DEBUG) debug("Contacts:ContactChanged: " + msg.contactID + ", " + msg.reason);
@@ -235,6 +222,7 @@ ContactManager.prototype = {
askPermission: function (aAccess, aRequest, aAllowCallback, aCancelCallback) {
if (DEBUG) debug("askPermission for contacts");
+
let access;
switch(aAccess) {
case "create":
@@ -255,38 +243,42 @@ ContactManager.prototype = {
}
// Shortcut for ALLOW_ACTION so we avoid a parent roundtrip
+ let principal = this._window.document.nodePrincipal;
let type = "contacts-" + access;
let permValue =
- Services.perms.testExactPermissionFromPrincipal(this._window.document.nodePrincipal, type);
+ Services.perms.testExactPermissionFromPrincipal(principal, type);
if (permValue == Ci.nsIPermissionManager.ALLOW_ACTION) {
aAllowCallback();
return;
+ } else if (permValue == Ci.nsIPermissionManager.DENY_ACTION) {
+ aCancelCallback();
}
- let requestID = this.getRequestId({
- request: aRequest,
- allow: function() {
- aAllowCallback();
- }.bind(this),
- cancel : function() {
- if (aCancelCallback) {
- aCancelCallback()
- } else if (aRequest) {
- Services.DOMRequest.fireError(aRequest, "Not Allowed");
- }
- }.bind(this)
- });
-
- let principal = this._window.document.nodePrincipal;
- cpmm.sendAsyncMessage("PermissionPromptHelper:AskPermission", {
+ // Create an array with a single nsIContentPermissionType element.
+ let type = {
type: "contacts",
access: access,
- requestID: requestID,
- origin: principal.origin,
- appID: principal.appId,
- browserFlag: principal.isInBrowserElement,
- windowID: this._window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).outerWindowID
- });
+ options: null,
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionType])
+ };
+ let typeArray = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
+ typeArray.appendElement(type, false);
+
+ // create a nsIContentPermissionRequest
+ let request = {
+ types: typeArray,
+ principal: principal,
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionRequest]),
+ allow: aAllowCallback,
+ cancel: aCancelCallback,
+ window: this._window
+ };
+
+ // Using askPermission from nsIDOMWindowUtils that takes care of the
+ // remoting if needed.
+ let windowUtils = this._window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindowUtils);
+ windowUtils.askPermission(request);
},
save: function save(aContact) {
@@ -464,7 +456,6 @@ ContactManager.prototype = {
"Contact:Save:Return:OK", "Contact:Save:Return:KO",
"Contact:Remove:Return:OK", "Contact:Remove:Return:KO",
"Contact:Changed",
- "PermissionPromptHelper:AskPermission:OK",
"Contacts:GetAll:Next", "Contacts:GetAll:Return:KO",
"Contacts:Count",
"Contacts:Revision", "Contacts:GetRevision:Return:KO",]);
diff --git a/dom/contacts/tests/shared.js b/dom/contacts/tests/shared.js
index 893469ecef7..1fbf7dacd80 100644
--- a/dom/contacts/tests/shared.js
+++ b/dom/contacts/tests/shared.js
@@ -3,7 +3,6 @@
// Fix the environment to run Contacts tests
if (SpecialPowers.isMainProcess()) {
SpecialPowers.Cu.import("resource://gre/modules/ContactService.jsm");
- SpecialPowers.Cu.import("resource://gre/modules/PermissionPromptHelper.jsm");
}
SpecialPowers.addPermission("contacts-write", true, document);
diff --git a/dom/inputmethod/mochitest/file_inputmethod.html b/dom/inputmethod/mochitest/file_inputmethod.html
index f228a8d8d12..193cb050567 100644
--- a/dom/inputmethod/mochitest/file_inputmethod.html
+++ b/dom/inputmethod/mochitest/file_inputmethod.html
@@ -3,15 +3,22 @@
+Mozilla Bug 953044
+
+
+
+
+
diff --git a/dom/inputmethod/mochitest/mochitest.ini b/dom/inputmethod/mochitest/mochitest.ini
index 61392de93f4..6b4be4b9ec9 100644
--- a/dom/inputmethod/mochitest/mochitest.ini
+++ b/dom/inputmethod/mochitest/mochitest.ini
@@ -11,6 +11,7 @@ support-files =
[test_basic.html]
[test_bug944397.html]
[test_bug949059.html]
+[test_bug953044.html]
[test_bug960946.html]
[test_bug978918.html]
[test_delete_focused_element.html]
diff --git a/dom/inputmethod/mochitest/test_bug944397.html b/dom/inputmethod/mochitest/test_bug944397.html
index 8f9647dca34..ee7000ab5f4 100644
--- a/dom/inputmethod/mochitest/test_bug944397.html
+++ b/dom/inputmethod/mochitest/test_bug944397.html
@@ -23,85 +23,85 @@ inputmethod_setup(function() {
function appFrameScript() {
let input = content.document.getElementById('test-input');
input.oninput = function() {
- dump('oninput was called in file_test_app.html.');
- sendAsyncMessage('test:InputMethod:oninput', {});
+ sendAsyncMessage('test:InputMethod:oninput', {
+ value: input.value
+ });
};
-
- /*
- * Bug 957213. Sometimes we need to refocus the input field to avoid
- * intermittent test failure.
- */
- content.setInterval(function() {
- input.focus();
- }, 500);
}
function runTest() {
- let timeoutId = null;
+ let app, keyboard;
- // Create an app frame to recieve keyboard inputs.
- let app = document.createElement('iframe');
- app.src = 'file_test_app.html';
- app.setAttribute('mozbrowser', true);
- document.body.appendChild(app);
- app.addEventListener('mozbrowserloadend', function() {
- let mm = SpecialPowers.getBrowserFrameMessageManager(app);
- mm.loadFrameScript('data:,(' + appFrameScript.toString() + ')();', false);
- mm.addMessageListener("test:InputMethod:oninput", function() {
- ok(true, 'Keyboard input was received.');
- clearTimeout(timeoutId);
- inputmethod_cleanup();
- });
- });
-
- // Create a browser frame to load the input method app.
- let keyboard = document.createElement('iframe');
- keyboard.setAttribute('mozbrowser', true);
- document.body.appendChild(keyboard);
-
- // Bug 953044 setInputMethodActive(false) before input method app loads should
- // always succeed.
- let req = keyboard.setInputMethodActive(false);
- req.onsuccess = function() {
- ok(true, 'setInputMethodActive before loading succeeded.');
- };
-
- req.onerror = function() {
- ok(false, 'setInputMethodActive before loading failed: ' + this.error.name);
- clearTimeout(timeoutId);
- inputmethod_cleanup();
- };
+ /**
+ * So this test does the following:
+ * 1. Create a mozbrowser iframe with a text field in it, and focus the text field
+ * 2. 100ms. after loading we create new keyboard iframe, that will try to execute
+ * replaceSurroundingText on the current active inputcontext
+ * 3. That should trigger 'input' event on the said text field
+ * 4. And if that happens we know everything is OK
+ */
let path = location.pathname;
- let imeUrl = location.protocol + '//' + location.host +
- path.substring(0, path.lastIndexOf('/')) +
- '/file_inputmethod.html#data';
- SpecialPowers.pushPermissions([{
- type: 'input',
- allow: true,
- context: imeUrl
- }], function() {
- let req = keyboard.setInputMethodActive(true);
+ let basePath = location.protocol + '//' + location.host +
+ path.substring(0, path.lastIndexOf('/'));
- req.onsuccess = function() {
- ok(true, 'setInputMethodActive succeeded.');
- };
+ // STEP 1: Create an app frame to recieve keyboard inputs.
+ function step1() {
+ app = document.createElement('iframe');
+ app.src = basePath + '/file_test_app.html';
+ app.setAttribute('mozbrowser', true);
+ document.body.appendChild(app);
+ app.addEventListener('mozbrowserloadend', function() {
+ let mm = SpecialPowers.getBrowserFrameMessageManager(app);
+ mm.loadFrameScript('data:,(' + appFrameScript.toString() + ')();', false);
+ mm.addMessageListener("test:InputMethod:oninput", function(ev) {
+ step4(SpecialPowers.wrap(ev).json.value);
+ });
- req.onerror = function() {
- ok(false, 'setInputMethodActive failed: ' + this.error.name);
- inputmethod_cleanup();
- };
+ step2();
+ });
+ }
- // Loads the input method app to the browser frame after a delay.
- SpecialPowers.DOMWindowUtils.focus(app);
- setTimeout(function() {
- keyboard.src = imeUrl;
- timeoutId = setTimeout(function() {
- inputmethod_cleanup();
- ok(false, 'Failed to generate keyboard input.');
- }, 20000);
- }, 100);
- });
+ function step2() {
+ // STEP 2a: Create a browser frame to load the input method app.
+ keyboard = document.createElement('iframe');
+ keyboard.setAttribute('mozbrowser', true);
+ document.body.appendChild(keyboard);
+
+ // STEP 2b: Grant input privileges to the keyboard iframe
+ let imeUrl = basePath + '/file_inputmethod.html#data';
+
+ SpecialPowers.pushPermissions([{
+ type: 'input',
+ allow: true,
+ context: imeUrl
+ }], function() {
+ // STEP 2c: Tell Gecko to use this iframe as its keyboard app
+ let req = keyboard.setInputMethodActive(true);
+
+ req.onsuccess = function() {
+ ok(true, 'setInputMethodActive succeeded.');
+ };
+
+ req.onerror = function() {
+ ok(false, 'setInputMethodActive failed: ' + this.error.name);
+ inputmethod_cleanup();
+ };
+
+ // STEP 3: Loads the input method app to the browser frame after a delay.
+ setTimeout(function() {
+ keyboard.src = imeUrl;
+ }, 100);
+ });
+ }
+
+ function step4(val) {
+ ok(true, 'Keyboard input was received.');
+ is(val, '#dataYuan', 'Input value');
+ inputmethod_cleanup();
+ }
+
+ step1();
}
diff --git a/dom/inputmethod/mochitest/test_bug953044.html b/dom/inputmethod/mochitest/test_bug953044.html
new file mode 100644
index 00000000000..45625923b6e
--- /dev/null
+++ b/dom/inputmethod/mochitest/test_bug953044.html
@@ -0,0 +1,52 @@
+
+
+
+