mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge m-c to inbound
This commit is contained in:
commit
adf51a28f6
1
.hgtags
1
.hgtags
@ -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
|
||||
|
2
CLOBBER
2
CLOBBER
@ -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
|
3
addon-sdk/source/examples/actor-repl/README.md
Normal file
3
addon-sdk/source/examples/actor-repl/README.md
Normal 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
264
addon-sdk/source/examples/actor-repl/data/codemirror.css
Normal file
264
addon-sdk/source/examples/actor-repl/data/codemirror.css
Normal 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;
|
||||
}
|
||||
}
|
108
addon-sdk/source/examples/actor-repl/data/index.html
Normal file
108
addon-sdk/source/examples/actor-repl/data/index.html
Normal 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>
|
117
addon-sdk/source/examples/actor-repl/data/main.css
Normal file
117
addon-sdk/source/examples/actor-repl/data/main.css
Normal 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);
|
||||
}
|
||||
}
|
BIN
addon-sdk/source/examples/actor-repl/data/robot.png
Normal file
BIN
addon-sdk/source/examples/actor-repl/data/robot.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.1 KiB |
37
addon-sdk/source/examples/actor-repl/index.js
Normal file
37
addon-sdk/source/examples/actor-repl/index.js
Normal 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 }
|
||||
});
|
10
addon-sdk/source/examples/actor-repl/package.json
Normal file
10
addon-sdk/source/examples/actor-repl/package.json
Normal 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"
|
||||
}
|
816
addon-sdk/source/examples/debug-client/data/client.js
Normal file
816
addon-sdk/source/examples/debug-client/data/client.js
Normal 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);
|
||||
|
50
addon-sdk/source/examples/debug-client/data/index.html
Normal file
50
addon-sdk/source/examples/debug-client/data/index.html
Normal 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>
|
BIN
addon-sdk/source/examples/debug-client/data/plugin.png
Normal file
BIN
addon-sdk/source/examples/debug-client/data/plugin.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.7 KiB |
28
addon-sdk/source/examples/debug-client/data/task.js
Normal file
28
addon-sdk/source/examples/debug-client/data/task.js
Normal 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 = {});
|
33
addon-sdk/source/examples/debug-client/index.js
Normal file
33
addon-sdk/source/examples/debug-client/index.js
Normal 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 }
|
||||
});
|
10
addon-sdk/source/examples/debug-client/package.json
Normal file
10
addon-sdk/source/examples/debug-client/package.json
Normal 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"
|
||||
}
|
95
addon-sdk/source/lib/dev/debuggee.js
Normal file
95
addon-sdk/source/lib/dev/debuggee.js
Normal 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;
|
115
addon-sdk/source/lib/dev/frame-script.js
Normal file
115
addon-sdk/source/lib/dev/frame-script.js
Normal file
@ -0,0 +1,115 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"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);
|
223
addon-sdk/source/lib/dev/panel.js
Normal file
223
addon-sdk/source/lib/dev/panel.js
Normal 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();
|
||||
});
|
64
addon-sdk/source/lib/dev/ports.js
Normal file
64
addon-sdk/source/lib/dev/ports.js
Normal 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;
|
75
addon-sdk/source/lib/dev/toolbox.js
Normal file
75
addon-sdk/source/lib/dev/toolbox.js
Normal 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;
|
38
addon-sdk/source/lib/dev/utils.js
Normal file
38
addon-sdk/source/lib/dev/utils.js
Normal 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;
|
3760
addon-sdk/source/lib/dev/volcan.js
Normal file
3760
addon-sdk/source/lib/dev/volcan.js
Normal file
File diff suppressed because one or more lines are too long
@ -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
|
||||
|
@ -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.'
|
||||
}
|
||||
};
|
||||
|
@ -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')
|
||||
|
@ -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 }) {
|
||||
|
@ -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, {
|
||||
|
@ -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 };
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -66,6 +66,10 @@ function get(key, n, locales) {
|
||||
localized = getKey(locale, key);
|
||||
}
|
||||
|
||||
if (!localized) {
|
||||
localized = getKey(locale, key + '[other]');
|
||||
}
|
||||
|
||||
if (localized) {
|
||||
return localized;
|
||||
}
|
||||
|
12
addon-sdk/source/lib/sdk/messaging.js
Normal file
12
addon-sdk/source/lib/sdk/messaging.js
Normal 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;
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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,";
|
||||
|
@ -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"] },
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
});
|
||||
|
@ -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";
|
||||
|
||||
|
@ -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]
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
});
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
});
|
||||
|
||||
|
@ -1,3 +0,0 @@
|
||||
<script>
|
||||
addon.postMessage("hello addon")
|
||||
</script>
|
@ -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);
|
@ -1,3 +0,0 @@
|
||||
{
|
||||
"id": "test-privileged-addon"
|
||||
}
|
@ -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(/^\.\//, "");
|
||||
|
1
addon-sdk/source/test/fixtures/border-style.css
vendored
Normal file
1
addon-sdk/source/test/fixtures/border-style.css
vendored
Normal file
@ -0,0 +1 @@
|
||||
div { border-style: dashed; }
|
@ -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
|
||||
|
@ -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)];
|
||||
|
@ -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,
|
||||
|
@ -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"),
|
||||
|
236
addon-sdk/source/test/test-dev-panel.js
Normal file
236
addon-sdk/source/test/test-dev-panel.js
Normal file
File diff suppressed because one or more lines are too long
@ -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");
|
||||
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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';
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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"/>
|
||||
|
@ -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 -->
|
||||
|
@ -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"/>
|
||||
|
@ -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"/>
|
||||
|
@ -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 -->
|
||||
|
@ -4,6 +4,6 @@
|
||||
"remote": "",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "cea1598e51f188fe8c48196cbb2c73aacbf3b1dc",
|
||||
"revision": "591c866cb04a88306e472c873429669229f28540",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
@ -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"/>
|
||||
|
@ -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"/>
|
||||
|
@ -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 -->
|
||||
|
@ -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"/>
|
||||
|
@ -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
|
||||
|
@ -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":
|
||||
|
@ -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)
|
||||
|
@ -1 +1 @@
|
||||
33.0a1
|
||||
34.0a1
|
||||
|
@ -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]
|
||||
|
@ -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;
|
||||
});
|
22
browser/devtools/debugger/test/doc_native-event-handler.html
Normal file
22
browser/devtools/debugger/test/doc_native-event-handler.html
Normal 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>
|
@ -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.
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -10,4 +10,4 @@
|
||||
# hardcoded milestones in the tree from these two files.
|
||||
#--------------------------------------------------------
|
||||
|
||||
33.0a1
|
||||
34.0a1
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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',
|
||||
|
256
dom/mobileconnection/interfaces/nsICellInfo.idl
Normal file
256
dom/mobileconnection/interfaces/nsICellInfo.idl
Normal 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;
|
||||
};
|
@ -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__
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user