Merge m-c to inbound.

This commit is contained in:
Ryan VanderMeulen 2013-11-06 14:59:07 -05:00
commit 200939b5bc
59 changed files with 2163 additions and 504 deletions

View File

@ -1,4 +1,4 @@
{
"revision": "5cc807be4ece02c364c1d7f1cd2becbe1a1b441e",
"revision": "d995823e80fa7f2e67263699bb6e42a6bcde65ea",
"repo_path": "/integration/gaia-central"
}

View File

@ -104,7 +104,12 @@ Selection.prototype = {
this.emit("pseudoclass");
}
if (detached) {
this.emit("detached", parentNode ? parentNode.rawNode() : null);
let rawNode = null;
if (parentNode && parentNode.isLocal_toBeDeprecated()) {
rawNode = parentNode.rawNode();
}
this.emit("detached", rawNode, null);
this.emit("detached-front", parentNode);
}
},
@ -156,7 +161,10 @@ Selection.prototype = {
setNodeFront: function(value, reason="unknown") {
this.reason = reason;
if (value !== this._nodeFront) {
let rawValue = value ? value.rawNode() : value;
let rawValue = null;
if (value && value.isLocal_toBeDeprecated()) {
rawValue = value.rawNode();
}
this.emit("before-new-node", rawValue, reason);
this.emit("before-new-node-front", value, reason);
let previousNode = this._node;
@ -208,7 +216,10 @@ Selection.prototype = {
// As long as there are still tools going around
// accessing node.rawNode, this needs to stay.
let rawNode = node.rawNode();
let rawNode = null;
if (node.isLocal_toBeDeprecated()) {
rawNode = node.rawNode();
}
if (rawNode) {
try {
let doc = this.document;

View File

@ -35,6 +35,7 @@ browser.jar:
content/browser/devtools/codemirror/xml.js (sourceeditor/codemirror/xml.js)
content/browser/devtools/codemirror/css.js (sourceeditor/codemirror/css.js)
content/browser/devtools/codemirror/htmlmixed.js (sourceeditor/codemirror/htmlmixed.js)
content/browser/devtools/codemirror/clike.js (sourceeditor/codemirror/clike.js)
content/browser/devtools/codemirror/activeline.js (sourceeditor/codemirror/activeline.js)
content/browser/devtools/codemirror/matchbrackets.js (sourceeditor/codemirror/matchbrackets.js)
content/browser/devtools/codemirror/closebrackets.js (sourceeditor/codemirror/closebrackets.js)

View File

@ -1570,7 +1570,9 @@ function ElementEditor(aContainer, aNode) {
// Create the main editor
this.template("element", this);
this.rawNode = aNode.rawNode();
if (aNode.isLocal_toBeDeprecated()) {
this.rawNode = aNode.rawNode();
}
// Make the tag name editable (unless this is a remote node or
// a document element)

View File

@ -38,7 +38,6 @@ const SHADERS_AUTOGROW_ITEMS = 4;
const GUTTER_ERROR_PANEL_OFFSET_X = 7; // px
const GUTTER_ERROR_PANEL_DELAY = 100; // ms
const DEFAULT_EDITOR_CONFIG = {
mode: Editor.modes.text,
gutters: ["errors"],
lineNumbers: true,
showAnnotationRuler: true
@ -389,6 +388,7 @@ let ShadersEditorsView = {
// in the ether of a resolved promise's value.
let parent = $("#" + type +"-editor");
let editor = new Editor(DEFAULT_EDITOR_CONFIG);
editor.config.mode = Editor.modes[type];
editor.appendTo(parent).then(() => deferred.resolve(editor));
return deferred.promise;

View File

@ -15,6 +15,7 @@ To confirm the functionality run mochitests for the following components:
* scratchpad
* debugger
* styleditor
* netmonitor
The sourceeditor component contains imported CodeMirror tests [3]. Some
tests were commented out because we don't use that functionality within
@ -38,7 +39,10 @@ in the LICENSE file:
* comment.js
* dialog/dialog.css
* dialog/dialog.js
* xml.js
* css.js
* javascript.js
* clike.js
* matchbrackets.js
* closebrackets.js
* search/match-highlighter.js

View File

@ -0,0 +1,362 @@
CodeMirror.defineMode("clike", function(config, parserConfig) {
var indentUnit = config.indentUnit,
statementIndentUnit = parserConfig.statementIndentUnit || indentUnit,
dontAlignCalls = parserConfig.dontAlignCalls,
keywords = parserConfig.keywords || {},
builtin = parserConfig.builtin || {},
blockKeywords = parserConfig.blockKeywords || {},
atoms = parserConfig.atoms || {},
hooks = parserConfig.hooks || {},
multiLineStrings = parserConfig.multiLineStrings;
var isOperatorChar = /[+\-*&%=<>!?|\/]/;
var curPunc;
function tokenBase(stream, state) {
var ch = stream.next();
if (hooks[ch]) {
var result = hooks[ch](stream, state);
if (result !== false) return result;
}
if (ch == '"' || ch == "'") {
state.tokenize = tokenString(ch);
return state.tokenize(stream, state);
}
if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
curPunc = ch;
return null;
}
if (/\d/.test(ch)) {
stream.eatWhile(/[\w\.]/);
return "number";
}
if (ch == "/") {
if (stream.eat("*")) {
state.tokenize = tokenComment;
return tokenComment(stream, state);
}
if (stream.eat("/")) {
stream.skipToEnd();
return "comment";
}
}
if (isOperatorChar.test(ch)) {
stream.eatWhile(isOperatorChar);
return "operator";
}
stream.eatWhile(/[\w\$_]/);
var cur = stream.current();
if (keywords.propertyIsEnumerable(cur)) {
if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement";
return "keyword";
}
if (builtin.propertyIsEnumerable(cur)) {
if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement";
return "builtin";
}
if (atoms.propertyIsEnumerable(cur)) return "atom";
return "variable";
}
function tokenString(quote) {
return function(stream, state) {
var escaped = false, next, end = false;
while ((next = stream.next()) != null) {
if (next == quote && !escaped) {end = true; break;}
escaped = !escaped && next == "\\";
}
if (end || !(escaped || multiLineStrings))
state.tokenize = null;
return "string";
};
}
function tokenComment(stream, state) {
var maybeEnd = false, ch;
while (ch = stream.next()) {
if (ch == "/" && maybeEnd) {
state.tokenize = null;
break;
}
maybeEnd = (ch == "*");
}
return "comment";
}
function Context(indented, column, type, align, prev) {
this.indented = indented;
this.column = column;
this.type = type;
this.align = align;
this.prev = prev;
}
function pushContext(state, col, type) {
var indent = state.indented;
if (state.context && state.context.type == "statement")
indent = state.context.indented;
return state.context = new Context(indent, col, type, null, state.context);
}
function popContext(state) {
var t = state.context.type;
if (t == ")" || t == "]" || t == "}")
state.indented = state.context.indented;
return state.context = state.context.prev;
}
// Interface
return {
startState: function(basecolumn) {
return {
tokenize: null,
context: new Context((basecolumn || 0) - indentUnit, 0, "top", false),
indented: 0,
startOfLine: true
};
},
token: function(stream, state) {
var ctx = state.context;
if (stream.sol()) {
if (ctx.align == null) ctx.align = false;
state.indented = stream.indentation();
state.startOfLine = true;
}
if (stream.eatSpace()) return null;
curPunc = null;
var style = (state.tokenize || tokenBase)(stream, state);
if (style == "comment" || style == "meta") return style;
if (ctx.align == null) ctx.align = true;
if ((curPunc == ";" || curPunc == ":" || curPunc == ",") && ctx.type == "statement") popContext(state);
else if (curPunc == "{") pushContext(state, stream.column(), "}");
else if (curPunc == "[") pushContext(state, stream.column(), "]");
else if (curPunc == "(") pushContext(state, stream.column(), ")");
else if (curPunc == "}") {
while (ctx.type == "statement") ctx = popContext(state);
if (ctx.type == "}") ctx = popContext(state);
while (ctx.type == "statement") ctx = popContext(state);
}
else if (curPunc == ctx.type) popContext(state);
else if (((ctx.type == "}" || ctx.type == "top") && curPunc != ';') || (ctx.type == "statement" && curPunc == "newstatement"))
pushContext(state, stream.column(), "statement");
state.startOfLine = false;
return style;
},
indent: function(state, textAfter) {
if (state.tokenize != tokenBase && state.tokenize != null) return CodeMirror.Pass;
var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
if (ctx.type == "statement" && firstChar == "}") ctx = ctx.prev;
var closing = firstChar == ctx.type;
if (ctx.type == "statement") return ctx.indented + (firstChar == "{" ? 0 : statementIndentUnit);
else if (ctx.align && (!dontAlignCalls || ctx.type != ")")) return ctx.column + (closing ? 0 : 1);
else if (ctx.type == ")" && !closing) return ctx.indented + statementIndentUnit;
else return ctx.indented + (closing ? 0 : indentUnit);
},
electricChars: "{}",
blockCommentStart: "/*",
blockCommentEnd: "*/",
lineComment: "//",
fold: "brace"
};
});
(function() {
function words(str) {
var obj = {}, words = str.split(" ");
for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
return obj;
}
var cKeywords = "auto if break int case long char register continue return default short do sizeof " +
"double static else struct entry switch extern typedef float union for unsigned " +
"goto while enum void const signed volatile";
function cppHook(stream, state) {
if (!state.startOfLine) return false;
for (;;) {
if (stream.skipTo("\\")) {
stream.next();
if (stream.eol()) {
state.tokenize = cppHook;
break;
}
} else {
stream.skipToEnd();
state.tokenize = null;
break;
}
}
return "meta";
}
// C#-style strings where "" escapes a quote.
function tokenAtString(stream, state) {
var next;
while ((next = stream.next()) != null) {
if (next == '"' && !stream.eat('"')) {
state.tokenize = null;
break;
}
}
return "string";
}
function mimes(ms, mode) {
for (var i = 0; i < ms.length; ++i) CodeMirror.defineMIME(ms[i], mode);
}
mimes(["text/x-csrc", "text/x-c", "text/x-chdr"], {
name: "clike",
keywords: words(cKeywords),
blockKeywords: words("case do else for if switch while struct"),
atoms: words("null"),
hooks: {"#": cppHook}
});
mimes(["text/x-c++src", "text/x-c++hdr"], {
name: "clike",
keywords: words(cKeywords + " asm dynamic_cast namespace reinterpret_cast try bool explicit new " +
"static_cast typeid catch operator template typename class friend private " +
"this using const_cast inline public throw virtual delete mutable protected " +
"wchar_t"),
blockKeywords: words("catch class do else finally for if struct switch try while"),
atoms: words("true false null"),
hooks: {"#": cppHook}
});
CodeMirror.defineMIME("text/x-java", {
name: "clike",
keywords: words("abstract assert boolean break byte case catch char class const continue default " +
"do double else enum extends final finally float for goto if implements import " +
"instanceof int interface long native new package private protected public " +
"return short static strictfp super switch synchronized this throw throws transient " +
"try void volatile while"),
blockKeywords: words("catch class do else finally for if switch try while"),
atoms: words("true false null"),
hooks: {
"@": function(stream) {
stream.eatWhile(/[\w\$_]/);
return "meta";
}
}
});
CodeMirror.defineMIME("text/x-csharp", {
name: "clike",
keywords: words("abstract as base break case catch checked class const continue" +
" default delegate do else enum event explicit extern finally fixed for" +
" foreach goto if implicit in interface internal is lock namespace new" +
" operator out override params private protected public readonly ref return sealed" +
" sizeof stackalloc static struct switch this throw try typeof unchecked" +
" unsafe using virtual void volatile while add alias ascending descending dynamic from get" +
" global group into join let orderby partial remove select set value var yield"),
blockKeywords: words("catch class do else finally for foreach if struct switch try while"),
builtin: words("Boolean Byte Char DateTime DateTimeOffset Decimal Double" +
" Guid Int16 Int32 Int64 Object SByte Single String TimeSpan UInt16 UInt32" +
" UInt64 bool byte char decimal double short int long object" +
" sbyte float string ushort uint ulong"),
atoms: words("true false null"),
hooks: {
"@": function(stream, state) {
if (stream.eat('"')) {
state.tokenize = tokenAtString;
return tokenAtString(stream, state);
}
stream.eatWhile(/[\w\$_]/);
return "meta";
}
}
});
CodeMirror.defineMIME("text/x-scala", {
name: "clike",
keywords: words(
/* scala */
"abstract case catch class def do else extends false final finally for forSome if " +
"implicit import lazy match new null object override package private protected return " +
"sealed super this throw trait try trye type val var while with yield _ : = => <- <: " +
"<% >: # @ " +
/* package scala */
"assert assume require print println printf readLine readBoolean readByte readShort " +
"readChar readInt readLong readFloat readDouble " +
"AnyVal App Application Array BufferedIterator BigDecimal BigInt Char Console Either " +
"Enumeration Equiv Error Exception Fractional Function IndexedSeq Integral Iterable " +
"Iterator List Map Numeric Nil NotNull Option Ordered Ordering PartialFunction PartialOrdering " +
"Product Proxy Range Responder Seq Serializable Set Specializable Stream StringBuilder " +
"StringContext Symbol Throwable Traversable TraversableOnce Tuple Unit Vector :: #:: " +
/* package java.lang */
"Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " +
"Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " +
"Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " +
"StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void"
),
blockKeywords: words("catch class do else finally for forSome if match switch try while"),
atoms: words("true false null"),
hooks: {
"@": function(stream) {
stream.eatWhile(/[\w\$_]/);
return "meta";
}
}
});
mimes(["x-shader/x-vertex", "x-shader/x-fragment"], {
name: "clike",
keywords: words("float int bool void " +
"vec2 vec3 vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4 " +
"mat2 mat3 mat4 " +
"sampler1D sampler2D sampler3D samplerCube " +
"sampler1DShadow sampler2DShadow" +
"const attribute uniform varying " +
"break continue discard return " +
"for while do if else struct " +
"in out inout"),
blockKeywords: words("for while do if else struct"),
builtin: words("radians degrees sin cos tan asin acos atan " +
"pow exp log exp2 sqrt inversesqrt " +
"abs sign floor ceil fract mod min max clamp mix step smootstep " +
"length distance dot cross normalize ftransform faceforward " +
"reflect refract matrixCompMult " +
"lessThan lessThanEqual greaterThan greaterThanEqual " +
"equal notEqual any all not " +
"texture1D texture1DProj texture1DLod texture1DProjLod " +
"texture2D texture2DProj texture2DLod texture2DProjLod " +
"texture3D texture3DProj texture3DLod texture3DProjLod " +
"textureCube textureCubeLod " +
"shadow1D shadow2D shadow1DProj shadow2DProj " +
"shadow1DLod shadow2DLod shadow1DProjLod shadow2DProjLod " +
"dFdx dFdy fwidth " +
"noise1 noise2 noise3 noise4"),
atoms: words("true false " +
"gl_FragColor gl_SecondaryColor gl_Normal gl_Vertex " +
"gl_MultiTexCoord0 gl_MultiTexCoord1 gl_MultiTexCoord2 gl_MultiTexCoord3 " +
"gl_MultiTexCoord4 gl_MultiTexCoord5 gl_MultiTexCoord6 gl_MultiTexCoord7 " +
"gl_FogCoord " +
"gl_Position gl_PointSize gl_ClipVertex " +
"gl_FrontColor gl_BackColor gl_FrontSecondaryColor gl_BackSecondaryColor " +
"gl_TexCoord gl_FogFragCoord " +
"gl_FragCoord gl_FrontFacing " +
"gl_FragColor gl_FragData gl_FragDepth " +
"gl_ModelViewMatrix gl_ProjectionMatrix gl_ModelViewProjectionMatrix " +
"gl_TextureMatrix gl_NormalMatrix gl_ModelViewMatrixInverse " +
"gl_ProjectionMatrixInverse gl_ModelViewProjectionMatrixInverse " +
"gl_TexureMatrixTranspose gl_ModelViewMatrixInverseTranspose " +
"gl_ProjectionMatrixInverseTranspose " +
"gl_ModelViewProjectionMatrixInverseTranspose " +
"gl_TextureMatrixInverseTranspose " +
"gl_NormalScale gl_DepthRange gl_ClipPlane " +
"gl_Point gl_FrontMaterial gl_BackMaterial gl_LightSource gl_LightModel " +
"gl_FrontLightModelProduct gl_BackLightModelProduct " +
"gl_TextureColor gl_EyePlaneS gl_EyePlaneT gl_EyePlaneR gl_EyePlaneQ " +
"gl_FogParameters " +
"gl_MaxLights gl_MaxClipPlanes gl_MaxTextureUnits gl_MaxTextureCoords " +
"gl_MaxVertexAttribs gl_MaxVertexUniformComponents gl_MaxVaryingFloats " +
"gl_MaxVertexTextureImageUnits gl_MaxTextureImageUnits " +
"gl_MaxFragmentUniformComponents gl_MaxCombineTextureImageUnits " +
"gl_MaxDrawBuffers"),
hooks: {"#": cppHook}
});
}());

View File

@ -46,6 +46,7 @@ const CM_SCRIPTS = [
"chrome://browser/content/devtools/codemirror/xml.js",
"chrome://browser/content/devtools/codemirror/css.js",
"chrome://browser/content/devtools/codemirror/htmlmixed.js",
"chrome://browser/content/devtools/codemirror/clike.js",
"chrome://browser/content/devtools/codemirror/activeline.js"
];
@ -66,8 +67,9 @@ const CM_IFRAME =
const CM_MAPPING = [
"focus",
"hasFocus",
"getCursor",
"lineCount",
"somethingSelected",
"getCursor",
"setSelection",
"getSelection",
"replaceSelection",
@ -76,7 +78,6 @@ const CM_MAPPING = [
"clearHistory",
"openDialog",
"cursorCoords",
"lineCount",
"refresh"
];
@ -91,9 +92,11 @@ const editors = new WeakMap();
Editor.modes = {
text: { name: "text" },
js: { name: "javascript" },
html: { name: "htmlmixed" },
css: { name: "css" }
css: { name: "css" },
js: { name: "javascript" },
vs: { name: "x-shader/x-vertex" },
fs: { name: "x-shader/x-fragment" }
};
function ctrl(k) {
@ -250,63 +253,12 @@ Editor.prototype = {
},
/**
* Returns true if there's something to undo and false otherwise.
* Returns the currently active highlighting mode.
* See Editor.modes for the list of all suppoert modes.
*/
canUndo: function () {
getMode: function () {
let cm = editors.get(this);
return cm.historySize().undo > 0;
},
/**
* Returns true if there's something to redo and false otherwise.
*/
canRedo: function () {
let cm = editors.get(this);
return cm.historySize().redo > 0;
},
/**
* Calculates and returns one or more {line, ch} objects for
* a zero-based index who's value is relative to the start of
* the editor's text.
*
* If only one argument is given, this method returns a single
* {line,ch} object. Otherwise it returns an array.
*/
getPosition: function (...args) {
let cm = editors.get(this);
let res = args.map((ind) => cm.posFromIndex(ind));
return args.length === 1 ? res[0] : res;
},
/**
* The reverse of getPosition. Similarly to getPosition this
* method returns a single value if only one argument was given
* and an array otherwise.
*/
getOffset: function (...args) {
let cm = editors.get(this);
let res = args.map((pos) => cm.indexFromPos(pos));
return args.length > 1 ? res : res[0];
},
/**
* Returns text from the text area. If line argument is provided
* the method returns only that line.
*/
getText: function (line) {
let cm = editors.get(this);
return line == null ?
cm.getValue() : (cm.lineInfo(line) ? cm.lineInfo(line).text : "");
},
/**
* Replaces whatever is in the text area with the contents of
* the 'value' argument.
*/
setText: function (value) {
let cm = editors.get(this);
cm.setValue(value);
return cm.getOption("mode");
},
/**
@ -319,20 +271,26 @@ Editor.prototype = {
},
/**
* Returns the currently active highlighting mode.
* See Editor.modes for the list of all suppoert modes.
* Returns text from the text area. If line argument is provided
* the method returns only that line.
*/
getMode: function () {
getText: function (line) {
let cm = editors.get(this);
return cm.getOption("mode");
if (line == null)
return cm.getValue();
let info = cm.lineInfo(line);
return info ? cm.lineInfo(line).text : "";
},
/**
* True if the editor is in the read-only mode, false otherwise.
* Replaces whatever is in the text area with the contents of
* the 'value' argument.
*/
isReadOnly: function () {
setText: function (value) {
let cm = editors.get(this);
return cm.getOption("readOnly");
cm.setValue(value);
},
/**
@ -374,57 +332,6 @@ Editor.prototype = {
this.setCursor(this.getCursor());
},
/**
* Marks the contents as clean and returns the current
* version number.
*/
setClean: function () {
let cm = editors.get(this);
this.version = cm.changeGeneration();
return this.version;
},
/**
* Returns true if contents of the text area are
* clean i.e. no changes were made since the last version.
*/
isClean: function () {
let cm = editors.get(this);
return cm.isClean(this.version);
},
/**
* Displays a context menu at the point x:y. The first
* argument, container, should be a DOM node that contains
* a context menu element specified by the ID from
* config.contextMenu.
*/
showContextMenu: function (container, x, y) {
if (this.config.contextMenu == null)
return;
let popup = container.getElementById(this.config.contextMenu);
popup.openPopupAtScreen(x, y, true);
},
/**
* This method opens an in-editor dialog asking for a line to
* jump to. Once given, it changes cursor to that line.
*/
jumpToLine: function () {
this.openDialog(CM_JUMP_DIALOG, (line) =>
this.setCursor({ line: line - 1, ch: 0 }));
},
/**
* Returns a {line, ch} object that corresponds to the
* left, top coordinates.
*/
getPositionFromCoords: function (left, top) {
let cm = editors.get(this);
return cm.coordsChar({ left: left, top: top });
},
/**
* Extends the current selection to the position specified
* by the provided {line, ch} object.
@ -437,35 +344,6 @@ Editor.prototype = {
cm.setSelection(anchor, head);
},
/**
* Extends an instance of the Editor object with additional
* functions. Each function will be called with context as
* the first argument. Context is a {ed, cm} object where
* 'ed' is an instance of the Editor object and 'cm' is an
* instance of the CodeMirror object. Example:
*
* function hello(ctx, name) {
* let { cm, ed } = ctx;
* cm; // CodeMirror instance
* ed; // Editor instance
* name; // 'Mozilla'
* }
*
* editor.extend({ hello: hello });
* editor.hello('Mozilla');
*/
extend: function (funcs) {
Object.keys(funcs).forEach((name) => {
let cm = editors.get(this);
let ctx = { ed: this, cm: cm };
if (name === "initialize")
return void funcs[name](ctx);
this[name] = funcs[name].bind(null, ctx);
});
},
/**
* Gets the first visible line number in the editor.
*/
@ -641,6 +519,135 @@ Editor.prototype = {
cm.removeLineClass(line, "wrap", className);
},
/**
* Calculates and returns one or more {line, ch} objects for
* a zero-based index who's value is relative to the start of
* the editor's text.
*
* If only one argument is given, this method returns a single
* {line,ch} object. Otherwise it returns an array.
*/
getPosition: function (...args) {
let cm = editors.get(this);
let res = args.map((ind) => cm.posFromIndex(ind));
return args.length === 1 ? res[0] : res;
},
/**
* The reverse of getPosition. Similarly to getPosition this
* method returns a single value if only one argument was given
* and an array otherwise.
*/
getOffset: function (...args) {
let cm = editors.get(this);
let res = args.map((pos) => cm.indexFromPos(pos));
return args.length > 1 ? res : res[0];
},
/**
* Returns a {line, ch} object that corresponds to the
* left, top coordinates.
*/
getPositionFromCoords: function (left, top) {
let cm = editors.get(this);
return cm.coordsChar({ left: left, top: top });
},
/**
* Returns true if there's something to undo and false otherwise.
*/
canUndo: function () {
let cm = editors.get(this);
return cm.historySize().undo > 0;
},
/**
* Returns true if there's something to redo and false otherwise.
*/
canRedo: function () {
let cm = editors.get(this);
return cm.historySize().redo > 0;
},
/**
* Marks the contents as clean and returns the current
* version number.
*/
setClean: function () {
let cm = editors.get(this);
this.version = cm.changeGeneration();
return this.version;
},
/**
* Returns true if contents of the text area are
* clean i.e. no changes were made since the last version.
*/
isClean: function () {
let cm = editors.get(this);
return cm.isClean(this.version);
},
/**
* True if the editor is in the read-only mode, false otherwise.
*/
isReadOnly: function () {
let cm = editors.get(this);
return cm.getOption("readOnly");
},
/**
* Displays a context menu at the point x:y. The first
* argument, container, should be a DOM node that contains
* a context menu element specified by the ID from
* config.contextMenu.
*/
showContextMenu: function (container, x, y) {
if (this.config.contextMenu == null)
return;
let popup = container.getElementById(this.config.contextMenu);
popup.openPopupAtScreen(x, y, true);
},
/**
* This method opens an in-editor dialog asking for a line to
* jump to. Once given, it changes cursor to that line.
*/
jumpToLine: function () {
this.openDialog(CM_JUMP_DIALOG, (line) =>
this.setCursor({ line: line - 1, ch: 0 }));
},
/**
* Extends an instance of the Editor object with additional
* functions. Each function will be called with context as
* the first argument. Context is a {ed, cm} object where
* 'ed' is an instance of the Editor object and 'cm' is an
* instance of the CodeMirror object. Example:
*
* function hello(ctx, name) {
* let { cm, ed } = ctx;
* cm; // CodeMirror instance
* ed; // Editor instance
* name; // 'Mozilla'
* }
*
* editor.extend({ hello: hello });
* editor.hello('Mozilla');
*/
extend: function (funcs) {
Object.keys(funcs).forEach((name) => {
let cm = editors.get(this);
let ctx = { ed: this, cm: cm };
if (name === "initialize")
return void funcs[name](ctx);
this[name] = funcs[name].bind(null, ctx);
});
},
destroy: function () {
this.container = null;
this.config = null;

View File

@ -1217,9 +1217,11 @@ SelectorView.prototype = {
}
let contentDoc = null;
let rawNode = this.tree.viewedElement.rawNode();
if (rawNode) {
contentDoc = rawNode.ownerDocument;
if (this.tree.viewedElement.isLocal_toBeDeprecated()) {
let rawNode = this.tree.viewedElement.rawNode();
if (rawNode) {
contentDoc = rawNode.ownerDocument;
}
}
let viewSourceUtils = inspector.viewSourceUtils;

View File

@ -223,6 +223,7 @@ skip-if = os == "linux"
[browser_webconsole_execution_scope.js]
[browser_webconsole_for_of.js]
[browser_webconsole_history.js]
[browser_webconsole_input_field_focus_on_panel_select.js]
[browser_webconsole_js_input_expansion.js]
[browser_webconsole_jsterm.js]
[browser_webconsole_live_filtering_of_message_types.js]

View File

@ -0,0 +1,46 @@
/* Any copyright is dedicated to the Public Domain
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// Test that the JS input field is focused when the user switches back to the
// web console from other tools, see bug 891581.
const TEST_URI = "data:text/html;charset=utf8,<p>hello";
function test()
{
addTab(TEST_URI);
browser.addEventListener("load", function onLoad() {
browser.removeEventListener("load", onLoad, true);
openConsole(null, consoleOpened);
}, true);
}
function consoleOpened(hud)
{
is(hud.jsterm.inputNode.hasAttribute("focused"), true,
"inputNode should be focused");
hud.ui.filterBox.focus();
is(hud.ui.filterBox.hasAttribute("focused"), true,
"filterBox should be focused");
is(hud.jsterm.inputNode.hasAttribute("focused"), false,
"inputNode shouldn't be focused");
openDebugger().then(debuggerOpened);
}
function debuggerOpened()
{
openConsole(null, consoleReopened);
}
function consoleReopened(hud)
{
is(hud.jsterm.inputNode.hasAttribute("focused"), true,
"inputNode should be focused");
finishTest();
}

View File

@ -31,6 +31,7 @@ loader.lazyImporter(this, "ObjectClient", "resource://gre/modules/devtools/dbg-c
loader.lazyImporter(this, "VariablesView", "resource:///modules/devtools/VariablesView.jsm");
loader.lazyImporter(this, "VariablesViewController", "resource:///modules/devtools/VariablesViewController.jsm");
loader.lazyImporter(this, "PluralForm", "resource://gre/modules/PluralForm.jsm");
loader.lazyImporter(this, "gDevTools", "resource:///modules/devtools/gDevTools.jsm");
const STRINGS_URI = "chrome://browser/locale/devtools/webconsole.properties";
let l10n = new WebConsoleUtils.l10n(STRINGS_URI);
@ -198,6 +199,7 @@ function WebConsoleFrame(aWebConsoleOwner)
this.output = new ConsoleOutput(this);
this._toggleFilter = this._toggleFilter.bind(this);
this._onPanelSelected = this._onPanelSelected.bind(this);
this._flushMessageQueue = this._flushMessageQueue.bind(this);
this._outputTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
@ -555,6 +557,21 @@ WebConsoleFrame.prototype = {
this.jsterm = new JSTerm(this);
this.jsterm.init();
this.jsterm.inputNode.focus();
let toolbox = gDevTools.getToolbox(this.owner.target);
if (toolbox) {
toolbox.on("webconsole-selected", this._onPanelSelected);
}
},
/**
* Sets the focus to JavaScript input field when the web console tab is
* selected.
* @private
*/
_onPanelSelected: function WCF__onPanelSelected()
{
this.jsterm.inputNode.focus();
},
/**
@ -2826,6 +2843,11 @@ WebConsoleFrame.prototype = {
this._destroyer = promise.defer();
let toolbox = gDevTools.getToolbox(this.owner.target);
if (toolbox) {
toolbox.off("webconsole-selected", this._onPanelSelected);
}
this._repeatNodes = {};
this._outputQueue = [];
this._pruneCategoriesQueue = {};

View File

@ -79,23 +79,21 @@
}
.theme-fg-color1,
.cm-s-mozilla .cm-variable-2,
.cm-s-mozilla .cm-quote { /* green */
.cm-s-mozilla .cm-number { /* green */
color: #5c9966;
}
.theme-fg-color2,
.cm-s-mozilla .cm-attribute,
.cm-s-mozilla .cm-builtin,
.cm-s-mozilla .cm-variable,
.cm-s-mozilla .cm-def,
.cm-s-mozilla .cm-variable-3,
.cm-s-mozilla .cm-property,
.cm-s-mozilla .cm-qualifier { /* blue */
color: #3689b2;
}
.theme-fg-color3,
.cm-s-mozilla .cm-builtin,
.cm-s-mozilla .cm-tag,
.cm-s-mozilla .cm-header { /* pink/lavender */
color: #a673bf;
@ -107,18 +105,19 @@
.theme-fg-color5,
.cm-s-mozilla .cm-bracket,
.cm-s-mozilla .cm-atom,
.cm-s-mozilla .cm-keyword { /* Yellow */
color: #a18650;
}
.theme-fg-color6,
.cm-s-mozilla .cm-string { /* Orange */
.cm-s-mozilla .cm-string,
.cm-s-mozilla .cm-string-2 { /* Orange */
color: #b26b47;
}
.theme-fg-color7,
.cm-s-mozilla .cm-string-2,
.cm-s-mozilla .cm-atom,
.cm-s-mozilla .cm-quote,
.cm-s-mozilla .cm-error { /* Red */
color: #bf5656;
}
@ -144,9 +143,10 @@
}
.CodeMirror pre,
.cm-s-mozilla .cm-variable-2,
.cm-s-mozilla .cm-variable-3,
.cm-s-mozilla .cm-operator,
.cm-s-mozilla .cm-special,
.cm-s-mozilla .cm-number { /* theme-body color */
.cm-s-mozilla .cm-special { /* theme-body color */
color: #8fa1b2;
}
@ -162,10 +162,15 @@
background: rgb(176, 176, 176);
}
.CodeMirror-activeline-background { /* selected color with alpha */
.cm-s-mozilla .CodeMirror-activeline-background { /* selected color with alpha */
background: rgba(185, 215, 253, .15);
}
div.cm-s-mozilla span.CodeMirror-matchingbracket { /* highlight brackets */
outline: solid 1px rgba(255, 255, 255, .25);
color: white;
}
.cm-s-mozilla .CodeMirror-linenumber { /* line number text */
color: #5f7387;
}

View File

@ -79,23 +79,21 @@
}
.theme-fg-color1,
.cm-s-mozilla .cm-variable-2,
.cm-s-mozilla .cm-quote { /* green */
.cm-s-mozilla .cm-number { /* green */
color: hsl(72,100%,27%);
}
.theme-fg-color2,
.cm-s-mozilla .cm-attribute,
.cm-s-mozilla .cm-builtin,
.cm-s-mozilla .cm-variable,
.cm-s-mozilla .cm-def,
.cm-s-mozilla .cm-variable-3,
.cm-s-mozilla .cm-property,
.cm-s-mozilla .cm-qualifier { /* blue */
color: hsl(208,56%,40%);
}
.theme-fg-color3,
.cm-s-mozilla .cm-variable,
.cm-s-mozilla .cm-tag,
.cm-s-mozilla .cm-header { /* dark blue */
color: hsl(208,81%,21%)
@ -107,18 +105,19 @@
.theme-fg-color5,
.cm-s-mozilla .cm-bracket,
.cm-s-mozilla .cm-keyword,
.cm-s-mozilla .cm-atom { /* Yellow */
.cm-s-mozilla .cm-keyword { /* Yellow */
color: #a18650;
}
.theme-fg-color6,
.cm-s-mozilla .cm-string { /* Orange */
.cm-s-mozilla .cm-string,
.cm-s-mozilla .cm-string-2 { /* Orange */
color: hsl(24,85%,39%);
}
.theme-fg-color7,
.cm-s-mozilla .cm-string-2,
.cm-s-mozilla .cm-atom,
.cm-s-mozilla .cm-quote,
.cm-s-mozilla .cm-error { /* Red */
color: #bf5656;
}
@ -144,9 +143,10 @@
}
.CodeMirror pre,
.cm-s-mozilla .cm-variable-2,
.cm-s-mozilla .cm-variable-3,
.cm-s-mozilla .cm-operator,
.cm-s-mozilla .cm-special,
.cm-s-mozilla .cm-number { /* theme-body color */
.cm-s-mozilla .cm-special { /* theme-body color */
color: black;
}
@ -162,7 +162,7 @@
background: rgb(176, 176, 176);
}
.CodeMirror-activeline-background { /* selected color with alpha */
.cm-s-mozilla .CodeMirror-activeline-background { /* selected color with alpha */
background: rgba(185, 215, 253, .35);
}

View File

@ -250,7 +250,9 @@ const kEventConstructors = {
return new MozSmsEvent(aName, aProps);
},
},
MozStkCommandEvent: { create: null, // Cannot create untrusted event from JS.
MozStkCommandEvent: { create: function (aName, aProps) {
return new MozStkCommandEvent(aName, aProps);
},
},
MozVoicemailEvent: { create: function (aName, aProps) {
return new MozVoicemailEvent(aName, aProps);

View File

@ -792,10 +792,6 @@ DOMInterfaces = {
'nativeType': 'mozilla::dom::PowerManager',
},
'MozStkCommandEvent' : {
'nativeType': 'mozilla::dom::StkCommandEvent',
},
'MozTimeManager': {
'nativeType': 'mozilla::dom::time::TimeManager',
},

View File

@ -1249,6 +1249,7 @@ private:
nsRefPtr<RegisterAgentReplyHandler> handler =
new RegisterAgentReplyHandler(aAgentVTable);
MOZ_ASSERT(handler.get());
MOZ_ASSERT(!sAdapterPath.IsEmpty());
bool success = threadConnection->SendWithReply(
RegisterAgentReplyHandler::Callback, handler.get(), -1,
@ -1686,17 +1687,22 @@ BluetoothDBusService::StartInternal()
sPairingReqTable = new nsDataHashtable<nsStringHashKey, DBusMessage* >;
}
// Normally we'll receive the signal 'AdapterAdded' for the default
// adapter from the DBus daemon during start up. If we restart after
// a crash, the default adapter might already be available, so we ask
// the daemon explicitly here.
bool success = mConnection->SendWithReply(OnDefaultAdapterReply, nullptr,
1000, "/",
DBUS_ADAPTER_IFACE,
"DefaultAdapter",
DBUS_TYPE_INVALID);
if (!success) {
BT_WARNING("Failed to query default adapter!");
/**
* Normally we'll receive the signal 'AdapterAdded' with the adapter object
* path from the DBus daemon during start up. So, there's no need to query
* the object path of default adapter here. However, if we restart from a
* crash, the default adapter might already be available, so we ask the daemon
* explicitly here.
*/
if (sAdapterPath.IsEmpty()) {
bool success = mConnection->SendWithReply(OnDefaultAdapterReply, nullptr,
1000, "/",
DBUS_MANAGER_IFACE,
"DefaultAdapter",
DBUS_TYPE_INVALID);
if (!success) {
BT_WARNING("Failed to query default adapter!");
}
}
return NS_OK;
@ -1941,6 +1947,7 @@ BluetoothDBusService::SendDiscoveryMessage(const char* aMessageName,
BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!sAdapterPath.IsEmpty());
if (!IsReady()) {
NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!");
@ -1978,6 +1985,7 @@ BluetoothDBusService::SendInputMessage(const nsAString& aDeviceAddress,
return NS_ERROR_FAILURE;
}
MOZ_ASSERT(!sAdapterPath.IsEmpty());
nsString objectPath = GetObjectPathFromAddress(sAdapterPath, aDeviceAddress);
return SendAsyncDBusMessage(objectPath, DBUS_INPUT_IFACE, aMessage, callback);
}
@ -2030,6 +2038,7 @@ BluetoothDBusService::SendSinkMessage(const nsAString& aDeviceAddress,
return NS_ERROR_FAILURE;
}
MOZ_ASSERT(!sAdapterPath.IsEmpty());
nsString objectPath = GetObjectPathFromAddress(sAdapterPath, aDeviceAddress);
return SendAsyncDBusMessage(objectPath, DBUS_SINK_IFACE, aMessage, callback);
}
@ -2143,6 +2152,7 @@ protected:
bool SendNextGetProperties()
{
MOZ_ASSERT(mProcessedDeviceAddresses < mDeviceAddresses.Length());
MOZ_ASSERT(!sAdapterPath.IsEmpty());
// cache object path for reply
mObjectPath = GetObjectPathFromAddress(sAdapterPath,
@ -2256,6 +2266,7 @@ BluetoothDBusService::SetProperty(BluetoothObjectType aType,
}
MOZ_ASSERT(aType < ArrayLength(sBluetoothDBusIfaces));
MOZ_ASSERT(!sAdapterPath.IsEmpty());
const char* interface = sBluetoothDBusIfaces[aType];
/* Compose the command */
@ -2363,6 +2374,7 @@ BluetoothDBusService::CreatePairedDeviceInternal(
sIsPairing++;
nsRefPtr<BluetoothReplyRunnable> runnable = aRunnable;
MOZ_ASSERT(!sAdapterPath.IsEmpty());
// Then send CreatePairedDevice, it will register a temp device agent then
// unregister it after pairing process is over
@ -2411,6 +2423,7 @@ BluetoothDBusService::RemoveDeviceInternal(const nsAString& aDeviceAddress,
return NS_OK;
}
MOZ_ASSERT(!sAdapterPath.IsEmpty());
nsCString deviceObjectPath =
NS_ConvertUTF16toUTF8(GetObjectPathFromAddress(sAdapterPath,
aDeviceAddress));
@ -2785,6 +2798,7 @@ BluetoothDBusService::GetServiceChannel(const nsAString& aDeviceAddress,
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!sAdapterPath.IsEmpty());
nsString objectPath(GetObjectPathFromAddress(sAdapterPath, aDeviceAddress));
#ifdef MOZ_WIDGET_GONK
@ -2841,6 +2855,7 @@ BluetoothDBusService::UpdateSdpRecords(const nsAString& aDeviceAddress,
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!aDeviceAddress.IsEmpty());
MOZ_ASSERT(!sAdapterPath.IsEmpty());
MOZ_ASSERT(aManager);
MOZ_ASSERT(mConnection);
@ -3025,6 +3040,7 @@ BluetoothDBusService::SendMetaData(const nsAString& aTitle,
return;
}
MOZ_ASSERT(!sAdapterPath.IsEmpty());
nsAutoString address;
a2dp->GetAddress(address);
nsString objectPath =
@ -3157,6 +3173,7 @@ BluetoothDBusService::SendPlayStatus(int64_t aDuration,
UpdateNotification(ControlEventId::EVENT_PLAYBACK_POS_CHANGED, aPosition);
}
MOZ_ASSERT(!sAdapterPath.IsEmpty());
nsAutoString address;
a2dp->GetAddress(address);
nsString objectPath =
@ -3204,6 +3221,7 @@ BluetoothDBusService::UpdatePlayStatus(uint32_t aDuration,
NS_ENSURE_TRUE_VOID(a2dp);
MOZ_ASSERT(a2dp->IsConnected());
MOZ_ASSERT(a2dp->IsAvrcpConnected());
MOZ_ASSERT(!sAdapterPath.IsEmpty());
nsAutoString address;
a2dp->GetAddress(address);
@ -3234,6 +3252,7 @@ BluetoothDBusService::UpdateNotification(ControlEventId aEventId,
NS_ENSURE_TRUE_VOID(a2dp);
MOZ_ASSERT(a2dp->IsConnected());
MOZ_ASSERT(a2dp->IsAvrcpConnected());
MOZ_ASSERT(!sAdapterPath.IsEmpty());
nsAutoString address;
a2dp->GetAddress(address);

View File

@ -494,16 +494,6 @@ dictionary MozStkCommand
jsval options;
};
[scriptable, builtinclass, uuid(21cd2f25-ebea-43f8-8255-eaa4e1182858)]
interface nsIDOMMozStkCommandEvent : nsIDOMEvent
{
/**
* See nsIDOMMozStkCommand for the detail of command.
*/
[implicit_jscontext]
readonly attribute jsval command;
};
dictionary MozStkResponse
{
/**

View File

@ -55,16 +55,11 @@ interface nsIDOMMozGsmIccInfo : nsIDOMMozIccInfo
readonly attribute DOMString msisdn;
};
[scriptable, uuid(013e973e-8b56-4525-b634-d23166b86edb)]
[scriptable, uuid(10b89660-2988-443a-a6f0-4ed3618fee41)]
interface nsIDOMMozCdmaIccInfo : nsIDOMMozIccInfo
{
/**
* Mobile Directory Number (MDN) of the subscriber, aka his phone number.
*/
readonly attribute DOMString mdn;
/**
* Mobile Identification Number (MIN) of the subscriber.
*/
readonly attribute DOMString min;
};

View File

@ -5,10 +5,11 @@
#include "mozilla/dom/IccManager.h"
#include "GeneratedEvents.h"
#include "mozilla/dom/StkCommandEvent.h"
#include "mozilla/dom/MozStkCommandEvent.h"
#include "mozilla/Services.h"
#include "nsIDOMClassInfo.h"
#include "nsIDOMIccInfo.h"
#include "nsJSON.h"
#include "SimToolKit.h"
#define NS_RILCONTENTHELPER_CONTRACTID "@mozilla.org/ril/content-helper;1"
@ -264,10 +265,30 @@ NS_IMPL_EVENT_HANDLER(IccManager, iccinfochange)
NS_IMETHODIMP
IccManager::NotifyStkCommand(const nsAString& aMessage)
{
nsRefPtr<StkCommandEvent> event = StkCommandEvent::Create(this, aMessage);
NS_ASSERTION(event, "This should never fail!");
nsresult rv;
nsIScriptContext* sc = GetContextForEventHandlers(&rv);
NS_ENSURE_SUCCESS(rv, rv);
return event->Dispatch(this, NS_LITERAL_STRING("stkcommand"));
AutoPushJSContext cx(sc->GetNativeContext());
JS::Rooted<JS::Value> value(cx);
if (!aMessage.IsEmpty()) {
nsCOMPtr<nsIJSON> json(new nsJSON());
nsresult rv = json->DecodeToJSVal(aMessage, cx, value.address());
NS_ENSURE_SUCCESS(rv, rv);
} else {
value = JSVAL_VOID;
}
MozStkCommandEventInit init;
init.mBubbles = false;
init.mCancelable = false;
init.mCommand = value;
nsRefPtr<MozStkCommandEvent> event =
MozStkCommandEvent::Constructor(this, NS_LITERAL_STRING("stkcommand"), init);
return DispatchTrustedEvent(event);
}
NS_IMETHODIMP

View File

@ -1,47 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/StkCommandEvent.h"
#include "jsfriendapi.h"
#include "nsJSON.h"
#include "SimToolKit.h"
namespace mozilla {
namespace dom {
already_AddRefed<StkCommandEvent>
StkCommandEvent::Create(EventTarget* aOwner,
const nsAString& aMessage)
{
nsRefPtr<StkCommandEvent> event = new StkCommandEvent(aOwner);
event->mCommand = aMessage;
return event.forget();
}
NS_IMPL_ADDREF_INHERITED(StkCommandEvent, nsDOMEvent)
NS_IMPL_RELEASE_INHERITED(StkCommandEvent, nsDOMEvent)
NS_INTERFACE_MAP_BEGIN(StkCommandEvent)
NS_INTERFACE_MAP_ENTRY(nsIDOMMozStkCommandEvent)
NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent)
NS_IMETHODIMP
StkCommandEvent::GetCommand(JSContext* aCx, JS::Value* aCommand)
{
nsCOMPtr<nsIJSON> json(new nsJSON());
if (!mCommand.IsEmpty()) {
nsresult rv = json->DecodeToJSVal(mCommand, aCx, aCommand);
NS_ENSURE_SUCCESS(rv, rv);
} else {
*aCommand = JSVAL_VOID;
}
return NS_OK;
}
} // namespace dom
} // namespace mozilla

View File

@ -1,75 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_StkCommandEvent_h
#define mozilla_dom_StkCommandEvent_h
#include "mozilla/dom/MozStkCommandEventBinding.h"
#include "nsDOMEvent.h"
#include "SimToolKit.h"
namespace mozilla {
namespace dom {
class StkCommandEvent : public nsDOMEvent,
public nsIDOMMozStkCommandEvent
{
nsString mCommand;
public:
NS_DECL_ISUPPORTS_INHERITED
NS_FORWARD_TO_NSDOMEVENT
NS_DECL_NSIDOMMOZSTKCOMMANDEVENT
static already_AddRefed<StkCommandEvent>
Create(EventTarget* aOwner, const nsAString& aMessage);
nsresult
Dispatch(EventTarget* aTarget, const nsAString& aEventType)
{
NS_ASSERTION(aTarget, "Null pointer!");
NS_ASSERTION(!aEventType.IsEmpty(), "Empty event type!");
nsresult rv = InitEvent(aEventType, false, false);
NS_ENSURE_SUCCESS(rv, rv);
SetTrusted(true);
nsDOMEvent* thisEvent = this;
bool dummy;
rv = aTarget->DispatchEvent(thisEvent, &dummy);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aScope) MOZ_OVERRIDE
{
return MozStkCommandEventBinding::Wrap(aCx, aScope, this);
}
JS::Value GetCommand(JSContext* aCx, ErrorResult& aRv)
{
JS::Rooted<JS::Value> retVal(aCx);
aRv = GetCommand(aCx, retVal.address());
return retVal;
}
private:
StkCommandEvent(EventTarget* aOwner)
: nsDOMEvent(aOwner, nullptr, nullptr)
{
SetIsDOMBinding();
}
~StkCommandEvent()
{ }
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_StkCommandEvent_h

View File

@ -6,12 +6,10 @@
EXPORTS.mozilla.dom += [
'IccManager.h',
'StkCommandEvent.h',
]
SOURCES += [
'IccManager.cpp',
'StkCommandEvent.cpp',
]
FAIL_ON_WARNINGS = True

View File

@ -4,12 +4,15 @@
#include "nsISupports.idl"
[scriptable, builtinclass, uuid(5efe7dc5-4f72-4b8e-9bcd-fb0676b554ca)]
[scriptable, builtinclass, uuid(9672c75d-61a2-470e-964b-2396dcff7cf6)]
interface nsIDOMMozMobileMessageThread : nsISupports
{
// Unique identity of the thread.
readonly attribute unsigned long long id;
// Last (MMS) message subject.
readonly attribute DOMString lastMessageSubject;
// Message body of the last message in the thread.
readonly attribute DOMString body;

View File

@ -14,7 +14,7 @@ interface nsIDOMMozSmsSegmentInfo;
#define MOBILE_MESSAGE_SERVICE_CONTRACTID "@mozilla.org/mobilemessage/mobilemessageservice;1"
%}
[scriptable, builtinclass, uuid(7a39eeb4-827e-4c70-9804-288f94174ebe)]
[scriptable, builtinclass, uuid(c9a1aa14-7088-4f22-9d7f-b0c6ce9cf484)]
interface nsIMobileMessageService : nsISupports
{
[implicit_jscontext]
@ -55,6 +55,7 @@ interface nsIMobileMessageService : nsISupports
nsIDOMMozMobileMessageThread createThread(in unsigned long long id,
in jsval participants,
in jsval timestamp,
in DOMString lastMessageSubject,
in DOMString body,
in unsigned long long unreadCount,
in DOMString aLastMessageType);

View File

@ -112,6 +112,7 @@ NS_IMETHODIMP
MobileMessageService::CreateThread(uint64_t aId,
const JS::Value& aParticipants,
const JS::Value& aTimestamp,
const nsAString& aLastMessageSubject,
const nsAString& aBody,
uint64_t aUnreadCount,
const nsAString& aLastMessageType,
@ -121,6 +122,7 @@ MobileMessageService::CreateThread(uint64_t aId,
return MobileMessageThread::Create(aId,
aParticipants,
aTimestamp,
aLastMessageSubject,
aBody,
aUnreadCount,
aLastMessageType,

View File

@ -32,6 +32,7 @@ NS_IMPL_RELEASE(MobileMessageThread)
MobileMessageThread::Create(const uint64_t aId,
const JS::Value& aParticipants,
const JS::Value& aTimestamp,
const nsAString& aLastMessageSubject,
const nsAString& aBody,
const uint64_t aUnreadCount,
const nsAString& aLastMessageType,
@ -44,6 +45,7 @@ MobileMessageThread::Create(const uint64_t aId,
// to them.
ThreadData data;
data.id() = aId;
data.lastMessageSubject().Assign(aLastMessageSubject);
data.body().Assign(aBody);
data.unreadCount() = aUnreadCount;
@ -114,10 +116,12 @@ MobileMessageThread::Create(const uint64_t aId,
MobileMessageThread::MobileMessageThread(const uint64_t aId,
const nsTArray<nsString>& aParticipants,
const uint64_t aTimestamp,
const nsString& aLastMessageSubject,
const nsString& aBody,
const uint64_t aUnreadCount,
MessageType aLastMessageType)
: mData(aId, aParticipants, aTimestamp, aBody, aUnreadCount, aLastMessageType)
: mData(aId, aParticipants, aTimestamp, aLastMessageSubject, aBody,
aUnreadCount, aLastMessageType)
{
MOZ_ASSERT(aParticipants.Length());
}
@ -135,6 +139,13 @@ MobileMessageThread::GetId(uint64_t* aId)
return NS_OK;
}
NS_IMETHODIMP
MobileMessageThread::GetLastMessageSubject(nsAString& aLastMessageSubject)
{
aLastMessageSubject = mData.lastMessageSubject();
return NS_OK;
}
NS_IMETHODIMP
MobileMessageThread::GetBody(nsAString& aBody)
{

View File

@ -26,6 +26,7 @@ public:
MobileMessageThread(const uint64_t aId,
const nsTArray<nsString>& aParticipants,
const uint64_t aTimestamp,
const nsString& aLastMessageSubject,
const nsString& aBody,
const uint64_t aUnreadCount,
mobilemessage::MessageType aLastMessageType);
@ -35,6 +36,7 @@ public:
static nsresult Create(const uint64_t aId,
const JS::Value& aParticipants,
const JS::Value& aTimestamp,
const nsAString& aLastMessageSubject,
const nsAString& aBody,
const uint64_t aUnreadCount,
const nsAString& aLastMessageType,

View File

@ -24,7 +24,7 @@ const DISABLE_MMS_GROUPING_FOR_RECEIVING = true;
const DB_NAME = "sms";
const DB_VERSION = 17;
const DB_VERSION = 18;
const MESSAGE_STORE_NAME = "sms";
const THREAD_STORE_NAME = "thread";
const PARTICIPANT_STORE_NAME = "participant";
@ -241,6 +241,10 @@ MobileMessageDatabaseService.prototype = {
self.upgradeSchema16(event.target.transaction, next);
break;
case 17:
if (DEBUG) debug("Upgrade to version 18. Add last message subject into threadRecord.");
self.upgradeSchema17(event.target.transaction, next);
break;
case 18:
// This will need to be moved for each new version
if (DEBUG) debug("Upgrade finished.");
break;
@ -1102,6 +1106,44 @@ MobileMessageDatabaseService.prototype = {
};
},
upgradeSchema17: function upgradeSchema17(transaction, next) {
let threadStore = transaction.objectStore(THREAD_STORE_NAME);
let messageStore = transaction.objectStore(MESSAGE_STORE_NAME);
// Add 'lastMessageSubject' to each thread record.
threadStore.openCursor().onsuccess = function(event) {
let cursor = event.target.result;
if (!cursor) {
next();
return;
}
let threadRecord = cursor.value;
// We have defined 'threadRecord.subject' in upgradeSchema7(), but it
// actually means 'threadRecord.body'. Swap the two values first.
threadRecord.body = threadRecord.subject;
delete threadRecord.subject;
// Only MMS supports subject so assign null for non-MMS one.
if (threadRecord.lastMessageType != "mms") {
threadRecord.lastMessageSubject = null;
cursor.update(threadRecord);
cursor.continue();
return;
}
messageStore.get(threadRecord.lastMessageId).onsuccess = function(event) {
let messageRecord = event.target.result;
let subject = messageRecord.headers.subject;
threadRecord.lastMessageSubject = subject || null;
cursor.update(threadRecord);
cursor.continue();
};
};
},
matchParsedPhoneNumbers: function matchParsedPhoneNumbers(addr1, parsedAddr1,
addr2, parsedAddr2) {
if ((parsedAddr1.internationalNumber &&
@ -1488,8 +1530,13 @@ MobileMessageDatabaseService.prototype = {
let needsUpdate = false;
if (threadRecord.lastTimestamp <= timestamp) {
let lastMessageSubject;
if (aMessageRecord.type == "mms") {
lastMessageSubject = aMessageRecord.headers.subject;
}
threadRecord.lastMessageSubject = lastMessageSubject || null;
threadRecord.lastTimestamp = timestamp;
threadRecord.subject = aMessageRecord.body;
threadRecord.body = aMessageRecord.body;
threadRecord.lastMessageId = aMessageRecord.id;
threadRecord.lastMessageType = aMessageRecord.type;
needsUpdate = true;
@ -1508,11 +1555,16 @@ MobileMessageDatabaseService.prototype = {
return;
}
let lastMessageSubject;
if (aMessageRecord.type == "mms") {
lastMessageSubject = aMessageRecord.headers.subject;
}
threadStore.add({participantIds: participantIds,
participantAddresses: aAddresses,
lastMessageId: aMessageRecord.id,
lastTimestamp: timestamp,
subject: aMessageRecord.body,
lastMessageSubject: lastMessageSubject || null,
body: aMessageRecord.body,
unreadCount: aMessageRecord.read ? 0 : 1,
lastMessageType: aMessageRecord.type})
.onsuccess = function (event) {
@ -1744,6 +1796,63 @@ MobileMessageDatabaseService.prototype = {
threadParticipants = threadParticipants.concat(slicedReceivers);
},
updateThreadByMessageChange: function updateThreadByMessageChange(messageStore,
threadStore,
threadId,
messageId,
messageRead) {
threadStore.get(threadId).onsuccess = function(event) {
// This must exist.
let threadRecord = event.target.result;
if (DEBUG) debug("Updating thread record " + JSON.stringify(threadRecord));
if (!messageRead) {
threadRecord.unreadCount--;
}
if (threadRecord.lastMessageId == messageId) {
// Check most recent sender/receiver.
let range = IDBKeyRange.bound([threadId, 0], [threadId, ""]);
let request = messageStore.index("threadId")
.openCursor(range, PREV);
request.onsuccess = function(event) {
let cursor = event.target.result;
if (!cursor) {
if (DEBUG) {
debug("Deleting mru entry for thread id " + threadId);
}
threadStore.delete(threadId);
return;
}
let nextMsg = cursor.value;
let lastMessageSubject;
if (nextMsg.type == "mms") {
lastMessageSubject = nextMsg.headers.subject;
}
threadRecord.lastMessageSubject = lastMessageSubject || null;
threadRecord.lastMessageId = nextMsg.id;
threadRecord.lastTimestamp = nextMsg.timestamp;
threadRecord.body = nextMsg.body;
threadRecord.lastMessageType = nextMsg.type;
if (DEBUG) {
debug("Updating mru entry: " +
JSON.stringify(threadRecord));
}
threadStore.put(threadRecord);
};
} else if (!messageRead) {
// Shortcut, just update the unread count.
if (DEBUG) {
debug("Updating unread count for thread id " + threadId + ": " +
(threadRecord.unreadCount + 1) + " -> " +
threadRecord.unreadCount);
}
threadStore.put(threadRecord);
}
};
},
/**
* nsIRilMobileMessageDatabaseService API
*/
@ -1981,58 +2090,6 @@ MobileMessageDatabaseService.prototype = {
this.getMessageRecordById(aMessageId, notifyCallback);
},
updateThreadByMessageChange: function updateThreadByMessageChange(messageStore,
threadStore,
threadId,
messageId,
messageRead) {
threadStore.get(threadId).onsuccess = function(event) {
// This must exist.
let threadRecord = event.target.result;
if (DEBUG) debug("Updating thread record " + JSON.stringify(threadRecord));
if (!messageRead) {
threadRecord.unreadCount--;
}
if (threadRecord.lastMessageId == messageId) {
// Check most recent sender/receiver.
let range = IDBKeyRange.bound([threadId, 0], [threadId, ""]);
let request = messageStore.index("threadId")
.openCursor(range, PREV);
request.onsuccess = function(event) {
let cursor = event.target.result;
if (!cursor) {
if (DEBUG) {
debug("Deleting mru entry for thread id " + threadId);
}
threadStore.delete(threadId);
return;
}
let nextMsg = cursor.value;
threadRecord.lastMessageId = nextMsg.id;
threadRecord.lastTimestamp = nextMsg.timestamp;
threadRecord.subject = nextMsg.body;
threadRecord.lastMessageType = nextMsg.type;
if (DEBUG) {
debug("Updating mru entry: " +
JSON.stringify(threadRecord));
}
threadStore.put(threadRecord);
};
} else if (!messageRead) {
// Shortcut, just update the unread count.
if (DEBUG) {
debug("Updating unread count for thread id " + threadId + ": " +
(threadRecord.unreadCount + 1) + " -> " +
threadRecord.unreadCount);
}
threadStore.put(threadRecord);
}
};
},
deleteMessage: function deleteMessage(messageIds, length, aRequest) {
if (DEBUG) debug("deleteMessage: message ids " + JSON.stringify(messageIds));
let deleted = [];
@ -2837,7 +2894,8 @@ GetThreadsCursor.prototype = {
gMobileMessageService.createThread(threadRecord.id,
threadRecord.participantAddresses,
threadRecord.lastTimestamp,
threadRecord.subject,
threadRecord.lastMessageSubject || "",
threadRecord.body,
threadRecord.unreadCount,
threadRecord.lastMessageType);
self.callback.notifyCursorResult(thread);

View File

@ -92,6 +92,7 @@ struct ThreadData
uint64_t id;
nsString[] participants;
uint64_t timestamp;
nsString lastMessageSubject;
nsString body;
uint64_t unreadCount;
MessageType lastMessageType;

View File

@ -0,0 +1,301 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
const {Cc: Cc, Ci: Ci, Cr: Cr, Cu: Cu} = SpecialPowers;
let Promise = Cu.import("resource://gre/modules/Promise.jsm").Promise;
/* Push required permissions and test if |navigator.mozMobileMessage| exists.
* Resolve if it does, reject otherwise.
*
* Forfill params:
* manager -- an reference to navigator.mozMobileMessage.
*
* Reject params: (none)
*
* @return A deferred promise.
*/
let manager;
function ensureMobileMessage() {
let deferred = Promise.defer();
let permissions = [{
"type": "sms",
"allow": 1,
"context": document,
}];
SpecialPowers.pushPermissions(permissions, function() {
ok(true, "permissions pushed: " + JSON.stringify(permissions));
manager = window.navigator.mozMobileMessage;
if (manager) {
log("navigator.mozMobileMessage is instance of " + manager.constructor);
} else {
log("navigator.mozMobileMessage is undefined.");
}
if (manager instanceof MozMobileMessageManager) {
deferred.resolve(manager);
} else {
deferred.reject();
}
});
return deferred.promise;
}
/* Send a SMS message to a single receiver. Resolve if it succeeds, reject
* otherwise.
*
* Forfill params:
* message -- the sent SmsMessage.
*
* Reject params:
* error -- a DOMError.
*
* @param aReceiver the address of the receiver.
* @param aText the text body of the message.
*
* @return A deferred promise.
*/
function sendSmsWithSuccess(aReceiver, aText) {
let deferred = Promise.defer();
let request = manager.send(aReceiver, aText);
request.onsuccess = function(event) {
deferred.resolve(event.target.result);
};
request.onerror = function(event) {
deferred.reject(event.target.error);
};
return deferred.promise;
}
/* Send a MMS message with specified parameters. Resolve if it fails, reject
* otherwise.
*
* Forfill params:
* message -- the failed MmsMessage
*
* Reject params: (none)
*
* @param aMmsParameters a MmsParameters instance.
*
* @return A deferred promise.
*/
function sendMmsWithFailure(aMmsParameters) {
let deferred = Promise.defer();
manager.onfailed = function(event) {
manager.onfailed = null;
deferred.resolve(event.message);
};
let request = manager.sendMMS(aMmsParameters);
request.onsuccess = function(event) {
deferred.reject();
};
return deferred.promise;
}
/* Retrieve messages from database.
*
* Forfill params:
* messages -- an array of {Sms,Mms}Message instances.
*
* Reject params:
* event -- a DOMEvent
*
* @param aFilter an optional MozSmsFilter instance.
* @param aReverse a boolean value indicating whether the order of the messages
* should be reversed.
*
* @return A deferred promise.
*/
function getMessages(aFilter, aReverse) {
let deferred = Promise.defer();
if (!aFilter) {
aFilter = new MozSmsFilter;
}
let messages = [];
let cursor = manager.getMessages(aFilter, aReverse || false);
cursor.onsuccess = function(aEvent) {
if (cursor.result) {
messages.push(cursor.result);
cursor.continue();
return;
}
deferred.resolve(messages);
};
cursor.onerror = deferred.reject.bind(deferred);
return deferred.promise;
}
/* Retrieve all messages from database.
*
* Forfill params:
* messages -- an array of {Sms,Mms}Message instances.
*
* Reject params:
* event -- a DOMEvent
*
* @return A deferred promise.
*/
function getAllMessages() {
return getMessages(null, false);
}
/* Retrieve all threads from database.
*
* Forfill params:
* threads -- an array of MozMobileMessageThread instances.
*
* Reject params:
* event -- a DOMEvent
*
* @return A deferred promise.
*/
function getAllThreads() {
let deferred = Promise.defer();
let threads = [];
let cursor = manager.getThreads();
cursor.onsuccess = function(aEvent) {
if (cursor.result) {
threads.push(cursor.result);
cursor.continue();
return;
}
deferred.resolve(threads);
};
cursor.onerror = deferred.reject.bind(deferred);
return deferred.promise;
}
/* Retrieve a single specified thread from database.
*
* Forfill params:
* thread -- a MozMobileMessageThread instance.
*
* Reject params:
* event -- a DOMEvent if an error occurs in the retrieving process, or
* undefined if there's no such thread.
*
* @aThreadId a numeric value identifying the target thread.
*
* @return A deferred promise.
*/
function getThreadById(aThreadId) {
return getAllThreads()
.then(function(aThreads) {
for (let thread of aThreads) {
if (thread.id == aThreadId) {
return thread;
}
}
throw undefined;
});
}
/* Delete messages specified from database.
*
* Forfill params:
* result -- an array of boolean values indicating whether delesion was
* actually performed on the message record with corresponding id.
*
* Reject params:
* event -- a DOMEvent.
*
* @aMessageId an array of numeric values identifying the target messages.
*
* @return An empty array if nothing to be deleted; otherwise, a deferred promise.
*/
function deleteMessagesById(aMessageIds) {
if (!aMessageIds.length) {
ok(true, "no message to be deleted");
return [];
}
let deferred = Promise.defer();
let request = manager.delete(aMessageIds);
request.onsuccess = function(event) {
deferred.resolve(event.target.result);
};
request.onerror = deferred.reject.bind(deferred);
return deferred.promise;
}
/* Delete messages specified from database.
*
* Forfill params:
* result -- an array of boolean values indicating whether delesion was
* actually performed on the message record with corresponding id.
*
* Reject params:
* event -- a DOMEvent.
*
* @aMessages an array of {Sms,Mms}Message instances.
*
* @return A deferred promise.
*/
function deleteMessages(aMessages) {
let ids = messagesToIds(aMessages);
return deleteMessagesById(ids);
}
/* Delete all messages from database.
*
* Forfill params:
* ids -- an array of numeric values identifying those deleted
* {Sms,Mms}Messages.
*
* Reject params:
* event -- a DOMEvent.
*
* @return A deferred promise.
*/
function deleteAllMessages() {
return getAllMessages().then(deleteMessages);
}
/* Create a new array of id attribute of input messages.
*
* @param aMessages an array of {Sms,Mms}Message instances.
*
* @return an array of numeric values.
*/
function messagesToIds(aMessages) {
let ids = [];
for (let message of aMessages) {
ids.push(message.id);
}
return ids;
}
/* Flush permission settings and call |finish()|.
*/
function cleanUp() {
SpecialPowers.flushPermissions(function() {
// Use ok here so that we have at least one test run.
ok(true, "permissions flushed");
finish();
});
}
function startTestCommon(aTestCaseMain) {
ensureMobileMessage()
.then(deleteAllMessages)
.then(aTestCaseMain)
.then(deleteAllMessages)
.then(cleanUp, cleanUp);
}

View File

@ -38,3 +38,4 @@ qemu = true
[test_getthreads.js]
[test_smsc_address.js]
[test_dsds_default_service_id.js]
[test_thread_subject.js]

View File

@ -0,0 +1,67 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
MARIONETTE_TIMEOUT = 60000;
MARIONETTE_HEAD_JS = 'head.js';
const PHONE_NUMBER = "+1234567890";
// Have a long long subject causes the send fails, so we don't need
// networking here.
const MMS_MAX_LENGTH_SUBJECT = 40;
function genMmsSubject(sep) {
return "Hello " + (new Array(MMS_MAX_LENGTH_SUBJECT).join(sep)) + " World!";
}
function testSms(aProgressStr, aText) {
log("Testing thread subject: " + aProgressStr);
return sendSmsWithSuccess(PHONE_NUMBER, aText)
.then(function(message) {
log(" SMS sent, retrieving thread of id " + message.threadId);
return getThreadById(message.threadId);
})
.then(function(thread) {
log(" Got thread.lastMessageSubject = '" + thread.lastMessageSubject + "'");
is(thread.lastMessageSubject, "", "thread.lastMessageSubject");
});
}
function testMms(aProgressStr, aSubject) {
log("Testing thread subject: " + aProgressStr);
let mmsParameters = {
receivers: [PHONE_NUMBER],
subject: aSubject,
attachments: [],
};
// We use a long long message subject so it will always fail.
return sendMmsWithFailure(mmsParameters)
.then(function(message) {
log(" MMS sent, retrieving thread of id " + message.threadId);
return getThreadById(message.threadId);
})
.then(function(thread) {
log(" Got thread.lastMessageSubject = '" + thread.lastMessageSubject + "'");
is(thread.lastMessageSubject, aSubject, "thread.lastMessageSubject");
});
}
startTestCommon(function testCaseMain() {
return testSms("SMS", "text")
.then(testMms.bind(null, "SMS..MMS", genMmsSubject(" ")))
.then(testSms.bind(null, "SMS..MMS..SMS", "text"))
.then(deleteAllMessages)
.then(testMms.bind(null, "MMS", genMmsSubject(" ")))
.then(testSms.bind(null, "MMS..SMS", "text"))
.then(testMms.bind(null, "MMS..SMS..MMS", genMmsSubject(" ")))
.then(deleteAllMessages)
.then(testSms.bind(null, "SMS", "1"))
.then(testSms.bind(null, "SMS..SMS", "2"))
.then(testSms.bind(null, "SMS..SMS..SMS", "3"))
.then(deleteAllMessages)
.then(testMms.bind(null, "MMS", genMmsSubject("a")))
.then(testMms.bind(null, "MMS..MMS", genMmsSubject("b")))
.then(testMms.bind(null, "MMS..MMS..MMS", genMmsSubject("c")));
});

View File

@ -189,8 +189,7 @@ CdmaIccInfo.prototype = {
// nsIDOMMozCdmaIccInfo
mdn: null,
min: null
mdn: null
};
function VoicemailInfo() {}

View File

@ -527,8 +527,7 @@ CdmaIccInfo.prototype = {
// nsIDOMMozCdmaIccInfo
mdn: null,
min: null
mdn: null
};
function RadioInterfaceLayer() {

View File

@ -2772,16 +2772,18 @@ this.PDU_CDMA_MSG_CODING_7BITS_GSM = 0x09; // GSM 7-bit default alphabet(7-bi
this.PDU_CDMA_MSG_CODING_GSM_DCS = 0x0A; // GSM Data-Coding-Scheme, Not supported
// SMS Message Type, as defined in 3GPP2 C.S0015-A v2.0, Table 4.5.1-1
this.PDU_CDMA_MSG_TYPE_DELIVER = 0x01; // Receive
this.PDU_CDMA_MSG_TYPE_SUBMIT = 0x02; // Send
this.PDU_CDMA_MSG_TYPE_DELIVER = 0x01; // Deliver
this.PDU_CDMA_MSG_TYPE_SUBMIT = 0x02; // Submit
this.PDU_CDMA_MSG_TYPE_DELIVER_ACK = 0x04; // Delivery Acknowledgment
// SMS User Data Subparameters, as defined in 3GPP2 C.S0015-A v2.0, Table 4.5-1
this.PDU_CDMA_MSG_USERDATA_MSG_ID = 0x00; // Message Identifier
this.PDU_CDMA_MSG_USERDATA_BODY = 0x01; // User Data Body
this.PDU_CDMA_MSG_USERDATA_TIMESTAMP = 0x03; // Message Center Time Stamp
this.PDU_CDMA_REPLY_OPTION = 0x0A; // Reply Option
this.PDU_CDMA_LANGUAGE_INDICATOR = 0x0D; // Language Indicator
this.PDU_CDMA_MSG_USERDATA_MSG_ID = 0x00; // Message Identifier
this.PDU_CDMA_MSG_USERDATA_BODY = 0x01; // User Data Body
this.PDU_CDMA_MSG_USERDATA_TIMESTAMP = 0x03; // Message Center Time Stamp
this.PDU_CDMA_MSG_USERDATA_REPLY_OPTION = 0x0A; // Reply Option
this.PDU_CDMA_LANGUAGE_INDICATOR = 0x0D; // Language Indicator
this.PDU_CDMA_MSG_USERDATA_CALLBACK_NUMBER = 0x0E; // Callback Number
this.PDU_CDMA_MSG_USER_DATA_MSG_STATUS = 0x14; // Message Status
// CDMA Language Indicator: Language groups
// see 3GPP2 C.R1001-F table 9.2-1

View File

@ -4306,6 +4306,46 @@ let RIL = {
return PDU_FCS_OK;
},
/**
* Helper for processing CDMA SMS Delivery Acknowledgment Message
*
* @param message
* decoded SMS Delivery ACK message from CdmaPDUHelper.
*
* @return A failure cause defined in 3GPP 23.040 clause 9.2.3.22.
*/
_processCdmaSmsStatusReport: function _processCdmaSmsStatusReport(message) {
let options = this._pendingSentSmsMap[message.msgId];
if (!options) {
if (DEBUG) debug("no pending SMS-SUBMIT message");
return PDU_FCS_OK;
}
if (message.errorClass === 2) {
if (DEBUG) debug("SMS-STATUS-REPORT: delivery still pending, msgStatus: " + message.msgStatus);
return PDU_FCS_OK;
}
delete this._pendingSentSmsMap[message.msgId];
if (message.errorClass === -1 && message.body) {
// Process as normal incoming SMS, if errorClass is invalid
// but message body is available.
return this._processSmsMultipart(message);
}
let deliveryStatus = (message.errorClass === 0)
? GECKO_SMS_DELIVERY_STATUS_SUCCESS
: GECKO_SMS_DELIVERY_STATUS_ERROR;
this.sendChromeMessage({
rilMessageType: options.rilMessageType,
rilMessageToken: options.rilMessageToken,
deliveryStatus: deliveryStatus
});
return PDU_FCS_OK;
},
/**
* Helper for processing received multipart SMS.
*
@ -5893,7 +5933,7 @@ RIL[REQUEST_CDMA_SUBSCRIPTION] = function REQUEST_CDMA_SUBSCRIPTION(length, opti
this.iccInfo.mdn = result[0];
// The result[1] is Home SID. (Already be handled in readCDMAHome())
// The result[2] is Home NID. (Already be handled in readCDMAHome())
this.iccInfo.min = result[3];
// The result[3] is MIN.
// The result[4] is PRL version.
ICCUtilsHelper.handleICCInfoChange();
@ -6206,7 +6246,11 @@ RIL[UNSOLICITED_RESPONSE_CDMA_NEW_SMS] = function UNSOLICITED_RESPONSE_CDMA_NEW_
let [message, result] = CdmaPDUHelper.processReceivedSms(length);
if (message) {
result = this._processSmsMultipart(message);
if (message.subMsgType === PDU_CDMA_MSG_TYPE_DELIVER_ACK) {
result = this._processCdmaSmsStatusReport(message);
} else {
result = this._processSmsMultipart(message);
}
}
if (result == PDU_FCS_RESERVED || result == MOZ_FCS_WAIT_FOR_EXPLICIT_ACK) {
@ -8667,6 +8711,9 @@ let CdmaPDUHelper = {
// User Data
this.encodeUserDataMsg(options);
// Reply Option
this.encodeUserDataReplyOption(options);
return userDataBuffer;
},
@ -8791,6 +8838,21 @@ let CdmaPDUHelper = {
BitBufferHelper.overwriteWriteBuffer(lengthPosition - 1, [currentPosition - lengthPosition]);
},
/**
* User data subparameter encoder : Reply Option
*
* @see 3GGP2 C.S0015-B 2.0, 4.5.11 Reply Option
*/
encodeUserDataReplyOption: function cdma_encodeUserDataReplyOption(options) {
if (options.requestStatusReport) {
BitBufferHelper.writeBits(PDU_CDMA_MSG_USERDATA_REPLY_OPTION, 8);
BitBufferHelper.writeBits(1, 8);
BitBufferHelper.writeBits(0, 1); // USER_ACK_REQ
BitBufferHelper.writeBits(1, 1); // DAK_REQ
BitBufferHelper.flushWithPadding();
}
},
/**
* Entry point for SMS decoding, the returned object is made compatible
* with existing readMessage() of GsmPDUHelper
@ -8838,39 +8900,56 @@ let CdmaPDUHelper = {
message.sender += String.fromCharCode(addrDigit);
}
// User Data
// Bearer Data
this.decodeUserData(message);
// Bearer Data Sub-Parameter: User Data
let userData = message[PDU_CDMA_MSG_USERDATA_BODY];
[message.header, message.body, message.encoding] =
(userData)? [userData.header, userData.body, userData.encoding]
: [null, null, null];
// Bearer Data Sub-Parameter: Message Status
// Success Delivery (0) if both Message Status and User Data are absent.
// Message Status absent (-1) if only User Data is available.
let msgStatus = message[PDU_CDMA_MSG_USER_DATA_MSG_STATUS];
[message.errorClass, message.msgStatus] =
(msgStatus)? [msgStatus.errorClass, msgStatus.msgStatus]
: ((message.body)? [-1, -1]: [0, 0]);
// Transform message to GSM msg
let msg = {
SMSC: "",
mti: 0,
udhi: 0,
sender: message.sender,
recipient: null,
pid: PDU_PID_DEFAULT,
epid: PDU_PID_DEFAULT,
dcs: 0,
mwi: null, //message[PDU_CDMA_MSG_USERDATA_BODY].header ? message[PDU_CDMA_MSG_USERDATA_BODY].header.mwi : null,
replace: false,
header: message[PDU_CDMA_MSG_USERDATA_BODY].header,
body: message[PDU_CDMA_MSG_USERDATA_BODY].body,
data: null,
timestamp: message[PDU_CDMA_MSG_USERDATA_TIMESTAMP],
language: message[PDU_CDMA_LANGUAGE_INDICATOR],
status: null,
scts: null,
dt: null,
encoding: message[PDU_CDMA_MSG_USERDATA_BODY].encoding,
messageClass: GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_NORMAL],
messageType: message.messageType,
serviceCategory: message.service
SMSC: "",
mti: 0,
udhi: 0,
sender: message.sender,
recipient: null,
pid: PDU_PID_DEFAULT,
epid: PDU_PID_DEFAULT,
dcs: 0,
mwi: null,
replace: false,
header: message.header,
body: message.body,
data: null,
timestamp: message[PDU_CDMA_MSG_USERDATA_TIMESTAMP],
language: message[PDU_CDMA_LANGUAGE_INDICATOR],
status: null,
scts: null,
dt: null,
encoding: message.encoding,
messageClass: GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_NORMAL],
messageType: message.messageType,
serviceCategory: message.service,
subMsgType: message[PDU_CDMA_MSG_USERDATA_MSG_ID].msgType,
msgId: message[PDU_CDMA_MSG_USERDATA_MSG_ID].msgId,
errorClass: message.errorClass,
msgStatus: message.msgStatus
};
return msg;
},
/**
* Helper for processing received SMS parcel data.
*
@ -8963,8 +9042,8 @@ let CdmaPDUHelper = {
case PDU_CDMA_MSG_USERDATA_TIMESTAMP:
message[id] = this.decodeUserDataTimestamp();
break;
case PDU_CDMA_REPLY_OPTION:
message[id] = this.decodeUserDataReplyAction();
case PDU_CDMA_MSG_USERDATA_REPLY_OPTION:
message[id] = this.decodeUserDataReplyOption();
break;
case PDU_CDMA_LANGUAGE_INDICATOR:
message[id] = this.decodeLanguageIndicator();
@ -8972,6 +9051,9 @@ let CdmaPDUHelper = {
case PDU_CDMA_MSG_USERDATA_CALLBACK_NUMBER:
message[id] = this.decodeUserDataCallbackNumber();
break;
case PDU_CDMA_MSG_USER_DATA_MSG_STATUS:
message[id] = this.decodeUserDataMsgStatus();
break;
}
userDataLength -= (length + 2);
@ -9338,7 +9420,7 @@ let CdmaPDUHelper = {
*
* @see 3GGP2 C.S0015-B 2.0, 4.5.11 Reply Option
*/
decodeUserDataReplyAction: function cdma_decodeUserDataReplyAction() {
decodeUserDataReplyOption: function cdma_decodeUserDataReplyOption() {
let replyAction = BitBufferHelper.readBits(4),
result = { userAck: (replyAction & 0x8) ? true : false,
deliverAck: (replyAction & 0x4) ? true : false,
@ -9386,6 +9468,20 @@ let CdmaPDUHelper = {
return result;
},
/**
* User data subparameter decoder : Message Status
*
* @see 3GGP2 C.S0015-B 2.0, 4.5.21 Message Status
*/
decodeUserDataMsgStatus: function cdma_decodeUserDataMsgStatus() {
let result = {
errorClass: BitBufferHelper.readBits(2),
msgStatus: BitBufferHelper.readBits(6)
};
return result;
},
/**
* Decode information record parcel.
*/

View File

@ -0,0 +1,100 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
subscriptLoader.loadSubScript("resource://gre/modules/ril_consts.js", this);
function run_test() {
run_next_test();
}
function _getWorker() {
let _postedMessage;
let _worker = newWorker({
postRILMessage: function fakePostRILMessage(data) {
},
postMessage: function fakePostMessage(message) {
_postedMessage = message;
}
});
return {
get postedMessage() {
return _postedMessage;
},
get worker() {
return _worker;
}
};
}
/**
* Verify CDMA SMS Delivery ACK Message.
*/
add_test(function test_processCdmaSmsStatusReport() {
let workerHelper = _getWorker();
let worker = workerHelper.worker;
function test_StatusReport(errorClass, msgStatus) {
let msgId = 0;
let sentSmsMap = worker.RIL._pendingSentSmsMap;
sentSmsMap[msgId] = {};
let message = {
SMSC: "",
mti: 0,
udhi: 0,
sender: "0987654321",
recipient: null,
pid: PDU_PID_DEFAULT,
epid: PDU_PID_DEFAULT,
dcs: 0,
mwi: null,
replace: false,
header: null,
body: "Status: Sent, Dest: 0987654321",
data: null,
timestamp: new Date().valueOf(),
language: null,
status: null,
scts: null,
dt: null,
encoding: PDU_CDMA_MSG_CODING_7BITS_ASCII,
messageClass: GECKO_SMS_MESSAGE_CLASSES[PDU_DCS_MSG_CLASS_NORMAL],
messageType: PDU_CDMA_MSG_TYPE_P2P,
serviceCategory: 0,
subMsgType: PDU_CDMA_MSG_TYPE_DELIVER_ACK,
msgId: msgId,
errorClass: errorClass,
msgStatus: msgStatus
};
worker.RIL._processCdmaSmsStatusReport(message);
let postedMessage = workerHelper.postedMessage;
// Check if pending token is removed.
do_check_true((errorClass === 2)? !!sentSmsMap[msgId]: !sentSmsMap[msgId]);
// Check the response message accordingly.
if (errorClass === -1) {
// Check if the report is treated as normal incoming SMS
do_check_eq("sms-received", postedMessage.rilMessageType);
} else if (errorClass === 2) {
// Do nothing.
} else {
// Check Delivery Status
if (errorClass === 0) {
do_check_eq(postedMessage.deliveryStatus, GECKO_SMS_DELIVERY_STATUS_SUCCESS);
} else {
do_check_eq(postedMessage.deliveryStatus, GECKO_SMS_DELIVERY_STATUS_ERROR);
}
}
}
test_StatusReport(-1, -1); // Message Status Sub-parameter is absent.
test_StatusReport(0, 0); // 00|000000: no error|Message accepted
test_StatusReport(2, 4); // 10|000100: temporary condition|Network congestion
test_StatusReport(3, 5); // 11|000101: permanent condition|Network error
run_next_test();
});

View File

@ -0,0 +1,73 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
subscriptLoader.loadSubScript("resource://gre/modules/ril_consts.js", this);
function run_test() {
run_next_test();
}
/**
* Verify CdmaPDUHelper#encodeUserDataReplyOption.
*/
add_test(function test_CdmaPDUHelper_encodeUserDataReplyOption() {
let worker = newWorker({
postRILMessage: function fakePostRILMessage(data) {
// Do nothing
},
postMessage: function fakePostMessage(message) {
// Do nothing
}
});
let testDataBuffer = [];
worker.BitBufferHelper.startWrite(testDataBuffer);
let helper = worker.CdmaPDUHelper;
helper.encodeUserDataReplyOption({requestStatusReport: true});
let expectedDataBuffer = [PDU_CDMA_MSG_USERDATA_REPLY_OPTION, 0x01, 0x40];
do_check_eq(testDataBuffer.length, expectedDataBuffer.length);
for (let i = 0; i < expectedDataBuffer.length; i++) {
do_check_eq(testDataBuffer[i], expectedDataBuffer[i]);
}
run_next_test();
});
/**
* Verify CdmaPDUHelper#cdma_decodeUserDataMsgStatus.
*/
add_test(function test_CdmaPDUHelper_decodeUserDataMsgStatus() {
let worker = newWorker({
postRILMessage: function fakePostRILMessage(data) {
// Do nothing
},
postMessage: function fakePostMessage(message) {
// Do nothing
}
});
let helper = worker.CdmaPDUHelper;
function test_MsgStatus(octet) {
let testDataBuffer = [octet];
worker.BitBufferHelper.startRead(testDataBuffer);
let result = helper.decodeUserDataMsgStatus();
do_check_eq(result.errorClass, octet >>> 6);
do_check_eq(result.msgStatus, octet & 0x3F);
}
// 00|000000: no error|Message accepted
test_MsgStatus(0x00);
// 10|000100: temporary condition|Network congestion
test_MsgStatus(0x84);
// 11|000101: permanent condition|Network error
test_MsgStatus(0xC5);
run_next_test();
});

View File

@ -7,6 +7,8 @@ tail =
[test_ril_worker_sms.js]
# Bug 916067 - B2G RIL: test_ril_worker_sms.js takes too long to finish
skip-if = true
[test_ril_worker_sms_cdma.js]
[test_ril_worker_sms_cdmapduhelper.js]
[test_ril_worker_sms_nl_tables.js]
[test_ril_worker_sms_gsmpduhelper.js]
[test_ril_worker_sms_segment_info.js]

View File

@ -4,8 +4,14 @@
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
[Pref="dom.icc.enabled",
Constructor(DOMString type, optional MozStkCommandEventInit eventInitDict)]
interface MozStkCommandEvent : Event
{
[Throws]
readonly attribute any command;
};
dictionary MozStkCommandEventInit : EventInit
{
any command = null;
};

View File

@ -515,7 +515,6 @@ if CONFIG['MOZ_B2G_RIL']:
'MozCellBroadcastEvent.webidl',
'MozEmergencyCbModeEvent.webidl',
'MozOtaStatusEvent.webidl',
'MozStkCommandEvent.webidl',
'MozVoicemail.webidl',
'MozVoicemailEvent.webidl',
'USSDReceivedEvent.webidl',
@ -560,6 +559,7 @@ GENERATED_EVENTS_WEBIDL_FILES = [
'MediaStreamEvent.webidl',
'MozContactChangeEvent.webidl',
'MozInterAppMessageEvent.webidl',
'MozStkCommandEvent.webidl',
'RTCDataChannelEvent.webidl',
'RTCPeerConnectionIceEvent.webidl',
'TrackEvent.webidl',
@ -572,4 +572,3 @@ if CONFIG['MOZ_GAMEPAD']:
'GamepadButtonEvent.webidl',
'GamepadEvent.webidl',
]

View File

@ -266,6 +266,9 @@ public:
SetCompositor(nullptr);
mAttached = false;
mKeepAttached = false;
if (mBackendData) {
mBackendData->ClearData();
}
}
}
bool IsAttached() { return mAttached; }

View File

@ -1717,6 +1717,10 @@ var NativeWindow = {
* the checked state as an argument.
*/
show: function(aMessage, aValue, aButtons, aTabID, aOptions) {
if (aButtons == null) {
aButtons = [];
}
aButtons.forEach((function(aButton) {
this._callbacks[this._callbacksId] = { cb: aButton.callback, prompt: this._promptId };
aButton.callback = this._callbacksId;

View File

@ -520,11 +520,11 @@ ImportCertsIntoPermanentStorage(const ScopedCERTCertList &certChain, const SECCe
chainNode = CERT_LIST_NEXT(chainNode), i++) {
rawArray[i] = &chainNode->cert->derCert;
}
CERT_ImportCerts(certdb, usage, chainLen,
rawArray, nullptr, true, caOnly, nullptr);
SECStatus srv = CERT_ImportCerts(certdb, usage, chainLen, rawArray,
nullptr, true, caOnly, nullptr);
PORT_Free(rawArray);
return SECSuccess;
PORT_Free(rawArray);
return srv;
}
@ -800,7 +800,10 @@ nsNSSCertificateDB::ImportValidCACertsInList(CERTCertList *certList, nsIInterfac
continue;
}
ImportCertsIntoPermanentStorage(certChain, certUsageAnyCA, true);
rv = ImportCertsIntoPermanentStorage(certChain, certUsageAnyCA, true);
if (rv != SECSuccess) {
return NS_ERROR_FAILURE;
}
}
return NS_OK;

View File

@ -1209,19 +1209,18 @@ Engine.prototype = {
fileInStream.init(this._file, MODE_RDONLY, PERMS_FILE, false);
switch (this._dataType) {
case SEARCH_DATA_XML:
var domParser = Cc["@mozilla.org/xmlextras/domparser;1"].
createInstance(Ci.nsIDOMParser);
var doc = domParser.parseFromStream(fileInStream, "UTF-8",
this._file.fileSize,
"text/xml");
if (this._dataType == SEARCH_DATA_XML) {
var domParser = Cc["@mozilla.org/xmlextras/domparser;1"].
createInstance(Ci.nsIDOMParser);
var doc = domParser.parseFromStream(fileInStream, "UTF-8",
this._file.fileSize,
"text/xml");
this._data = doc.documentElement;
break;
default:
ERROR("Unsuppored engine _dataType in _initFromFile: \"" + this._dataType + "\"",
Cr.NS_ERROR_UNEXPECTED);
this._data = doc.documentElement;
} else {
ERROR("Unsuppored engine _dataType in _initFromFile: \"" +
this._dataType + "\"",
Cr.NS_ERROR_UNEXPECTED);
}
fileInStream.close();
@ -1230,14 +1229,41 @@ Engine.prototype = {
},
/**
* Retrieves the engine data from a URI.
* Retrieves the data from the engine's file asynchronously. If the engine's
* dataType is XML, the document element is placed in the engine's data field.
*
* @returns {Promise} A promise, resolved successfully if initializing from
* data succeeds, rejected if it fails.
*/
_initFromURI: function SRCH_ENG_initFromURI() {
_asyncInitFromFile: function SRCH_ENG__asyncInitFromFile() {
return TaskUtils.spawn(function() {
if (!this._file || !(yield OS.File.exists(this._file.path)))
FAIL("File must exist before calling initFromFile!", Cr.NS_ERROR_UNEXPECTED);
if (this._dataType == SEARCH_DATA_XML) {
let fileURI = NetUtil.ioService.newFileURI(this._file);
yield this._retrieveSearchXMLData(fileURI.spec);
} else {
ERROR("Unsuppored engine _dataType in _initFromFile: \"" +
this._dataType + "\"",
Cr.NS_ERROR_UNEXPECTED);
}
// Now that the data is loaded, initialize the engine object
this._initFromData();
}.bind(this));
},
/**
* Retrieves the engine data from a URI. Initializes the engine, flushes to
* disk, and notifies the search service once initialization is complete.
*/
_initFromURIAndLoad: function SRCH_ENG_initFromURIAndLoad() {
ENSURE_WARN(this._uri instanceof Ci.nsIURI,
"Must have URI when calling _initFromURI!",
"Must have URI when calling _initFromURIAndLoad!",
Cr.NS_ERROR_UNEXPECTED);
LOG("_initFromURI: Downloading engine from: \"" + this._uri.spec + "\".");
LOG("_initFromURIAndLoad: Downloading engine from: \"" + this._uri.spec + "\".");
var chan = NetUtil.ioService.newChannelFromURI(this._uri);
@ -1251,7 +1277,47 @@ Engine.prototype = {
chan.notificationCallbacks = listener;
chan.asyncOpen(listener, null);
},
/**
* Retrieves the engine data from a URI asynchronously and initializes it.
*
* @returns {Promise} A promise, resolved successfully if retrieveing data
* succeeds.
*/
_asyncInitFromURI: function SRCH_ENG__asyncInitFromURI() {
return TaskUtils.spawn(function() {
LOG("_asyncInitFromURI: Loading engine from: \"" + this._uri.spec + "\".");
yield this._retrieveSearchXMLData(this._uri.spec);
// Now that the data is loaded, initialize the engine object
this._initFromData();
}.bind(this));
},
/**
* Retrieves the engine data for a given URI asynchronously.
*
* @returns {Promise} A promise, resolved successfully if retrieveing data
* succeeds.
*/
_retrieveSearchXMLData: function SRCH_ENG__retrieveSearchXMLData(aURL) {
let deferred = Promise.defer();
let request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
createInstance(Ci.nsIXMLHttpRequest);
request.overrideMimeType("text/xml");
request.onload = (aEvent) => {
let responseXML = aEvent.target.responseXML;
this._data = responseXML.documentElement;
deferred.resolve();
};
request.onerror = function(aEvent) {
deferred.resolve();
};
request.open("GET", aURL, true);
request.send();
return deferred.promise;
},
_initFromURISync: function SRCH_ENG_initFromURISync() {
ENSURE_WARN(this._uri instanceof Ci.nsIURI,
"Must have URI when calling _initFromURISync!",
@ -1575,7 +1641,6 @@ Engine.prototype = {
* Initialize this Engine object from the collected data.
*/
_initFromData: function SRCH_ENG_initFromData() {
ENSURE_WARN(this._data, "Can't init an engine with no data!",
Cr.NS_ERROR_UNEXPECTED);
@ -2642,6 +2707,26 @@ function executeSoon(func) {
Services.tm.mainThread.dispatch(func, Ci.nsIThread.DISPATCH_NORMAL);
}
/**
* Check for sync initialization has completed or not.
*
* @param {aPromise} A promise.
*
* @returns the value returned by the invoked method.
* @throws NS_ERROR_ALREADY_INITIALIZED if sync initialization has completed.
*/
function checkForSyncCompletion(aPromise) {
return aPromise.then(function(aValue) {
if (gInitialized) {
throw Components.Exception("Synchronous fallback was called and has " +
"finished so no need to pursue asynchronous " +
"initialization",
Cr.NS_ERROR_ALREADY_INITIALIZED);
}
return aValue;
});
}
// nsIBrowserSearchService
function SearchService() {
// Replace empty LOG function with the useful one if the log pref is set.
@ -2658,6 +2743,9 @@ SearchService.prototype = {
// initialization is complete, only if an error has been encountered so far.
_initRV: Cr.NS_OK,
// The boolean indicates that the initialization has started or not.
_initStarted: null,
// If initialization has not been completed yet, perform synchronous
// initialization.
// Throws in case of initialization error.
@ -2687,9 +2775,10 @@ SearchService.prototype = {
// Synchronous implementation of the initializer.
// Used by |_ensureInitialized| as a fallback if initialization is not
// complete. In this implementation, it is also used by |init|.
// complete.
_syncInit: function SRCH_SVC__syncInit() {
LOG("_syncInit start");
this._initStarted = true;
try {
this._syncLoadEngines();
} catch (ex) {
@ -2707,6 +2796,30 @@ SearchService.prototype = {
LOG("_syncInit end");
},
/**
* Asynchronous implementation of the initializer.
*
* @returns {Promise} A promise, resolved successfully if the initialization
* succeeds.
*/
_asyncInit: function SRCH_SVC__asyncInit() {
return TaskUtils.spawn(function() {
LOG("_asyncInit start");
try {
yield checkForSyncCompletion(this._asyncLoadEngines());
} catch (ex if ex.result != Cr.NS_ERROR_ALREADY_INITIALIZED) {
this._initRV = Cr.NS_ERROR_FAILURE;
LOG("_asyncInit: failure loading engines: " + ex);
}
this._addObservers();
gInitialized = true;
this._initObservers.resolve(this._initRV);
Services.obs.notifyObservers(null, SEARCH_SERVICE_TOPIC, "init-complete");
LOG("_asyncInit: Completed _asyncInit");
}.bind(this));
},
_engines: { },
__sortedEngines: null,
get _sortedEngines() {
@ -2880,6 +2993,115 @@ SearchService.prototype = {
LOG("_loadEngines: done");
},
/**
* Loads engines asynchronously.
*
* @returns {Promise} A promise, resolved successfully if loading data
* succeeds.
*/
_asyncLoadEngines: function SRCH_SVC__asyncLoadEngines() {
return TaskUtils.spawn(function() {
LOG("_asyncLoadEngines: start");
// See if we have a cache file so we don't have to parse a bunch of XML.
let cache = {};
let cacheEnabled = getBoolPref(BROWSER_SEARCH_PREF + "cache.enabled", true);
if (cacheEnabled) {
let cacheFilePath = OS.Path.join(OS.Constants.Path.profileDir, "search.json");
cache = yield checkForSyncCompletion(this._asyncReadCacheFile(cacheFilePath));
}
// Add all the non-empty directories of NS_APP_SEARCH_DIR_LIST to
// loadDirs.
let loadDirs = [];
let locations = getDir(NS_APP_SEARCH_DIR_LIST, Ci.nsISimpleEnumerator);
while (locations.hasMoreElements()) {
let dir = locations.getNext().QueryInterface(Ci.nsIFile);
let iterator = new OS.File.DirectoryIterator(dir.path,
{ winPattern: "*.xml" });
try {
// Add dir to loadDirs if it contains any files.
yield checkForSyncCompletion(iterator.next());
loadDirs.push(dir);
} catch (ex if ex.result != Cr.NS_ERROR_ALREADY_INITIALIZED) {
// Catch for StopIteration exception.
} finally {
iterator.close();
}
}
let loadFromJARs = getBoolPref(BROWSER_SEARCH_PREF + "loadFromJars", false);
let chromeURIs = [];
let chromeFiles = [];
if (loadFromJARs) {
Services.obs.notifyObservers(null, SEARCH_SERVICE_TOPIC, "find-jar-engines");
[chromeFiles, chromeURIs] =
yield checkForSyncCompletion(this._asyncFindJAREngines());
}
let toLoad = chromeFiles.concat(loadDirs);
function hasModifiedDir(aList) {
return TaskUtils.spawn(function() {
let modifiedDir = false;
for (let dir of aList) {
if (!cache.directories || !cache.directories[dir.path]) {
modifiedDir = true;
break;
}
let info = yield OS.File.stat(dir.path);
if (cache.directories[dir.path].lastModifiedTime !=
info.lastModificationDate.getTime()) {
modifiedDir = true;
break;
}
}
throw new Task.Result(modifiedDir);
});
}
function notInCachePath(aPathToLoad)
cachePaths.indexOf(aPathToLoad.path) == -1;
let buildID = Services.appinfo.platformBuildID;
let cachePaths = [path for (path in cache.directories)];
let rebuildCache = !cache.directories ||
cache.version != CACHE_VERSION ||
cache.locale != getLocale() ||
cache.buildID != buildID ||
cachePaths.length != toLoad.length ||
toLoad.some(notInCachePath) ||
(yield checkForSyncCompletion(hasModifiedDir(toLoad)));
if (!cacheEnabled || rebuildCache) {
LOG("_asyncLoadEngines: Absent or outdated cache. Loading engines from disk.");
let engines = [];
for (let loadDir of loadDirs) {
let enginesFromDir =
yield checkForSyncCompletion(this._asyncLoadEnginesFromDir(loadDir));
engines = engines.concat(enginesFromDir);
}
let enginesFromURLs =
yield checkForSyncCompletion(this._asyncLoadFromChromeURLs(chromeURIs));
engines = engines.concat(enginesFromURLs);
for (let engine of engines) {
this._addEngineToStore(engine);
}
if (cacheEnabled)
this._buildCache();
return;
}
LOG("_asyncLoadEngines: loading from cache directories");
for each (let dir in cache.directories)
this._loadEnginesFromCache(dir);
LOG("_asyncLoadEngines: done");
}.bind(this));
},
_readCacheFile: function SRCH_SVC__readCacheFile(aFile) {
let stream = Cc["@mozilla.org/network/file-input-stream;1"].
createInstance(Ci.nsIFileInputStream);
@ -2896,6 +3118,28 @@ SearchService.prototype = {
return false;
},
/**
* Read from a given cache file asynchronously.
*
* @param aPath the file path.
*
* @returns {Promise} A promise, resolved successfully if retrieveing data
* succeeds.
*/
_asyncReadCacheFile: function SRCH_SVC__asyncReadCacheFile(aPath) {
return TaskUtils.spawn(function() {
let json;
try {
let bytes = yield OS.File.read(aPath);
json = JSON.parse(new TextDecoder().decode(bytes));
} catch (ex) {
LOG("_asyncReadCacheFile: Error reading cache file: " + ex);
json = {};
}
throw new Task.Result(json);
});
},
_batchTimer: null,
_batchCacheInvalidation: function SRCH_SVC__batchCacheInvalidation() {
let callback = {
@ -3042,15 +3286,64 @@ SearchService.prototype = {
}
},
/**
* Loads engines from a given directory asynchronously.
*
* @param aDir the directory.
*
* @returns {Promise} A promise, resolved successfully if retrieveing data
* succeeds.
*/
_asyncLoadEnginesFromDir: function SRCH_SVC__asyncLoadEnginesFromDir(aDir) {
LOG("_asyncLoadEnginesFromDir: Searching in " + aDir.path + " for search engines.");
// Check whether aDir is the user profile dir
let isInProfile = aDir.equals(getDir(NS_APP_USER_SEARCH_DIR));
let iterator = new OS.File.DirectoryIterator(aDir.path);
return TaskUtils.spawn(function() {
let osfiles = yield iterator.nextBatch();
iterator.close();
let engines = [];
for (let osfile of osfiles) {
if (osfile.isDir || osfile.isSymLink)
continue;
let fileInfo = yield OS.File.stat(osfile.path);
if (fileInfo.size == 0)
continue;
let parts = osfile.path.split(".");
if (parts.length <= 1 || (parts.pop()).toLowerCase() != "xml") {
// Not an engine
continue;
}
let addedEngine = null;
try {
let file = new FileUtils.File(osfile.path);
let isWritable = isInProfile;
addedEngine = new Engine(file, SEARCH_DATA_XML, !isWritable);
yield checkForSyncCompletion(addedEngine._asyncInitFromFile());
} catch (ex if ex.result != Cr.NS_ERROR_ALREADY_INITIALIZED) {
LOG("_asyncLoadEnginesFromDir: Failed to load " + file.path + "!\n" + ex);
continue;
}
engines.push(addedEngine);
}
throw new Task.Result(engines);
}.bind(this));
},
_loadFromChromeURLs: function SRCH_SVC_loadFromChromeURLs(aURLs) {
aURLs.forEach(function (url) {
try {
LOG("_loadFromChromeURLs: loading engine from chrome url: " + url);
let engine = new Engine(makeURI(url), SEARCH_DATA_XML, true);
engine._initFromURISync();
this._addEngineToStore(engine);
} catch (ex) {
LOG("_loadFromChromeURLs: failed to load engine: " + ex);
@ -3058,6 +3351,31 @@ SearchService.prototype = {
}, this);
},
/**
* Loads engines from Chrome URLs asynchronously.
*
* @param aURLs a list of URLs.
*
* @returns {Promise} A promise, resolved successfully if loading data
* succeeds.
*/
_asyncLoadFromChromeURLs: function SRCH_SVC__asyncLoadFromChromeURLs(aURLs) {
return TaskUtils.spawn(function() {
let engines = [];
for (let url of aURLs) {
try {
LOG("_asyncLoadFromChromeURLs: loading engine from chrome url: " + url);
let engine = new Engine(NetUtil.newURI(url), SEARCH_DATA_XML, true);
yield checkForSyncCompletion(engine._asyncInitFromURI());
engines.push(engine);
} catch (ex if ex.result != Cr.NS_ERROR_ALREADY_INITIALIZED) {
LOG("_asyncLoadFromChromeURLs: failed to load engine: " + ex);
}
}
throw new Task.Result(engines);
}.bind(this));
},
_findJAREngines: function SRCH_SVC_findJAREngines() {
LOG("_findJAREngines: looking for engines in JARs")
@ -3119,6 +3437,77 @@ SearchService.prototype = {
return [chromeFiles, uris];
},
/**
* Loads jar engines asynchronously.
*
* @returns {Promise} A promise, resolved successfully if finding jar engines
* succeeds.
*/
_asyncFindJAREngines: function SRCH_SVC__asyncFindJAREngines() {
return TaskUtils.spawn(function() {
LOG("_asyncFindJAREngines: looking for engines in JARs")
let rootURIPref = "";
try {
rootURIPref = Services.prefs.getCharPref(BROWSER_SEARCH_PREF + "jarURIs");
} catch (ex) {}
if (!rootURIPref) {
LOG("_asyncFindJAREngines: no JAR URIs were specified");
throw new Task.Result([[], []]);
}
let rootURIs = rootURIPref.split(",");
let uris = [];
let chromeFiles = [];
for (let root of rootURIs) {
// Find the underlying JAR file for this chrome package (_loadEngines uses
// it to determine whether it needs to invalidate the cache)
let chromeFile;
try {
let chromeURI = gChromeReg.convertChromeURL(makeURI(root));
let fileURI = chromeURI; // flat packaging
while (fileURI instanceof Ci.nsIJARURI)
fileURI = fileURI.JARFile; // JAR packaging
fileURI.QueryInterface(Ci.nsIFileURL);
chromeFile = fileURI.file;
} catch (ex) {
LOG("_asyncFindJAREngines: failed to get chromeFile for " + root + ": " + ex);
}
if (!chromeFile) {
return;
}
chromeFiles.push(chromeFile);
// Read list.txt from the chrome package to find the engines we need to
// load
let listURL = root + "list.txt";
let deferred = Promise.defer();
let request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
createInstance(Ci.nsIXMLHttpRequest);
request.onload = function(aEvent) {
deferred.resolve(aEvent.target.responseText);
};
request.onerror = function(aEvent) {
LOG("_asyncFindJAREngines: failed to retrieve list.txt from " + listURL);
deferred.resolve("");
};
request.open("GET", NetUtil.newURI(listURL).spec, true);
request.send();
let list = yield deferred.promise;
let names = [];
names = list.split("\n").filter(function (n) !!n);
names.forEach(function (n) uris.push(root + n + ".xml"));
}
throw new Task.Result([chromeFiles, uris]);
});
},
_saveSortedEngineList: function SRCH_SVC_saveSortedEngineList() {
LOG("SRCH_SVC_saveSortedEngineList: starting");
@ -3262,17 +3651,13 @@ SearchService.prototype = {
this._initStarted = true;
TaskUtils.spawn(function task() {
try {
yield engineMetadataService.init();
if (gInitialized) {
// No need to pursue asynchronous initialization,
// synchronous fallback had to be called and has finished.
return;
}
// Complete initialization. In the current implementation,
// this is done by calling the synchronous initializer.
// Future versions might introduce an actually synchronous
// implementation.
self._syncInit();
yield checkForSyncCompletion(engineMetadataService.init());
// Complete initialization by calling asynchronous initializer.
yield self._asyncInit();
TelemetryStopwatch.finish("SEARCH_SERVICE_INIT_MS");
} catch (ex if ex.result == Cr.NS_ERROR_ALREADY_INITIALIZED) {
// No need to pursue asynchronous because synchronous fallback was
// called and has finished.
TelemetryStopwatch.finish("SEARCH_SERVICE_INIT_MS");
} catch (ex) {
self._initObservers.reject(ex);
@ -3428,7 +3813,7 @@ SearchService.prototype = {
engine._installCallback = null;
};
}
engine._initFromURI();
engine._initFromURIAndLoad();
} catch (ex) {
// Drop the reference to the callback, if set
if (engine)
@ -3855,6 +4240,9 @@ var engineMetadataService = {
*/
syncInit: function epsSyncInit() {
LOG("metadata syncInit start");
if (this._initState == engineMetadataService._InitStates.FINISHED_SUCCESS) {
return;
}
switch (this._initState) {
case engineMetadataService._InitStates.NOT_STARTED:
let jsonFile = new FileUtils.File(this._jsonFile);
@ -4065,7 +4453,7 @@ var engineUpdateService = {
ULOG("updating " + engine.name + " from " + updateURI.spec);
testEngine = new Engine(updateURI, dataType, false);
testEngine._engineToUpdate = engine;
testEngine._initFromURI();
testEngine._initFromURIAndLoad();
} else
ULOG("invalid updateURI");

View File

@ -315,7 +315,13 @@ SuggestAutoComplete.prototype = {
this._clearServerErrors();
var serverResults = JSON.parse(responseText);
try {
var serverResults = JSON.parse(responseText);
} catch(ex) {
Components.utils.reportError("Failed to parse JSON from " + this._suggestURI.spec + ": " + ex);
return;
}
var searchString = serverResults[0] || "";
var results = serverResults[1] || [];

View File

@ -0,0 +1,36 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
function run_test() {
do_test_pending();
removeMetadata();
removeCacheFile();
do_load_manifest("data/chrome.manifest");
let url = "chrome://testsearchplugin/locale/searchplugins/";
Services.prefs.setCharPref("browser.search.jarURIs", url);
Services.prefs.setBoolPref("browser.search.loadFromJars", true);
do_check_false(Services.search.isInitialized);
Services.search.init(function search_initialized(aStatus) {
do_check_true(Components.isSuccessCode(aStatus));
do_check_true(Services.search.isInitialized);
// test engines from dir are loaded.
let engines = Services.search.getEngines();
do_check_true(engines.length > 1);
// test jar engine is loaded ok.
let engine = Services.search.getEngineByName("bug645970");
do_check_neq(engine, null);
Services.prefs.clearUserPref("browser.search.jarURIs");
Services.prefs.clearUserPref("browser.search.loadFromJars");
do_test_finished();
});
}

View File

@ -0,0 +1,29 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
function run_test() {
removeMetadata();
removeCacheFile();
do_load_manifest("data/chrome.manifest");
let url = "chrome://testsearchplugin/locale/searchplugins/";
Services.prefs.setCharPref("browser.search.jarURIs", url);
Services.prefs.setBoolPref("browser.search.loadFromJars", true);
do_check_false(Services.search.isInitialized);
// test engines from dir are loaded.
let engines = Services.search.getEngines();
do_check_true(engines.length > 1);
do_check_true(Services.search.isInitialized);
// test jar engine is loaded ok.
let engine = Services.search.getEngineByName("bug645970");
do_check_neq(engine, null);
Services.prefs.clearUserPref("browser.search.jarURIs");
Services.prefs.clearUserPref("browser.search.loadFromJars");
}

View File

@ -0,0 +1,57 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
function run_test() {
do_test_pending();
removeMetadata();
removeCacheFile();
do_load_manifest("data/chrome.manifest");
let url = "chrome://testsearchplugin/locale/searchplugins/";
Services.prefs.setCharPref("browser.search.jarURIs", url);
Services.prefs.setBoolPref("browser.search.loadFromJars", true);
do_check_false(Services.search.isInitialized);
let fallback = false;
Services.search.init(function search_initialized(aStatus) {
do_check_true(fallback);
do_check_true(Components.isSuccessCode(aStatus));
do_check_true(Services.search.isInitialized);
// test engines from dir are loaded.
let engines = Services.search.getEngines();
do_check_true(engines.length > 1);
// test jar engine is loaded ok.
let engine = Services.search.getEngineByName("bug645970");
do_check_neq(engine, null);
Services.prefs.clearUserPref("browser.search.jarURIs");
Services.prefs.clearUserPref("browser.search.loadFromJars");
do_test_finished();
});
// Execute test for the sync fallback while the async code is being executed.
Services.obs.addObserver(function searchServiceObserver(aResult, aTopic, aVerb) {
if (aVerb == "find-jar-engines") {
Services.obs.removeObserver(searchServiceObserver, aTopic);
fallback = true;
do_check_false(Services.search.isInitialized);
// test engines from dir are loaded.
let engines = Services.search.getEngines();
do_check_true(engines.length > 1);
// test jar engine is loaded ok.
let engine = Services.search.getEngineByName("bug645970");
do_check_neq(engine, null);
do_check_true(Services.search.isInitialized);
}
}, "browser-search-service", false);
}

View File

@ -0,0 +1,48 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
function run_test() {
do_test_pending();
removeMetadata();
removeCacheFile();
do_load_manifest("data/chrome.manifest");
let url = "chrome://testsearchplugin/locale/searchplugins/";
Services.prefs.setCharPref("browser.search.jarURIs", url);
Services.prefs.setBoolPref("browser.search.loadFromJars", true);
do_check_false(Services.search.isInitialized);
Services.search.init(function search_initialized(aStatus) {
do_check_true(Components.isSuccessCode(aStatus));
do_check_true(Services.search.isInitialized);
// test engines from dir are loaded.
let engines = Services.search.getEngines();
do_check_true(engines.length > 1);
// test jar engine is loaded ok.
let engine = Services.search.getEngineByName("bug645970");
do_check_neq(engine, null);
Services.prefs.clearUserPref("browser.search.jarURIs");
Services.prefs.clearUserPref("browser.search.loadFromJars");
do_test_finished();
});
do_check_false(Services.search.isInitialized);
// test engines from dir are loaded.
let engines = Services.search.getEngines();
do_check_true(engines.length > 1);
do_check_true(Services.search.isInitialized);
// test jar engine is loaded ok.
let engine = Services.search.getEngineByName("bug645970");
do_check_neq(engine, null);
}

View File

@ -27,3 +27,7 @@ support-files =
[test_prefSync.js]
[test_notifications.js]
[test_addEngine_callback.js]
[test_async.js]
[test_sync.js]
[test_sync_fallback.js]
[test_sync_delay_fallback.js]

View File

@ -386,8 +386,8 @@ struct TelemetryHistogram {
uint32_t max;
uint32_t bucketCount;
uint32_t histogramType;
uint16_t id_offset;
uint16_t comment_offset;
uint32_t id_offset;
uint32_t comment_offset;
bool extendedStatisticsOK;
const char *id() const;

View File

@ -76,7 +76,7 @@ def write_histogram_table(histograms):
strtab_name = "gHistogramStringTable"
table.writeDefinition(sys.stdout, strtab_name)
static_assert("sizeof(%s) <= UINT16_MAX" % strtab_name,
static_assert("sizeof(%s) <= UINT32_MAX" % strtab_name,
"index overflow")
# Write out static asserts for histogram data. We'd prefer to perform

View File

@ -583,6 +583,17 @@ let NodeFront = protocol.FrontClass(NodeActor, {
return ret;
},
/**
* Do we use a local target?
* Useful to know if a rawNode is available or not.
*
* This will, one day, be removed. External code should
* not need to know if the target is remote or not.
*/
isLocal_toBeDeprecated: function() {
return !!this.conn._transport._serverConnection;
},
/**
* Get an nsIDOMNode for the given node front. This only works locally,
* and is only intended as a stopgap during the transition to the remote

View File

@ -524,7 +524,9 @@ EHTable::EHTable(const void *aELF, size_t aSize, const std::string &aName)
file.e_ident[EI_DATA] != hostEndian ||
file.e_ident[EI_VERSION] != EV_CURRENT ||
file.e_ident[EI_OSABI] != ELFOSABI_SYSV ||
#ifdef EI_ABIVERSION
file.e_ident[EI_ABIVERSION] != 0 ||
#endif
file.e_machine != EM_ARM ||
file.e_version != EV_CURRENT)
// e_flags?

View File

@ -2163,7 +2163,8 @@ nsNativeThemeCocoa::DrawWidgetBackground(nsRenderingContext* aContext,
case NS_THEME_WINDOW_TITLEBAR: {
NSWindow* win = NativeWindowForFrame(aFrame);
BOOL isMain = [win isMainWindow];
float unifiedToolbarHeight = [(ToolbarWindow*)win unifiedToolbarHeight];
float unifiedToolbarHeight = [win isKindOfClass:[ToolbarWindow class]] ?
[(ToolbarWindow*)win unifiedToolbarHeight] : macRect.size.height;
DrawNativeTitlebar(cgContext, macRect, unifiedToolbarHeight, isMain);
}
break;

View File

@ -492,7 +492,6 @@ MetroInput::OnPointerPressed(UI::Core::ICoreWindow* aSender,
// tracking flags.
mContentConsumingTouch = false;
mRecognizerWantsEvents = true;
mIsFirstTouchMove = true;
mCancelable = true;
mCanceledIds.Clear();
}
@ -560,24 +559,15 @@ MetroInput::OnPointerMoved(UI::Core::ICoreWindow* aSender,
return S_OK;
}
AddPointerMoveDataToRecognizer(aArgs);
// If the point hasn't moved, filter it out per the spec. Pres shell does
// this as well, but we need to know when our first touchmove is going to
// get delivered so we can check the result.
if (!HasPointMoved(touch, currentPoint.Get())) {
// The recognizer needs the intermediate data otherwise it acts flaky
AddPointerMoveDataToRecognizer(aArgs);
return S_OK;
}
// If we've accumulated a batch of pointer moves and we're now on a new batch
// at a new position send the previous batch. (perf opt)
if (!mIsFirstTouchMove && touch->mChanged) {
WidgetTouchEvent* touchEvent =
new WidgetTouchEvent(true, NS_TOUCH_MOVE, mWidget.Get());
InitTouchEventTouchList(touchEvent);
DispatchAsyncTouchEvent(touchEvent);
}
touch = CreateDOMTouch(currentPoint.Get());
touch->mChanged = true;
// replacing old touch point in mTouches map
@ -585,15 +575,8 @@ MetroInput::OnPointerMoved(UI::Core::ICoreWindow* aSender,
WidgetTouchEvent* touchEvent =
new WidgetTouchEvent(true, NS_TOUCH_MOVE, mWidget.Get());
// If this is the first touch move of our session, dispatch it now.
if (mIsFirstTouchMove) {
InitTouchEventTouchList(touchEvent);
DispatchAsyncTouchEvent(touchEvent);
mIsFirstTouchMove = false;
}
AddPointerMoveDataToRecognizer(aArgs);
InitTouchEventTouchList(touchEvent);
DispatchAsyncTouchEvent(touchEvent);
return S_OK;
}

View File

@ -203,7 +203,6 @@ private:
// For example, a set of mousemove, mousedown, and mouseup events might
// be sent if a tap is detected.
bool mContentConsumingTouch;
bool mIsFirstTouchMove;
bool mCancelable;
bool mRecognizerWantsEvents;
nsTArray<uint32_t> mCanceledIds;