mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge m-c to inbound.
This commit is contained in:
commit
200939b5bc
@ -1,4 +1,4 @@
|
||||
{
|
||||
"revision": "5cc807be4ece02c364c1d7f1cd2becbe1a1b441e",
|
||||
"revision": "d995823e80fa7f2e67263699bb6e42a6bcde65ea",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
362
browser/devtools/sourceeditor/codemirror/clike.js
Normal file
362
browser/devtools/sourceeditor/codemirror/clike.js
Normal 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}
|
||||
});
|
||||
}());
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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]
|
||||
|
@ -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();
|
||||
}
|
@ -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 = {};
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -792,10 +792,6 @@ DOMInterfaces = {
|
||||
'nativeType': 'mozilla::dom::PowerManager',
|
||||
},
|
||||
|
||||
'MozStkCommandEvent' : {
|
||||
'nativeType': 'mozilla::dom::StkCommandEvent',
|
||||
},
|
||||
|
||||
'MozTimeManager': {
|
||||
'nativeType': 'mozilla::dom::time::TimeManager',
|
||||
},
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
{
|
||||
/**
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -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
|
@ -6,12 +6,10 @@
|
||||
|
||||
EXPORTS.mozilla.dom += [
|
||||
'IccManager.h',
|
||||
'StkCommandEvent.h',
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
'IccManager.cpp',
|
||||
'StkCommandEvent.cpp',
|
||||
]
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -92,6 +92,7 @@ struct ThreadData
|
||||
uint64_t id;
|
||||
nsString[] participants;
|
||||
uint64_t timestamp;
|
||||
nsString lastMessageSubject;
|
||||
nsString body;
|
||||
uint64_t unreadCount;
|
||||
MessageType lastMessageType;
|
||||
|
301
dom/mobilemessage/tests/marionette/head.js
Normal file
301
dom/mobilemessage/tests/marionette/head.js
Normal 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);
|
||||
}
|
@ -38,3 +38,4 @@ qemu = true
|
||||
[test_getthreads.js]
|
||||
[test_smsc_address.js]
|
||||
[test_dsds_default_service_id.js]
|
||||
[test_thread_subject.js]
|
||||
|
67
dom/mobilemessage/tests/marionette/test_thread_subject.js
Normal file
67
dom/mobilemessage/tests/marionette/test_thread_subject.js
Normal 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")));
|
||||
});
|
@ -189,8 +189,7 @@ CdmaIccInfo.prototype = {
|
||||
|
||||
// nsIDOMMozCdmaIccInfo
|
||||
|
||||
mdn: null,
|
||||
min: null
|
||||
mdn: null
|
||||
};
|
||||
|
||||
function VoicemailInfo() {}
|
||||
|
@ -527,8 +527,7 @@ CdmaIccInfo.prototype = {
|
||||
|
||||
// nsIDOMMozCdmaIccInfo
|
||||
|
||||
mdn: null,
|
||||
min: null
|
||||
mdn: null
|
||||
};
|
||||
|
||||
function RadioInterfaceLayer() {
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
*/
|
||||
|
100
dom/system/gonk/tests/test_ril_worker_sms_cdma.js
Normal file
100
dom/system/gonk/tests/test_ril_worker_sms_cdma.js
Normal 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();
|
||||
});
|
73
dom/system/gonk/tests/test_ril_worker_sms_cdmapduhelper.js
Normal file
73
dom/system/gonk/tests/test_ril_worker_sms_cdmapduhelper.js
Normal 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();
|
||||
});
|
@ -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]
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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',
|
||||
]
|
||||
|
||||
|
@ -266,6 +266,9 @@ public:
|
||||
SetCompositor(nullptr);
|
||||
mAttached = false;
|
||||
mKeepAttached = false;
|
||||
if (mBackendData) {
|
||||
mBackendData->ClearData();
|
||||
}
|
||||
}
|
||||
}
|
||||
bool IsAttached() { return mAttached; }
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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] || [];
|
||||
|
||||
|
36
toolkit/components/search/tests/xpcshell/test_async.js
Normal file
36
toolkit/components/search/tests/xpcshell/test_async.js
Normal 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();
|
||||
});
|
||||
}
|
||||
|
29
toolkit/components/search/tests/xpcshell/test_sync.js
Normal file
29
toolkit/components/search/tests/xpcshell/test_sync.js
Normal 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");
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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]
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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?
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user