Merge m-c to inbound

This commit is contained in:
Wes Kocher 2014-07-21 17:34:59 -07:00
commit adf51a28f6
139 changed files with 7825 additions and 702 deletions

View File

@ -102,3 +102,4 @@ ba2cc1eda988a1614d8986ae145d28e1268409b9 FIREFOX_AURORA_29_BASE
83c9853e136451474dfa6d1aaa60a7fca7d2d83a FIREFOX_AURORA_30_BASE
cfde3603b0206e119abea76fdd6e134b634348f1 FIREFOX_AURORA_31_BASE
16f3cac5e8fe471e12f76d6a94a477b14e78df7c FIREFOX_AURORA_32_BASE
dc23164ba2a289a8b22902e30990c77d9677c214 FIREFOX_AURORA_33_BASE

View File

@ -22,4 +22,4 @@
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
# don't change CLOBBER for WebIDL changes any more.
Clobber to work around bug 959928.
Merge day clobber

View File

@ -0,0 +1,3 @@
# Actor REPL
Simple REPL for a Firefox debugging protocol.

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,264 @@
/* BASICS */
.CodeMirror {
/* Set height, width, borders, and global font properties here */
font-family: monospace;
height: 300px;
}
.CodeMirror-scroll {
/* Set scrolling behaviour here */
overflow: auto;
}
/* PADDING */
.CodeMirror-lines {
padding: 4px 0; /* Vertical padding around content */
}
.CodeMirror pre {
padding: 0 4px; /* Horizontal padding of content */
}
.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
background-color: white; /* The little square between H and V scrollbars */
}
/* GUTTER */
.CodeMirror-gutters {
border-right: 1px solid #ddd;
background-color: #f7f7f7;
white-space: nowrap;
}
.CodeMirror-linenumbers {}
.CodeMirror-linenumber {
padding: 0 3px 0 5px;
min-width: 20px;
text-align: right;
color: #999;
-moz-box-sizing: content-box;
box-sizing: content-box;
}
/* CURSOR */
.CodeMirror div.CodeMirror-cursor {
border-left: 1px solid black;
z-index: 3;
}
/* Shown when moving in bi-directional text */
.CodeMirror div.CodeMirror-secondarycursor {
border-left: 1px solid silver;
}
.CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursor {
width: auto;
border: 0;
background: #7e7;
z-index: 1;
}
/* Can style cursor different in overwrite (non-insert) mode */
.CodeMirror div.CodeMirror-cursor.CodeMirror-overwrite {}
.cm-tab { display: inline-block; }
.CodeMirror-ruler {
border-left: 1px solid #ccc;
position: absolute;
}
/* DEFAULT THEME */
.cm-s-default .cm-keyword {color: #708;}
.cm-s-default .cm-atom {color: #219;}
.cm-s-default .cm-number {color: #164;}
.cm-s-default .cm-def {color: #00f;}
.cm-s-default .cm-variable {color: black;}
.cm-s-default .cm-variable-2 {color: #05a;}
.cm-s-default .cm-variable-3 {color: #085;}
.cm-s-default .cm-property {color: black;}
.cm-s-default .cm-operator {color: black;}
.cm-s-default .cm-comment {color: #a50;}
.cm-s-default .cm-string {color: #a11;}
.cm-s-default .cm-string-2 {color: #f50;}
.cm-s-default .cm-meta {color: #555;}
.cm-s-default .cm-qualifier {color: #555;}
.cm-s-default .cm-builtin {color: #30a;}
.cm-s-default .cm-bracket {color: #997;}
.cm-s-default .cm-tag {color: #170;}
.cm-s-default .cm-attribute {color: #00c;}
.cm-s-default .cm-header {color: blue;}
.cm-s-default .cm-quote {color: #090;}
.cm-s-default .cm-hr {color: #999;}
.cm-s-default .cm-link {color: #00c;}
.cm-negative {color: #d44;}
.cm-positive {color: #292;}
.cm-header, .cm-strong {font-weight: bold;}
.cm-em {font-style: italic;}
.cm-link {text-decoration: underline;}
.cm-s-default .cm-error {color: #f00;}
.cm-invalidchar {color: #f00;}
div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
.CodeMirror-activeline-background {background: #e8f2ff;}
/* STOP */
/* The rest of this file contains styles related to the mechanics of
the editor. You probably shouldn't touch them. */
.CodeMirror {
line-height: 1;
position: relative;
overflow: hidden;
background: white;
color: black;
}
.CodeMirror-scroll {
/* 30px is the magic margin used to hide the element's real scrollbars */
/* See overflow: hidden in .CodeMirror */
margin-bottom: -30px; margin-right: -30px;
padding-bottom: 30px;
height: 100%;
outline: none; /* Prevent dragging from highlighting the element */
position: relative;
-moz-box-sizing: content-box;
box-sizing: content-box;
}
.CodeMirror-sizer {
position: relative;
border-right: 30px solid transparent;
-moz-box-sizing: content-box;
box-sizing: content-box;
}
/* The fake, visible scrollbars. Used to force redraw during scrolling
before actuall scrolling happens, thus preventing shaking and
flickering artifacts. */
.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
position: absolute;
z-index: 6;
display: none;
}
.CodeMirror-vscrollbar {
right: 0; top: 0;
overflow-x: hidden;
overflow-y: scroll;
}
.CodeMirror-hscrollbar {
bottom: 0; left: 0;
overflow-y: hidden;
overflow-x: scroll;
}
.CodeMirror-scrollbar-filler {
right: 0; bottom: 0;
}
.CodeMirror-gutter-filler {
left: 0; bottom: 0;
}
.CodeMirror-gutters {
position: absolute; left: 0; top: 0;
padding-bottom: 30px;
z-index: 3;
}
.CodeMirror-gutter {
white-space: normal;
height: 100%;
-moz-box-sizing: content-box;
box-sizing: content-box;
padding-bottom: 30px;
margin-bottom: -32px;
display: inline-block;
/* Hack to make IE7 behave */
*zoom:1;
*display:inline;
}
.CodeMirror-gutter-elt {
position: absolute;
cursor: default;
z-index: 4;
}
.CodeMirror-lines {
cursor: text;
}
.CodeMirror pre {
/* Reset some styles that the rest of the page might have set */
-moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
border-width: 0;
background: transparent;
font-family: inherit;
font-size: inherit;
margin: 0;
white-space: pre;
word-wrap: normal;
line-height: inherit;
color: inherit;
z-index: 2;
position: relative;
overflow: visible;
}
.CodeMirror-wrap pre {
word-wrap: break-word;
white-space: pre-wrap;
word-break: normal;
}
.CodeMirror-linebackground {
position: absolute;
left: 0; right: 0; top: 0; bottom: 0;
z-index: 0;
}
.CodeMirror-linewidget {
position: relative;
z-index: 2;
overflow: auto;
}
.CodeMirror-widget {}
.CodeMirror-wrap .CodeMirror-scroll {
overflow-x: hidden;
}
.CodeMirror-measure {
position: absolute;
width: 100%;
height: 0;
overflow: hidden;
visibility: hidden;
}
.CodeMirror-measure pre { position: static; }
.CodeMirror div.CodeMirror-cursor {
position: absolute;
visibility: hidden;
border-right: none;
width: 0;
}
.CodeMirror-focused div.CodeMirror-cursor {
visibility: visible;
}
.CodeMirror-selected { background: #d9d9d9; }
.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
.cm-searching {
background: #ffa;
background: rgba(255, 255, 0, .4);
}
/* IE7 hack to prevent it from returning funny offsetTops on the spans */
.CodeMirror span { *vertical-align: text-bottom; }
@media print {
/* Hide the cursor when printing */
.CodeMirror div.CodeMirror-cursor {
visibility: hidden;
}
}

View File

@ -0,0 +1,108 @@
<!-- 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/. -->
<html>
<head>
<link rel="stylesheet" href="./codemirror.css">
<link rel="stylesheet" href="./main.css">
<script src="./codemirror-compressed.js"></script>
<views>
<section class="task cm-s-default">
<pre class="request "></pre>
<pre class="response"><span class="one"></span><span class="two"></span><span class="three"></span></pre>
</section>
</views>
</head>
<body>
<pre class="input"></pre>
</body>
<script>
function debounce(fn, ms) {
var id
return function(...args) {
clearTimeout(id)
id = setTimeout(fn, ms, ...args)
}
}
function Try(fn) {
return function(...args) {
try { return fn(...args) }
catch (error) { return null }
}
}
var parse = Try(JSON.parse)
function send(editor) {
var input = editor.getWrapperElement().parentNode
var code = editor.getValue().trim()
var packet = parse(code)
if (packet) {
var task = document.querySelector("views .task").cloneNode(true)
var request = task.querySelector(".request")
var response = task.querySelector(".response")
input.parentNode.insertBefore(task, input)
CodeMirror.runMode(JSON.stringify(packet, 2, 2),
"application/json",
request)
response.classList.add("pending")
editor.setValue("")
document.activeElement.scrollIntoView()
port.postMessage(packet);
}
}
var editor = CodeMirror(document.querySelector(".input"), {
autofocus: true,
mode: "application/json",
matchBrackets: true,
value: '{"to": "root", "type": "requestTypes"}',
extraKeys: {"Cmd-Enter": send,
"Ctrl-Enter": send}
});
editor.on("change", debounce(function(editor) {
var input = editor.getWrapperElement().parentNode;
if (parse(editor.getValue().trim()))
input.classList.remove("invalid")
else
input.classList.add("invalid")
}, 800))
</script>
<script>
window.addEventListener("message", event => {
console.log("REPL", event);
window.port = event.ports[0];
port.onmessage = onMessage;
});
var onMessage = (event) => {
var packet = event.data;
var code = JSON.stringify(packet, 2, 2);
var input = editor.getWrapperElement().parentNode;
var response = document.querySelector(".task .response.pending")
if (!response) {
message = document.querySelector("views .task").cloneNode(true)
response = message.querySelector(".response")
response.classList.add("message")
input.parentNode.insertBefore(message, input);
}
if (packet.error)
response.classList.add("error");
CodeMirror.runMode(code, "application/json", response);
response.classList.remove("pending");
document.activeElement.scrollIntoView()
};
</script>
</html>

View File

@ -0,0 +1,117 @@
/* 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/. */
body
{
position: absolute;
width: 100%;
margin: 0;
padding: 0;
background: white;
}
pre
{
margin: 0;
}
section
{
border-top: 1px solid rgba(150, 150, 150, 0.5);
}
.CodeMirror {
height: auto;
}
.CodeMirror-scroll {
overflow-y: hidden;
overflow-x: auto;
}
.request,
.response,
.input
{
border-left: 5px solid;
padding-left: 10px;
}
.request:not(:empty),
.response.pending
{
padding: 5px;
}
.input
{
padding-left: 6px;
border-color: lightgreen;
}
.input.invalid
{
border-color: orange;
}
.request
{
border-color: lightgrey;
}
.response
{
border-color: grey;
}
.response.error
{
border-color: red;
}
.response.message
{
border-color: lightblue;
}
.response .one,
.response .two,
.response .three
{
width: 0;
height: auto;
}
.response.pending .one,
.response.pending .two,
.response.pending .three
{
width: 10px;
height: 10px;
background-color: rgba(150, 150, 150, 0.5);
border-radius: 100%;
display: inline-block;
animation: bouncedelay 1.4s infinite ease-in-out;
/* Prevent first frame from flickering when animation starts */
animation-fill-mode: both;
}
.response.pending .one
{
animation-delay: -0.32s;
}
.response.pending .two
{
animation-delay: -0.16s;
}
@keyframes bouncedelay {
0%, 80%, 100% {
transform: scale(0.0);
} 40% {
transform: scale(1.0);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@ -0,0 +1,37 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { Panel } = require("dev/panel");
const { Tool } = require("dev/toolbox");
const { Class } = require("sdk/core/heritage");
const REPLPanel = Class({
extends: Panel,
label: "Actor REPL",
tooltip: "Firefox debugging protocol REPL",
icon: "./robot.png",
url: "./index.html",
setup: function({debuggee}) {
this.debuggee = debuggee;
},
dispose: function() {
this.debuggee = null;
},
onReady: function() {
console.log("repl panel document is interactive");
this.debuggee.start();
this.postMessage("RDP", [this.debuggee]);
},
onLoad: function() {
console.log("repl panel document is fully loaded");
}
});
exports.REPLPanel = REPLPanel;
const replTool = new Tool({
panels: { repl: REPLPanel }
});

View File

@ -0,0 +1,10 @@
{
"name": "actor-repl",
"id": "@actor-repl",
"title": "Actor REPL",
"description": "Actor REPL",
"version": "0.0.1",
"author": "Irakli Gozalishvili",
"main": "./index.js",
"license": "MPL 2.0"
}

View File

@ -0,0 +1,816 @@
/* 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/. */
(function(exports) {
"use strict";
var describe = Object.getOwnPropertyDescriptor;
var Class = fields => {
var constructor = fields.constructor || function() {};
var ancestor = fields.extends || Object;
var descriptor = {};
for (var key of Object.keys(fields))
descriptor[key] = describe(fields, key);
var prototype = Object.create(ancestor.prototype, descriptor);
constructor.prototype = prototype;
prototype.constructor = constructor;
return constructor;
};
var bus = function Bus() {
var parser = new DOMParser();
return parser.parseFromString("<EventTarget/>", "application/xml").documentElement;
}();
var GUID = new WeakMap();
GUID.id = 0;
var guid = x => GUID.get(x);
var setGUID = x => {
GUID.set(x, ++ GUID.id);
};
var Emitter = Class({
extends: EventTarget,
constructor: function() {
this.setupEmitter();
},
setupEmitter: function() {
setGUID(this);
},
addEventListener: function(type, listener, capture) {
bus.addEventListener(type + "@" + guid(this),
listener, capture);
},
removeEventListener: function(type, listener, capture) {
bus.removeEventListener(type + "@" + guid(this),
listener, capture);
}
});
function dispatch(target, type, data) {
var event = new MessageEvent(type + "@" + guid(target), {
bubbles: true,
cancelable: false,
data: data
});
bus.dispatchEvent(event);
}
var supervisedWorkers = new WeakMap();
var supervised = supervisor => {
if (!supervisedWorkers.has(supervisor)) {
supervisedWorkers.set(supervisor, new Map());
supervisor.connection.addActorPool(supervisor);
}
return supervisedWorkers.get(supervisor);
};
var Supervisor = Class({
extends: Emitter,
constructor: function(...params) {
this.setupEmitter(...params);
this.setupSupervisor(...params);
},
Supervisor: function(connection) {
this.connection = connection;
},
/**
* Return the parent pool for this client.
*/
supervisor: function() {
return this.connection.poolFor(this.actorID);
},
/**
* Override this if you want actors returned by this actor
* to belong to a different actor by default.
*/
marshallPool: function() { return this; },
/**
* Add an actor as a child of this pool.
*/
supervise: function(actor) {
if (!actor.actorID)
actor.actorID = this.connection.allocID(actor.actorPrefix ||
actor.typeName);
supervised(this).set(actor.actorID, actor);
return actor;
},
/**
* Remove an actor as a child of this pool.
*/
abandon: function(actor) {
supervised(this).delete(actor.actorID);
},
// true if the given actor ID exists in the pool.
has: function(actorID) {
return supervised(this).has(actorID);
},
// Same as actor, should update debugger connection to use 'actor'
// and then remove this.
get: function(actorID) {
return supervised(this).get(actorID);
},
actor: function(actorID) {
return supervised(this).get(actorID);
},
isEmpty: function() {
return supervised(this).size === 0;
},
/**
* For getting along with the debugger server pools, should be removable
* eventually.
*/
cleanup: function() {
this.destroy();
},
destroy: function() {
var supervisor = this.supervisor();
if (supervisor)
supervisor.abandon(this);
for (var actor of supervised(this).values()) {
if (actor !== this) {
var destroy = actor.destroy;
// Disconnect destroy while we're destroying in case of (misbehaving)
// circular ownership.
if (destroy) {
actor.destroy = null;
destroy.call(actor);
actor.destroy = destroy;
}
}
}
this.connection.removeActorPool(this);
supervised(this).clear();
}
});
var mailbox = new WeakMap();
var clientRequests = new WeakMap();
var inbox = client => mailbox.get(client).inbox;
var outbox = client => mailbox.get(client).outbox;
var requests = client => clientRequests.get(client);
var Receiver = Class({
receive: function(packet) {
if (packet.error)
this.reject(packet.error);
else
this.resolve(this.read(packet));
}
});
var Connection = Class({
constructor: function() {
// Queue of the outgoing messages.
this.outbox = [];
// Map of pending requests.
this.pending = new Map();
this.pools = new Set();
},
isConnected: function() {
return !!this.port
},
connect: function(port) {
this.port = port;
port.addEventListener("message", this);
port.start();
this.flush();
},
addPool: function(pool) {
this.pools.add(pool);
},
removePool: function(pool) {
this.pools.delete(pool);
},
poolFor: function(id) {
for (let pool of this.pools.values()) {
if pool.has(id)
return pool;
}
},
get: function(id) {
var pool = this.poolFor(id);
return pool && pool.get(id);
},
disconnect: function() {
this.port.stop();
this.port = null;
for (var request of this.pending.values()) {
request.catch(new Error("Connection closed"));
}
this.pending.clear();
var requests = this.outbox.splice(0);
for (var request of request) {
requests.catch(new Error("Connection closed"));
}
},
handleEvent: function(event) {
this.receive(event.data);
},
flush: function() {
if (this.isConnected()) {
for (var request of this.outbox) {
if (!this.pending.has(request.to)) {
this.outbox.splice(this.outbox.indexOf(request), 1);
this.pending.set(request.to, request);
this.send(request.packet);
}
}
}
},
send: function(packet) {
this.port.postMessage(packet);
},
request: function(packet) {
return new Promise(function(resolve, reject) {
this.outbox.push({
to: packet.to,
packet: packet,
receive: resolve,
catch: reject
});
this.flush();
});
},
receive: function(packet) {
var { from, type, why } = packet;
var receiver = this.pending.get(from);
if (!receiver) {
console.warn("Unable to handle received packet", data);
} else {
this.pending.delete(from);
if (packet.error)
receiver.catch(packet.error);
else
receiver.receive(packet);
}
this.flush();
},
});
/**
* Base class for client-side actor fronts.
*/
var Client = Class({
extends: Supervisor,
constructor: function(from=null, detail=null, connection=null) {
this.Client(from, detail, connection);
},
Client: function(form, detail, connection) {
this.Supervisor(connection);
if (form) {
this.actorID = form.actor;
this.from(form, detail);
}
},
connect: function(port) {
this.connection = new Connection(port);
},
actorID: null,
actor: function() {
return this.actorID;
},
/**
* Update the actor from its representation.
* Subclasses should override this.
*/
form: function(form) {
},
/**
* Method is invokeid when packet received constitutes an
* event. By default such packets are demarshalled and
* dispatched on the client instance.
*/
dispatch: function(packet) {
},
/**
* Method is invoked when packet is returned in response to
* a request. By default respond delivers response to a first
* request in a queue.
*/
read: function(input) {
throw new TypeError("Subclass must implement read method");
},
write: function(input) {
throw new TypeError("Subclass must implement write method");
},
respond: function(packet) {
var [resolve, reject] = requests(this).shift();
if (packet.error)
reject(packet.error);
else
resolve(this.read(packet));
},
receive: function(packet) {
if (this.isEventPacket(packet)) {
this.dispatch(packet);
}
else if (requests(this).length) {
this.respond(packet);
}
else {
this.catch(packet);
}
},
send: function(packet) {
Promise.cast(packet.to || this.actor()).then(id => {
packet.to = id;
this.connection.send(packet);
})
},
request: function(packet) {
return this.connection.request(packet);
}
});
var Destructor = method => {
return function(...args) {
return method.apply(this, args).then(result => {
this.destroy();
return result;
});
};
};
var Profiled = (method, id) => {
return function(...args) {
var start = new Date();
return method.apply(this, args).then(result => {
var end = new Date();
this.telemetry.add(id, +end - start);
return result;
});
};
};
var Method = (request, response) => {
return response ? new BidirectionalMethod(request, response) :
new UnidirecationalMethod(request);
};
var UnidirecationalMethod = request => {
return function(...args) {
var packet = request.write(args, this);
this.connection.send(packet);
return Promise.resolve(void(0));
};
};
var BidirectionalMethod = (request, response) => {
return function(...args) {
var packet = request.write(args, this);
return this.connection.request(packet).then(packet => {
return response.read(packet, this);
});
};
};
Client.from = ({category, typeName, methods, events}) => {
var proto = {
constructor: function(...args) {
this.Client(...args);
},
extends: Client,
name: typeName
};
methods.forEach(({telemetry, request, response, name, oneway, release}) => {
var [reader, writer] = oneway ? [, new Request(request)] :
[new Request(request), new Response(response)];
var method = new Method(request, response);
var profiler = telemetry ? new Profiler(method) : method;
var destructor = release ? new Destructor(profiler) : profiler;
proto[name] = destructor;
});
return Class(proto);
};
var defineType = (client, descriptor) => {
var type = void(0)
if (typeof(descriptor) === "string") {
if (name.indexOf(":") > 0)
type = makeCompoundType(descriptor);
else if (name.indexOf("#") > 0)
type = new ActorDetail(descriptor);
else if (client.specification[descriptor])
type = makeCategoryType(client.specification[descriptor]);
} else {
type = makeCategoryType(descriptor);
}
if (type)
client.types.set(type.name, type);
else
throw TypeError("Invalid type: " + descriptor);
};
var makeCompoundType = name => {
var index = name.indexOf(":");
var [baseType, subType] = [name.slice(0, index), parts.slice(1)];
return baseType === "array" ? new ArrayOf(subType) :
baseType === "nullable" ? new Maybe(subType) :
null;
};
var makeCategoryType = (descriptor) => {
var { category } = descriptor;
return category === "dict" ? new Dictionary(descriptor) :
category === "actor" ? new Actor(descriptor) :
null;
};
var typeFor = (client, type="primitive") => {
if (!client.types.has(type))
defineType(client, type);
return client.types.get(type);
};
var Client = Class({
constructor: function() {
},
setupTypes: function(specification) {
this.specification = specification;
this.types = new Map();
},
read: function(input, type) {
return typeFor(this, type).read(input, this);
},
write: function(input, type) {
return typeFor(this, type).write(input, this);
}
});
var Type = Class({
get name() {
return this.category ? this.category + ":" + this.type :
this.type;
},
read: function(input, client) {
throw new TypeError("`Type` subclass must implement `read`");
},
write: function(input, client) {
throw new TypeError("`Type` subclass must implement `write`");
}
});
var Primitve = Class({
extends: Type,
constuctor: function(type) {
this.type = type;
},
read: function(input, client) {
return input;
},
write: function(input, client) {
return input;
}
});
var Maybe = Class({
extends: Type,
category: "nullable",
constructor: function(type) {
this.type = type;
},
read: function(input, client) {
return input === null ? null :
input === void(0) ? void(0) :
client.read(input, this.type);
},
write: function(input, client) {
return input === null ? null :
input === void(0) ? void(0) :
client.write(input, this.type);
}
});
var ArrayOf = Class({
extends: Type,
category: "array",
constructor: function(type) {
this.type = type;
},
read: function(input, client) {
return input.map($ => client.read($, this.type));
},
write: function(input, client) {
return input.map($ => client.write($, this.type));
}
});
var Dictionary = Class({
exteds: Type,
category: "dict",
get name() { return this.type; },
constructor: function({typeName, specializations}) {
this.type = typeName;
this.types = specifications;
},
read: function(input, client) {
var output = {};
for (var key in input) {
output[key] = client.read(input[key], this.types[key]);
}
return output;
},
write: function(input, client) {
var output = {};
for (var key in input) {
output[key] = client.write(value, this.types[key]);
}
return output;
}
});
var Actor = Class({
exteds: Type,
category: "actor",
get name() { return this.type; },
constructor: function({typeName}) {
this.type = typeName;
},
read: function(input, client, detail) {
var id = value.actor;
var actor = void(0);
if (client.connection.has(id)) {
return client.connection.get(id).form(input, detail, client);
} else {
actor = Client.from(detail, client);
actor.actorID = id;
client.supervise(actor);
}
},
write: function(input, client, detail) {
if (input instanceof Actor) {
if (!input.actorID) {
client.supervise(input);
}
return input.from(detail);
}
return input.actorID;
}
});
var Root = Client.from({
"category": "actor",
"typeName": "root",
"methods": [
{"name": "listTabs",
"request": {},
"response": {
}
},
{"name": "listAddons"
},
{"name": "echo",
},
{"name": "protocolDescription",
}
]
});
var ActorDetail = Class({
extends: Actor,
constructor: function(name, actor, detail) {
this.detail = detail;
this.actor = actor;
},
read: function(input, client) {
this.actor.read(input, client, this.detail);
},
write: function(input, client) {
this.actor.write(input, client, this.detail);
}
});
var registeredLifetimes = new Map();
var LifeTime = Class({
extends: Type,
category: "lifetime",
constructor: function(lifetime, type) {
this.name = lifetime + ":" + type.name;
this.field = registeredLifetimes.get(lifetime);
},
read: function(input, client) {
return this.type.read(input, client[this.field]);
},
write: function(input, client) {
return this.type.write(input, client[this.field]);
}
});
var primitive = new Primitve("primitive");
var string = new Primitve("string");
var number = new Primitve("number");
var boolean = new Primitve("boolean");
var json = new Primitve("json");
var array = new Primitve("array");
var TypedValue = Class({
extends: Type,
constructor: function(name, type) {
this.TypedValue(name, type);
},
TypedValue: function(name, type) {
this.name = name;
this.type = type;
},
read: function(input, client) {
return this.client.read(input, this.type);
},
write: function(input, client) {
return this.client.write(input, this.type);
}
});
var Return = Class({
extends: TypedValue,
constructor: function(type) {
this.type = type
}
});
var Argument = Class({
extends: TypedValue,
constructor: function(...args) {
this.Argument(...args);
},
Argument: function(index, type) {
this.index = index;
this.TypedValue("argument[" + index + "]", type);
},
read: function(input, client, target) {
return target[this.index] = client.read(input, this.type);
}
});
var Option = Class({
extends: Argument,
constructor: function(...args) {
return this.Argument(...args);
},
read: function(input, client, target, name) {
var param = target[this.index] || (target[this.index] = {});
param[name] = input === void(0) ? input : client.read(input, this.type);
},
write: function(input, client, name) {
var value = input && input[name];
return value === void(0) ? value : client.write(value, this.type);
}
});
var Request = Class({
extends: Type,
constructor: function(template={}) {
this.type = template.type;
this.template = template;
this.params = findPlaceholders(template, Argument);
},
read: function(packet, client) {
var args = [];
for (var param of this.params) {
var {placeholder, path} = param;
var name = path[path.length - 1];
placeholder.read(getPath(packet, path), client, args, name);
// TODO:
// args[placeholder.index] = placeholder.read(query(packet, path), client);
}
return args;
},
write: function(input, client) {
return JSON.parse(JSON.stringify(this.template, (key, value) => {
return value instanceof Argument ? value.write(input[value.index],
client, key) :
value;
}));
}
});
var Response = Class({
extends: Type,
constructor: function(template={}) {
this.template = template;
var [x] = findPlaceholders(template, Return);
var {placeholder, path} = x;
this.return = placeholder;
this.path = path;
},
read: function(packet, client) {
var value = query(packet, this.path);
return this.return.read(value, client);
},
write: function(input, client) {
return JSON.parse(JSON.stringify(this.template, (key, value) => {
return value instanceof Return ? value.write(input) :
input
}));
}
});
// Returns array of values for the given object.
var values = object => Object.keys(object).map(key => object[key]);
// Returns [key, value] pairs for the given object.
var pairs = object => Object.keys(object).map(key => [key, object[key]]);
// Queries an object for the field nested with in it.
var query = (object, path) => path.reduce((object, entry) => object && object[entry],
object);
var Root = Client.from({
"category": "actor",
"typeName": "root",
"methods": [
{
"name": "echo",
"request": {
"string": { "_arg": 0, "type": "string" }
},
"response": {
"string": { "_retval": "string" }
}
},
{
"name": "listTabs",
"request": {},
"response": { "_retval": "tablist" }
},
{
"name": "actorDescriptions",
"request": {},
"response": { "_retval": "json" }
}
],
"events": {
"tabListChanged": {}
}
});
var Tab = Client.from({
"category": "dict",
"typeName": "tab",
"specifications": {
"title": "string",
"url": "string",
"outerWindowID": "number",
"console": "console",
"inspectorActor": "inspector",
"callWatcherActor": "call-watcher",
"canvasActor": "canvas",
"webglActor": "webgl",
"webaudioActor": "webaudio",
"styleSheetsActor": "stylesheets",
"styleEditorActor": "styleeditor",
"storageActor": "storage",
"gcliActor": "gcli",
"memoryActor": "memory",
"eventLoopLag": "eventLoopLag"
"trace": "trace", // missing
}
});
var tablist = Client.from({
"category": "dict",
"typeName": "tablist",
"specializations": {
"selected": "number",
"tabs": "array:tab"
}
});
})(this);

View File

@ -0,0 +1,50 @@
<!-- 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/. -->
<html>
<head>
<script src="resource://sdk/dev/volcan.js"></script>
<script src="./task.js"></script>
</head>
<body>
</body>
<script>
const wait = (target, type, capture) => new Promise((resolve, reject) => {
const listener = event => {
target.removeEventListener(type, listener, capture);
resolve(event);
};
target.addEventListener(type, listener, capture);
});
const display = message =>
document.body.innerHTML += message + "<br/>";
Task.spawn(function*() {
var event = yield wait(window, "message");
var port = event.ports[0];
display("Port received");
var root = yield volcan.connect(port);
display("Connected to a debugger");
var message = yield root.echo("hello")
display("Received echo for: " + message);
var list = yield root.listTabs();
display("You have " + list.tabs.length + " open tabs");
var activeTab = list.tabs[list.selected];
display("Your active tab url is: " + activeTab.url);
var sheets = yield activeTab.styleSheetsActor.getStyleSheets();
display("Page in active tab has " + sheets.length + " stylesheets");
});
</script>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -0,0 +1,28 @@
/* 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/. */
(function(exports) {
"use strict";
const spawn = (task, ...args) => {
return new Promise((resolve, reject) => {
try {
const routine = task(...args);
const raise = error => routine.throw(error);
const step = data => {
const { done, value } = routine.next(data);
if (done)
resolve(value);
else
Promise.resolve(value).then(step, raise);
}
step();
} catch(error) {
reject(error);
}
});
}
exports.spawn = spawn;
})(Task = {});

View File

@ -0,0 +1,33 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { Panel } = require("dev/panel");
const { Tool } = require("dev/toolbox");
const { Class } = require("sdk/core/heritage");
const LadybugPanel = Class({
extends: Panel,
label: "Ladybug",
tooltip: "Debug client example",
icon: "./plugin.png",
url: "./index.html",
setup: function({debuggee}) {
this.debuggee = debuggee;
},
dispose: function() {
delete this.debuggee;
},
onReady: function() {
this.debuggee.start();
this.postMessage("RDP", [this.debuggee]);
},
});
exports.LadybugPanel = LadybugPanel;
const ladybug = new Tool({
panels: { ladybug: LadybugPanel }
});

View File

@ -0,0 +1,10 @@
{
"name": "debug-client",
"id": "@debug-client",
"title": "Debug client",
"description": "Example debug client",
"version": "0.0.1",
"author": "Irakli Gozalishvili",
"main": "./index.js",
"license": "MPL 2.0"
}

View File

@ -0,0 +1,95 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
module.metadata = {
"stability": "experimental"
};
const { Cu } = require("chrome");
const { Class } = require("../sdk/core/heritage");
const { MessagePort, MessageChannel } = require("../sdk/messaging");
const { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
const outputs = new WeakMap();
const inputs = new WeakMap();
const targets = new WeakMap();
const transports = new WeakMap();
const inputFor = port => inputs.get(port);
const outputFor = port => outputs.get(port);
const transportFor = port => transports.get(port);
const fromTarget = target => {
const debuggee = new Debuggee();
const { port1, port2 } = new MessageChannel();
inputs.set(debuggee, port1);
outputs.set(debuggee, port2);
targets.set(debuggee, target);
return debuggee;
};
exports.fromTarget = fromTarget;
const Debuggee = Class({
extends: MessagePort.prototype,
close: function() {
const server = transportFor(this);
if (server) {
transports.delete(this);
server.close();
}
outputFor(this).close();
},
start: function() {
const target = targets.get(this);
if (target.isLocalTab) {
// Since a remote protocol connection will be made, let's start the
// DebuggerServer here, once and for all tools.
if (!DebuggerServer.initialized) {
DebuggerServer.init();
DebuggerServer.addBrowserActors();
}
transports.set(this, DebuggerServer.connectPipe());
}
// TODO: Implement support for remote connections (See Bug 980421)
else {
throw Error("Remote targets are not yet supported");
}
// pipe messages send to the debuggee to an actual
// server via remote debugging protocol transport.
inputFor(this).addEventListener("message", ({data}) =>
transportFor(this).send(data));
// pipe messages received from the remote debugging
// server transport onto the this debuggee.
transportFor(this).hooks = {
onPacket: packet => inputFor(this).postMessage(packet),
onClosed: () => inputFor(this).close()
};
inputFor(this).start();
outputFor(this).start();
},
postMessage: function(data) {
return outputFor(this).postMessage(data);
},
get onmessage() {
return outputFor(this).onmessage;
},
set onmessage(onmessage) {
outputFor(this).onmessage = onmessage;
},
addEventListener: function(...args) {
return outputFor(this).addEventListener(...args);
},
removeEventListener: function(...args) {
return outputFor(this).removeEventListener(...args);
}
});
exports.Debuggee = Debuggee;

View File

@ -0,0 +1,115 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
(function({content, sendSyncMessage, addMessageListener, sendAsyncMessage}) {
const Cc = Components.classes;
const Ci = Components.interfaces;
const observerService = Cc["@mozilla.org/observer-service;1"]
.getService(Ci.nsIObserverService);
const channels = new Map();
const handles = new WeakMap();
// Takes remote port handle and creates a local one.
// also set's up a messaging channel between them.
// This is temporary workaround until Bug 914974 is fixed
// and port can be transfered through message manager.
const demarshal = (handle) => {
if (handle.type === "MessagePort") {
if (!channels.has(handle.id)) {
const channel = new content.MessageChannel();
channels.set(handle.id, channel);
handles.set(channel.port1, handle);
channel.port1.onmessage = onOutPort;
}
return channels.get(handle.id).port2;
}
return null;
};
const onOutPort = event => {
const handle = handles.get(event.target);
sendAsyncMessage("sdk/port/message", {
port: handle,
message: event.data
});
};
const onInPort = ({data}) => {
const channel = channels.get(data.port.id);
if (channel)
channel.port1.postMessage(data.message);
};
const onOutEvent = event =>
sendSyncMessage("sdk/event/" + event.type,
{ type: event.type,
data: event.data });
const onInMessage = (message) => {
const {type, data, origin, bubbles, cancelable, ports} = message.data;
const event = new content.MessageEvent(type, {
bubbles: bubbles,
cancelable: cancelable,
data: data,
origin: origin,
target: content,
source: content,
ports: ports.map(demarshal)
});
content.dispatchEvent(event);
};
const onReady = event => {
channels.clear();
};
addMessageListener("sdk/event/message", onInMessage);
addMessageListener("sdk/port/message", onInPort);
const observer = {
observe: (document, topic, data) => {
// When frame associated with message manager is removed from document `docShell`
// is set to `null` but observer is still kept alive. At this point accesing
// `content.document` throws "can't access dead object" exceptions. In order to
// avoid leaking observer and logged errors observer is going to be removed when
// `docShell` is set to `null`.
if (!docShell) {
observerService.removeObserver(observer, topic);
}
else if (document === content.document) {
if (topic === "content-document-interactive") {
sendAsyncMessage("sdk/event/ready", {
type: "ready",
readyState: document.readyState,
uri: document.documentURI
});
}
if (topic === "content-document-loaded") {
sendAsyncMessage("sdk/event/load", {
type: "load",
readyState: document.readyState,
uri: document.documentURI
});
}
if (topic === "content-page-hidden") {
channels.clear();
sendAsyncMessage("sdk/event/unload", {
type: "unload",
readyState: "uninitialized",
uri: document.documentURI
});
}
}
}
};
observerService.addObserver(observer, "content-document-interactive", false);
observerService.addObserver(observer, "content-document-loaded", false);
observerService.addObserver(observer, "content-page-hidden", false);
})(this);

View File

@ -0,0 +1,223 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
module.metadata = {
"stability": "experimental"
};
const { Cu } = require("chrome");
const { Class } = require("../sdk/core/heritage");
const { curry } = require("../sdk/lang/functional");
const { EventTarget } = require("../sdk/event/target");
const { Disposable, setup, dispose } = require("../sdk/core/disposable");
const { emit, off, setListeners } = require("../sdk/event/core");
const { when } = require("../sdk/event/utils");
const { getFrameElement } = require("../sdk/window/utils");
const { contract, validate } = require("../sdk/util/contract");
const { data: { url: resolve }} = require("../sdk/self");
const { identify } = require("../sdk/ui/id");
const { isLocalURL, URL } = require("../sdk/url");
const { defer } = require("../sdk/core/promise");
const { encode } = require("../sdk/base64");
const { marshal, demarshal } = require("./ports");
const { fromTarget } = require("./debuggee");
const { removed } = require("../sdk/dom/events");
const { id: addonID } = require("../sdk/self");
const OUTER_FRAME_URI = module.uri.replace(/\.js$/, ".html");
const FRAME_SCRIPT = module.uri.replace("/panel.js", "/frame-script.js");
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
const HTML_NS = "http://www.w3.org/1999/xhtml";
const makeID = name =>
("dev-panel-" + addonID + "-" + name).
split("/").join("-").
split(".").join("-").
split(" ").join("-").
replace(/[^A-Za-z0-9_\-]/g, "");
// Weak mapping between `Panel` instances and their frame's
// `nsIMessageManager`.
const managers = new WeakMap();
// Return `nsIMessageManager` for the given `Panel` instance.
const managerFor = x => managers.get(x);
// Weak mappinging between iframe's and their owner
// `Panel` instances.
const panels = new WeakMap();
const panelFor = frame => panels.get(frame);
// Weak mapping between panels and debugees they're targeting.
const debuggees = new WeakMap();
const debuggeeFor = panel => debuggees.get(panel);
const setAttributes = (node, attributes) => {
for (var key in attributes)
node.setAttribute(key, attributes[key]);
};
const onStateChange = ({target, data}) => {
const panel = panelFor(target);
panel.readyState = data.readyState;
emit(panel, data.type, { target: panel, type: data.type });
};
// port event listener on the message manager that demarshalls
// and forwards to the actual receiver. This is a workaround
// until Bug 914974 is fixed.
const onPortMessage = ({data, target}) => {
const port = demarshal(target, data.port);
if (port)
port.postMessage(data.message);
};
// When frame is removed from the toolbox destroy panel
// associated with it to release all the resources.
const onFrameRemove = frame => {
panelFor(frame).destroy();
};
const onFrameInited = frame => {
frame.style.visibility = "visible";
}
const inited = frame => new Promise(resolve => {
const { messageManager } = frame.frameLoader;
const listener = message => {
messageManager.removeMessageListener("sdk/event/ready", listener);
resolve(frame);
};
messageManager.addMessageListener("sdk/event/ready", listener);
});
const getTarget = ({target}) => target;
const Panel = Class({
extends: Disposable,
implements: [EventTarget],
get id() {
return makeID(this.name || this.label);
},
readyState: "uninitialized",
ready: function() {
const { readyState } = this;
const isReady = readyState === "complete" ||
readyState === "interactive";
return isReady ? Promise.resolve(this) :
when(this, "ready").then(getTarget);
},
loaded: function() {
const { readyState } = this;
const isLoaded = readyState === "complete";
return isLoaded ? Promise.resolve(this) :
when(this, "load").then(getTarget);
},
unloaded: function() {
const { readyState } = this;
const isUninitialized = readyState === "uninitialized";
return isUninitialized ? Promise.resolve(this) :
when(this, "unload").then(getTarget);
},
postMessage: function(data, ports) {
const manager = managerFor(this);
manager.sendAsyncMessage("sdk/event/message", {
type: "message",
bubbles: false,
cancelable: false,
data: data,
origin: this.url,
ports: ports.map(marshal(manager))
});
}
});
exports.Panel = Panel;
validate.define(Panel, contract({
label: {
is: ["string"],
msg: "The `option.label` must be a provided"
},
tooltip: {
is: ["string", "undefined"],
msg: "The `option.tooltip` must be a string"
},
icon: {
is: ["string"],
map: x => x && resolve(x),
ok: x => isLocalURL(x),
msg: "The `options.icon` must be a valid local URI."
},
url: {
map: x => resolve(x.toString()),
is: ["string"],
ok: x => isLocalURL(x),
msg: "The `options.url` must be a valid local URI."
}
}));
setup.define(Panel, (panel, {window, toolbox, url}) => {
// Hack: Given that iframe created by devtools API is no good for us,
// we obtain original iframe and replace it with the one that has
// desired configuration.
const original = getFrameElement(window);
const frame = original.cloneNode(true);
setAttributes(frame, {
"src": url,
"sandbox": "allow-scripts",
// It would be great if we could allow remote iframes for sandboxing
// panel documents in a content process, but for now platform implementation
// is buggy on linux so this is disabled.
// "remote": true,
"type": "content",
"transparent": true,
"seamless": "seamless"
});
original.parentNode.replaceChild(frame, original);
frame.style.visibility = "hidden";
// associate panel model with a frame view.
panels.set(frame, panel);
const debuggee = fromTarget(toolbox.target);
// associate debuggee with a panel.
debuggees.set(panel, debuggee);
// Setup listeners for the frame message manager.
const { messageManager } = frame.frameLoader;
messageManager.addMessageListener("sdk/event/ready", onStateChange);
messageManager.addMessageListener("sdk/event/load", onStateChange);
messageManager.addMessageListener("sdk/event/unload", onStateChange);
messageManager.addMessageListener("sdk/port/message", onPortMessage);
messageManager.loadFrameScript(FRAME_SCRIPT, false);
managers.set(panel, messageManager);
// destroy panel if frame is removed.
removed(frame).then(onFrameRemove);
// show frame when it is initialized.
inited(frame).then(onFrameInited);
// set listeners if there are ones defined on the prototype.
setListeners(panel, Object.getPrototypeOf(panel));
panel.setup({ debuggee: debuggee });
});
dispose.define(Panel, function(panel) {
debuggeeFor(panel).close();
debuggees.delete(panel);
managers.delete(panel);
panel.readyState = "destroyed";
panel.dispose();
});

View File

@ -0,0 +1,64 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
module.metadata = {
"stability": "experimental"
};
// This module provides `marshal` and `demarshal` functions
// that can be used to send MessagePort's over `nsIFrameMessageManager`
// until Bug 914974 is fixed.
const { add, iterator } = require("../sdk/lang/weak-set");
const { curry } = require("../sdk/lang/functional");
let id = 0;
const ports = new WeakMap();
// Takes `nsIFrameMessageManager` and `MessagePort` instances
// and returns a handle representing given `port`. Messages
// received on given `port` will be forwarded to a message
// manager under `sdk/port/message` and messages like:
// { port: { type: "MessagePort", id: 2}, data: data }
// Where id is an identifier associated with a given `port`
// and `data` is an `event.data` received on port.
const marshal = curry((manager, port) => {
if (!ports.has(port)) {
id = id + 1;
const handle = {type: "MessagePort", id: id};
// Bind id to the given port
ports.set(port, handle);
// Obtain a weak reference to a port.
add(exports, port);
port.onmessage = event => {
manager.sendAsyncMessage("sdk/port/message", {
port: handle,
message: event.data
});
};
return handle;
}
return ports.get(port);
});
exports.marshal = marshal;
// Takes `nsIFrameMessageManager` instance and a handle returned
// `marshal(manager, port)` returning a `port` that was passed
// to it. Note that `port` may be GC-ed in which case returned
// value will be `null`.
const demarshal = curry((manager, {type, id}) => {
if (type === "MessagePort") {
for (let port of iterator(exports)) {
if (id === ports.get(port).id)
return port;
}
}
return null;
});
exports.demarshal = demarshal;

View File

@ -0,0 +1,75 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
module.metadata = {
"stability": "experimental"
};
const { Cu, Cc, Ci } = require("chrome");
const { Class } = require("../sdk/core/heritage");
const { Disposable, setup } = require("../sdk/core/disposable");
const { contract, validate } = require("../sdk/util/contract");
const { each, pairs, values } = require("../sdk/util/sequence");
const { gDevTools } = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
// This is temporary workaround to allow loading of the developer tools client - volcan
// into a toolbox panel, this hack won't be necessary as soon as devtools patch will be
// shipped in nightly, after which it can be removed. Bug 1038517
const registerSDKURI = () => {
const ioService = Cc['@mozilla.org/network/io-service;1']
.getService(Ci.nsIIOService);
const resourceHandler = ioService.getProtocolHandler("resource")
.QueryInterface(Ci.nsIResProtocolHandler);
const uri = module.uri.replace("dev/toolbox.js", "");
resourceHandler.setSubstitution("sdk", ioService.newURI(uri, null, null));
};
registerSDKURI();
const Tool = Class({
extends: Disposable,
setup: function(params={}) {
const { panels } = validate(this, params);
this.panels = panels;
each(([key, Panel]) => {
const { url, label, tooltip, icon } = validate(Panel.prototype);
const { id } = Panel.prototype;
gDevTools.registerTool({
id: id,
url: "about:blank",
label: label,
tooltip: tooltip,
icon: icon,
isTargetSupported: target => target.isLocalTab,
build: (window, toolbox) => {
const panel = new Panel();
setup(panel, { window: window,
toolbox: toolbox,
url: url });
return panel.ready();
}
});
}, pairs(panels));
},
dispose: function() {
each(Panel => gDevTools.unregisterTool(Panel.prototype.id),
values(this.panels));
}
});
validate.define(Tool, contract({
panels: {
is: ["object", "undefined"]
}
}));
exports.Tool = Tool;

View File

@ -0,0 +1,38 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { Cu } = require("chrome");
const { gDevTools } = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
const { getActiveTab } = require("../sdk/tabs/utils");
const { getMostRecentBrowserWindow } = require("../sdk/window/utils");
const targetFor = target => {
target = target || getActiveTab(getMostRecentBrowserWindow());
return devtools.TargetFactory.forTab(target);
};
const getCurrentPanel = toolbox => toolbox.getCurrentPanel();
exports.getCurrentPanel = getCurrentPanel;
const openToolbox = (id, tab) => {
id = id.prototype.id || id.id || id;
return gDevTools.showToolbox(targetFor(tab), id);
};
exports.openToolbox = openToolbox;
const closeToolbox = tab => gDevTools.closeToolbox(targetFor(tab));
exports.closeToolbox = closeToolbox;
const getToolbox = tab => gDevTools.getToolbox(targetFor(tab));
exports.getToolbox = getToolbox;
const openToolboxPanel = (id, tab) => {
id = id.prototype.id || id.id || id;
return gDevTools.showToolbox(targetFor(tab), id).then(getCurrentPanel);
};
exports.openToolboxPanel = openToolboxPanel;

File diff suppressed because one or more lines are too long

View File

@ -7,6 +7,7 @@ module.metadata = {
};
const { Cc, Ci } = require('chrome');
const { isNative } = require('@loader/options');
const { descriptor, Sandbox, evaluate, main, resolveURI } = require('toolkit/loader');
const { once } = require('../system/events');
const { exit, env, staticArgs } = require('../system');
@ -104,7 +105,8 @@ function startup(reason, options) {
require('../l10n/loader').
load(rootURI).
then(null, function failure(error) {
console.info("Error while loading localization: " + error.message);
if (!isNative)
console.info("Error while loading localization: " + error.message);
}).
then(function onLocalizationReady(data) {
// Exports data to a pseudo module so that api-utils/l10n/core

View File

@ -9,81 +9,50 @@ module.metadata = {
};
const { EventEmitter } = require('../deprecated/events');
const { validateOptions } = require('../deprecated/api-utils');
const { isValidURI, URL } = require('../url');
const { isValidURI, isLocalURL, URL } = require('../url');
const file = require('../io/file');
const { contract } = require('../util/contract');
const { isString, instanceOf } = require('../lang/type');
const { isString, isNil, instanceOf } = require('../lang/type');
const { validateOptions,
string, array, object, either, required } = require('../deprecated/api-utils');
const LOCAL_URI_SCHEMES = ['resource', 'data'];
const isJSONable = (value) => {
try {
JSON.parse(JSON.stringify(value));
} catch (e) {
return false;
}
return true;
};
// Returns `null` if `value` is `null` or `undefined`, otherwise `value`.
function ensureNull(value) value == null ? null : value
const isValidScriptFile = (value) =>
(isString(value) || instanceOf(value, URL)) && isLocalURL(value);
// map of property validations
const valid = {
contentURL: {
map: function(url) !url ? ensureNull(url) : url.toString(),
is: ['undefined', 'null', 'string'],
ok: function (url) {
if (url === null)
return true;
return isValidURI(url);
},
is: either(string, object),
ok: url => isNil(url) || isLocalURL(url) || isValidURI(url),
msg: 'The `contentURL` option must be a valid URL.'
},
contentScriptFile: {
is: ['undefined', 'null', 'string', 'array', 'object'],
map: ensureNull,
ok: function(value) {
if (value === null)
return true;
value = [].concat(value);
// Make sure every item is a string or an
// URL instance, and also a local file URL.
return value.every(function (item) {
if (!isString(item) && !(item instanceof URL))
return false;
try {
return ~LOCAL_URI_SCHEMES.indexOf(URL(item).scheme);
}
catch(e) {
return false;
}
});
},
is: either(string, object, array),
ok: value => isNil(value) || [].concat(value).every(isValidScriptFile),
msg: 'The `contentScriptFile` option must be a local URL or an array of URLs.'
},
contentScript: {
is: ['undefined', 'null', 'string', 'array'],
map: ensureNull,
ok: function(value) {
return !Array.isArray(value) || value.every(
function(item) { return typeof item === 'string' }
);
},
is: either(string, array),
ok: value => isNil(value) || [].concat(value).every(isString),
msg: 'The `contentScript` option must be a string or an array of strings.'
},
contentScriptWhen: {
is: ['string'],
ok: function(value) { return ~['start', 'ready', 'end'].indexOf(value) },
map: function(value) {
return value || 'end';
},
is: required(string),
map: value => value || 'end',
ok: value => ~['start', 'ready', 'end'].indexOf(value),
msg: 'The `contentScriptWhen` option must be either "start", "ready" or "end".'
},
contentScriptOptions: {
ok: function(value) {
if ( value === undefined ) { return true; }
try { JSON.parse( JSON.stringify( value ) ); } catch(e) { return false; }
return true;
},
map: function(value) 'undefined' === getTypeOf(value) ? null : value,
ok: value => isNil(value) || isJSONable(value),
msg: 'The contentScriptOptions should be a jsonable value.'
}
};

View File

@ -20,7 +20,7 @@ const { merge } = require('../util/object');
const { getTabForContentWindow } = require('../tabs/utils');
const { getInnerId } = require('../window/utils');
const { PlainTextConsole } = require('../console/plain-text');
const { data } = require('../self');
// WeakMap of sandboxes so we can access private values
const sandboxes = new WeakMap();
@ -247,9 +247,12 @@ const WorkerSandbox = Class({
// The order of `contentScriptFile` and `contentScript` evaluation is
// intentional, so programs can load libraries like jQuery from script URLs
// and use them in scripts.
let contentScriptFile = ('contentScriptFile' in worker) ? worker.contentScriptFile
let contentScriptFile = ('contentScriptFile' in worker)
? worker.contentScriptFile
: null,
contentScript = ('contentScript' in worker) ? worker.contentScript : null;
contentScript = ('contentScript' in worker)
? worker.contentScript
: null;
if (contentScriptFile)
importScripts.apply(null, [this].concat(contentScriptFile));
@ -285,7 +288,8 @@ exports.WorkerSandbox = WorkerSandbox;
function importScripts (workerSandbox, ...urls) {
let { worker, sandbox } = modelFor(workerSandbox);
for (let i in urls) {
let contentScriptFile = urls[i];
let contentScriptFile = data.url(urls[i]);
try {
let uri = URL(contentScriptFile);
if (uri.scheme === 'resource')

View File

@ -8,13 +8,14 @@ module.metadata = {
};
let { merge } = require('../util/object');
let assetsURI = require('../self').data.url();
let { data } = require('../self');
let assetsURI = data.url();
let isArray = Array.isArray;
let method = require('../../method/core');
function isAddonContent({ contentURL }) {
return typeof(contentURL) === 'string' && contentURL.indexOf(assetsURI) === 0;
}
const isAddonContent = ({ contentURL }) =>
contentURL && data.url(contentURL).startsWith(assetsURI);
exports.isAddonContent = isAddonContent;
function hasContentScript({ contentScript, contentScriptFile }) {

View File

@ -25,6 +25,7 @@ const { EventTarget } = require("./event/target");
const { emit } = require('./event/core');
const { when } = require('./system/unload');
const selection = require('./selection');
const { contract: loaderContract } = require('./content/loader');
// All user items we add have this class.
const ITEM_CLASS = "addon-context-menu-item";
@ -257,7 +258,7 @@ function populateCallbackNodeData(node) {
}
data.selectionText = selection.text;
data.srcURL = node.src || null;
data.value = node.value || null;
@ -265,7 +266,7 @@ function populateCallbackNodeData(node) {
data.linkURL = node.href || null;
node = node.parentNode;
}
return data;
}
@ -298,30 +299,11 @@ let baseItemRules = {
msg: "The 'context' option must be a Context object or an array of " +
"Context objects."
},
contentScript: {
is: ["string", "array", "undefined"],
ok: function (v) {
return !Array.isArray(v) ||
v.every(function (s) typeof(s) === "string");
}
},
contentScriptFile: {
is: ["string", "array", "undefined"],
ok: function (v) {
if (!v)
return true;
let arr = Array.isArray(v) ? v : [v];
return arr.every(function (s) {
return getTypeOf(s) === "string" &&
getScheme(s) === 'resource';
});
},
msg: "The 'contentScriptFile' option must be a local file URL or " +
"an array of local file URLs."
},
onMessage: {
is: ["function", "undefined"]
}
},
contentScript: loaderContract.rules.contentScript,
contentScriptFile: loaderContract.rules.contentScriptFile
};
let labelledItemRules = mix(baseItemRules, {

View File

@ -172,6 +172,9 @@ exports.boolean = boolean;
let object = { is: ['object', 'undefined', 'null'] };
exports.object = object;
let array = { is: ['array', 'undefined', 'null'] };
exports.array = array;
let isTruthyType = type => !(type === 'undefined' || type === 'null');
let findTypes = v => { while (!isArray(v) && v.is) v = v.is; return v };

View File

@ -143,11 +143,17 @@ TestFinder.prototype = {
suiteModule = cuddlefish.main(loader, suite);
}
catch (e) {
// If `Unsupported Application` error thrown during test,
// skip the test suite
suiteModule = {
'test suite skipped': assert => assert.pass(e.message)
};
if (/Unsupported Application/i.test(e.message)) {
// If `Unsupported Application` error thrown during test,
// skip the test suite
suiteModule = {
'test suite skipped': assert => assert.pass(e.message)
};
}
else {
console.exception(e);
throw e;
}
}
if (this.testInProcess) {

View File

@ -33,7 +33,7 @@ function getInitializerName(category) {
* [event type](https://developer.mozilla.org/en/DOM/event.type) to
* listen for.
* @param {Function} listener
* Function that is called whenever an event of the specified `type`
* Function that is called whenever an event of the specified `type`
* occurs.
* @param {Boolean} capture
* If true, indicates that the user wishes to initiate capture. After
@ -62,7 +62,7 @@ exports.on = on;
* [event type](https://developer.mozilla.org/en/DOM/event.type) to
* listen for.
* @param {Function} listener
* Function that is called whenever an event of the specified `type`
* Function that is called whenever an event of the specified `type`
* occurs.
* @param {Boolean} capture
* If true, indicates that the user wishes to initiate capture. After
@ -92,7 +92,7 @@ exports.once = once;
* [event type](https://developer.mozilla.org/en/DOM/event.type) to
* listen for.
* @param {Function} listener
* Function that is called whenever an event of the specified `type`
* Function that is called whenever an event of the specified `type`
* occurs.
* @param {Boolean} capture
* If true, indicates that the user wishes to initiate capture. After
@ -137,3 +137,33 @@ function emit(element, type, { category, initializer, settings }) {
element.dispatchEvent(event);
};
exports.emit = emit;
// Takes DOM `element` and returns promise which is resolved
// when given element is removed from it's parent node.
const removed = element => {
return new Promise(resolve => {
const { MutationObserver } = element.ownerDocument.defaultView;
const observer = new MutationObserver(mutations => {
for (let mutation of mutations) {
for (let node of mutation.removedNodes || []) {
if (node === element) {
observer.disconnect();
resolve(element);
}
}
}
});
observer.observe(element.parentNode, {childList: true});
});
};
exports.removed = removed;
const when = (element, eventName, capture=false) => new Promise(resolve => {
const listener = event => {
element.removeEventListener(eventName, listener, capture);
resolve(event);
};
element.addEventListener(eventName, listener, capture);
});
exports.when = when;

View File

@ -260,7 +260,7 @@ exports.Reactor = Reactor;
* Takes an object used as options with potential keys like 'onMessage',
* used to be called `require('sdk/event/core').setListeners` on.
* This strips all keys that would trigger a listener to be set.
*
*
* @params {Object} object
* @return {Object}
*/
@ -273,3 +273,8 @@ function stripListeners (object) {
}, {});
}
exports.stripListeners = stripListeners;
const when = (target, type) => new Promise(resolve => {
once(target, type, resolve);
});
exports.when = when;

View File

@ -34,7 +34,8 @@ exports.get = function get(k) {
if (arguments.length <= 1)
return localized;
let args = arguments;
let args = Array.slice(arguments);
let placeholders = [null, ...args.slice(typeof(n) === "number" ? 2 : 1)];
if (typeof localized == "object" && "other" in localized) {
// # Plural form:
@ -76,11 +77,15 @@ exports.get = function get(k) {
// in translation.
// * In case of plural form, we has `%d` instead of `%s`.
let offset = 1;
localized = localized.replace(/%(\d*)(s|d)/g, function (v, n) {
let rv = args[n != "" ? n : offset];
offset++;
return rv;
});
if (placeholders.length > 1) {
args = placeholders;
}
localized = localized.replace(/%(\d*)[sd]/g, (v, n) => {
let rv = args[n != "" ? n : offset];
offset++;
return rv;
});
return localized;
}

View File

@ -28,6 +28,7 @@ exports.get = function get(k) {
exports.locale = function locale() {
return bestMatchingLocale;
}
// Returns the short locale code: ja, en, fr
exports.language = function language() {
return bestMatchingLocale ? bestMatchingLocale.split("-")[0].toLowerCase()

View File

@ -66,6 +66,10 @@ function get(key, n, locales) {
localized = getKey(locale, key);
}
if (!localized) {
localized = getKey(locale, key + '[other]');
}
if (localized) {
return localized;
}

View File

@ -0,0 +1,12 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
module.metadata = {
"stability": "unstable"
};
const { window } = require("sdk/addon/window");
exports.MessageChannel = window.MessageChannel;
exports.MessagePort = window.MessagePort;

View File

@ -12,7 +12,8 @@ const { Cc, Ci, Cr } = require("chrome");
const apiUtils = require("./deprecated/api-utils");
const errors = require("./deprecated/errors");
const { isString, isUndefined, instanceOf } = require('./lang/type');
const { URL } = require('./url');
const { URL, isLocalURL } = require('./url');
const { data } = require('./self');
const NOTIFICATION_DIRECTIONS = ["auto", "ltr", "rtl"];
@ -39,7 +40,10 @@ exports.notify = function notifications_notify(options) {
}
};
function notifyWithOpts(notifyFn) {
notifyFn(valOpts.iconURL, valOpts.title, valOpts.text, !!clickObserver,
let { iconURL } = valOpts;
iconURL = iconURL && isLocalURL(iconURL) ? data.url(iconURL) : iconURL;
notifyFn(iconURL, valOpts.title, valOpts.text, !!clickObserver,
valOpts.data, clickObserver, valOpts.tag, valOpts.dir, valOpts.lang);
}
try {
@ -47,7 +51,7 @@ exports.notify = function notifications_notify(options) {
}
catch (err) {
if (err instanceof Ci.nsIException && err.result == Cr.NS_ERROR_FILE_NOT_FOUND) {
console.warn("The notification icon named by " + valOpts.iconURL +
console.warn("The notification icon named by " + iconURL +
" does not exist. A default icon will be used instead.");
delete valOpts.iconURL;
notifyWithOpts(notify);

View File

@ -26,6 +26,7 @@ const { contract: loaderContract } = require('./content/loader');
const { has } = require('./util/array');
const { Rules } = require('./util/rules');
const { merge } = require('./util/object');
const { data } = require('./self');
const views = WeakMap();
const workers = WeakMap();
@ -92,10 +93,13 @@ const Page = Class({
setup: function Page(options) {
let page = this;
options = pageContract(options);
let uri = options.contentURL;
let view = makeFrame(window.document, {
nodeName: 'iframe',
type: 'content',
uri: options.contentURL,
uri: uri ? data.url(uri) : '',
allowJavascript: options.allow.script,
allowPlugins: true,
allowAuth: true
@ -124,12 +128,19 @@ const Page = Class({
let allowJavascript = pageContract({ allow: value }).allow.script;
return allowJavascript ? enableScript(this) : disableScript(this);
},
get contentURL() { return viewFor(this).getAttribute('src'); },
get contentURL() { return viewFor(this).getAttribute("data-src") },
set contentURL(value) {
if (!isValidURL(this, value)) return;
let view = viewFor(this);
let contentURL = pageContract({ contentURL: value }).contentURL;
view.setAttribute('src', contentURL);
// page-worker doesn't have a model like other APIs, so to be consitent
// with the behavior "what you set is what you get", we need to store
// the original `contentURL` given.
// Even if XUL elements doesn't support `dataset`, properties, to
// indicate that is a custom attribute the syntax "data-*" is used.
view.setAttribute('data-src', contentURL);
view.setAttribute('src', data.url(contentURL));
},
dispose: function () {
if (isDisposed(this)) return;

View File

@ -307,13 +307,13 @@ on(hides, "data", ({target}) => emit(panelFor(target), "hide"));
on(ready, "data", ({target}) => {
let panel = panelFor(target);
let window = domPanel.getContentDocument(target).defaultView;
workerFor(panel).attach(window);
});
on(start, "data", ({target}) => {
let panel = panelFor(target);
let window = domPanel.getContentDocument(target).defaultView;
attach(styleFor(panel), window);
});

View File

@ -17,6 +17,8 @@ const { getMostRecentBrowserWindow, getOwnerBrowserWindow,
const { create: createFrame, swapFrameLoaders } = require("../frame/utils");
const { window: addonWindow } = require("../addon/window");
const { isNil } = require("../lang/type");
const { data } = require('../self');
const events = require("../system/events");
@ -402,7 +404,10 @@ exports.getContentFrame = getContentFrame;
function getContentDocument(panel) getContentFrame(panel).contentDocument
exports.getContentDocument = getContentDocument;
function setURL(panel, url) getContentFrame(panel).setAttribute("src", url)
function setURL(panel, url) {
getContentFrame(panel).setAttribute("src", url ? data.url(url) : url);
}
exports.setURL = setURL;
function allowContextMenu(panel, allow) {

View File

@ -28,7 +28,7 @@ const permissions = metadata.permissions || {};
const isPacked = rootURI && rootURI.indexOf("jar:") === 0;
const uri = (path="") =>
path.contains(":") ? path : addonDataURI + path;
path.contains(":") ? path : addonDataURI + path.replace(/^\.\//, "");
// Some XPCOM APIs require valid URIs as an argument for certain operations

View File

@ -9,24 +9,14 @@ module.metadata = {
const { Cc, Ci } = require("chrome");
const { Class } = require("../core/heritage");
const { ns } = require("../core/namespace");
const { URL } = require('../url');
const { URL, isLocalURL } = require('../url');
const events = require("../system/events");
const { loadSheet, removeSheet, isTypeValid } = require("./utils");
const { isString } = require("../lang/type");
const { attachTo, detachFrom, getTargetWindow } = require("../content/mod");
const { data } = require('../self');
const { freeze, create } = Object;
const LOCAL_URI_SCHEMES = ['resource', 'data'];
function isLocalURL(item) {
try {
return LOCAL_URI_SCHEMES.indexOf(URL(item).scheme) > -1;
}
catch(e) {}
return false;
}
function Style({ source, uri, type }) {
source = source == null ? null : freeze([].concat(source));
@ -54,7 +44,7 @@ exports.Style = Style;
attachTo.define(Style, function (style, window) {
if (style.uri) {
for (let uri of style.uri)
loadSheet(window, uri, style.type);
loadSheet(window, data.url(uri), style.type);
}
if (style.source) {
@ -69,7 +59,7 @@ attachTo.define(Style, function (style, window) {
detachFrom.define(Style, function (style, window) {
if (style.uri)
for (let uri of style.uri)
removeSheet(window, uri);
removeSheet(window, data.url(uri));
if (style.source) {
let uri = "data:text/css;charset=utf-8,";

View File

@ -3,16 +3,20 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
'use strict';
const { validateOptions } = require('../deprecated/api-utils');
const { validateOptions } = require("../deprecated/api-utils");
const { data } = require("../self");
function Options(options) {
if ('string' === typeof options)
options = { url: options };
return validateOptions(options, {
url: { is: ["string"] },
url: {
is: ["string"],
map: (v) => v ? data.url(v) : v
},
inBackground: {
map: function(v) !!v,
map: Boolean,
is: ["undefined", "boolean"]
},
isPinned: { is: ["undefined", "boolean"] },

View File

@ -17,7 +17,7 @@ const { off, emit, setListeners } = require('../event/core');
const { EventTarget } = require('../event/target');
const { URL } = require('../url');
const { add, remove, has, clear, iterator } = require('../lang/weak-set');
const { id: addonID } = require('../self');
const { id: addonID, data } = require('../self');
const { WindowTracker } = require('../deprecated/window-utils');
const { isShowing } = require('./sidebar/utils');
const { isBrowser, getMostRecentBrowserWindow, windows, isWindowPrivate } = require('../window/utils');
@ -35,6 +35,8 @@ const { identify } = require('./id');
const { uuid } = require('../util/uuid');
const { viewFor } = require('../view/core');
const resolveURL = (url) => url ? data.url(url) : url;
const sidebarNS = ns();
const WEB_PANEL_BROWSER_ID = 'web-panels-browser';
@ -107,7 +109,7 @@ const Sidebar = Class({
let sbTitle = window.document.getElementById('sidebar-title');
function onWebPanelSidebarCreated() {
if (panelBrowser.contentWindow.location != model.url ||
if (panelBrowser.contentWindow.location != resolveURL(model.url) ||
sbTitle.value != model.title) {
return;
}
@ -264,6 +266,8 @@ const Sidebar = Class({
exports.Sidebar = Sidebar;
function validateTitleAndURLCombo(sidebar, title, url) {
url = resolveURL(url);
if (sidebar.title == title && sidebar.url == url) {
return false;
}

View File

@ -14,11 +14,13 @@ const { models, buttons, views, viewsFor, modelFor } = require('./namespace');
const { isBrowser, getMostRecentBrowserWindow, windows, isWindowPrivate } = require('../../window/utils');
const { setStateFor } = require('../state');
const { defer } = require('../../core/promise');
const { isPrivateBrowsingSupported } = require('../../self');
const { isPrivateBrowsingSupported, data } = require('../../self');
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
const WEB_PANEL_BROWSER_ID = 'web-panels-browser';
const resolveURL = (url) => url ? data.url(url) : url;
function create(window, details) {
let id = makeID(details.id);
let { document } = window;
@ -29,7 +31,7 @@ function create(window, details) {
let menuitem = document.createElementNS(XUL_NS, 'menuitem');
menuitem.setAttribute('id', id);
menuitem.setAttribute('label', details.title);
menuitem.setAttribute('sidebarurl', details.sidebarurl);
menuitem.setAttribute('sidebarurl', resolveURL(details.sidebarurl));
menuitem.setAttribute('checked', 'false');
menuitem.setAttribute('type', 'checkbox');
menuitem.setAttribute('group', 'sidebar');
@ -74,6 +76,8 @@ exports.updateTitle = updateTitle;
function updateURL(sidebar, url) {
let eleID = makeID(sidebar.id);
url = resolveURL(url);
for (let window of windows(null, { includePrivate: true })) {
// update the menuitem
let mi = window.document.getElementById(eleID);
@ -111,8 +115,10 @@ function isSidebarShowing(window, sidebar) {
}
if (sidebarTitle.value == modelFor(sidebar).title) {
let url = resolveURL(modelFor(sidebar).url);
// checks if the sidebar is loading
if (win.gWebPanelURI == modelFor(sidebar).url) {
if (win.gWebPanelURI == url) {
return true;
}
@ -122,11 +128,11 @@ function isSidebarShowing(window, sidebar) {
return false;
}
if (ele.getAttribute('cachedurl') == modelFor(sidebar).url) {
if (ele.getAttribute('cachedurl') == url) {
return true;
}
if (ele && ele.contentWindow && ele.contentWindow.location == modelFor(sidebar).url) {
if (ele && ele.contentWindow && ele.contentWindow.location == url) {
return true;
}
}
@ -154,7 +160,7 @@ function showSidebar(window, sidebar, newURL) {
let menuitem = window.document.getElementById(makeID(model.id));
menuitem.setAttribute('checked', true);
window.openWebPanel(model.title, newURL || model.url);
window.openWebPanel(model.title, resolveURL(newURL || model.url));
}
return promise;

View File

@ -9,6 +9,7 @@ module.metadata = {
};
const { validateOptions: valid } = require("../deprecated/api-utils");
const method = require("method/core");
// Function takes property validation rules and returns function that given
// an `options` object will return validated / normalized options back. If
@ -18,9 +19,9 @@ const { validateOptions: valid } = require("../deprecated/api-utils");
// property getter and setters can be mixed into prototype. For more details
// see `properties` function below.
function contract(rules) {
function validator(options) {
return valid(options || {}, rules);
}
const validator = (instance, options) => {
return valid(options || instance || {}, rules);
};
validator.rules = rules
validator.properties = function(modelFor) {
return properties(modelFor, rules);
@ -48,4 +49,7 @@ function properties(modelFor, rules) {
}, {});
return Object.create(Object.prototype, descriptor);
}
exports.properties = properties
exports.properties = properties;
const validate = method("contract/validate");
exports.validate = validate;

View File

@ -19,7 +19,7 @@ const { EventTarget } = require('../event/target');
const { when: unload } = require('../system/unload');
const { windowIterator } = require('../deprecated/window-utils');
const { List, addListItem, removeListItem } = require('../util/list');
const { isPrivateBrowsingSupported } = require('../self');
const { isPrivateBrowsingSupported, data } = require('../self');
const { isTabPBSupported, ignoreWindow } = require('../private-browsing/utils');
const mainWindow = windowNS(browserWindows.activeWindow).window;
@ -55,7 +55,8 @@ const Tabs = Class({
console.error(ERR_FENNEC_MSG); // TODO
}
let rawTab = openTab(windowNS(activeWin).window, options.url, {
let url = options.url ? data.url(options.url) : options.url;
let rawTab = openTab(windowNS(activeWin).window, url, {
inBackground: options.inBackground,
isPrivate: supportPrivateTabs && options.isPrivate
});

View File

@ -17,7 +17,7 @@ const { getOwnerWindow, getActiveTab, getTabs,
const { Options } = require("../tabs/common");
const { observer: tabsObserver } = require("../tabs/observer");
const { ignoreWindow } = require("../private-browsing/utils");
const { when: unload } = require('../system/unload');
const { when: unload } = require("../system/unload");
const TAB_BROWSER = "tabbrowser";

View File

@ -27,6 +27,9 @@ skip = [
"packages/api-utils/tests/test-querystring.js", # MIT
"packages/api-utils/lib/promise.js", # MIT
"packages/api-utils/tests/test-promise.js", # MIT
"examples/actor-repl/README.md", # It's damn readme file
"examples/actor-repl/data/codemirror-compressed.js", # MIT
"examples/actor-repl/data/codemirror.css", # MIT
]
absskip = [from_sdk_top(os.path.join(*fn.split("/"))) for fn in skip]

View File

@ -20,3 +20,9 @@ explicitPlural[other]=other
# file parser)
unicodeEscape = \u0020\u0040\u0020
# this string equals to " @ "
# bug 1033309 plurals with multiple placeholders
first_identifier[one]=first entry is %s and the second one is %s.
first_identifier=the entries are %s and %s.
second_identifier[other]=first entry is %s and the second one is %s.
third_identifier=first entry is %s and the second one is %s.

View File

@ -163,6 +163,20 @@ exports.testEnUsLocaleName = createTest("en-GB", function(assert, loader, done)
"other",
"PluralForm form can be omitting generic key [i.e. without ...[other] at end of key)");
assert.equal(_("first_identifier", "ONE", "TWO"), "the entries are ONE and TWO.", "first_identifier no count");
assert.equal(_("first_identifier", 0, "ONE", "TWO"), "the entries are ONE and TWO.", "first_identifier with count = 0");
assert.equal(_("first_identifier", 1, "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "first_identifier with count = 1");
assert.equal(_("first_identifier", 2, "ONE", "TWO"), "the entries are ONE and TWO.", "first_identifier with count = 2");
assert.equal(_("second_identifier", "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "second_identifier with no count");
assert.equal(_("second_identifier", 0, "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "second_identifier with count = 0");
assert.equal(_("second_identifier", 1, "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "second_identifier with count = 1");
assert.equal(_("second_identifier", 2, "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "second_identifier with count = 2");
assert.equal(_("third_identifier", "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "third_identifier with no count");
assert.equal(_("third_identifier", 0, "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "third_identifier with count = 0");
assert.equal(_("third_identifier", 2, "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "third_identifier with count = 2");
done();
});

View File

@ -20,3 +20,9 @@ explicitPlural[other]=other
# file parser)
unicodeEscape = \u0020\u0040\u0020
# this string equals to " @ "
# bug 1033309 plurals with multiple placeholders
first_identifier[one]=first entry is %s and the second one is %s.
first_identifier=the entries are %s and %s.
second_identifier[other]=first entry is %s and the second one is %s.
third_identifier=first entry is %s and the second one is %s.

View File

@ -87,7 +87,6 @@ exports.testExactMatching = createTest("fr-FR", function(assert, loader, done) {
});
exports.testHtmlLocalization = createTest("en-GB", function(assert, loader, done) {
// Ensure initing html component that watch document creations
// Note that this module is automatically initialized in
// cuddlefish.js:Loader.main in regular addons. But it isn't for unit tests.
@ -165,6 +164,20 @@ exports.testEnUsLocaleName = createTest("en-US", function(assert, loader, done)
"other",
"PluralForm form can be omitting generic key [i.e. without ...[other] at end of key)");
assert.equal(_("first_identifier", "ONE", "TWO"), "the entries are ONE and TWO.", "first_identifier no count");
assert.equal(_("first_identifier", 0, "ONE", "TWO"), "the entries are ONE and TWO.", "first_identifier with count = 0");
assert.equal(_("first_identifier", 1, "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "first_identifier with count = 1");
assert.equal(_("first_identifier", 2, "ONE", "TWO"), "the entries are ONE and TWO.", "first_identifier with count = 2");
assert.equal(_("second_identifier", "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "second_identifier with no count");
assert.equal(_("second_identifier", 0, "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "second_identifier with count = 0");
assert.equal(_("second_identifier", 1, "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "second_identifier with count = 1");
assert.equal(_("second_identifier", 2, "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "second_identifier with count = 2");
assert.equal(_("third_identifier", "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "third_identifier with no count");
assert.equal(_("third_identifier", 0, "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "third_identifier with count = 0");
assert.equal(_("third_identifier", 2, "ONE", "TWO"), "first entry is ONE and the second one is TWO.", "third_identifier with count = 2");
done();
});

View File

@ -1,3 +0,0 @@
<script>
addon.postMessage("hello addon")
</script>

View File

@ -1,34 +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/. */
"use strict";
const app = require("sdk/system/xul-app");
exports["test addon globa"] = app.is("Firefox") ? testAddonGlobal : unsupported;
function testAddonGlobal (assert, done) {
const { Panel } = require("sdk/panel")
const { data } = require("sdk/self")
let panel = Panel({
contentURL: //"data:text/html,now?",
data.url("./index.html"),
onMessage: function(message) {
assert.pass("got message from panel script");
panel.destroy();
done();
},
onError: function(error) {
assert.fail(Error("failed to recieve message"));
done();
}
});
};
function unsupported (assert) {
assert.pass("privileged-panel unsupported on platform");
}
require("sdk/test/runner").runTestsFromModule(module);

View File

@ -1,3 +0,0 @@
{
"id": "test-privileged-addon"
}

View File

@ -2,5 +2,9 @@
* 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/. */
exports.url = path =>
module.uri.substr(0, module.uri.lastIndexOf("/") + 1) + "fixtures/" + path
const { uri } = module;
const prefix = uri.substr(0, uri.lastIndexOf("/") + 1) + "fixtures/";
exports.url = (path="") => path && path.contains(":")
? path
: prefix + path.replace(/^\.\//, "");

View File

@ -0,0 +1 @@
div { border-style: dashed; }

View File

@ -1,10 +1,12 @@
<script>
addon.port.on('X', function(msg) {
// last message
addon.port.emit('X', msg + '3');
});
if ("addon" in window) {
addon.port.on('X', function(msg) {
// last message
addon.port.emit('X', msg + '3');
});
// start messaging chain
addon.port.emit('Y', '1');
// start messaging chain
addon.port.emit('Y', '1');
}
</script>
SIDEBAR TEST

View File

@ -9,6 +9,8 @@ const timer = require("sdk/timers");
const xulApp = require("sdk/system/xul-app");
const { Loader } = require("sdk/test/loader");
const { openTab, getBrowserForTab, closeTab } = require("sdk/tabs/utils");
const self = require("sdk/self");
const { merge } = require("sdk/util/object");
/**
* A helper function that creates a PageMod, then opens the specified URL
@ -31,7 +33,13 @@ exports.testPageMod = function testPageMod(assert, done, testURL, pageModOptions
return null;
}
let loader = Loader(module);
let loader = Loader(module, null, null, {
modules: {
"sdk/self": merge({}, self, {
data: merge({}, self.data, require("./fixtures"))
})
}
});
let pageMod = loader.require("sdk/page-mod");
var pageMods = [new pageMod.PageMod(opts) for each(opts in pageModOptions)];

View File

@ -12,13 +12,13 @@ exports['test:contentURL'] = function(assert) {
let loader = Loader(),
value, emitted = 0, changes = 0;
assert.throws(
function() loader.contentURL = 4,
assert.throws(() =>
loader.contentURL = 4,
/The `contentURL` option must be a valid URL./,
'Must throw an exception if `contentURL` is not URL.'
);
assert.throws(
function() loader.contentURL = { toString: function() 'Oops' },
assert.throws(() =>
loader.contentURL = { toString: function() 'Oops' },
/The `contentURL` option must be a valid URL./,
'Must throw an exception if `contentURL` is not URL.'
);
@ -78,6 +78,24 @@ exports['test:contentURL'] = function(assert) {
'must not emit `propertyChange` if same value is set'
);
loader.contentURL = value = './index.html';
assert.equal(
value,
'' + loader.contentURL,
'value must be set'
);
assert.equal(
++ changes,
emitted,
'had to emit `propertyChange`'
);
loader.contentURL = value;
assert.equal(
changes,
emitted,
'must not emit `propertyChange` if same value is set'
);
loader.removeListener('propertyChange', listener);
loader.contentURL = value = 'about:blank';
assert.equal(
@ -133,39 +151,37 @@ exports['test:contentScriptWhen'] = function(assert) {
exports['test:contentScript'] = function(assert) {
let loader = Loader(), value;
assert.equal(
null,
loader.contentScript,
'`contentScript` defaults to `null`'
);
loader.contentScript = value = 'let test = {};';
assert.equal(
value,
loader.contentScript
);
try {
loader.contentScript = { 1: value }
test.fail('must throw when wrong value is set');
} catch(e) {
assert.equal(
'The `contentScript` option must be a string or an array of strings.',
e.message
);
}
try {
loader.contentScript = ['oue', 2]
test.fail('must throw when wrong value is set');
} catch(e) {
assert.equal(
'The `contentScript` option must be a string or an array of strings.',
e.message
);
}
assert.throws(() =>
loader.contentScript = { 1: value },
/The `contentScript` option must be a string or an array of strings/,
'must throw when wrong value is set'
);
assert.throws(() =>
loader.contentScript = ['oue', 2],
/The `contentScript` option must be a string or an array of strings/,
'must throw when wrong value is set'
);
loader.contentScript = undefined;
assert.equal(
null,
loader.contentScript
);
loader.contentScript = value = ["1;", "2;"];
assert.equal(
value,
@ -185,36 +201,25 @@ exports['test:contentScriptFile'] = function(assert) {
value,
loader.contentScriptFile
);
try {
loader.contentScriptFile = { 1: uri }
test.fail('must throw when wrong value is set');
} catch(e) {
assert.equal(
'The `contentScriptFile` option must be a local URL or an array of URLs.',
e.message
);
}
try {
loader.contentScriptFile = [ 'oue', uri ]
test.fail('must throw when wrong value is set');
} catch(e) {
assert.equal(
'The `contentScriptFile` option must be a local URL or an array of URLs.',
e.message
);
}
assert.throws(() =>
loader.contentScriptFile = { 1: uri },
/The `contentScriptFile` option must be a local URL or an array of URLs/,
'must throw when wrong value is set'
);
assert.throws(() =>
loader.contentScriptFile = [ 'oue', uri ],
/The `contentScriptFile` option must be a local URL or an array of URLs/,
'must throw when wrong value is set'
);
let data = 'data:text/html,test';
try {
loader.contentScriptFile = [ { toString: () => data } ];
test.fail('must throw when non-URL object is set');
} catch(e) {
assert.equal(
'The `contentScriptFile` option must be a local URL or an array of URLs.',
e.message
);
}
assert.throws(() =>
loader.contentScriptFile = [ { toString: () => data } ],
/The `contentScriptFile` option must be a local URL or an array of URLs/,
'must throw when wrong value is set'
);
loader.contentScriptFile = new URL(data);
assert.ok(
@ -222,11 +227,18 @@ exports['test:contentScriptFile'] = function(assert) {
'must be able to set `contentScriptFile` to an instance of URL'
);
assert.equal(
data,
data,
loader.contentScriptFile.toString(),
'setting `contentScriptFile` to an instance of URL should preserve the url'
);
loader.contentScriptFile = './index.html';
assert.equal(
'./index.html',
loader.contentScriptFile,
'setting `contentScriptFile` to relative path is allowed'
);
loader.contentScriptFile = undefined;
assert.equal(
null,

View File

@ -879,6 +879,10 @@ exports.testContentContextMatchString = function (assert, done) {
exports.testContentScriptFile = function (assert, done) {
let test = new TestHelper(assert, done);
let loader = test.newLoader();
let { defer, all } = require("sdk/core/promise");
let itemScript = [defer(), defer()];
let menuShown = defer();
let menuPromises = itemScript.concat(menuShown).map(({promise}) => promise);
// Reject remote files
assert.throws(function() {
@ -887,20 +891,37 @@ exports.testContentScriptFile = function (assert, done) {
contentScriptFile: "http://mozilla.com/context-menu.js"
});
},
new RegExp("The 'contentScriptFile' option must be a local file URL " +
"or an array of local file URLs."),
/The `contentScriptFile` option must be a local URL or an array of URLs/,
"Item throws when contentScriptFile is a remote URL");
// But accept files from data folder
let item = new loader.cm.Item({
label: "item",
contentScriptFile: data.url("test-context-menu.js")
contentScriptFile: data.url("test-contentScriptFile.js"),
onMessage: (message) => {
assert.equal(message, "msg from contentScriptFile",
"contentScriptFile loaded with absolute url");
itemScript[0].resolve();
}
});
test.showMenu(null, function (popup) {
test.checkMenu([item], [], []);
test.done();
let item2 = new loader.cm.Item({
label: "item2",
contentScriptFile: "./test-contentScriptFile.js",
onMessage: (message) => {
assert.equal(message, "msg from contentScriptFile",
"contentScriptFile loaded with relative url");
itemScript[1].resolve();
}
});
console.log(item.contentScriptFile, item2.contentScriptFile);
test.showMenu(null, function (popup) {
test.checkMenu([item, item2], [], []);
menuShown.resolve();
});
all(menuPromises).then(() => test.done());
};
@ -3944,7 +3965,15 @@ TestHelper.prototype = {
// function that unloads the loader and associated resources.
newLoader: function () {
const self = this;
let loader = Loader(module);
const selfModule = require('sdk/self');
let loader = Loader(module, null, null, {
modules: {
"sdk/self": merge({}, selfModule, {
data: merge({}, selfModule.data, require("./fixtures"))
})
}
});
let wrapper = {
loader: loader,
cm: loader.require("sdk/context-menu"),

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,7 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { getRulesForLocale } = require("sdk/l10n/plural-rules");

View File

@ -1028,20 +1028,20 @@ exports.testPageModCss = function(assert, done) {
'data:text/html;charset=utf-8,<div style="background: silver">css test</div>', [{
include: ["*", "data:*"],
contentStyle: "div { height: 100px; }",
contentStyleFile: data.url("css-include-file.css")
contentStyleFile: [data.url("include-file.css"), "./border-style.css"]
}],
function(win, done) {
let div = win.document.querySelector("div");
assert.equal(
div.clientHeight,
100,
"PageMod contentStyle worked"
);
assert.equal(
div.offsetHeight,
120,
"PageMod contentStyleFile worked"
);
assert.equal(div.clientHeight, 100,
"PageMod contentStyle worked");
assert.equal(div.offsetHeight, 120,
"PageMod contentStyleFile worked");
assert.equal(win.getComputedStyle(div).borderTopStyle, "dashed",
"PageMod contentStyleFile with relative path worked");
done();
}
);
@ -1165,6 +1165,21 @@ exports.testPageModCssAutomaticDestroy = function(assert, done) {
});
};
exports.testPageModContentScriptFile = function(assert, done) {
testPageMod(assert, done, "about:license", [{
include: "about:*",
contentScriptWhen: "start",
contentScriptFile: "./test-contentScriptFile.js",
onMessage: message => {
assert.equal(message, "msg from contentScriptFile",
"PageMod contentScriptFile with relative path worked");
}
}],
(win, done) => done()
);
};
exports.testPageModTimeout = function(assert, done) {
let tab = null

View File

@ -268,6 +268,33 @@ exports.testLoadContentPage = function(assert, done) {
});
}
exports.testLoadContentPageRelativePath = function(assert, done) {
const self = require("sdk/self");
const { merge } = require("sdk/util/object");
let loader = Loader(module, null, null, {
modules: {
"sdk/self": merge({}, self, {
data: merge({}, self.data, fixtures)
})
}
});
let page = loader.require("sdk/page-worker").Page({
onMessage: function(message) {
// The message is an array whose first item is the test method to call
// and the rest of whose items are arguments to pass it.
let msg = message.shift();
if (msg == "done")
return done();
assert[msg].apply(assert, message);
},
contentURL: "./test-page-worker.html",
contentScriptFile: "./test-page-worker.js",
contentScriptWhen: "ready"
});
}
exports.testAllowScriptDefault = function(assert, done) {
let page = Page({
onMessage: function(message) {

View File

@ -27,7 +27,6 @@ const { wait } = require('./event/helpers');
const fixtures = require('./fixtures')
const SVG_URL = fixtures.url('mofo_logo.SVG');
const CSS_URL = fixtures.url('css-include-file.css');
const Isolate = fn => '(' + fn + ')()';
@ -979,7 +978,16 @@ exports['test panel can be constructed without any arguments'] = function (asser
};
exports['test panel CSS'] = function(assert, done) {
const loader = Loader(module);
const { merge } = require("sdk/util/object");
let loader = Loader(module, null, null, {
modules: {
"sdk/self": merge({}, self, {
data: merge({}, self.data, fixtures)
})
}
});
const { Panel } = loader.require('sdk/panel');
const { getActiveView } = loader.require('sdk/view/core');
@ -991,13 +999,19 @@ exports['test panel CSS'] = function(assert, done) {
contentURL: 'data:text/html;charset=utf-8,' +
'<div style="background: silver">css test</div>',
contentStyle: 'div { height: 100px; }',
contentStyleFile: CSS_URL,
contentStyleFile: [fixtures.url("include-file.css"), "./border-style.css"],
onShow: () => {
ready(getContentWindow(panel)).then(({ document }) => {
ready(getContentWindow(panel)).then(({ window, document }) => {
let div = document.querySelector('div');
assert.equal(div.clientHeight, 100, 'Panel contentStyle worked');
assert.equal(div.offsetHeight, 120, 'Panel contentStyleFile worked');
assert.equal(div.clientHeight, 100,
"Panel contentStyle worked");
assert.equal(div.offsetHeight, 120,
"Panel contentStyleFile worked");
assert.equal(window.getComputedStyle(div).borderTopStyle, "dashed",
"Panel contentStyleFile with relative path worked");
loader.unload();
done();
@ -1008,6 +1022,53 @@ exports['test panel CSS'] = function(assert, done) {
panel.show();
};
exports['test panel contentScriptFile'] = function(assert, done) {
const { merge } = require("sdk/util/object");
let loader = Loader(module, null, null, {
modules: {
"sdk/self": merge({}, self, {
data: merge({}, self.data, {url: fixtures.url})
})
}
});
const { Panel } = loader.require('sdk/panel');
const { getActiveView } = loader.require('sdk/view/core');
const getContentWindow = panel =>
getActiveView(panel).querySelector('iframe').contentWindow;
let whenMessage = defer();
let whenShown = defer();
let panel = Panel({
contentURL: './test.html',
contentScriptFile: "./test-contentScriptFile.js",
onMessage: (message) => {
assert.equal(message, "msg from contentScriptFile",
"Panel contentScriptFile with relative path worked");
whenMessage.resolve();
},
onShow: () => {
ready(getContentWindow(panel)).then(({ document }) => {
assert.equal(document.title, 'foo',
"Panel contentURL with relative path worked");
whenShown.resolve();
});
}
});
all([whenMessage.promise, whenShown.promise]).
then(loader.unload).
then(done, assert.fail);
panel.show();
};
exports['test panel CSS list'] = function(assert, done) {
const loader = Loader(module);
const { Panel } = loader.require('sdk/panel');
@ -1186,6 +1247,36 @@ exports['test panel contextmenu disabled'] = function*(assert) {
assert.equal(contextmenu.state, 'closed',
'contextmenu was never open');
loader.unload();
}
exports["test panel addon global object"] = function*(assert) {
const { merge } = require("sdk/util/object");
let loader = Loader(module, null, null, {
modules: {
"sdk/self": merge({}, self, {
data: merge({}, self.data, {url: fixtures.url})
})
}
});
const { Panel } = loader.require('sdk/panel');
let panel = Panel({
contentURL: "./test-trusted-document.html"
});
panel.show();
yield wait(panel, "show");
panel.port.emit('addon-to-document', 'ok');
yield wait(panel.port, "document-to-addon");
assert.pass("Received an event from the document");
loader.unload();
}

View File

@ -40,6 +40,38 @@ exports.testTabCounts = function(assert, done) {
});
};
exports.testTabRelativePath = function(assert, done) {
const { merge } = require("sdk/util/object");
const self = require("sdk/self");
let loader = Loader(module, null, null, {
modules: {
"sdk/self": merge({}, self, {
data: merge({}, self.data, require("./fixtures"))
})
}
});
let tabs = loader.require("sdk/tabs");
tabs.open({
url: "./test.html",
onReady: (tab) => {
assert.equal(tab.title, "foo",
"tab opened a document with relative path");
tab.attach({
contentScriptFile: "./test-contentScriptFile.js",
onMessage: (message) => {
assert.equal(message, "msg from contentScriptFile",
"Tab attach a contentScriptFile with relative path worked");
tab.close(done);
}
});
}
});
};
// TEST: tabs.activeTab getter
exports.testActiveTab_getter = function(assert, done) {

View File

@ -370,6 +370,48 @@ exports.testSidebarUnload = function(assert, done) {
assert.pass('showing the sidebar');
}
exports.testRelativeURL = function(assert, done) {
const { merge } = require('sdk/util/object');
const self = require('sdk/self');
let loader = Loader(module, null, null, {
modules: {
'sdk/self': merge({}, self, {
data: merge({}, self.data, require('./fixtures'))
})
}
});
const { Sidebar } = loader.require('sdk/ui/sidebar');
let testName = 'testRelativeURL';
let sidebar = Sidebar({
id: testName,
title: testName,
url: './test-sidebar-addon-global.html'
});
sidebar.on('attach', function(worker) {
assert.pass('sidebar was attached');
assert.ok(!!worker, 'attach event has worker');
worker.port.once('Y', function(msg) {
assert.equal(msg, '1', 'got event from worker');
worker.port.on('X', function(msg) {
assert.equal(msg, '123', 'the final message is correct');
sidebar.destroy();
done();
});
worker.port.emit('X', msg + '2');
})
});
sidebar.show();
}
exports.testRemoteContent = function(assert) {
const { Sidebar } = require('sdk/ui/sidebar');
let testName = 'testRemoteContent';

View File

@ -36,8 +36,6 @@ function openNewWindowTab(url, options) {
if (options.onLoad) {
options.onLoad({ target: { defaultView: window } })
}
return newTab;
});
}
@ -538,7 +536,7 @@ exports.testConstructor = function(assert, done) {
assert.equal(widgetCount2(), widgetStartCount2, "2nd window has correct number of child elements after second destroy");
close(browserWindow).then(doneTest);
}});
}}).catch(assert.fail);
});
// test window closing
@ -634,7 +632,7 @@ exports.testConstructor = function(assert, done) {
browserWindow.setToolbarVisibility(container(), true);
close(browserWindow2).then(doneTest);
}});
}}).catch(assert.fail);
});
}
@ -1179,7 +1177,7 @@ exports.testReinsertion = function(assert, done) {
loader.unload();
done();
});
}});
}}).catch(assert.fail);
};
exports.testWideWidget = function testWideWidget(assert) {

View File

@ -42,6 +42,33 @@ exports.testOpenAndCloseWindow = function(assert, done) {
});
};
exports.testOpenRelativePathWindow = function(assert, done) {
assert.equal(browserWindows.length, 1, "Only one window open");
const { merge } = require("sdk/util/object");
const self = require("sdk/self");
let loader = Loader(module, null, null, {
modules: {
"sdk/self": merge({}, self, {
data: merge({}, self.data, require("./../fixtures"))
})
}
});
loader.require("sdk/windows").browserWindows.open({
url: "./test.html",
onOpen: (window) => {
window.tabs.activeTab.once("ready", (tab) => {
assert.equal(tab.title, "foo",
"tab opened a document with relative path");
window.close(done);
});
}
})
}
exports.testAutomaticDestroy = function(assert, done) {
let windows = browserWindows;

View File

@ -19,13 +19,13 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1c9eb3d16167c2e5a9f7789eb46ee17830e6054e"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="649245c238a043af32acb109b2613f578323f8e1"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="227354333a185180b85471f2cc6abfb029e44718"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8b04b5aca4b0a894de40f4d53ae9750222d349a8"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="31215645c296ba7a62bcb3176f69e0014ab9be07"/>
<!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
<project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>

View File

@ -17,10 +17,10 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="1c9eb3d16167c2e5a9f7789eb46ee17830e6054e"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="649245c238a043af32acb109b2613f578323f8e1"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8b04b5aca4b0a894de40f4d53ae9750222d349a8"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="31215645c296ba7a62bcb3176f69e0014ab9be07"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
<!-- Stock Android things -->

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="276ce45e78b09c4a4ee643646f691d22804754c1">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="1c9eb3d16167c2e5a9f7789eb46ee17830e6054e"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="649245c238a043af32acb109b2613f578323f8e1"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
@ -23,7 +23,7 @@
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8b04b5aca4b0a894de40f4d53ae9750222d349a8"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="31215645c296ba7a62bcb3176f69e0014ab9be07"/>
<!-- Stock Android things -->
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="f92a936f2aa97526d4593386754bdbf02db07a12"/>
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="6e47ff2790f5656b5b074407829ceecf3e6188c4"/>

View File

@ -19,13 +19,13 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1c9eb3d16167c2e5a9f7789eb46ee17830e6054e"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="649245c238a043af32acb109b2613f578323f8e1"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="227354333a185180b85471f2cc6abfb029e44718"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8b04b5aca4b0a894de40f4d53ae9750222d349a8"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="31215645c296ba7a62bcb3176f69e0014ab9be07"/>
<!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
<project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>

View File

@ -17,10 +17,10 @@
</project>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="1c9eb3d16167c2e5a9f7789eb46ee17830e6054e"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="649245c238a043af32acb109b2613f578323f8e1"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8b04b5aca4b0a894de40f4d53ae9750222d349a8"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="31215645c296ba7a62bcb3176f69e0014ab9be07"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
<!-- Stock Android things -->

View File

@ -4,6 +4,6 @@
"remote": "",
"branch": ""
},
"revision": "cea1598e51f188fe8c48196cbb2c73aacbf3b1dc",
"revision": "591c866cb04a88306e472c873429669229f28540",
"repo_path": "/integration/gaia-central"
}

View File

@ -17,12 +17,12 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1c9eb3d16167c2e5a9f7789eb46ee17830e6054e"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="649245c238a043af32acb109b2613f578323f8e1"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8b04b5aca4b0a894de40f4d53ae9750222d349a8"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="31215645c296ba7a62bcb3176f69e0014ab9be07"/>
<!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
<project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>

View File

@ -15,7 +15,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1c9eb3d16167c2e5a9f7789eb46ee17830e6054e"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="649245c238a043af32acb109b2613f578323f8e1"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -17,10 +17,10 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="1c9eb3d16167c2e5a9f7789eb46ee17830e6054e"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="649245c238a043af32acb109b2613f578323f8e1"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8b04b5aca4b0a894de40f4d53ae9750222d349a8"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="31215645c296ba7a62bcb3176f69e0014ab9be07"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
<!-- Stock Android things -->

View File

@ -17,12 +17,12 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1c9eb3d16167c2e5a9f7789eb46ee17830e6054e"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="649245c238a043af32acb109b2613f578323f8e1"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="8b04b5aca4b0a894de40f4d53ae9750222d349a8"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="31215645c296ba7a62bcb3176f69e0014ab9be07"/>
<project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
<!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>

View File

@ -5,7 +5,7 @@
MOZ_APP_BASENAME=B2G
MOZ_APP_VENDOR=Mozilla
MOZ_APP_VERSION=33.0a1
MOZ_APP_VERSION=34.0a1
MOZ_APP_UA_NAME=Firefox
MOZ_UA_OS_AGNOSTIC=1

View File

@ -70,7 +70,8 @@ this.Translation = {
let trUI = aBrowser.translationUI;
// Set all values before showing a new translation infobar.
trUI._state = aData.state;
trUI._state = Translation.serviceUnavailable ? Translation.STATE_UNAVAILABLE
: aData.state;
trUI.detectedLanguage = aData.detectedLanguage;
trUI.translatedFrom = aData.translatedFrom;
trUI.translatedTo = aData.translatedTo;
@ -237,23 +238,6 @@ TranslationUI.prototype = {
return true;
},
showTranslationUI: function(aDetectedLanguage) {
this.detectedLanguage = aDetectedLanguage;
// Reset all values before showing a new translation infobar.
this.state = 0;
this.translatedFrom = "";
this.translatedTo = "";
this.originalShown = true;
this.showURLBarIcon();
if (!this.shouldShowInfoBar(this.browser.currentURI))
return null;
return this.showTranslationInfoBar();
},
receiveMessage: function(msg) {
switch (msg.name) {
case "Translation:Finished":

View File

@ -181,7 +181,7 @@
if (aTranslation.translatedFrom)
fromLanguage.value = aTranslation.translatedFrom;
// Fill the list of supporter target languages.
// Fill the list of supported target languages.
let toLanguage = this._getAnonElt("toLanguage");
for (let code of Translation.supportedTargetLanguages)
toLanguage.appendItem(bundle.GetStringFromName(code), code);
@ -263,8 +263,15 @@
let lang;
if (this.state == Translation.STATE_OFFER)
lang = this._getAnonElt("detectedLanguage").value;
else
else {
lang = this._getAnonElt("fromLanguage").value;
// If we have never attempted to translate the page before the
// service became unavailable, "fromLanguage" isn't set.
if (!lang && this.state == Translation.STATE_UNAVAILABLE)
lang = this.translation.detectedLanguage;
}
let langBundle =
Cc["@mozilla.org/intl/stringbundle;1"]
.getService(Ci.nsIStringBundleService)

View File

@ -1 +1 @@
33.0a1
34.0a1

View File

@ -65,6 +65,7 @@ support-files =
doc_large-array-buffer.html
doc_minified.html
doc_minified_bogus_map.html
doc_native-event-handler.html
doc_no-page-sources.html
doc_pause-exceptions.html
doc_pretty-print.html
@ -155,6 +156,7 @@ skip-if = true # Bug 933950 (leaky test)
[browser_dbg_editor-mode.js]
[browser_dbg_event-listeners-01.js]
[browser_dbg_event-listeners-02.js]
[browser_dbg_event-listeners-03.js]
[browser_dbg_file-reload.js]
[browser_dbg_function-display-name.js]
[browser_dbg_global-method-override.js]

View File

@ -0,0 +1,89 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests that the eventListeners request works when there are event handlers
* that the debugger cannot unwrap.
*/
const TAB_URL = EXAMPLE_URL + "doc_native-event-handler.html";
let gClient;
function test() {
if (!DebuggerServer.initialized) {
DebuggerServer.init(() => true);
DebuggerServer.addBrowserActors();
}
let transport = DebuggerServer.connectPipe();
gClient = new DebuggerClient(transport);
gClient.connect((aType, aTraits) => {
is(aType, "browser",
"Root actor should identify itself as a browser.");
addTab(TAB_URL)
.then(() => attachThreadActorForUrl(gClient, TAB_URL))
.then(pauseDebuggee)
.then(testEventListeners)
.then(closeConnection)
.then(finish)
.then(null, aError => {
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
});
});
}
function pauseDebuggee(aThreadClient) {
let deferred = promise.defer();
gClient.addOneTimeListener("paused", (aEvent, aPacket) => {
is(aPacket.type, "paused",
"We should now be paused.");
is(aPacket.why.type, "debuggerStatement",
"The debugger statement was hit.");
deferred.resolve(aThreadClient);
});
// Spin the event loop before causing the debuggee to pause, to allow
// this function to return first.
executeSoon(() => {
EventUtils.sendMouseEvent({ type: "click" },
content.document.querySelector("button"),
content);
});
return deferred.promise;
}
function testEventListeners(aThreadClient) {
let deferred = promise.defer();
aThreadClient.eventListeners(aPacket => {
if (aPacket.error) {
let msg = "Error getting event listeners: " + aPacket.message;
ok(false, msg);
deferred.reject(msg);
return;
}
// There are 4 event listeners in the page: button.onclick, window.onload
// and two more from the video element controls.
is(aPacket.listeners.length, 4, "Found all event listeners.");
aThreadClient.resume(deferred.resolve);
});
return deferred.promise;
}
function closeConnection() {
let deferred = promise.defer();
gClient.close(deferred.resolve);
return deferred.promise;
}
registerCleanupFunction(function() {
removeTab(gBrowser.selectedTab);
gClient = null;
});

View File

@ -0,0 +1,22 @@
<!-- Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ -->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>A video element with native event handlers</title>
<script type="text/javascript">
function initialSetup(event) {
debugger;
}
window.addEventListener("load", function() {}, false);
</script>
</head>
<body>
<button onclick="initialSetup()">Click me!</button>
<!-- the "controls" attribute ensures that there are extra event handlers in
the element. -->
<video controls></video>
</body>
</html>

View File

@ -996,15 +996,25 @@ TextProperty.prototype = {
dummyStyle.setProperty(this.name, this.value, this.priority);
this.computed = [];
for (let i = 0, n = dummyStyle.length; i < n; i++) {
let prop = dummyStyle.item(i);
this.computed.push({
textProp: this,
name: prop,
value: dummyStyle.getPropertyValue(prop),
priority: dummyStyle.getPropertyPriority(prop),
});
}
try {
// Manually get all the properties that are set when setting a value on
// this.name and check the computed style on dummyElement for each one.
// If we just read dummyStyle, it would skip properties when value == "".
let subProps = domUtils.getSubpropertiesForCSSProperty(this.name);
for (let prop of subProps) {
this.computed.push({
textProp: this,
name: prop,
value: dummyStyle.getPropertyValue(prop),
priority: dummyStyle.getPropertyPriority(prop),
});
}
} catch(e) {
// This is a partial property name, probably from cutting and pasting
// text. At this point don't check for computed properties.
}
},
/**

View File

@ -74,5 +74,6 @@ function* testCreateNew(view) {
yield onModifications;
is(textProp.value, "#XYZ", "Text prop should have been changed.");
is(textProp.overridden, false, "Property should not be overridden");
is(textProp.editor.isValid(), false, "#XYZ should not be a valid entry");
}

View File

@ -10,4 +10,4 @@
# hardcoded milestones in the tree from these two files.
#--------------------------------------------------------
33.0a1
34.0a1

View File

@ -457,7 +457,10 @@ BluetoothAdapter::StopDiscovery(ErrorResult& aRv)
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
nsRefPtr<Promise> promise = new Promise(global);
nsRefPtr<Promise> promise = Promise::Create(global, aRv);
if (aRv.Failed()) {
return nullptr;
}
/**
* Ensure
@ -819,11 +822,7 @@ BluetoothAdapter::EnableDisable(bool aEnable, ErrorResult& aRv)
}
// Notify applications of adapter state change to Enabling/Disabling
nsTArray<nsString> types;
BT_APPEND_ENUM_STRING(types,
BluetoothAdapterAttribute,
BluetoothAdapterAttribute::State);
DispatchAttributeEvent(types);
HandleAdapterStateChanged();
// Wrap runnable to handle result
nsRefPtr<BluetoothReplyRunnable> result =
@ -832,8 +831,11 @@ BluetoothAdapter::EnableDisable(bool aEnable, ErrorResult& aRv)
methodName);
if(NS_FAILED(bs->EnableDisable(aEnable, result))) {
// Restore mState and notify applications of adapter state change
mState = aEnable ? BluetoothAdapterState::Disabled
: BluetoothAdapterState::Enabled;
HandleAdapterStateChanged();
promise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
}
@ -894,6 +896,16 @@ BluetoothAdapter::IsAdapterAttributeChanged(BluetoothAdapterAttribute aType,
}
}
void
BluetoothAdapter::HandleAdapterStateChanged()
{
nsTArray<nsString> types;
BT_APPEND_ENUM_STRING(types,
BluetoothAdapterAttribute,
BluetoothAdapterAttribute::State);
DispatchAttributeEvent(types);
}
void
BluetoothAdapter::HandlePropertyChanged(const BluetoothValue& aValue)
{

View File

@ -193,6 +193,7 @@ private:
bool IsAdapterAttributeChanged(BluetoothAdapterAttribute aType,
const BluetoothValue& aValue);
void HandleAdapterStateChanged();
void HandlePropertyChanged(const BluetoothValue& aValue);
void DispatchAttributeEvent(const nsTArray<nsString>& aTypes);
BluetoothAdapterAttribute

View File

@ -5,6 +5,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
XPIDL_SOURCES += [
'nsICellInfo.idl',
'nsIMobileCellInfo.idl',
'nsIMobileConnectionInfo.idl',
'nsIMobileConnectionProvider.idl',

View File

@ -0,0 +1,256 @@
/* 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 "nsISupports.idl"
[scriptable, uuid(1aed4c36-979e-4d20-9fa0-55139da8301e)]
interface nsICellInfoListCallback : nsISupports
{
/**
* result is an array of nsICellInfo.
*/
void notifyGetCellInfoList(in jsval result);
/**
* Callback function with error message.
*/
void notifyGetCellInfoListFailed(in DOMString error);
};
[scriptable, uuid(86667898-c9ab-44ee-8a9a-026916b3183e)]
interface nsICellInfo : nsISupports
{
const long CELL_INFO_TYPE_GSM = 1;
const long CELL_INFO_TYPE_CDMA = 2;
const long CELL_INFO_TYPE_LTE = 3;
const long CELL_INFO_TYPE_WCDMA = 4;
const long TIMESTAMP_TYPE_UNKNOWN = 0;
const long TIMESTAMP_TYPE_ANTENNA = 1;
const long TIMESTAMP_TYPE_MODEM = 2;
const long TIMESTAMP_TYPE_OEM_RIL = 3;
const long TIMESTAMP_TYPE_JAVA_RIL = 4;
/**
* Network type. One of the CELL_INFO_TYPE_* constants.
*/
readonly attribute long type;
/*
* Registration state of this cell.
*/
readonly attribute boolean registered;
/**
* Time stamp type. One of the TIMESTAMP_TYPE_* constants.
*/
readonly attribute long timestampType;
/**
* Time in nanoseconds since boot.
*/
readonly attribute long long timestamp;
};
[scriptable, uuid(6345967c-61fc-45a1-8362-39e9261df052)]
interface nsIGsmCellInfo : nsICellInfo
{
/**
* 3-digit Mobile Country Code, 0..999, INT_MAX if unknown.
*/
readonly attribute long mcc;
/**
* 2 or 3-digit Mobile Network Code, 0..999, INT_MAX if unknown.
*/
readonly attribute long mnc;
/**
* 16-bit Location Area Code, 0..65535, INT_MAX if unknown.
*/
readonly attribute long lac;
/**
* 16-bit GSM Cell Identity described in TS 27.007, 0..65535, INT_MAX if unknown.
*/
readonly attribute long cid;
/**
* Valid values are 0-31 as defined in TS 27.007 8.5, 99 if unknown.
*/
readonly attribute long signalStrength;
/**
* Bit error rate 0-7 as defined in TS 27.007 8.5, 99 if unknown.
*/
readonly attribute long bitErrorRate;
};
[scriptable, uuid(19693f98-943d-45e7-a3e8-25373228ce6b)]
interface nsIWcdmaCellInfo : nsICellInfo
{
/**
* 2 or 3-digit Mobile Network Code, 0..999, INT_MAX if unknown.
*/
readonly attribute long mcc;
/**
* 2 or 3-digit Mobile Network Code, 0..999, INT_MAX if unknown.
*/
readonly attribute long mnc;
/**
* 16-bit Location Area Code, 0..65535, INT_MAX if unknown.
*/
readonly attribute long lac;
/**
* 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455,
* INT_MAX if unknown.
*/
readonly attribute long cid;
/**
* 9-bit UMTS Primary Scrambling Code described in TS 25.331, 0..511,
* INT_MAX if unknown.
*/
readonly attribute long psc;
/**
* Valid values are 0-31 as defined in TS 27.007 8.5, 99 if unknown.
*/
readonly attribute long signalStrength;
/**
* Bit error rate 0-7 as defined in TS 27.007 8.5, 99 if unknown.
*/
readonly attribute long bitErrorRate;
};
[scriptable, uuid(76b4a35d-7e45-42bc-a2e0-bc07a6434db3)]
interface nsICdmaCellInfo : nsICellInfo
{
/**
* Network Id, 0..65535, INT_MAX if unknown.
*/
readonly attribute long networkId;
/**
* CDMA System Id, 0..32767, INT_MAX if unknown.
*/
readonly attribute long systemId;
/**
* Base Station Id, 0..65535, INT_MAX if unknown.
*/
readonly attribute long baseStationId;
/**
* Longitude is a decimal number as specified in 3GPP2 C.S0005-A v6.0.
* It is represented in units of 0.25 seconds and ranges from -2592000 to
* 2592000, INT_MAX if unknown.
*/
readonly attribute long longitude;
/**
* Latitude is a decimal number as specified in 3GPP2 C.S0005-A v6.0.
* It is represented in units of 0.25 seconds and ranges from -1296000 to
* 1296000, INT_MAX if unknown.
*/
readonly attribute long latitude;
/**
* Valid values are positive integers, INT_MAX if unknown. This value is the
* actual RSSI value multiplied by -1.
*/
readonly attribute long cdmaDbm;
/**
* Valid values are positive integers, INT_MAX if unknown. This value is the
* actual Ec/Io multiplied by -10. -1 if unknown.
*/
readonly attribute long cdmaEcio;
/**
* Valid values are positive integers, INT_MAX if unknown. This value is the
* actual Evdo RSSI value multiplied by -1.
*/
readonly attribute long evdoDbm;
/**
* Valid values are positive integers, INT_MAX if unknown. This value is the
* actual Evdo Ec/Io multiplied by -10.
*/
readonly attribute long evdoEcio;
/**
* Valid values are 0-8, INT_MAX if unknown. 8 is the highest signal to noise
* ratio.
*/
readonly attribute long evdoSnr;
};
[scriptable, uuid(122937d9-1ee5-45e0-a360-5959d578bc31)]
interface nsILteCellInfo : nsICellInfo
{
/**
* 3-digit Mobile Country Code, 0..999, INT_MAX if unknown.
*/
readonly attribute long mcc;
/**
* 2 or 3-digit Mobile Network Code, 0..999, INT_MAX if unknown.
*/
readonly attribute long mnc;
/**
* 28-bit Cell Identity, 0..268435455, INT_MAX if unknown.
*/
readonly attribute long cid;
/**
* Physical cell id, 0..503, INT_MAX if unknown.
*/
readonly attribute long pcid;
/**
* 16-bit tracking area code, 0..65535, INT_MAX if unknown.
*/
readonly attribute long tac;
/**
* Valid values are 0-31 as defined in TS 27.007 8.5, 99 if unknown.
*/
readonly attribute long signalStrength;
/**
* The current Reference Signal Receive Power in dBm multipled by -1.
* Range: 44 to 140 dBm, INT_MAX if unknown.
*/
readonly attribute long rsrp;
/**
* The current Reference Signal Receive Quality in dB multiplied by -1.
* Range: 3 to 20 dB, INT_MAX if unknown.
*/
readonly attribute long rsrq;
/**
* The current reference signal signal-to-noise ratio in 0.1 dB units.
* Range: -200 to +300 (-200 = -20.0 dB, +300 = 30dB), INT_MAX if unknown.
*/
readonly attribute long rssnr;
/**
* The current Channel Quality Indicator. Range: 0 to 15, INT_MAX if unknown.
*/
readonly attribute long cqi;
/**
* Timing advance in micro seconds for a one way trip from cell to device.
* Approximate distance can be calculated using 300m/us * timingAdvance.
* Range: 0 to 0x7FFFFFFE, INT_MAX if unknown.
*/
readonly attribute long timingAdvance;
};

View File

@ -28,6 +28,7 @@
namespace android {
class MOZ_EXPORT MtpServer;
class MOZ_EXPORT MtpStorage;
class MOZ_EXPORT MtpStringBuffer;
class MOZ_EXPORT MtpDatabase;
class MOZ_EXPORT MtpDataPacket;
class MOZ_EXPORT MtpProperty;
@ -39,6 +40,7 @@ namespace android {
#include <MtpProperty.h>
#include <MtpServer.h>
#include <MtpStorage.h>
#include <MtpStringBuffer.h>
#include <MtpTypes.h>
#endif // mozilla_system_mtpcommon_h__

View File

@ -29,6 +29,7 @@ ObjectPropertyAsStr(MtpObjectProperty aProperty)
{
switch (aProperty) {
case MTP_PROPERTY_STORAGE_ID: return "MTP_PROPERTY_STORAGE_ID";
case MTP_PROPERTY_OBJECT_FILE_NAME: return "MTP_PROPERTY_OBJECT_FILE_NAME";
case MTP_PROPERTY_OBJECT_FORMAT: return "MTP_PROPERTY_OBJECT_FORMAT";
case MTP_PROPERTY_OBJECT_SIZE: return "MTP_PROPERTY_OBJECT_SIZE";
case MTP_PROPERTY_WIDTH: return "MTP_PROPERTY_WIDTH";
@ -102,6 +103,22 @@ MozMtpDatabase::BaseName(const nsCString& path)
return path;
}
static nsCString
GetPathWithoutFileName(const nsCString& aFullPath)
{
nsCString path;
int32_t offset = aFullPath.RFindChar('/');
if (offset != kNotFound) {
// The trailing slash will be as part of 'path'
path = StringHead(aFullPath, offset + 1);
}
MTP_LOG("returning '%s'", path.get());
return path;
}
void
MozMtpDatabase::ParseDirectory(const char *aDir, MtpObjectHandle aParent)
{
@ -353,14 +370,84 @@ MozMtpDatabase::getObjectPropertyValue(MtpObjectHandle aHandle,
return MTP_RESPONSE_OK;
}
static int
GetTypeOfObjectProp(MtpObjectProperty aProperty)
{
struct PropertyTableEntry {
MtpObjectProperty property;
int type;
};
static const PropertyTableEntry kObjectPropertyTable[] = {
{MTP_PROPERTY_STORAGE_ID, MTP_TYPE_UINT32 },
{MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16 },
{MTP_PROPERTY_PROTECTION_STATUS, MTP_TYPE_UINT16 },
{MTP_PROPERTY_OBJECT_SIZE, MTP_TYPE_UINT64 },
{MTP_PROPERTY_OBJECT_FILE_NAME, MTP_TYPE_STR },
{MTP_PROPERTY_DATE_MODIFIED, MTP_TYPE_STR },
{MTP_PROPERTY_PARENT_OBJECT, MTP_TYPE_UINT32 },
{MTP_PROPERTY_DISPLAY_NAME, MTP_TYPE_STR },
{MTP_PROPERTY_DATE_ADDED, MTP_TYPE_STR },
};
int count = sizeof(kObjectPropertyTable) / sizeof(kObjectPropertyTable[0]);
const PropertyTableEntry* entryProp = kObjectPropertyTable;
int type = 0;
for (int i = 0; i < count; ++i, ++entryProp) {
if (entryProp->property == aProperty) {
type = entryProp->type;
break;
}
}
return type;
}
//virtual
MtpResponseCode
MozMtpDatabase::setObjectPropertyValue(MtpObjectHandle aHandle,
MtpObjectProperty aProperty,
MtpDataPacket& aPacket)
{
MTP_LOG("Handle: 0x%08x (NOT SUPPORTED)", aHandle);
return MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
MTP_LOG("Handle: 0x%08x Property: 0x%08x", aHandle, aProperty);
// Only support file name change
if (aProperty != MTP_PROPERTY_OBJECT_FILE_NAME) {
MTP_ERR("property 0x%x not supported", aProperty);
return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
}
if (GetTypeOfObjectProp(aProperty) != MTP_TYPE_STR) {
MTP_ERR("property type 0x%x not supported", GetTypeOfObjectProp(aProperty));
return MTP_RESPONSE_GENERAL_ERROR;
}
RefPtr<DbEntry> entry = GetEntry(aHandle);
if (!entry) {
MTP_ERR("Invalid Handle: 0x%08x", aHandle);
return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
}
MtpStringBuffer buf;
aPacket.getString(buf);
nsDependentCString newFileName(buf);
nsCString newFileFullPath(GetPathWithoutFileName(entry->mPath) + newFileName);
if (PR_Rename(entry->mPath.get(), newFileFullPath.get()) != PR_SUCCESS) {
MTP_ERR("Failed to rename '%s' to '%s'",
entry->mPath.get(), newFileFullPath.get());
return MTP_RESPONSE_GENERAL_ERROR;
}
MTP_LOG("renamed '%s' to '%s'", entry->mPath.get(), newFileFullPath.get());
entry->mPath = newFileFullPath;
entry->mObjectName = BaseName(entry->mPath);
entry->mDisplayName = entry->mObjectName;
return MTP_RESPONSE_OK;
}
//virtual
@ -635,7 +722,13 @@ MozMtpDatabase::getObjectInfo(MtpObjectHandle aHandle,
aInfo.mStorageID = entry->mStorageID;
aInfo.mFormat = entry->mObjectFormat;
aInfo.mProtectionStatus = 0x0;
aInfo.mCompressedSize = 0;
if (entry->mObjectSize > 0xFFFFFFFFuLL) {
aInfo.mCompressedSize = 0xFFFFFFFFuLL;
} else {
aInfo.mCompressedSize = entry->mObjectSize;
}
aInfo.mThumbFormat = entry->mObjectFormat;
aInfo.mThumbCompressedSize = 20*20*4;
aInfo.mThumbPixWidth = 20;
@ -748,18 +841,36 @@ MozMtpDatabase::getObjectPropertyDesc(MtpObjectProperty aProperty,
{
MTP_LOG("Property: %s 0x%08x", ObjectPropertyAsStr(aProperty), aProperty);
// TODO: Perhaps Filesize should be 64-bit?
MtpProperty* result = nullptr;
switch (aProperty)
{
case MTP_PROPERTY_STORAGE_ID: result = new MtpProperty(aProperty, MTP_TYPE_UINT32); break;
case MTP_PROPERTY_OBJECT_FORMAT: result = new MtpProperty(aProperty, MTP_TYPE_UINT32); break;
case MTP_PROPERTY_OBJECT_SIZE: result = new MtpProperty(aProperty, MTP_TYPE_UINT32); break;
case MTP_PROPERTY_WIDTH: result = new MtpProperty(aProperty, MTP_TYPE_UINT32); break;
case MTP_PROPERTY_HEIGHT: result = new MtpProperty(aProperty, MTP_TYPE_UINT32); break;
case MTP_PROPERTY_IMAGE_BIT_DEPTH: result = new MtpProperty(aProperty, MTP_TYPE_UINT32); break;
case MTP_PROPERTY_DISPLAY_NAME: result = new MtpProperty(aProperty, MTP_TYPE_STR); break;
case MTP_PROPERTY_PROTECTION_STATUS:
result = new MtpProperty(aProperty, MTP_TYPE_UINT16);
break;
case MTP_PROPERTY_OBJECT_FORMAT:
result = new MtpProperty(aProperty, MTP_TYPE_UINT16, false, aFormat);
break;
case MTP_PROPERTY_STORAGE_ID:
case MTP_PROPERTY_PARENT_OBJECT:
case MTP_PROPERTY_WIDTH:
case MTP_PROPERTY_HEIGHT:
case MTP_PROPERTY_IMAGE_BIT_DEPTH:
result = new MtpProperty(aProperty, MTP_TYPE_UINT32);
break;
case MTP_PROPERTY_OBJECT_SIZE:
result = new MtpProperty(aProperty, MTP_TYPE_UINT64);
break;
case MTP_PROPERTY_DISPLAY_NAME:
result = new MtpProperty(aProperty, MTP_TYPE_STR);
break;
case MTP_PROPERTY_OBJECT_FILE_NAME:
result = new MtpProperty(aProperty, MTP_TYPE_STR, true);
break;
case MTP_PROPERTY_DATE_MODIFIED:
case MTP_PROPERTY_DATE_ADDED:
result = new MtpProperty(aProperty, MTP_TYPE_STR);
result->setFormDateTime();
break;
default:
break;
}

View File

@ -59,6 +59,14 @@ const CDMAICCINFO_CID =
Components.ID("{39ba3c08-aacc-46d0-8c04-9b619c387061}");
const NEIGHBORINGCELLINFO_CID =
Components.ID("{f9dfe26a-851e-4a8b-a769-cbb1baae7ded}");
const GSMCELLINFO_CID =
Components.ID("{41f6201e-7263-42e3-b31f-38a9dc8a280a}");
const WCDMACELLINFO_CID =
Components.ID("{eeaaf307-df6e-4c98-b121-e3302b1fc468}");
const CDMACELLINFO_CID =
Components.ID("{b497d6e4-4cb8-4d6e-b673-840c7d5ddf25}");
const LTECELLINFO_CID =
Components.ID("{c7e0a78a-4e99-42f5-9251-e6172c5ed8d8}");
const NS_XPCOM_SHUTDOWN_OBSERVER_ID = "xpcom-shutdown";
const kNetworkInterfaceStateChangedTopic = "network-interface-state-changed";
@ -97,6 +105,9 @@ const RADIO_POWER_OFF_TIMEOUT = 30000;
const SMS_HANDLED_WAKELOCK_TIMEOUT = 5000;
const HW_DEFAULT_CLIENT_ID = 0;
const INT32_MAX = 2147483647;
const UNKNOWN_RSSI = 99;
const RIL_IPC_MOBILECONNECTION_MSG_NAMES = [
"RIL:GetRilContext",
"RIL:GetAvailableNetworks",
@ -1076,7 +1087,109 @@ NeighboringCellInfo.prototype = {
gsmLocationAreaCode: -1,
gsmCellId: -1,
wcdmaPsc: -1,
signalStrength: 99
signalStrength: UNKNOWN_RSSI
};
function CellInfo() {}
CellInfo.prototype = {
type: null,
registered: false,
timestampType: Ci.nsICellInfo.TIMESTAMP_TYPE_UNKNOWN,
timestamp: 0
};
function GsmCellInfo() {}
GsmCellInfo.prototype = {
__proto__: CellInfo.prototype,
QueryInterface: XPCOMUtils.generateQI([Ci.nsIGsmCellInfo]),
classID: GSMCELLINFO_CID,
classInfo: XPCOMUtils.generateCI({
classID: GSMCELLINFO_CID,
classDescription: "GsmCellInfo",
interfaces: [Ci.nsIGsmCellInfo]
}),
// nsIGsmCellInfo
mcc: INT32_MAX,
mnc: INT32_MAX,
lac: INT32_MAX,
cid: INT32_MAX,
signalStrength: UNKNOWN_RSSI,
bitErrorRate: UNKNOWN_RSSI
};
function WcdmaCellInfo() {}
WcdmaCellInfo.prototype = {
__proto__: CellInfo.prototype,
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWcdmaCellInfo]),
classID: WCDMACELLINFO_CID,
classInfo: XPCOMUtils.generateCI({
classID: WCDMACELLINFO_CID,
classDescription: "WcdmaCellInfo",
interfaces: [Ci.nsIWcdmaCellInfo]
}),
// nsIWcdmaCellInfo
mcc: INT32_MAX,
mnc: INT32_MAX,
lac: INT32_MAX,
cid: INT32_MAX,
psc: INT32_MAX,
signalStrength: UNKNOWN_RSSI,
bitErrorRate: UNKNOWN_RSSI
};
function LteCellInfo() {}
LteCellInfo.prototype = {
__proto__: CellInfo.prototype,
QueryInterface: XPCOMUtils.generateQI([Ci.nsILteCellInfo]),
classID: LTECELLINFO_CID,
classInfo: XPCOMUtils.generateCI({
classID: LTECELLINFO_CID,
classDescription: "LteCellInfo",
interfaces: [Ci.nsILteCellInfo]
}),
// nsILteCellInfo
mcc: INT32_MAX,
mnc: INT32_MAX,
cid: INT32_MAX,
pcid: INT32_MAX,
tac: INT32_MAX,
signalStrength: UNKNOWN_RSSI,
rsrp: INT32_MAX,
rsrq: INT32_MAX,
rssnr: INT32_MAX,
cqi: INT32_MAX,
timingAdvance: INT32_MAX
};
function CdmaCellInfo() {}
CdmaCellInfo.prototype = {
__proto__: CellInfo.prototype,
QueryInterface: XPCOMUtils.generateQI([Ci.nsICdmaCellInfo]),
classID: CDMACELLINFO_CID,
classInfo: XPCOMUtils.generateCI({
classID: CDMACELLINFO_CID,
classDescription: "CdmaCellInfo",
interfaces: [Ci.nsICdmaCellInfo]
}),
// nsICdmaCellInfo
networkId: INT32_MAX,
systemId: INT32_MAX,
baseStationId: INT32_MAX,
longitude: INT32_MAX,
latitude: INT32_MAX,
cdmaDbm: INT32_MAX,
cdmaEcio: INT32_MAX,
evdoDbm: INT32_MAX,
evdoEcio: INT32_MAX,
evdoSnr: INT32_MAX
};
function DataConnectionHandler(clientId, radioInterface) {
@ -4420,6 +4533,45 @@ RadioInterface.prototype = {
}
},
getCellInfoList: function(callback) {
this.workerMessenger.send("getCellInfoList",
null,
function(response) {
if (response.errorMsg) {
callback.notifyGetCellInfoListFailed(response.errorMsg);
return;
}
let cellInfoList = [];
let count = response.result.length;
for (let i = 0; i < count; i++) {
let srcCellInfo = response.result[i];
let cellInfo;
switch (srcCellInfo.type) {
case RIL.CELL_INFO_TYPE_GSM:
cellInfo = new GsmCellInfo();
break;
case RIL.CELL_INFO_TYPE_WCDMA:
cellInfo = new WcdmaCellInfo();
break;
case RIL.CELL_INFO_TYPE_LTE:
cellInfo = new LteCellInfo();
break;
case RIL.CELL_INFO_TYPE_CDMA:
cellInfo = new CdmaCellInfo();
break;
}
if (!cellInfo) {
continue;
}
this.updateInfo(srcCellInfo, cellInfo);
cellInfoList.push(cellInfo);
}
callback.notifyGetCellInfoList(cellInfoList);
}.bind(this));
},
getNeighboringCellIds: function(callback) {
this.workerMessenger.send("getNeighboringCellIds",
null,

View File

@ -9,6 +9,7 @@ interface nsIDOMMozIccInfo;
interface nsIMobileConnectionInfo;
interface nsIMobileMessageCallback;
interface nsINeighboringCellIdsCallback;
interface nsICellInfoListCallback;
[scriptable, uuid(6e0f45b8-410e-11e3-8c8e-b715b2cd0128)]
interface nsIRilNetworkInterface : nsINetworkInterface
@ -56,7 +57,7 @@ interface nsIRilSendWorkerMessageCallback : nsISupports
boolean handleResponse(in jsval response);
};
[scriptable, uuid(31ba65b6-05c7-4bc8-abdc-f1a219811fb4)]
[scriptable, uuid(c13a8890-797b-4557-b92f-6b959f56c1d8)]
interface nsIRadioInterface : nsISupports
{
readonly attribute nsIRilContext rilContext;
@ -88,9 +89,15 @@ interface nsIRadioInterface : nsISupports
void getSmscAddress(in nsIMobileMessageCallback request);
/**
* Cell Info functionality.
* Request neighboring cell ids in GSM/UMTS network.
*/
void getNeighboringCellIds(in nsINeighboringCellIdsCallback callback);
/**
* Request all of the current cell information known to the radio, including
* neighboring cells.
*/
void getCellInfoList(in nsICellInfoListCallback callback);
};
[scriptable, uuid(78b65e8c-68e7-4510-9a05-65bba12b283e)]

Some files were not shown because too many files have changed in this diff Show More