This commit is contained in:
Ryan VanderMeulen 2013-08-26 20:20:23 -04:00
Родитель b7e11dbae6 9c2f0c5982
Коммит d6257fa5cd
91 изменённых файлов: 1158 добавлений и 842 удалений

Просмотреть файл

@ -13,7 +13,6 @@ const { exit, env, staticArgs } = require('../system');
const { when: unload } = require('../system/unload');
const { loadReason } = require('../self');
const { rootURI } = require("@loader/options");
const cfxArgs = require("@test/options");
const globals = require('../system/globals');
const xulApp = require('../system/xul-app');
const appShellService = Cc['@mozilla.org/appshell/appShellService;1'].
@ -102,11 +101,7 @@ function startup(reason, options) {
// Run the addon even in case of error (best effort approach)
require('../l10n/loader').
load(rootURI).
then(function l10nSuccess() {
if (cfxArgs.parseable) {
console.info("localization information has loaded successfully.");
}
}, function l10nFailure(error) {
then(null, function failure(error) {
console.info("Error while loading localization: " + error.message);
}).
then(function onLocalizationReady(data) {
@ -115,10 +110,6 @@ function startup(reason, options) {
definePseudo(options.loader, '@l10n/data', data ? data : null);
return ready;
}).then(function() {
if (cfxArgs.parseable) {
console.info("addon window has loaded successfully.");
}
run(options);
}).then(null, console.exception);
}
@ -137,7 +128,6 @@ function run(options) {
catch(error) {
console.exception(error);
}
// Initialize inline options localization, without preventing addon to be
// run in case of error
try {
@ -167,8 +157,7 @@ function run(options) {
quit: exit
});
}
}
catch (error) {
} catch (error) {
console.exception(error);
throw error;
}

Просмотреть файл

@ -1,17 +1,14 @@
/* 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"
};
var obsvc = require("../deprecated/observer-service");
var { exit, stdout } = require("../system");
var cfxArgs = require("@test/options");
var { Cc, Ci} = require("chrome");
function runTests(findAndRunTests) {
var harness = require("./harness");
@ -62,7 +59,7 @@ function printFailedTests(tests, print) {
iterationNumber++;
if (!singleIteration)
print(" Iteration " + iterationNumber + ":\n");
print(" Iteration " + iterationNumber + ":\n");
for each (let test in testRun) {
if (test.failed > 0) {

Просмотреть файл

@ -48,6 +48,20 @@ const prototypeOf = Object.getPrototypeOf;
const create = Object.create;
const keys = Object.keys;
const COMPONENT_ERROR = '`Components` is not available in this context.\n' +
'Functionality provided by Components may be available in an SDK\n' +
'module: https://jetpack.mozillalabs.com/sdk/latest/docs/ \n\n' +
'However, if you still need to import Components, you may use the\n' +
'`chrome` module\'s properties for shortcuts to Component properties:\n\n' +
'Shortcuts: \n' +
' Cc = Components' + '.classes \n' +
' Ci = Components' + '.interfaces \n' +
' Cu = Components' + '.utils \n' +
' CC = Components' + '.Constructor \n' +
'Example: \n' +
' let { Cc, Ci } = require(\'chrome\');\n';
// Workaround for bug 674195. Freezing objects from other compartments fail,
// so we use `Object.freeze` from the same component instead.
function freeze(object) {
@ -216,19 +230,26 @@ const load = iced(function load(loader, module) {
let { sandboxes, globals } = loader;
let require = Require(loader, module);
// We expose set of properties defined by `CommonJS` specification via
// prototype of the sandbox. Also globals are deeper in the prototype
// chain so that each module has access to them as well.
let descriptors = descriptor({
require: require,
module: module,
exports: module.exports,
get Components() {
// Expose `Components` property to throw error on usage with
// additional information
throw new ReferenceError(COMPONENT_ERROR);
}
});
let sandbox = sandboxes[module.uri] = Sandbox({
name: module.uri,
// Get an existing module sandbox, if any, so we can reuse its compartment
// when creating the new one to reduce memory consumption.
sandbox: sandboxes[keys(sandboxes).shift()],
// We expose set of properties defined by `CommonJS` specification via
// prototype of the sandbox. Also globals are deeper in the prototype
// chain so that each module has access to them as well.
prototype: create(globals, descriptor({
require: require,
module: module,
exports: module.exports
})),
prototype: create(globals, descriptors),
wantXrays: false
});

Просмотреть файл

@ -1,26 +1,24 @@
/* 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';
"use strict";
const { List } = require('sdk/deprecated/list');
function assertList(test, array, list) {
for (let i = 0, ii = array.length; i < ii; i < ii, i++) {
test.assertEqual(
function assertList(assert, array, list) {
for (let i = 0, l = array.length; i < l; i++) {
assert.equal(
array.length,
list.length,
'list must contain same amount of elements as array'
);
test.assertEqual(
assert.equal(
'List(' + array + ')',
list + '',
'toString must output array like result'
);
test.assert(
i in list,
'must contain element with index: ' + i
);
test.assertEqual(
assert.ok(i in list, 'must contain element with index: ' + i);
assert.equal(
array[i],
list[i],
'element with index: ' + i + ' should match'
@ -28,89 +26,83 @@ function assertList(test, array, list) {
}
}
const { List } = require('sdk/deprecated/list');
exports['test:test for'] = function(test) {
exports['test:test for'] = function(assert) {
let fixture = List(3, 2, 1);
test.assertEqual(3, fixture.length, 'length is 3');
assert.equal(3, fixture.length, 'length is 3');
let i = 0;
for (let key in fixture) {
test.assertEqual(i++, key, 'key should match');
assert.equal(i++, key, 'key should match');
}
};
exports['test:test for each'] = function(test) {
exports['test:test for each'] = function(assert) {
let fixture = new List(3, 2, 1);
test.assertEqual(3, fixture.length, 'length is 3');
let i = 3;
for each (let value in fixture) {
test.assertEqual(i--, value, 'value should match');
}
};
exports['test:test for of'] = function(test) {
let fixture = new List(3, 2, 1);
test.assertEqual(3, fixture.length, 'length is 3');
assert.equal(3, fixture.length, 'length is 3');
let i = 3;
for (let value of fixture) {
test.assertEqual(i--, value, 'value should match');
assert.equal(i--, value, 'value should match');
}
};
exports['test:test toString'] = function(test) {
exports['test:test for of'] = function(assert) {
let fixture = new List(3, 2, 1);
assert.equal(3, fixture.length, 'length is 3');
let i = 3;
for (let value of fixture) {
assert.equal(i--, value, 'value should match');
}
};
exports['test:test toString'] = function(assert) {
let fixture = List(3, 2, 1);
test.assertEqual(
assert.equal(
'List(3,2,1)',
fixture + '',
'toString must output array like result'
)
};
exports['test:test constructor with apply'] = function(test) {
exports['test:test constructor with apply'] = function(assert) {
let array = ['a', 'b', 'c'];
let fixture = List.apply(null, array);
test.assertEqual(
assert.equal(
3,
fixture.length,
'should have applied arguments'
);
};
exports['test:direct element access'] = function(test) {
let array = [1, 'foo', 2, 'bar', {}, 'bar', function a() {}, test, 1];
exports['test:direct element access'] = function(assert) {
let array = [1, 'foo', 2, 'bar', {}, 'bar', function a() {}, assert, 1];
let fixture = List.apply(null, array);
array.splice(5, 1);
array.splice(7, 1);
test.assertEqual(
assert.equal(
array.length,
fixture.length,
'list should omit duplicate elements'
);
test.assertEqual(
assert.equal(
'List(' + array + ')',
fixture.toString(),
'elements should not be rearranged'
);
for (let key in array) {
test.assert(key in fixture,'should contain key for index:' + key);
test.assertEqual(
array[key],
fixture[key],
'values should match for: ' + key
);
assert.ok(key in fixture,'should contain key for index:' + key);
assert.equal(array[key], fixture[key], 'values should match for: ' + key);
}
};
exports['test:removing adding elements'] = function(test) {
let array = [1, 'foo', 2, 'bar', {}, 'bar', function a() {}, test, 1];
exports['test:removing adding elements'] = function(assert) {
let array = [1, 'foo', 2, 'bar', {}, 'bar', function a() {}, assert, 1];
let fixture = List.compose({
add: function() this._add.apply(this, arguments),
remove: function() this._remove.apply(this, arguments),
@ -119,11 +111,11 @@ exports['test:removing adding elements'] = function(test) {
array.splice(5, 1);
array.splice(7, 1);
assertList(test, array, fixture);
assertList(assert, array, fixture);
array.splice(array.indexOf(2), 1);
fixture.remove(2);
assertList(test, array, fixture);
assertList(assert, array, fixture);
array.splice(array.indexOf('foo'), 1);
fixture.remove('foo');
@ -131,11 +123,11 @@ exports['test:removing adding elements'] = function(test) {
fixture.remove(1);
array.push('foo');
fixture.add('foo');
assertList(test, array, fixture);
assertList(assert, array, fixture);
array.splice(0);
fixture.clear(0);
assertList(test, array, fixture);
assertList(assert, array, fixture);
array.push(1, 'foo', 2, 'bar', 3);
fixture.add(1);
@ -144,26 +136,26 @@ exports['test:removing adding elements'] = function(test) {
fixture.add('bar');
fixture.add(3);
assertList(test, array, fixture);
assertList(assert, array, fixture);
};
exports['test: remove does not leave invalid numerical properties'] = function(test) {
exports['test: remove does not leave invalid numerical properties'] = function(assert) {
let fixture = List.compose({
remove: function() this._remove.apply(this, arguments),
}).apply(null, [1, 2, 3]);
fixture.remove(1);
test.assertEqual(fixture[fixture.length], undefined);
assert.equal(fixture[fixture.length], undefined);
}
exports['test:add list item from Iterator'] = function(test) {
exports['test:add list item from Iterator'] = function(assert) {
let array = [1, 2, 3, 4], sum = 0, added = false;
let fixture = List.compose({
add: function() this._add.apply(this, arguments),
}).apply(null, array);
for each (let item in fixture) {
for (let item of fixture) {
sum += item;
if (!added) {
@ -172,35 +164,37 @@ exports['test:add list item from Iterator'] = function(test) {
}
}
test.assertEqual(sum, 1 + 2 + 3 + 4);
assert.equal(sum, 1 + 2 + 3 + 4, 'sum = 1 + 2 + 3 + 4');
};
exports['test:remove list item from Iterator'] = function(test) {
exports['test:remove list item from Iterator'] = function(assert) {
let array = [1, 2, 3, 4], sum = 0;
let fixture = List.compose({
remove: function() this._remove.apply(this, arguments),
}).apply(null, array);
for each (let item in fixture) {
for (let item of fixture) {
sum += item;
fixture.remove(item);
}
test.assertEqual(sum, 1 + 2 + 3 + 4);
assert.equal(sum, 1 + 2 + 3 + 4, 'sum = 1 + 2 + 3 + 4');
};
exports['test:clear list from Iterator'] = function(test) {
exports['test:clear list from Iterator'] = function(assert) {
let array = [1, 2, 3, 4], sum = 0;
let fixture = List.compose({
clear: function() this._clear()
}).apply(null, array);
for each (let item in fixture) {
for (let item of fixture) {
sum += item;
fixture.clear();
}
test.assertEqual(sum, 1 + 2 + 3 + 4);
assert.equal(sum, 1 + 2 + 3 + 4, 'sum = 1 + 2 + 3 + 4');
};
require('sdk/test').run(exports);

Просмотреть файл

@ -7,17 +7,24 @@ Object.defineProperty(this, 'global', { value: this });
exports.testGlobals = function(assert) {
// the only globals in module scope should be:
assert.equal(typeof module, 'object', 'have "module" global');
assert.equal(typeof exports, 'object', 'have "exports" global');
assert.equal(typeof require, 'function', 'have "require" global');
assert.equal(typeof dump, 'function', 'have "dump" global');
assert.equal(typeof console, 'object', 'have "console" global');
// module, exports, require, dump, console
assert.equal(typeof module, 'object', 'have "module", good');
assert.equal(typeof exports, 'object', 'have "exports", good');
assert.equal(typeof require, 'function', 'have "require", good');
assert.equal(typeof dump, 'function', 'have "dump", good');
assert.equal(typeof console, 'object', 'have "console", good');
// in particular, these old globals should no longer be present
assert.ok(!('packaging' in global), 'no "packaging" global was found');
assert.ok(!('memory' in global), 'no "memory" global was found');
assert.ok(/test-globals\.js$/.test(module.uri), 'should contain filename');
assert.ok(!('packaging' in global), "no 'packaging', good");
assert.ok(!('memory' in global), "no 'memory', good");
assert.ok(/test-globals\.js$/.test(module.uri),
'should contain filename');
};
require("test").run(exports);
exports.testComponent = function (assert) {
assert.throws(() => {
Components;
}, /`Components` is not available/, 'using `Components` throws');
};
require('test').run(exports);

Просмотреть файл

@ -6,32 +6,32 @@
const { List, addListItem, removeListItem } = require('sdk/util/list');
const { Class } = require('sdk/core/heritage');
exports.testList = function(test) {
exports.testList = function(assert) {
let list = List();
addListItem(list, 1);
for (let key in list) {
test.assertEqual(key, 0, 'key is correct');
test.assertEqual(list[key], 1, 'value is correct');
assert.equal(key, 0, 'key is correct');
assert.equal(list[key], 1, 'value is correct');
}
let count = 0;
for each (let ele in list) {
test.assertEqual(ele, 1, 'ele is correct');
test.assertEqual(++count, 1, 'count is correct');
assert.equal(ele, 1, 'ele is correct');
assert.equal(++count, 1, 'count is correct');
}
count = 0;
for (let ele of list) {
test.assertEqual(ele, 1, 'ele is correct');
test.assertEqual(++count, 1, 'count is correct');
assert.equal(ele, 1, 'ele is correct');
assert.equal(++count, 1, 'count is correct');
}
removeListItem(list, 1);
test.assertEqual(list.length, 0, 'remove worked');
assert.equal(list.length, 0, 'remove worked');
};
exports.testImplementsList = function(test) {
exports.testImplementsList = function(assert) {
let List2 = Class({
implements: [List],
initialize: function() {
@ -42,15 +42,17 @@ exports.testImplementsList = function(test) {
let count = 0;
for each (let ele in list2) {
test.assertEqual(ele, count++, 'ele is correct');
assert.equal(ele, count++, 'ele is correct');
}
count = 0;
for (let ele of list2) {
test.assertEqual(ele, count++, 'ele is correct');
assert.equal(ele, count++, 'ele is correct');
}
addListItem(list2, 3);
test.assertEqual(list2.length, 4, '3 was added');
test.assertEqual(list2[list2.length-1], 3, '3 was added');
assert.equal(list2.length, 4, '3 was added');
assert.equal(list2[list2.length-1], 3, '3 was added');
}
require('sdk/test').run(exports);

Просмотреть файл

@ -1,24 +1,27 @@
/* 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 tmp = require("sdk/test/tmp-file");
const file = require("sdk/io/file");
const testFolderURL = module.uri.split('test-tmp-file.js')[0];
exports.testCreateFromString = function (test) {
exports.testCreateFromString = function (assert) {
let expectedContent = "foo";
let path = tmp.createFromString(expectedContent);
let content = file.read(path);
test.assertEqual(content, expectedContent,
"Temporary file contains the expected content");
assert.equal(content, expectedContent,
"Temporary file contains the expected content");
}
exports.testCreateFromURL = function (test) {
exports.testCreateFromURL = function (assert) {
let url = testFolderURL + "test-tmp-file.txt";
let path = tmp.createFromURL(url);
let content = file.read(path);
test.assertEqual(content, "foo",
"Temporary file contains the expected content");
assert.equal(content, "foo",
"Temporary file contains the expected content");
}
require("sdk/test").run(exports);

Просмотреть файл

@ -23,11 +23,11 @@
function createRootActor(connection)
{
let parameters = {
#ifndef MOZ_WIDGET_GONK
tabList: new ContentTabList(connection),
#else
tabList: [],
#endif
tabList: {
getList: function() {
return promise.resolve([]);
}
},
globalActorFactories: DebuggerServer.globalActorFactories,
onShutdown: sendShutdownEvent
};

Просмотреть файл

@ -1,4 +1,4 @@
{
"revision": "abcc2f6a74c69d6ed28ea29681f86ef1f4fd90ba",
"revision": "22cb357f03833f5ce63ba567c2fd4610a4c8061c",
"repo_path": "/integration/gaia-central"
}

Просмотреть файл

@ -171,7 +171,7 @@ var tests = {
domwindow.removeEventListener("load", _load, false);
domwindow.addEventListener("unload", function _close(event) {
if (event.target != doc)
if (event.target != doc)
return;
domwindow.removeEventListener("unload", _close, false);
ok(true, "window has been closed");

Просмотреть файл

@ -466,7 +466,8 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
let container = document.createElement("hbox");
container.id = "breakpoint-" + aOptions.actor;
container.className = "dbg-breakpoint side-menu-widget-item-other";
container.className = "dbg-breakpoint devtools-monospace" +
" side-menu-widget-item-other";
container.setAttribute("align", "center");
container.setAttribute("flex", "1");
@ -1429,7 +1430,7 @@ WatchExpressionsView.prototype = Heritage.extend(WidgetMethods, {
arrowNode.className = "dbg-expression-arrow";
let inputNode = document.createElement("textbox");
inputNode.className = "plain dbg-expression-input";
inputNode.className = "plain dbg-expression-input devtools-monospace";
inputNode.setAttribute("value", aAttachment.initialExpression);
inputNode.setAttribute("flex", "1");
@ -2194,9 +2195,10 @@ LineResults.prototype = {
let lineLength = 0;
let firstMatch = null;
lineNumberNode.className = "plain dbg-results-line-number";
lineNumberNode.className = "plain dbg-results-line-number devtools-monospace";
lineNumberNode.setAttribute("value", aLineNumber + 1);
lineContentsNode.className = "light list-widget-item dbg-results-line-contents";
lineContentsNode.className = "light list-widget-item devtools-monospace" +
" dbg-results-line-contents";
lineContentsNode.setAttribute("flex", "1");
for (let chunk of this._store) {

Просмотреть файл

@ -347,35 +347,35 @@
value="&debuggerUI.searchPanelOperators;"/>
<hbox align="center">
<button id="global-operator-button"
class="searchbox-panel-operator-button"
class="searchbox-panel-operator-button devtools-monospace"
command="globalSearchCommand"/>
<label id="global-operator-label"
class="plain searchbox-panel-operator-label"/>
</hbox>
<hbox align="center">
<button id="function-operator-button"
class="searchbox-panel-operator-button"
class="searchbox-panel-operator-button devtools-monospace"
command="functionSearchCommand"/>
<label id="function-operator-label"
class="plain searchbox-panel-operator-label"/>
</hbox>
<hbox align="center">
<button id="token-operator-button"
class="searchbox-panel-operator-button"
class="searchbox-panel-operator-button devtools-monospace"
command="tokenSearchCommand"/>
<label id="token-operator-label"
class="plain searchbox-panel-operator-label"/>
</hbox>
<hbox align="center">
<button id="line-operator-button"
class="searchbox-panel-operator-button"
class="searchbox-panel-operator-button devtools-monospace"
command="lineSearchCommand"/>
<label id="line-operator-label"
class="plain searchbox-panel-operator-label"/>
</hbox>
<hbox align="center">
<button id="variable-operator-button"
class="searchbox-panel-operator-button"
class="searchbox-panel-operator-button devtools-monospace"
command="variableSearchCommand"/>
<label id="variable-operator-label"
class="plain searchbox-panel-operator-label"/>

Просмотреть файл

@ -15,7 +15,6 @@ body {
#header {
-moz-box-sizing: border-box;
font: 12px/16px monospace;
width: 100%;
padding: 6px 9px;
display: -moz-box;
@ -44,7 +43,6 @@ body {
width: calc(100% - 2 * 10px);
position: absolute;
border-width: 1px;
font: 10px/12px monospace;
}
@media (min-width: 320px) {

Просмотреть файл

@ -65,11 +65,12 @@
]]>
</script>
<link rel="stylesheet" href="chrome://browser/skin/devtools/common.css" type="text/css"/>
<link rel="stylesheet" href="chrome://browser/skin/devtools/layoutview.css" type="text/css"/>
<link rel="stylesheet" href="view.css" type="text/css"/>
</head>
<body class="theme-body">
<body class="theme-body devtools-monospace">
<p id="header">
<span id="element-size"></span>

Просмотреть файл

@ -22,6 +22,10 @@ function test() {
getInplaceEditorForSpan: inplaceEditor
} = devtools.require("devtools/shared/inplace-editor");
// Prevent intermittent "test exceeded the timeout threshold" since this is
// a slow test: https://bugzilla.mozilla.org/show_bug.cgi?id=904953.
requestLongerTimeout(2);
waitForExplicitFinish();
// Will hold the doc we're viewing

Просмотреть файл

@ -288,7 +288,7 @@
<label class="plain tabpanel-summary-label"
value="&netmonitorUI.summary.url;"/>
<label id="headers-summary-url-value"
class="plain tabpanel-summary-value"
class="plain tabpanel-summary-value devtools-monospace"
crop="end"
flex="1"/>
</hbox>
@ -298,7 +298,7 @@
<label class="plain tabpanel-summary-label"
value="&netmonitorUI.summary.method;"/>
<label id="headers-summary-method-value"
class="plain tabpanel-summary-value"
class="plain tabpanel-summary-value devtools-monospace"
crop="end"
flex="1"/>
</hbox>
@ -310,7 +310,7 @@
<box id="headers-summary-status-circle"
class="requests-menu-status"/>
<label id="headers-summary-status-value"
class="plain tabpanel-summary-value"
class="plain tabpanel-summary-value devtools-monospace"
crop="end"
flex="1"/>
<button id="headers-summary-resend"
@ -324,7 +324,7 @@
<label class="plain tabpanel-summary-label"
value="&netmonitorUI.summary.version;"/>
<label id="headers-summary-version-value"
class="plain tabpanel-summary-value"
class="plain tabpanel-summary-value devtools-monospace"
crop="end"
flex="1"/>
</hbox>
@ -364,7 +364,7 @@
<label class="plain tabpanel-summary-label"
value="&netmonitorUI.response.name;"/>
<label id="response-content-image-name-value"
class="plain tabpanel-summary-value"
class="plain tabpanel-summary-value devtools-monospace"
crop="end"
flex="1"/>
</hbox>
@ -372,7 +372,7 @@
<label class="plain tabpanel-summary-label"
value="&netmonitorUI.response.dimensions;"/>
<label id="response-content-image-dimensions-value"
class="plain tabpanel-summary-value"
class="plain tabpanel-summary-value devtools-monospace"
crop="end"
flex="1"/>
</hbox>
@ -380,7 +380,7 @@
<label class="plain tabpanel-summary-label"
value="&netmonitorUI.response.mime;"/>
<label id="response-content-image-mime-value"
class="plain tabpanel-summary-value"
class="plain tabpanel-summary-value devtools-monospace"
crop="end"
flex="1"/>
</hbox>
@ -388,7 +388,7 @@
<label class="plain tabpanel-summary-label"
value="&netmonitorUI.response.encoding;"/>
<label id="response-content-image-encoding-value"
class="plain tabpanel-summary-value"
class="plain tabpanel-summary-value devtools-monospace"
crop="end"
flex="1"/>
</hbox>

Просмотреть файл

@ -59,7 +59,8 @@ function AutocompletePopup(aDocument, aOptions = {})
if (!this._panel) {
this._panel = this._document.createElementNS(XUL_NS, "panel");
this._panel.setAttribute("id", id);
this._panel.className = "devtools-autocomplete-popup " + theme + "-theme";
this._panel.className = "devtools-autocomplete-popup devtools-monospace "
+ theme + "-theme";
this._panel.setAttribute("noautofocus", "true");
this._panel.setAttribute("level", "top");

Просмотреть файл

@ -228,6 +228,7 @@ Messages.BaseMessage.prototype = {
let body = doc.createElementNS(XUL_NS, "description");
body.flex = 1;
body.classList.add("webconsole-msg-body");
body.classList.add("devtools-monospace");
container.appendChild(body);
return container;

Просмотреть файл

@ -2319,6 +2319,7 @@ WebConsoleFrame.prototype = {
let bodyNode = this.document.createElementNS(XUL_NS, "description");
bodyNode.flex = 1;
bodyNode.classList.add("webconsole-msg-body");
bodyNode.classList.add("devtools-monospace");
// Store the body text, since it is needed later for the variables view.
let body = aBody;
@ -2365,6 +2366,8 @@ WebConsoleFrame.prototype = {
// Create the timestamp.
let timestampNode = this.document.createElementNS(XUL_NS, "label");
timestampNode.classList.add("webconsole-timestamp");
timestampNode.classList.add("devtools-monospace");
let timestamp = aTimeStamp || Date.now();
let timestampString = l10n.timestampString(timestamp);
timestampNode.setAttribute("value", timestampString);
@ -2589,6 +2592,7 @@ WebConsoleFrame.prototype = {
locationNode.setAttribute("tooltiptext", aSourceURL);
locationNode.classList.add("webconsole-location");
locationNode.classList.add("text-link");
locationNode.classList.add("devtools-monospace");
// Make the location clickable.
locationNode.addEventListener("click", () => {

Просмотреть файл

@ -169,9 +169,10 @@ function goUpdateConsoleCommands() {
<hbox class="jsterm-input-container" style="direction:ltr">
<stack class="jsterm-stack-node" flex="1">
<textbox class="jsterm-complete-node" multiline="true" rows="1"
tabindex="-1"/>
<textbox class="jsterm-input-node" multiline="true" rows="1" tabindex="0"/>
<textbox class="jsterm-complete-node devtools-monospace"
multiline="true" rows="1" tabindex="-1"/>
<textbox class="jsterm-input-node devtools-monospace"
multiline="true" rows="1" tabindex="0"/>
</stack>
</hbox>
</vbox>

Просмотреть файл

@ -598,7 +598,7 @@ var BrowserUI = {
}
break;
case "metro_viewstate_changed":
this._adjustDOMforViewState();
this._adjustDOMforViewState(aData);
if (aData == "snapped") {
FlyoutPanelsUI.hide();
Elements.autocomplete.setAttribute("orient", "vertical");
@ -646,9 +646,9 @@ var BrowserUI = {
pullDesktopControlledPrefType(Ci.nsIPrefBranch.PREF_STRING, "setCharPref");
},
_adjustDOMforViewState: function() {
if (MetroUtils.immersive) {
let currViewState = "";
_adjustDOMforViewState: function(aState) {
let currViewState = aState;
if (!currViewState && MetroUtils.immersive) {
switch (MetroUtils.snappedState) {
case Ci.nsIWinMetroUtils.fullScreenLandscape:
currViewState = "landscape";
@ -663,8 +663,9 @@ var BrowserUI = {
currViewState = "snapped";
break;
}
Elements.windowState.setAttribute("viewstate", currViewState);
}
Elements.windowState.setAttribute("viewstate", currViewState);
},
_titleChanged: function(aBrowser) {

Просмотреть файл

@ -415,8 +415,7 @@
#if MOZ_UPDATE_CHANNEL != release
#ifdef MOZ_UPDATER
<description class="text-blurb" id="currentChannelText">&channel.description.start;<label id="currentChannel"/></description>
<description class="text-blurb" id="currentChannelText2">&channel.description.end;</description>
<description class="text-blurb" id="currentChannelText">&channel.description.start;<label id="currentChannel"/>&channel.description.end;</description>
#endif
#endif
<label id="about-policy-label"

Просмотреть файл

@ -109,9 +109,9 @@ var StartUI = {
}
},
_adjustDOMforViewState: function() {
if (this.chromeWin.MetroUtils.immersive) {
let currViewState = "";
_adjustDOMforViewState: function(aState) {
let currViewState = aState;
if (!currViewState && this.chromeWin.MetroUtils.immersive) {
switch (this.chromeWin.MetroUtils.snappedState) {
case Ci.nsIWinMetroUtils.fullScreenLandscape:
currViewState = "landscape";
@ -126,19 +126,20 @@ var StartUI = {
currViewState = "snapped";
break;
}
document.getElementById("bcast_windowState").setAttribute("viewstate", currViewState);
if (currViewState == "snapped") {
document.getElementById("start-topsites-grid").removeAttribute("tiletype");
} else {
document.getElementById("start-topsites-grid").setAttribute("tiletype", "thumbnail");
}
}
document.getElementById("bcast_windowState").setAttribute("viewstate", currViewState);
if (currViewState == "snapped") {
document.getElementById("start-topsites-grid").removeAttribute("tiletype");
} else {
document.getElementById("start-topsites-grid").setAttribute("tiletype", "thumbnail");
}
},
observe: function (aSubject, aTopic, aData) {
switch (aTopic) {
case "metro_viewstate_changed":
this._adjustDOMforViewState();
this._adjustDOMforViewState(aData);
break;
}
}

Просмотреть файл

@ -12,10 +12,11 @@ include $(DEPTH)/config/autoconf.mk
MOCHITEST_METRO_FILES = \
head.js \
browser_urlbar.js \
helpers/ViewStateHelper.js \
browser_bookmarks.js \
browser_canonizeURL.js \
browser_circular_progress_indicator.js \
browser_crashprompt.js \
browser_context_menu_tests.js \
browser_context_menu_tests_01.html \
browser_context_menu_tests_02.html \
@ -25,21 +26,22 @@ MOCHITEST_METRO_FILES = \
browser_downloads.js \
browser_findbar.js \
browser_findbar.html \
browser_form_auto_complete.js \
browser_form_auto_complete.html \
browser_history.js \
browser_inputsource.js \
browser_onscreen_keyboard.js \
browser_onscreen_keyboard.html \
browser_prefs_ui.js \
browser_progress_indicator.xul \
browser_remotetabs.js \
browser_snappedState.js \
browser_tabs.js \
browser_test.js \
browser_tiles.js \
browser_tilegrid.xul \
browser_topsites.js \
browser_form_auto_complete.js \
browser_form_auto_complete.html \
browser_crashprompt.js \
browser_inputsource.js \
browser_urlbar.js \
$(NULL)
ifndef MOZ_DEBUG

Просмотреть файл

@ -0,0 +1,119 @@
// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
var gStartDoc = null;
function test() {
if (!isLandscapeMode()) {
todo(false, "browser_snapped_tests need landscape mode to run.");
return;
}
runTests();
}
function showStartUI() {
if (!BrowserUI.isStartTabVisible) {
let tab = yield addTab("about:start");
gStartDoc = tab.browser.contentWindow.document;
} else {
gStartDoc = Browser.selectedBrowser.contentWindow.document;
}
}
function setUpSnapped() {
yield showStartUI();
yield setSnappedViewstate();
}
function setUpPortrait() {
yield showStartUI();
yield setPortraitViewstate();
}
function getNarrowTitle(aVboxId) {
let vbox = gStartDoc.getElementById(aVboxId);
return vbox.querySelector(".narrow-title");
}
function narrowTitleVisible(aVboxId) {
let title = getNarrowTitle(aVboxId);
let display = getComputedStyle(title).getPropertyValue("display");
return display !== "none";
}
function wideTitleVisible(aVboxId) {
let vbox = gStartDoc.getElementById(aVboxId);
let title = vbox.querySelector(".wide-title");
let display = getComputedStyle(title).getPropertyValue("display");
return display !== "none";
}
gTests.push({
desc: "Test Snapped titles",
setUp: setUpSnapped,
run: function() {
ok(narrowTitleVisible("start-topsites"), "topsites narrow title is visible");
ok(narrowTitleVisible("start-bookmarks"), "bookmarks narrow title is visible");
ok(narrowTitleVisible("start-history"), "history narrow title is visible");
ok(!wideTitleVisible("start-topsites"), "topsites wide title is not visible");
ok(!wideTitleVisible("start-bookmarks"), "bookmarks wide title is not visible");
ok(!wideTitleVisible("start-history"), "history wide title is not visible");
},
tearDown: restoreViewstate
});
gTests.push({
desc: "Test Snapped titles",
setUp: setUpSnapped,
run: function() {
let topsites = gStartDoc.getElementById("start-topsites");
let bookmarks = gStartDoc.getElementById("start-bookmarks");
let history = gStartDoc.getElementById("start-history");
ok(topsites.hasAttribute("expanded"), "topsites is expanded");
ok(!bookmarks.hasAttribute("expanded"), "bookmarks is collapsed");
ok(!history.hasAttribute("expanded"), "history is collapsed");
// Expand bookmarks
sendElementTap(Browser.selectedBrowser.contentWindow, getNarrowTitle("start-bookmarks"));
yield waitForCondition(() => bookmarks.hasAttribute("expanded"));
ok(!topsites.hasAttribute("expanded"), "topsites is collapsed");
ok(bookmarks.hasAttribute("expanded"), "bookmarks is expanded");
ok(!history.hasAttribute("expanded"), "history is collapsed");
},
tearDown: restoreViewstate
});
gTests.push({
desc: "Test Snapped scrolls vertically",
setUp: setUpSnapped,
run: function() {
todo(false, "We need to populate startUI to verify that it's scrollable");
},
tearDown: restoreViewstate
});
gTests.push({
desc: "Test Portrait titles",
setUp: setUpPortrait,
run: function() {
// Check title visibility
ok(!narrowTitleVisible("start-topsites"), "topsites narrow title is not visible");
ok(!narrowTitleVisible("start-bookmarks"), "bookmarks narrow title is not visible");
ok(!narrowTitleVisible("start-history"), "history narrow title is not visible");
ok(wideTitleVisible("start-topsites"), "topsites wide title is visible");
ok(wideTitleVisible("start-bookmarks"), "bookmarks wide title is visible");
ok(wideTitleVisible("start-history"), "history wide title is visible");
},
tearDown: restoreViewstate
});

Просмотреть файл

@ -18,6 +18,27 @@ const chromeRoot = getRootDirectory(gTestPath);
const kDefaultWait = 2000;
const kDefaultInterval = 50;
/*=============================================================================
Load Helpers
=============================================================================*/
let splitPath = chromeRoot.split('/');
if (!splitPath[splitPath.length-1]) {
splitPath.pop();
}
// ../mochitest to make sure we're looking for the libs on the right path
// even for mochiperf tests.
splitPath.pop();
splitPath.push('mochitest');
const mochitestPath = splitPath.join('/') + '/';
[
"ViewStateHelper.js"
].forEach(function(lib) {
Services.scriptloader.loadSubScript(mochitestPath + lib, this);
}, this);
/*=============================================================================
Metro ui helpers
=============================================================================*/
@ -147,7 +168,7 @@ function getSelection(aElement) {
// browser
return aElement.contentWindow.getSelection();
};
}
function getTrimmedSelection(aElement) {
let sel = getSelection(aElement);
@ -841,7 +862,7 @@ function stubMethod(aObj, aMethod) {
let func = function() {
func.calledWith = Array.slice(arguments);
func.callCount++;
}
};
func.callCount = 0;
func.restore = function() {
return (aObj[aMethod] = origFunc);

Просмотреть файл

@ -0,0 +1,53 @@
// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
/* 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 snappedSize = 330;
const portraitSize = 660;
function setSnappedViewstate() {
ok(isLandscapeMode(), "setSnappedViewstate expects landscape mode to work.");
// Communicate viewstate change
Services.obs.notifyObservers(null, 'metro_viewstate_changed', 'snapped');
let browser = Browser.selectedBrowser;
// Reduce browser width to simulate small screen size.
let fullWidth = browser.clientWidth;
let padding = fullWidth - snappedSize;
browser.style.borderRight = padding + "px solid gray";
// Make sure it renders the new mode properly
yield waitForMs(0);
}
function setPortraitViewstate() {
ok(isLandscapeMode(), "setPortraitViewstate expects landscape mode to work.");
Services.obs.notifyObservers(null, 'metro_viewstate_changed', 'portrait');
let browser = Browser.selectedBrowser;
let fullWidth = browser.clientWidth;
let padding = fullWidth - portraitSize;
browser.style.borderRight = padding + "px solid gray";
// Make sure it renders the new mode properly
yield waitForMs(0);
}
function restoreViewstate() {
ok(isLandscapeMode(), "restoreViewstate expects landscape mode to work.");
Services.obs.notifyObservers(null, 'metro_viewstate_changed', 'landscape');
Browser.selectedBrowser.style.removeProperty("border-right");
yield waitForMs(0);
}

Просмотреть файл

@ -792,3 +792,8 @@ appbar toolbar[labelled] toolbarbutton > .toolbarbutton-text {
.appbar-secondary {
list-style-image: url(chrome://browser/skin/images/appbar-icons.png);
}
.flyout-narrow description,
.flyout-narrow label {
max-width: 266px; /* Accounts for a 40px padding on each side of the flyout */
}

Просмотреть файл

@ -455,10 +455,10 @@ SocialErrorListener.prototype = {
},
onLocationChange: function SPL_onLocationChange(aWebProgress, aRequest, aLocation, aFlags) {
let failure = aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE;
if (failure && Social.provider.errorState != "frameworker-error") {
if (aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE) {
aRequest.cancel(Components.results.NS_BINDING_ABORTED);
Social.provider.errorState = "content-error";
if (!Social.provider.errorState)
Social.provider.errorState = "content-error";
schedule(function() {
this.setErrorMessage(aWebProgress.QueryInterface(Ci.nsIDocShell)
.chromeEventHandler);

Просмотреть файл

@ -114,9 +114,15 @@ this.webappsUI = {
DOMApplicationRegistry.confirmInstall(aData, false, localDir, null,
function (aManifest) {
if (WebappsInstaller.install(aData, aManifest)) {
installationSuccessNotification(aData, app, chromeWin);
}
WebappsInstaller.install(aData, aManifest).then(
function() {
installationSuccessNotification(aData, app, chromeWin);
},
function(error) {
Cu.reportError("Error installing webapp: " + error);
// TODO: Notify user that the installation has failed
}
);
}
);
} else {

Просмотреть файл

@ -11,7 +11,7 @@
.devtools-monospace {
font-family: monospace;
font-size: 85%;
font-size: 80%;
}
/* Toolbar and Toolbar items */

Просмотреть файл

@ -132,7 +132,6 @@
/* Sources and breakpoints view */
.dbg-breakpoint {
font-family: monospace;
-moz-margin-start: -14px;
}
@ -164,7 +163,6 @@
.dbg-expression-input {
-moz-padding-start: 2px !important;
font-family: Menlo, Monaco, monospace;
}
/* Searchbox and the search operations help panel */
@ -191,7 +189,6 @@
-moz-margin-start: 2px;
-moz-margin-end: 6px;
text-align: center;
font-family: Menlo, Monaco, monospace;
}
.searchbox-panel-operator-label {
@ -289,7 +286,6 @@
-moz-padding-end: 4px;
padding-top: 2px;
text-align: end;
font-family: monospace;
color: #8c8c8c;
}
@ -297,7 +293,6 @@
-moz-padding-start: 4px;
padding-top: 1px;
padding-bottom: 1px;
font-family: monospace;
}
.dbg-results-line-contents-string {

Просмотреть файл

@ -8,7 +8,7 @@ body {
}
#all-fonts {
padding: 0 5px;
padding: 0;
margin: 0;
}
@ -23,8 +23,7 @@ body {
}
.font {
padding: 10px 5px;
font-size: 0;
padding: 10px 10px;
}
.theme-dark .font {
@ -51,7 +50,6 @@ body {
}
.font-info {
font-size: 1rem;
display: block;
}

Просмотреть файл

@ -365,7 +365,6 @@ box.requests-menu-status[code^="5"] {
.tabpanel-summary-value {
-moz-padding-start: 3px;
font-family: Menlo, Monaco, monospace;
}
.variable-or-property:not(:focus) > .title > .token-string {

Просмотреть файл

@ -4,20 +4,6 @@
%include ../../shared/devtools/webconsole.inc.css
.webconsole-timestamp,
.webconsole-msg-body {
font-family: "DejaVu Sans Mono", monospace;
}
.jsterm-input-node,
.jsterm-complete-node {
font: 0.9em "DejaVu Sans Mono", monospace;
}
.hud-output-node {
font-size: 0.9em;
}
.jsterm-input-node {
width: 98%;
}

Просмотреть файл

@ -13,6 +13,7 @@
.devtools-monospace {
font-family: monospace;
font-size: 108%;
}
/* Toolbar and Toolbar items */

Просмотреть файл

@ -130,7 +130,6 @@
/* Sources and breakpoints view */
.dbg-breakpoint {
font-family: monospace;
-moz-margin-start: -14px;
}
@ -162,7 +161,6 @@
.dbg-expression-input {
-moz-padding-start: 2px !important;
font-family: Menlo, Monaco, monospace;
}
/* Searchbox and the search operations help panel */
@ -189,7 +187,6 @@
-moz-margin-start: 2px;
-moz-margin-end: 6px;
text-align: center;
font-family: Menlo, Monaco, monospace;
}
.searchbox-panel-operator-label {
@ -287,7 +284,6 @@
-moz-padding-end: 4px;
padding-top: 2px;
text-align: end;
font-family: monospace;
color: #8c8c8c;
}
@ -295,7 +291,6 @@
-moz-padding-start: 4px;
padding-top: 1px;
padding-bottom: 1px;
font-family: monospace;
}
.dbg-results-line-contents-string {

Просмотреть файл

@ -8,7 +8,7 @@ body {
}
#all-fonts {
padding: 0 5px;
padding: 0;
margin: 0;
}
@ -23,8 +23,7 @@ body {
}
.font {
padding: 10px 5px;
font-size: 0;
padding: 10px 10px;
}
.theme-dark .font {
@ -51,7 +50,6 @@ body {
}
.font-info {
font-size: 1rem;
display: block;
}

Просмотреть файл

@ -365,7 +365,6 @@ box.requests-menu-status[code^="5"] {
.tabpanel-summary-value {
-moz-padding-start: 3px;
font-family: Menlo, Monaco, monospace;
}
.variable-or-property:not(:focus) > .title > .token-string {

Просмотреть файл

@ -3,16 +3,4 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
%include ../shared.inc
%include ../../shared/devtools/webconsole.inc.css
/* General output styles */
.webconsole-timestamp,
.webconsole-msg-body {
font-family: Menlo, Monaco, monospace;
}
.jsterm-input-node,
.jsterm-complete-node {
font: 1em Menlo, Monaco, monospace;
}
%include ../../shared/devtools/webconsole.inc.css

Просмотреть файл

@ -8,7 +8,6 @@
color: GrayText;
margin-top: 0;
margin-bottom: 0;
font-family: monospace;
}
.hud-msg-node {
@ -33,7 +32,6 @@
-moz-margin-start: 3px;
-moz-margin-end: 6px;
white-space: pre-wrap;
font-family: monospace;
}
.webconsole-msg-body-piece {
@ -93,11 +91,6 @@
background: white;
}
.jsterm-input-node,
.jsterm-complete-node {
font-family: monospace;
}
.hud-output-node {
-moz-appearance: none;
border-bottom: 1px solid ThreeDShadow;

Просмотреть файл

@ -128,7 +128,6 @@
/* Sources and breakpoints view */
.dbg-breakpoint {
font-family: monospace;
-moz-margin-start: -14px;
}
@ -160,7 +159,6 @@
.dbg-expression-input {
-moz-padding-start: 2px !important;
font-family: Menlo, Monaco, monospace;
}
/* Searchbox and the search operations help panel */
@ -187,7 +185,6 @@
-moz-margin-start: 2px;
-moz-margin-end: 6px;
text-align: center;
font-family: Menlo, Monaco, monospace;
}
.searchbox-panel-operator-label {
@ -285,7 +282,6 @@
-moz-padding-end: 4px;
padding-top: 2px;
text-align: end;
font-family: monospace;
color: #8c8c8c;
}
@ -293,7 +289,6 @@
-moz-padding-start: 4px;
padding-top: 1px;
padding-bottom: 1px;
font-family: monospace;
}
.dbg-results-line-contents-string {

Просмотреть файл

@ -8,7 +8,7 @@ body {
}
#all-fonts {
padding: 0 5px;
padding: 0;
margin: 0;
}
@ -23,8 +23,7 @@ body {
}
.font {
padding: 10px 5px;
font-size: 0;
padding: 10px 10px;
}
.theme-dark .font {
@ -51,7 +50,6 @@ body {
}
.font-info {
font-size: 1rem;
display: block;
}

Просмотреть файл

@ -365,7 +365,6 @@ box.requests-menu-status[code^="5"] {
.tabpanel-summary-value {
-moz-padding-start: 3px;
font-family: Menlo, Monaco, monospace;
}
.variable-or-property:not(:focus) > .title > .token-string {

Просмотреть файл

@ -4,13 +4,6 @@
%include ../../shared/devtools/webconsole.inc.css
.webconsole-timestamp,
.webconsole-msg-body,
.jsterm-input-node,
.jsterm-complete-node {
font-family: Consolas, Lucida Console, monospace;
}
/*
* This hardcoded width likely due to a toolkit Windows specific bug.
* See http://hg.mozilla.org/mozilla-central/annotate/f38d6df93cad/toolkit/themes/winstripe/global/textbox-aero.css#l7

Просмотреть файл

@ -139,12 +139,14 @@ void MediaDecoder::SetDormantIfNecessary(bool aDormant)
mNextState = PLAY_STATE_PAUSED;
}
mNextState = mPlayState;
mIsDormant = aDormant;
mIsDormant = true;
mIsExitingDormant = false;
ChangeState(PLAY_STATE_LOADING);
} else if ((aDormant != true) && (mPlayState == PLAY_STATE_LOADING)) {
// exit dormant state
// just trigger to state machine.
// trigger to state machine.
mDecoderStateMachine->SetDormant(false);
mIsExitingDormant = true;
}
}
@ -371,6 +373,7 @@ MediaDecoder::MediaDecoder() :
mSameOriginMedia(false),
mReentrantMonitor("media.decoder"),
mIsDormant(false),
mIsExitingDormant(false),
mPlayState(PLAY_STATE_PAUSED),
mNextState(PLAY_STATE_PAUSED),
mCalledResourceLoaded(false),
@ -724,8 +727,11 @@ void MediaDecoder::MetadataLoaded(int aChannels, int aRate, bool aHasAudio, bool
{
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
if (mPlayState == PLAY_STATE_LOADING && mIsDormant) {
if (mPlayState == PLAY_STATE_LOADING && mIsDormant && !mIsExitingDormant) {
return;
} else if (mPlayState == PLAY_STATE_LOADING && mIsDormant && mIsExitingDormant) {
mIsDormant = false;
mIsExitingDormant = false;
}
mDuration = mDecoderStateMachine ? mDecoderStateMachine->GetDuration() : -1;
// Duration has changed so we should recompute playback rate
@ -1187,6 +1193,7 @@ void MediaDecoder::ChangeState(PlayState aState)
if (aState!= PLAY_STATE_LOADING) {
mIsDormant = false;
mIsExitingDormant = false;
}
GetReentrantMonitor().NotifyAll();

Просмотреть файл

@ -1035,6 +1035,10 @@ public:
// Should be true only when PlayState is PLAY_STATE_LOADING.
bool mIsDormant;
// True if this decoder is exiting from dormant state.
// Should be true only when PlayState is PLAY_STATE_LOADING.
bool mIsExitingDormant;
// Set to one of the valid play states.
// This can only be changed on the main thread while holding the decoder
// monitor. Thus, it can be safely read while holding the decoder monitor

Просмотреть файл

@ -627,6 +627,15 @@ InitDirs()
getter_AddRefs(sDirs->music));
#endif
// Eventually, on desktop, we want to do something smarter -- for example,
// detect when an sdcard is inserted, and use that instead of this.
dirService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
getter_AddRefs(sDirs->sdcard));
if (sDirs->sdcard) {
sDirs->sdcard->AppendRelativeNativePath(NS_LITERAL_CSTRING("fake-sdcard"));
}
#endif // !MOZ_WIDGET_GONK
#ifdef MOZ_WIDGET_GONK
NS_NewLocalFile(NS_LITERAL_STRING("/data"),
false,
@ -639,15 +648,6 @@ InitDirs()
}
#endif
// Eventually, on desktop, we want to do something smarter -- for example,
// detect when an sdcard is inserted, and use that instead of this.
dirService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
getter_AddRefs(sDirs->sdcard));
if (sDirs->sdcard) {
sDirs->sdcard->AppendRelativeNativePath(NS_LITERAL_CSTRING("fake-sdcard"));
}
#endif // !MOZ_WIDGET_GONK
if (XRE_GetProcessType() == GeckoProcessType_Default) {
NS_GetSpecialDirectory("UAppData", getter_AddRefs(sDirs->crashes));
if (sDirs->crashes) {

Просмотреть файл

@ -54,6 +54,7 @@ using RemoteDOMEvent;
using mozilla::dom::ScreenOrientation;
using mozilla::layers::TextureFactoryIdentifier;
using mozilla::CSSIntPoint;
using mozilla::CSSToScreenScale;
namespace mozilla {
namespace dom {
@ -287,7 +288,7 @@ parent:
* Updates any zoom constraints on the parent and anything tied to it. This
* is useful for control logic that resides outside of the remote browser.
*/
UpdateZoomConstraints(bool aAllowZoom, float aMinZoom, float aMaxZoom);
UpdateZoomConstraints(bool aAllowZoom, CSSToScreenScale aMinZoom, CSSToScreenScale aMaxZoom);
/**
* Notifies the parent about a scroll event. The pres shell ID and

Просмотреть файл

@ -421,16 +421,15 @@ TabChild::Observe(nsISupports *aSubject,
// Calculate a really simple resolution that we probably won't
// be keeping, as well as putting the scroll offset back to
// the top-left of the page.
mLastMetrics.mZoom = ScreenToScreenScale(1.0);
mLastMetrics.mViewport = CSSRect(CSSPoint(), kDefaultViewportSize);
mLastMetrics.mCompositionBounds = ScreenIntRect(ScreenIntPoint(), mInnerSize);
CSSToScreenScale resolution = mLastMetrics.CalculateResolution();
// We use ScreenToLayerScale(1) below in order to ask gecko to render
// what's currently visible on the screen. This is effectively turning
// the async zoom amount into the gecko zoom amount.
mLastMetrics.mZoom = mLastMetrics.CalculateIntrinsicScale();
// We use ScreenToLayerScale(1) below in order to turn the
// async zoom amount into the gecko zoom amount.
mLastMetrics.mResolution =
resolution / mLastMetrics.mDevPixelsPerCSSPixel * ScreenToLayerScale(1);
mLastMetrics.mZoom / mLastMetrics.mDevPixelsPerCSSPixel * ScreenToLayerScale(1);
mLastMetrics.mScrollOffset = CSSPoint(0, 0);
utils->SetResolution(mLastMetrics.mResolution.scale,
mLastMetrics.mResolution.scale);
@ -572,8 +571,8 @@ TabChild::HandlePossibleViewportChange()
nsViewportInfo viewportInfo =
nsContentUtils::GetViewportInfo(document, mInnerSize.width, mInnerSize.height);
SendUpdateZoomConstraints(viewportInfo.IsZoomAllowed(),
viewportInfo.GetMinZoom(),
viewportInfo.GetMaxZoom());
CSSToScreenScale(viewportInfo.GetMinZoom()),
CSSToScreenScale(viewportInfo.GetMaxZoom()));
float screenW = mInnerSize.width;
float screenH = mInnerSize.height;
@ -647,6 +646,16 @@ TabChild::HandlePossibleViewportChange()
viewport.height = std::max(viewport.height, screenH / minScale);
SetCSSViewport(viewport);
float oldScreenWidth = mLastMetrics.mCompositionBounds.width;
if (!oldScreenWidth) {
oldScreenWidth = mInnerSize.width;
}
FrameMetrics metrics(mLastMetrics);
metrics.mViewport = CSSRect(CSSPoint(), viewport);
metrics.mScrollableRect = CSSRect(CSSPoint(), pageSize);
metrics.mCompositionBounds = ScreenIntRect(ScreenIntPoint(), mInnerSize);
// This change to the zoom accounts for all types of changes I can conceive:
// 1. screen size changes, CSS viewport does not (pages with no meta viewport
// or a fixed size viewport)
@ -659,15 +668,8 @@ TabChild::HandlePossibleViewportChange()
// In all of these cases, we maintain how much actual content is visible
// within the screen width. Note that "actual content" may be different with
// respect to CSS pixels because of the CSS viewport size changing.
int32_t oldScreenWidth = mLastMetrics.mCompositionBounds.width;
if (!oldScreenWidth) {
oldScreenWidth = mInnerSize.width;
}
FrameMetrics metrics(mLastMetrics);
metrics.mViewport = CSSRect(CSSPoint(), viewport);
metrics.mScrollableRect = CSSRect(CSSPoint(), pageSize);
metrics.mCompositionBounds = ScreenIntRect(ScreenIntPoint(), mInnerSize);
float oldIntrinsicScale = oldScreenWidth / oldBrowserWidth;
metrics.mZoom.scale *= metrics.CalculateIntrinsicScale().scale / oldIntrinsicScale;
// Changing the zoom when we're not doing a first paint will get ignored
// by AsyncPanZoomController and causes a blurry flash.
@ -675,20 +677,17 @@ TabChild::HandlePossibleViewportChange()
nsresult rv = utils->GetIsFirstPaint(&isFirstPaint);
MOZ_ASSERT(NS_SUCCEEDED(rv));
if (NS_FAILED(rv) || isFirstPaint) {
CSSToScreenScale intrinsicScale = metrics.CalculateIntrinsicScale();
// FIXME/bug 799585(?): GetViewportInfo() returns a defaultZoom of
// 0.0 to mean "did not calculate a zoom". In that case, we default
// it to the intrinsic scale.
if (viewportInfo.GetDefaultZoom() < 0.01f) {
viewportInfo.SetDefaultZoom(intrinsicScale.scale);
viewportInfo.SetDefaultZoom(metrics.CalculateIntrinsicScale().scale);
}
double defaultZoom = viewportInfo.GetDefaultZoom();
MOZ_ASSERT(viewportInfo.GetMinZoom() <= defaultZoom &&
defaultZoom <= viewportInfo.GetMaxZoom());
// GetViewportInfo() returns a resolution-dependent scale factor.
// Convert that to a resolution-indepedent zoom.
metrics.mZoom = ScreenToScreenScale(defaultZoom / intrinsicScale.scale);
metrics.mZoom = CSSToScreenScale(defaultZoom);
}
metrics.mDisplayPort = AsyncPanZoomController::CalculatePendingDisplayPort(
@ -696,8 +695,7 @@ TabChild::HandlePossibleViewportChange()
// new CSS viewport, so we know that there's no velocity, acceleration, and
// we have no idea how long painting will take.
metrics, gfx::Point(0.0f, 0.0f), gfx::Point(0.0f, 0.0f), 0.0);
CSSToScreenScale resolution = metrics.CalculateResolution();
metrics.mResolution = resolution / metrics.mDevPixelsPerCSSPixel * ScreenToLayerScale(1);
metrics.mResolution = metrics.mZoom / metrics.mDevPixelsPerCSSPixel * ScreenToLayerScale(1);
utils->SetResolution(metrics.mResolution.scale, metrics.mResolution.scale);
// Force a repaint with these metrics. This, among other things, sets the
@ -1617,7 +1615,7 @@ TabChild::ProcessUpdateFrame(const FrameMetrics& aFrameMetrics)
}
// set the resolution
LayoutDeviceToLayerScale resolution = aFrameMetrics.CalculateResolution()
LayoutDeviceToLayerScale resolution = aFrameMetrics.mZoom
/ aFrameMetrics.mDevPixelsPerCSSPixel * ScreenToLayerScale(1);
utils->SetResolution(resolution.scale, resolution.scale);

Просмотреть файл

@ -300,7 +300,7 @@ public:
void GetDPI(float* aDPI);
void GetDefaultScale(double *aScale);
ScreenToScreenScale GetZoom() { return mLastMetrics.mZoom; }
CSSToScreenScale GetZoom() { return mLastMetrics.mZoom; }
ScreenOrientation GetOrientation() { return mOrientation; }

Просмотреть файл

@ -1552,8 +1552,8 @@ TabParent::RecvZoomToRect(const CSSRect& aRect)
bool
TabParent::RecvUpdateZoomConstraints(const bool& aAllowZoom,
const float& aMinZoom,
const float& aMaxZoom)
const CSSToScreenScale& aMinZoom,
const CSSToScreenScale& aMaxZoom)
{
if (RenderFrameParent* rfp = GetRenderFrame()) {
rfp->UpdateZoomConstraints(aAllowZoom, aMinZoom, aMaxZoom);

Просмотреть файл

@ -158,8 +158,8 @@ public:
virtual bool RecvGetWidgetNativeData(WindowsHandle* aValue);
virtual bool RecvZoomToRect(const CSSRect& aRect);
virtual bool RecvUpdateZoomConstraints(const bool& aAllowZoom,
const float& aMinZoom,
const float& aMaxZoom);
const CSSToScreenScale& aMinZoom,
const CSSToScreenScale& aMaxZoom);
virtual bool RecvUpdateScrollOffset(const uint32_t& aPresShellId, const ViewID& aViewId, const CSSIntPoint& aScrollOffset);
virtual bool RecvContentReceivedTouch(const bool& aPreventDefault);
virtual PContentDialogParent* AllocPContentDialogParent(const uint32_t& aType,

Просмотреть файл

@ -1,7 +1,7 @@
<!DOCTYPE html>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id={770731}
https://bugzilla.mozilla.org/show_bug.cgi?id=770731
-->
<head>
<title>Test for Bug {770731} Permissions</title>
@ -11,7 +11,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id={770731}
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id={770731}">Mozilla Bug {674720}</a>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=770731">Mozilla Bug 770731</a>
<p id="display"></p>
<div id="content" style="display: none">
@ -21,6 +21,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id={770731}
"use strict";
SimpleTest.waitForExplicitFinish();
var testPrivApp = {
'manifestURL' : 'https://aprivileged.com/manifest.webapp'
};
@ -29,93 +31,79 @@ var testCertApp = {
'manifestURL' : 'https://acertified.com/manifest.webapp'
};
SpecialPowers.addPermission("permissions", true, document);
SpecialPowers.pushPrefEnv({ "set": [["dom.mozPermissionSettings.enabled", true]] },
function() {
SpecialPowers.removePermission("permissions", document);
});
// Any permission explicit for privileged and implicit for certified serves
var testPerm = "contacts-read";
// Any permission explicit for privileged and certified apps
var explicitPerm = "geolocation";
SpecialPowers.Cu.import("resource://gre/modules/PermissionSettings.jsm");
var mozPermissions = window.navigator.mozPermissionSettings;
// Simulate that the app requested the permissions
SpecialPowers.pushPermissions([{'type': 'permissions', 'allow': true, 'context': document}, {'type': testPerm, 'allow': true, 'context': testPrivApp}, {'type': testPerm, 'allow': true, 'context': testCertApp}, {'type': explicitPerm, 'allow': true, 'context': testPrivApp}, {'type': explicitPerm, 'allow': true, 'context': testCertApp}], function() {
SpecialPowers.pushPrefEnv({ "set": [["dom.mozPermissionSettings.enabled", true]] }, permissionTest);
});
function permissionTest() {
// Any permission explicit for privileged and implicit for certified serves
var testPerm = "contacts-read";
// Any permission explicit for privileged and certified apps
var explicitPerm = "geolocation";
if (SpecialPowers.isMainProcess()) {
SpecialPowers.Cu.import("resource://gre/modules/PermissionSettings.jsm");
}
// Simulate that the app requested the permissions
SpecialPowers.addPermission(testPerm, true, testPrivApp);
SpecialPowers.addPermission(testPerm, true, testCertApp);
SpecialPowers.addPermission(explicitPerm, true, testPrivApp);
SpecialPowers.addPermission(explicitPerm, true, testCertApp);
var mozPermissions = window.navigator.mozPermissionSettings;
isnot(mozPermissions, null, "mozPermissionSettings is null when not enabled.");
if (gPermissionsEnabled) {
var certAppManifest = testCertApp.manifestURL;
var privAppManifest = testPrivApp.manifestURL;
var originPriv = "https://aprivileged.com";
var originCert = "https://acertified.com";
var originOther = "http://test";
var certAppManifest = testCertApp.manifestURL;
var privAppManifest = testPrivApp.manifestURL;
var originPriv = "https://aprivileged.com";
var originCert = "https://acertified.com";
var originOther = "http://test";
// Trying to make any change to implicit permissions should fail
// Trying to make any change to implicit permissions should fail
try {
mozPermissions.set(testPerm, "allow", certAppManifest, originCert, false);
ok(false, "Change implicit permission");
} catch (e) {
ok(true, "Change implicit permission");
}
var result=mozPermissions.get(testPerm, certAppManifest, originCert, false);
is(result, "allow", "same result");
// Removing a permission from the same origin, even an explicit one, should fail
try {
mozPermissions.set(testPerm, "unknown", privAppManifest, originPriv);
ok(false, "Setting a permission to unknown");
} catch (e) {
ok(true, "Setting a permission to unknown");
}
// Removing an explicit permission from a different origin should work
var testRemove = function(aPerm, aManifest, aOrigin, aTestMsg) {
try {
mozPermissions.set(testPerm, "allow", certAppManifest, originCert, false);
ok(false, "Change implicit permission");
mozPermissions.remove(aPerm, aManifest, aOrigin);
var status = mozPermissions.get(aPerm, aManifest, aOrigin, false);
is(status, "unknown", aTestMsg);
} catch (e) {
ok(true, "Change implicit permission");
ok(false, aTestMsg);
}
}
var result=mozPermissions.get(testPerm, certAppManifest, originCert, false);
is(result, "allow", "same result");
// Removing a permission from the same origin, even an explicit one, should fail
try {
mozPermissions.set(testPerm, "unknown", privAppManifest, originPriv);
ok(false, "Setting a permission to unknown");
} catch (e) {
ok(true, "Setting a permission to unknown");
}
// Removing an explicit permission from a different origin should work
var testRemove = function(aPerm, aManifest, aOrigin, aTestMsg) {
try {
mozPermissions.remove(aPerm, aManifest, aOrigin);
var status = mozPermissions.get(aPerm, aManifest, aOrigin, false);
is(status, "unknown", aTestMsg);
} catch (e) {
ok(false, aTestMsg);
}
}
testRemove(explicitPerm, privAppManifest, originOther,
testRemove(explicitPerm, privAppManifest, originOther,
"Remove explicit permission of privileged app");
testRemove(explicitPerm, certAppManifest, originOther,
testRemove(explicitPerm, certAppManifest, originOther,
"Remove explicit permission of certified app");
mozPermissions.set(testPerm, "allow", privAppManifest, originPriv, false);
result = mozPermissions.get(testPerm, privAppManifest, originPriv, false);
is(result, "allow", "Set to allow");
mozPermissions.set(testPerm, "deny", privAppManifest, originPriv, false);
result = mozPermissions.get(testPerm, privAppManifest, originPriv, false);
is(result, "deny", "Set to deny");
mozPermissions.set(testPerm, "prompt", privAppManifest, originPriv, false);
result = mozPermissions.get(testPerm, privAppManifest, originPriv, false);
is(result, "prompt", "Set to prompt");
SimpleTest.finish();
} else {
is(mozPermissions, null, "mozPermissionSettings is null when not enabled.");
SimpleTest.finish();
}
SpecialPowers.removePermission(testPerm, testPrivApp);
SpecialPowers.removePermission(testPerm, testCertApp);
mozPermissions.set(testPerm, "allow", privAppManifest, originPriv, false);
result = mozPermissions.get(testPerm, privAppManifest, originPriv, false);
is(result, "allow", "Set to allow");
mozPermissions.set(testPerm, "deny", privAppManifest, originPriv, false);
result = mozPermissions.get(testPerm, privAppManifest, originPriv, false);
is(result, "deny", "Set to deny");
mozPermissions.set(testPerm, "prompt", privAppManifest, originPriv, false);
result = mozPermissions.get(testPerm, privAppManifest, originPriv, false);
is(result, "prompt", "Set to prompt");
SimpleTest.finish();
}
var gPermissionsEnabled = SpecialPowers.getBoolPref("dom.mozPermissionSettings.enabled");
SimpleTest.waitForExplicitFinish();
addLoadEvent(permissionTest);
ok(true, "test passed");
</script>
</pre>
</body>
</html>
</html>

Просмотреть файл

@ -50,6 +50,22 @@ struct ScaleFactor {
return !(*this == aOther);
}
bool operator<(const ScaleFactor<src, dst>& aOther) const {
return scale < aOther.scale;
}
bool operator<=(const ScaleFactor<src, dst>& aOther) const {
return scale <= aOther.scale;
}
bool operator>(const ScaleFactor<src, dst>& aOther) const {
return scale > aOther.scale;
}
bool operator>=(const ScaleFactor<src, dst>& aOther) const {
return scale >= aOther.scale;
}
template<class other>
ScaleFactor<other, dst> operator/(const ScaleFactor<src, other>& aOther) const {
return ScaleFactor<other, dst>(scale / aOther.scale);

Просмотреть файл

@ -103,20 +103,9 @@ public:
return CSSToScreenScale(float(mCompositionBounds.width) / float(mViewport.width));
}
/**
* Return the resolution that content should be rendered at given
* the configuration in this metrics object: viewport dimensions,
* zoom factor, etc. (The mResolution member of this metrics is
* ignored.)
*/
CSSToScreenScale CalculateResolution() const
{
return CalculateIntrinsicScale() * mZoom;
}
CSSRect CalculateCompositedRectInCssPixels() const
{
return CSSRect(gfx::RoundedIn(mCompositionBounds / CalculateResolution()));
return CSSRect(gfx::RoundedIn(mCompositionBounds / mZoom));
}
// ---------------------------------------------------------------------------
@ -232,19 +221,11 @@ public:
// resolution of parent layers is opaque to this metric.
LayoutDeviceToLayerScale mResolution;
// The resolution-independent "user zoom". For example, if a page
// configures the viewport to a zoom value of 2x, then this member
// will always be 2.0 no matter what the viewport or composition
// bounds.
//
// In the steady state (no animations), the following is usually true
//
// intrinsicScale = (mCompositionBounds / mViewport)
// mResolution = mZoom * intrinsicScale / mDevPixelsPerCSSPixel
//
// When this is not true, we're probably asynchronously sampling a
// zoom animation for content.
ScreenToScreenScale mZoom;
// The "user zoom". Content is painted by gecko at mResolution * mDevPixelsPerCSSPixel,
// but will be drawn to the screen at mZoom. In the steady state, the
// two will be the same, but during an async zoom action the two may
// diverge.
CSSToScreenScale mZoom;
// The conversion factor between CSS pixels and device pixels for this frame.
// This can vary based on a variety of things, such as reflowing-zoom. The

Просмотреть файл

@ -401,8 +401,8 @@ APZCTreeManager::ContentReceivedTouch(const ScrollableLayerGuid& aGuid,
void
APZCTreeManager::UpdateZoomConstraints(const ScrollableLayerGuid& aGuid,
bool aAllowZoom,
float aMinScale,
float aMaxScale)
const CSSToScreenScale& aMinScale,
const CSSToScreenScale& aMaxScale)
{
nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
if (apzc) {

Просмотреть файл

@ -208,8 +208,8 @@ public:
*/
void UpdateZoomConstraints(const ScrollableLayerGuid& aGuid,
bool aAllowZoom,
float aMinScale,
float aMaxScale);
const CSSToScreenScale& aMinScale,
const CSSToScreenScale& aMaxScale);
/**
* Update mFrameMetrics.mScrollOffset to the given offset.

Просмотреть файл

@ -98,12 +98,12 @@ StaticAutoPtr<ComputedTimingFunction> gComputedTimingFunction;
/**
* Maximum zoom amount, always used, even if a page asks for higher.
*/
static const float MAX_ZOOM = 8.0f;
static const CSSToScreenScale MAX_ZOOM(8.0f);
/**
* Minimum zoom amount, always used, even if a page asks for lower.
*/
static const float MIN_ZOOM = 0.125f;
static const CSSToScreenScale MIN_ZOOM(0.125f);
/**
* Amount of time before we timeout touch event listeners. For example, if
@ -536,16 +536,15 @@ nsEventStatus AsyncPanZoomController::OnScale(const PinchGestureInput& aEvent) {
return nsEventStatus_eConsumeNoDefault;
}
float spanRatio = aEvent.mCurrentSpan / aEvent.mPreviousSpan;
ScreenToScreenScale spanRatio(aEvent.mCurrentSpan / aEvent.mPreviousSpan);
{
ReentrantMonitorAutoEnter lock(mMonitor);
CSSToScreenScale resolution = mFrameMetrics.CalculateResolution();
gfxFloat userZoom = mFrameMetrics.mZoom.scale;
CSSToScreenScale userZoom = mFrameMetrics.mZoom;
ScreenPoint focusPoint = aEvent.mFocusPoint;
CSSPoint focusChange = (mLastZoomFocus - focusPoint) / resolution;
CSSPoint focusChange = (mLastZoomFocus - focusPoint) / userZoom;
// If displacing by the change in focus point will take us off page bounds,
// then reduce the displacement such that it doesn't.
if (mX.DisplacementWillOverscroll(focusChange.x) != Axis::OVERSCROLL_NONE) {
@ -561,18 +560,13 @@ nsEventStatus AsyncPanZoomController::OnScale(const PinchGestureInput& aEvent) {
// either axis such that we don't overscroll the boundaries when zooming.
gfx::Point neededDisplacement;
float maxZoom = mMaxZoom / mFrameMetrics.CalculateIntrinsicScale().scale;
float minZoom = mMinZoom / mFrameMetrics.CalculateIntrinsicScale().scale;
bool doScale = (spanRatio > 1.0 && userZoom < maxZoom) ||
(spanRatio < 1.0 && userZoom > minZoom);
bool doScale = (spanRatio > ScreenToScreenScale(1.0) && userZoom < mMaxZoom) ||
(spanRatio < ScreenToScreenScale(1.0) && userZoom > mMinZoom);
if (doScale) {
if (userZoom * spanRatio > maxZoom) {
spanRatio = maxZoom / userZoom;
} else if (userZoom * spanRatio < minZoom) {
spanRatio = minZoom / userZoom;
}
spanRatio.scale = clamped(spanRatio.scale,
mMinZoom.scale / userZoom.scale,
mMaxZoom.scale / userZoom.scale);
switch (mX.ScaleWillOverscroll(spanRatio, focusPoint.x))
{
@ -643,8 +637,7 @@ nsEventStatus AsyncPanZoomController::OnLongPress(const TapGestureInput& aEvent)
if (controller) {
ReentrantMonitorAutoEnter lock(mMonitor);
CSSToScreenScale resolution = mFrameMetrics.CalculateResolution();
CSSPoint point = WidgetSpaceToCompensatedViewportSpace(aEvent.mPoint, resolution);
CSSPoint point = WidgetSpaceToCompensatedViewportSpace(aEvent.mPoint, mFrameMetrics.mZoom);
controller->HandleLongTap(gfx::RoundedToInt(point));
return nsEventStatus_eConsumeNoDefault;
}
@ -660,8 +653,7 @@ nsEventStatus AsyncPanZoomController::OnSingleTapConfirmed(const TapGestureInput
if (controller) {
ReentrantMonitorAutoEnter lock(mMonitor);
CSSToScreenScale resolution = mFrameMetrics.CalculateResolution();
CSSPoint point = WidgetSpaceToCompensatedViewportSpace(aEvent.mPoint, resolution);
CSSPoint point = WidgetSpaceToCompensatedViewportSpace(aEvent.mPoint, mFrameMetrics.mZoom);
controller->HandleSingleTap(gfx::RoundedToInt(point));
return nsEventStatus_eConsumeNoDefault;
}
@ -674,8 +666,7 @@ nsEventStatus AsyncPanZoomController::OnDoubleTap(const TapGestureInput& aEvent)
ReentrantMonitorAutoEnter lock(mMonitor);
if (mAllowZoom) {
CSSToScreenScale resolution = mFrameMetrics.CalculateResolution();
CSSPoint point = WidgetSpaceToCompensatedViewportSpace(aEvent.mPoint, resolution);
CSSPoint point = WidgetSpaceToCompensatedViewportSpace(aEvent.mPoint, mFrameMetrics.mZoom);
controller->HandleDoubleTap(gfx::RoundedToInt(point));
}
@ -741,7 +732,7 @@ void AsyncPanZoomController::TrackTouch(const MultiTouchInput& aEvent) {
// We want to inversely scale it because when you're zoomed further in, a
// larger swipe should move you a shorter distance.
ScreenToCSSScale inverseResolution = mFrameMetrics.CalculateResolution().Inverse();
ScreenToCSSScale inverseResolution = mFrameMetrics.mZoom.Inverse();
gfx::Point displacement(mX.GetDisplacementForDuration(inverseResolution.scale,
timeDelta),
@ -785,7 +776,7 @@ bool AsyncPanZoomController::DoFling(const TimeDuration& aDelta) {
// We want to inversely scale it because when you're zoomed further in, a
// larger swipe should move you a shorter distance.
ScreenToCSSScale inverseResolution = mFrameMetrics.CalculateResolution().Inverse();
ScreenToCSSScale inverseResolution = mFrameMetrics.mZoom.Inverse();
ScrollBy(CSSPoint::FromUnknownPoint(gfx::Point(
mX.GetDisplacementForDuration(inverseResolution.scale, aDelta),
@ -812,20 +803,18 @@ void AsyncPanZoomController::ScrollBy(const CSSPoint& aOffset) {
mFrameMetrics.mScrollOffset += aOffset;
}
void AsyncPanZoomController::ScaleWithFocus(float aZoom,
void AsyncPanZoomController::ScaleWithFocus(const CSSToScreenScale& aZoom,
const ScreenPoint& aFocus) {
float zoomFactor = aZoom / mFrameMetrics.mZoom.scale;
CSSToScreenScale resolution = mFrameMetrics.CalculateResolution();
ScreenToScreenScale zoomFactor(aZoom.scale / mFrameMetrics.mZoom.scale);
CSSToScreenScale resolution = mFrameMetrics.mZoom;
SetZoomAndResolution(ScreenToScreenScale(aZoom));
SetZoomAndResolution(aZoom);
// If the new scale is very small, we risk multiplying in huge rounding
// errors, so don't bother adjusting the scroll offset.
if (resolution.scale >= 0.01f) {
mFrameMetrics.mScrollOffset.x +=
aFocus.x * (zoomFactor - 1.0) / resolution.scale;
mFrameMetrics.mScrollOffset.y +=
aFocus.y * (zoomFactor - 1.0) / resolution.scale;
zoomFactor.scale -= 1.0;
mFrameMetrics.mScrollOffset += aFocus * zoomFactor / resolution;
}
}
@ -877,8 +866,7 @@ const CSSRect AsyncPanZoomController::CalculatePendingDisplayPort(
double estimatedPaintDuration =
aEstimatedPaintDuration > EPSILON ? aEstimatedPaintDuration : 1.0;
CSSToScreenScale resolution = aFrameMetrics.CalculateResolution();
CSSIntRect compositionBounds = gfx::RoundedIn(aFrameMetrics.mCompositionBounds / resolution);
CSSIntRect compositionBounds = gfx::RoundedIn(aFrameMetrics.mCompositionBounds / aFrameMetrics.mZoom);
CSSRect scrollableRect = aFrameMetrics.mScrollableRect;
// Ensure the scrollableRect is at least as big as the compositionBounds
@ -983,11 +971,11 @@ void AsyncPanZoomController::RequestContentRepaint() {
// Cache the zoom since we're temporarily changing it for
// acceleration-scaled painting.
ScreenToScreenScale actualZoom = mFrameMetrics.mZoom;
CSSToScreenScale actualZoom = mFrameMetrics.mZoom;
// Calculate the factor of acceleration based on the faster of the two axes.
float accelerationFactor =
clamped(std::max(mX.GetAccelerationFactor(), mY.GetAccelerationFactor()),
MIN_ZOOM / 2.0f, MAX_ZOOM);
MIN_ZOOM.scale / 2.0f, MAX_ZOOM.scale);
// Scale down the resolution a bit based on acceleration.
mFrameMetrics.mZoom.scale /= accelerationFactor;
@ -1048,10 +1036,9 @@ bool AsyncPanZoomController::SampleContentTransformForFrame(const TimeStamp& aSa
// will affect the final computed resolution.
double sampledPosition = gComputedTimingFunction->GetValue(animPosition);
ScreenToScreenScale startZoom = mStartZoomToMetrics.mZoom;
ScreenToScreenScale endZoom = mEndZoomToMetrics.mZoom;
mFrameMetrics.mZoom = ScreenToScreenScale(endZoom.scale * sampledPosition +
startZoom.scale * (1 - sampledPosition));
mFrameMetrics.mZoom = CSSToScreenScale(
mEndZoomToMetrics.mZoom.scale * sampledPosition +
mStartZoomToMetrics.mZoom.scale * (1 - sampledPosition));
mFrameMetrics.mScrollOffset = CSSPoint::FromUnknownPoint(gfx::Point(
mEndZoomToMetrics.mScrollOffset.x * sampledPosition +
@ -1076,7 +1063,7 @@ bool AsyncPanZoomController::SampleContentTransformForFrame(const TimeStamp& aSa
break;
}
aScrollOffset = mFrameMetrics.mScrollOffset * mFrameMetrics.CalculateResolution();
aScrollOffset = mFrameMetrics.mScrollOffset * mFrameMetrics.mZoom;
*aNewTransform = GetCurrentAsyncTransform();
mCurrentAsyncScrollOffset = mFrameMetrics.mScrollOffset;
@ -1121,10 +1108,10 @@ ViewTransform AsyncPanZoomController::GetCurrentAsyncTransform() {
if (mLastContentPaintMetrics.IsScrollable()) {
lastPaintScrollOffset = mLastContentPaintMetrics.mScrollOffset;
}
CSSToScreenScale localScale = mFrameMetrics.CalculateResolution();
LayerPoint translation = (mFrameMetrics.mScrollOffset - lastPaintScrollOffset)
* mLastContentPaintMetrics.LayersPixelsPerCSSPixel();
return ViewTransform(-translation, localScale / mLastContentPaintMetrics.mDevPixelsPerCSSPixel);
return ViewTransform(-translation,
mFrameMetrics.mZoom / mLastContentPaintMetrics.mDevPixelsPerCSSPixel);
}
void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetrics, bool aIsFirstPaint) {
@ -1141,10 +1128,13 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetri
aLayerMetrics.mCompositionBounds.height == mFrameMetrics.mCompositionBounds.height) {
// Remote content has sync'd up to the composition geometry
// change, so we can accept the viewport it's calculated.
CSSToScreenScale previousResolution = mFrameMetrics.CalculateResolution();
CSSToScreenScale previousResolution = mFrameMetrics.CalculateIntrinsicScale();
mFrameMetrics.mViewport = aLayerMetrics.mViewport;
CSSToScreenScale newResolution = mFrameMetrics.CalculateResolution();
needContentRepaint |= (previousResolution != newResolution);
CSSToScreenScale newResolution = mFrameMetrics.CalculateIntrinsicScale();
if (previousResolution != newResolution) {
needContentRepaint = true;
mFrameMetrics.mZoom.scale *= newResolution.scale / previousResolution.scale;
}
}
if (aIsFirstPaint || isDefault) {
@ -1187,7 +1177,8 @@ void AsyncPanZoomController::UpdateCompositionBounds(const ScreenIntRect& aCompo
// has gone out of view, the buffer will be cleared elsewhere anyways.
if (aCompositionBounds.width && aCompositionBounds.height &&
oldCompositionBounds.width && oldCompositionBounds.height) {
SetZoomAndResolution(mFrameMetrics.mZoom);
ScreenToScreenScale adjustmentFactor(float(aCompositionBounds.width) / float(oldCompositionBounds.width));
SetZoomAndResolution(mFrameMetrics.mZoom * adjustmentFactor);
// Repaint on a rotation so that our new resolution gets properly updated.
RequestContentRepaint();
@ -1215,28 +1206,24 @@ void AsyncPanZoomController::ZoomToRect(CSSRect aRect) {
ScreenIntRect compositionBounds = mFrameMetrics.mCompositionBounds;
CSSRect cssPageRect = mFrameMetrics.mScrollableRect;
CSSPoint scrollOffset = mFrameMetrics.mScrollOffset;
float currentZoom = mFrameMetrics.mZoom.scale;
float targetZoom;
float intrinsicScale = mFrameMetrics.CalculateIntrinsicScale().scale;
CSSToScreenScale currentZoom = mFrameMetrics.mZoom;
CSSToScreenScale targetZoom;
// The minimum zoom to prevent over-zoom-out.
// If the zoom factor is lower than this (i.e. we are zoomed more into the page),
// then the CSS content rect, in layers pixels, will be smaller than the
// composition bounds. If this happens, we can't fill the target composited
// area with this frame.
float localMinZoom = std::max(mMinZoom,
std::max(compositionBounds.width / cssPageRect.width,
compositionBounds.height / cssPageRect.height))
/ intrinsicScale;
float localMaxZoom = mMaxZoom / intrinsicScale;
CSSToScreenScale localMinZoom(std::max(mMinZoom.scale,
std::max(compositionBounds.width / cssPageRect.width,
compositionBounds.height / cssPageRect.height)));
CSSToScreenScale localMaxZoom = mMaxZoom;
if (!aRect.IsEmpty()) {
// Intersect the zoom-to-rect to the CSS rect to make sure it fits.
aRect = aRect.Intersect(cssPageRect);
float targetResolution =
std::min(compositionBounds.width / aRect.width,
compositionBounds.height / aRect.height);
targetZoom = targetResolution / intrinsicScale;
targetZoom = CSSToScreenScale(std::min(compositionBounds.width / aRect.width,
compositionBounds.height / aRect.height));
}
// 1. If the rect is empty, request received from browserElementScrolling.js
// 2. currentZoom is equal to mMaxZoom and user still double-tapping it
@ -1256,14 +1243,12 @@ void AsyncPanZoomController::ZoomToRect(CSSRect aRect) {
cssPageRect.width,
newHeight);
aRect = aRect.Intersect(cssPageRect);
float targetResolution =
std::min(compositionBounds.width / aRect.width,
compositionBounds.height / aRect.height);
targetZoom = targetResolution / intrinsicScale;
targetZoom = CSSToScreenScale(std::min(compositionBounds.width / aRect.width,
compositionBounds.height / aRect.height));
}
targetZoom = clamped(targetZoom, localMinZoom, localMaxZoom);
mEndZoomToMetrics.mZoom = ScreenToScreenScale(targetZoom);
targetZoom.scale = clamped(targetZoom.scale, localMinZoom.scale, localMaxZoom.scale);
mEndZoomToMetrics.mZoom = targetZoom;
// Adjust the zoomToRect to a sensible position to prevent overscrolling.
FrameMetrics metricsAfterZoom = mFrameMetrics;
@ -1360,22 +1345,21 @@ void AsyncPanZoomController::TimeoutTouchListeners() {
ContentReceivedTouch(false);
}
void AsyncPanZoomController::SetZoomAndResolution(const ScreenToScreenScale& aZoom) {
void AsyncPanZoomController::SetZoomAndResolution(const CSSToScreenScale& aZoom) {
mMonitor.AssertCurrentThreadIn();
mFrameMetrics.mZoom = aZoom;
CSSToScreenScale resolution = mFrameMetrics.CalculateResolution();
// We use ScreenToLayerScale(1) below in order to ask gecko to render
// what's currently visible on the screen. This is effectively turning
// the async zoom amount into the gecko zoom amount.
mFrameMetrics.mResolution = resolution / mFrameMetrics.mDevPixelsPerCSSPixel * ScreenToLayerScale(1);
mFrameMetrics.mResolution = aZoom / mFrameMetrics.mDevPixelsPerCSSPixel * ScreenToLayerScale(1);
}
void AsyncPanZoomController::UpdateZoomConstraints(bool aAllowZoom,
float aMinZoom,
float aMaxZoom) {
const CSSToScreenScale& aMinZoom,
const CSSToScreenScale& aMaxZoom) {
mAllowZoom = aAllowZoom;
mMinZoom = std::max(MIN_ZOOM, aMinZoom);
mMaxZoom = std::min(MAX_ZOOM, aMaxZoom);
mMinZoom = (MIN_ZOOM > aMinZoom ? MIN_ZOOM : aMinZoom);
mMaxZoom = (MAX_ZOOM > aMaxZoom ? aMaxZoom : MAX_ZOOM);
}
void AsyncPanZoomController::PostDelayedTask(Task* aTask, int aDelayMs) {

Просмотреть файл

@ -145,7 +145,9 @@ public:
* We try to obey everything it asks us elsewhere, but here we only handle
* minimum-scale, maximum-scale, and user-scalable.
*/
void UpdateZoomConstraints(bool aAllowZoom, float aMinScale, float aMaxScale);
void UpdateZoomConstraints(bool aAllowZoom,
const mozilla::CSSToScreenScale& aMinScale,
const mozilla::CSSToScreenScale& aMaxScale);
/**
* Schedules a runnable to run on the controller/UI thread at some time
@ -348,7 +350,8 @@ protected:
*
* XXX: Fix focus point calculations.
*/
void ScaleWithFocus(float aScale, const ScreenPoint& aFocus);
void ScaleWithFocus(const mozilla::CSSToScreenScale& aScale,
const ScreenPoint& aFocus);
/**
* Schedules a composite on the compositor thread. Wrapper for
@ -453,7 +456,7 @@ protected:
*
* *** The monitor must be held while calling this.
*/
void SetZoomAndResolution(const ScreenToScreenScale& aZoom);
void SetZoomAndResolution(const mozilla::CSSToScreenScale& aZoom);
/**
* Timeout function for mozbrowserasyncscroll event. Because we throttle
@ -543,8 +546,8 @@ private:
// values; for example, allowing a min zoom of 0.0 can cause very bad things
// to happen.
bool mAllowZoom;
float mMinZoom;
float mMaxZoom;
mozilla::CSSToScreenScale mMinZoom;
mozilla::CSSToScreenScale mMaxZoom;
// The last time the compositor has sampled the content transform for this
// frame.

Просмотреть файл

@ -252,12 +252,12 @@ float Axis::DisplacementWillOverscrollAmount(float aDisplacement) {
}
}
Axis::Overscroll Axis::ScaleWillOverscroll(float aScale, float aFocus) {
float originAfterScale = (GetOrigin() + aFocus) * aScale - aFocus;
Axis::Overscroll Axis::ScaleWillOverscroll(ScreenToScreenScale aScale, float aFocus) {
float originAfterScale = (GetOrigin() + aFocus) * aScale.scale - aFocus;
bool both = ScaleWillOverscrollBothSides(aScale);
bool minus = originAfterScale < GetPageStart() * aScale;
bool plus = (originAfterScale + GetCompositionLength()) > GetPageEnd() * aScale;
bool minus = originAfterScale < GetPageStart() * aScale.scale;
bool plus = (originAfterScale + GetCompositionLength()) > GetPageEnd() * aScale.scale;
if ((minus && plus) || both) {
return OVERSCROLL_BOTH;
@ -271,12 +271,12 @@ Axis::Overscroll Axis::ScaleWillOverscroll(float aScale, float aFocus) {
return OVERSCROLL_NONE;
}
float Axis::ScaleWillOverscrollAmount(float aScale, float aFocus) {
float originAfterScale = (GetOrigin() + aFocus) * aScale - aFocus;
float Axis::ScaleWillOverscrollAmount(ScreenToScreenScale aScale, float aFocus) {
float originAfterScale = (GetOrigin() + aFocus) * aScale.scale - aFocus;
switch (ScaleWillOverscroll(aScale, aFocus)) {
case OVERSCROLL_MINUS: return originAfterScale - GetPageStart() * aScale;
case OVERSCROLL_MINUS: return originAfterScale - GetPageStart() * aScale.scale;
case OVERSCROLL_PLUS: return (originAfterScale + GetCompositionLength()) -
NS_lround(GetPageEnd() * aScale);
NS_lround(GetPageEnd() * aScale.scale);
// Don't handle OVERSCROLL_BOTH. Client code is expected to deal with it.
default: return 0;
}
@ -319,12 +319,12 @@ float Axis::GetPageLength() {
return GetRectLength(pageRect);
}
bool Axis::ScaleWillOverscrollBothSides(float aScale) {
bool Axis::ScaleWillOverscrollBothSides(ScreenToScreenScale aScale) {
const FrameMetrics& metrics = mAsyncPanZoomController->GetFrameMetrics();
CSSRect cssContentRect = metrics.mScrollableRect;
CSSToScreenScale scale(metrics.CalculateResolution().scale * aScale);
CSSToScreenScale scale = metrics.mZoom * aScale;
CSSIntRect cssCompositionBounds = RoundedIn(metrics.mCompositionBounds / scale);
return GetRectLength(cssContentRect) < GetRectLength(CSSRect(cssCompositionBounds));

Просмотреть файл

@ -142,7 +142,7 @@ public:
* scroll offset in such a way that it remains in the same place on the page
* relative.
*/
Overscroll ScaleWillOverscroll(float aScale, float aFocus);
Overscroll ScaleWillOverscroll(ScreenToScreenScale aScale, float aFocus);
/**
* If a scale will overscroll the axis, this returns the amount and in what
@ -152,7 +152,7 @@ public:
* scroll offset in such a way that it remains in the same place on the page
* relative.
*/
float ScaleWillOverscrollAmount(float aScale, float aFocus);
float ScaleWillOverscrollAmount(ScreenToScreenScale aScale, float aFocus);
/**
* Checks if an axis will overscroll in both directions by computing the
@ -161,7 +161,7 @@ public:
*
* This gets called by ScaleWillOverscroll().
*/
bool ScaleWillOverscrollBothSides(float aScale);
bool ScaleWillOverscrollBothSides(ScreenToScreenScale aScale);
float GetOrigin();
float GetCompositionLength();

Просмотреть файл

@ -178,7 +178,7 @@ TEST(AsyncPanZoomController, ComplexTransform) {
metrics.mScrollOffset = CSSPoint(10, 10);
metrics.mScrollableRect = CSSRect(0, 0, 50, 50);
metrics.mResolution = LayoutDeviceToLayerScale(2);
metrics.mZoom = ScreenToScreenScale(1);
metrics.mZoom = CSSToScreenScale(6);
metrics.mDevPixelsPerCSSPixel = CSSToLayoutDeviceScale(3);
metrics.mScrollId = FrameMetrics::ROOT_SCROLL_ID;

Просмотреть файл

@ -118,7 +118,12 @@ if (!isWindows) {
tests.push([ 'bug240933-2.html' , 'bug240933-1-ref.html' ]); // bug 681162
tests.push([ 'bug389321-1.html' , 'bug389321-1-ref.html' ]); // bug 683163
tests.push([ 'bug482484.html' , 'bug482484-ref.html' ]); // bug 688575
tests.push([ 'bug512295-2.html' , 'bug512295-2-ref.html' ]); // bug 681331
if (navigator.appVersion.indexOf("Android") == -1 &&
SpecialPowers.Services.appinfo.name != "B2G") {
tests.push([ 'bug512295-2.html' , 'bug512295-2-ref.html' ]); // bug 681331
} else {
is(SpecialPowers.getIntPref("layout.spellcheckDefault"), 0, "Spellcheck should be turned off for this platrom or this if..else check removed");
}
tests.push([ 'bug597519-1.html' , 'bug597519-1-ref.html' ]); // bug 680579
tests.push([ 'bug602141-1.html' , 'bug602141-1-ref.html' ]); // bug 681334
tests.push([ 'bug602141-2.html' , 'bug602141-2-ref.html' ]); // bug 682836

Просмотреть файл

@ -1008,7 +1008,9 @@ RenderFrameParent::ContentReceivedTouch(bool aPreventDefault)
}
void
RenderFrameParent::UpdateZoomConstraints(bool aAllowZoom, float aMinZoom, float aMaxZoom)
RenderFrameParent::UpdateZoomConstraints(bool aAllowZoom,
const CSSToScreenScale& aMinZoom,
const CSSToScreenScale& aMaxZoom)
{
if (GetApzcTreeManager()) {
GetApzcTreeManager()->UpdateZoomConstraints(ScrollableLayerGuid(mLayersId),

Просмотреть файл

@ -102,7 +102,9 @@ public:
void ContentReceivedTouch(bool aPreventDefault);
void UpdateZoomConstraints(bool aAllowZoom, float aMinZoom, float aMaxZoom);
void UpdateZoomConstraints(bool aAllowZoom,
const CSSToScreenScale& aMinZoom,
const CSSToScreenScale& aMaxZoom);
void UpdateScrollOffset(uint32_t aPresShellId,
ViewID aViewId,

Просмотреть файл

@ -493,12 +493,10 @@ RES_LAYOUT = \
res/layout/pin_bookmark_dialog.xml \
res/layout/preference_rightalign_icon.xml \
res/layout/preference_search_tip.xml \
res/layout/search_engine_row.xml \
res/layout/site_setting_item.xml \
res/layout/site_setting_title.xml \
res/layout/shared_ui_components.xml \
res/layout/site_identity.xml \
res/layout/suggestion_item.xml \
res/layout/remote_tabs_child.xml \
res/layout/remote_tabs_group.xml \
res/layout/search_engine_row.xml \

Просмотреть файл

@ -141,6 +141,7 @@ class PromptInput {
"week",
"time",
"datetime-local",
"datetime",
"month"
};

Просмотреть файл

@ -118,6 +118,7 @@ public class BookmarksPage extends HomeFragment {
mList = (BookmarksListView) view.findViewById(R.id.bookmarks_list);
mList.setTag(HomePager.LIST_TAG_BOOKMARKS);
mList.setOnUrlOpenListener(listener);
mList.setHeaderDividersEnabled(false);
mTopBookmarks.setOnUrlOpenListener(listener);
mTopBookmarks.setOnPinBookmarkListener(mPinBookmarkListener);
@ -406,6 +407,7 @@ public class BookmarksPage extends HomeFragment {
case LOADER_ID_BOOKMARKS_LIST: {
mListAdapter.swapCursor(c);
loadFavicons(c);
mList.setHeaderDividersEnabled(c != null && c.getCount() > 0);
break;
}

Просмотреть файл

@ -165,7 +165,13 @@ public class ReadingListPage extends HomeFragment {
if (imageSpanIndex != -1) {
final ImageSpan readingListIcon = new ImageSpan(getActivity(), R.drawable.reader_cropped, ImageSpan.ALIGN_BOTTOM);
final SpannableStringBuilder hintBuilder = new SpannableStringBuilder(readingListHint);
hintBuilder.setSpan(readingListIcon, imageSpanIndex, imageSpanIndex + 2, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
// Add additional spacing.
hintBuilder.insert(imageSpanIndex + 2, " ");
hintBuilder.insert(imageSpanIndex, " ");
// Add icon.
hintBuilder.setSpan(readingListIcon, imageSpanIndex + 1, imageSpanIndex + 3, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
emptyHint.setText(hintBuilder, TextView.BufferType.SPANNABLE);
}

Просмотреть файл

@ -281,7 +281,7 @@ size. -->
<!-- Localization note (home_reading_list_hint): The "TIP" string is synonymous to "hint", "clue", etc. This string is displayed
as an advisory message on how to add content to the reading list when the reading list empty.
The "&#37;I" in the string will be replaced by a small image of the icon described, and can be moved to wherever
it is applicable.. -->
it is applicable. Please keep the spacing around the "&#37;I" string. -->
<!ENTITY home_reading_list_hint "TIP: Save articles to your reading list by long pressing the &#37;I icon when it appears in the title bar.">
<!ENTITY home_most_visited_empty "Websites you visit most frequently show up here.">

Просмотреть файл

@ -31,6 +31,7 @@ import android.widget.TextView;
import java.util.LinkedList;
import org.mozilla.gecko.animation.PropertyAnimator;
import org.mozilla.gecko.R;
import org.mozilla.gecko.gfx.BitmapUtils;
@ -123,11 +124,9 @@ public class ButtonToast {
mView.setVisibility(View.VISIBLE);
int duration = immediate ? 0 : mView.getResources().getInteger(android.R.integer.config_longAnimTime);
mView.clearAnimation();
AlphaAnimation alpha = new AlphaAnimation(0.0f, 1.0f);
alpha.setDuration(duration);
alpha.setFillAfter(true);
mView.startAnimation(alpha);
PropertyAnimator animator = new PropertyAnimator(duration);
animator.attach(mView, PropertyAnimator.Property.ALPHA, 1.0f);
animator.start();
}
public void hide(boolean immediate, ReasonHidden reason) {
@ -149,20 +148,20 @@ public class ButtonToast {
mView.setVisibility(View.GONE);
showNextInQueue();
} else {
AlphaAnimation alpha = new AlphaAnimation(1.0f, 0.0f);
alpha.setDuration(duration);
alpha.setFillAfter(true);
alpha.setAnimationListener(new Animation.AnimationListener () {
// Using Android's animation frameworks will not correctly turn off clicking.
// See bug 885717.
PropertyAnimator animator = new PropertyAnimator(duration);
animator.attach(mView, PropertyAnimator.Property.ALPHA, 0.0f);
animator.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener () {
// If we are showing a toast and go in the background
// onAnimationEnd will be called when the app is restored
public void onAnimationEnd(Animation animation) {
public void onPropertyAnimationEnd() {
mView.setVisibility(View.GONE);
showNextInQueue();
}
public void onAnimationRepeat(Animation animation) { }
public void onAnimationStart(Animation animation) { }
public void onPropertyAnimationStart() { }
});
mView.startAnimation(alpha);
animator.start();
}
}

Просмотреть файл

@ -111,7 +111,6 @@
"docshell/test/navigation/test_popup-navigates-children.html": "bug 783589",
"docshell/test/navigation/test_sessionhistory.html": "RANDOM",
"docshell/test/navigation/test_bug344861.html": "",
"docshell/test/test_bug94514.html": "TIMED_OUT",
"docshell/test/test_bug413310.html": "",
"docshell/test/test_bug590573.html": "bug 823022",
"docshell/test/test_bug598895.html": "",
@ -147,7 +146,7 @@
"dom/indexedDB/test/test_webapp_clearBrowserData_oop_inproc.html": "No test app installed",
"dom/network/tests/test_network_basics.html": "",
"dom/permission/tests/test_permission_basics.html": "",
"dom/mobilemessage/tests/test_sms_basics.html": "",
"dom/mobilemessage/tests/test_sms_basics.html": "Bug 909036",
"dom/tests/mochitest/ajax/jquery/test_jQuery.html": "bug 775227",
"dom/tests/mochitest/ajax/offline/test_simpleManifest.html": "TIMED_OUT",
"dom/tests/mochitest/ajax/offline/test_updatingManifest.html": "TIMED_OUT",
@ -240,11 +239,11 @@
"layout/base/tests/test_bug629838.html": "",
"layout/base/tests/test_flush_on_paint.html": "",
"layout/base/tests/test_mozPaintCount.html": "",
"layout/base/tests/test_reftests_with_caret.html": "",
"layout/base/tests/test_reftests_with_caret.html": "1 test failing because of spellcheck, rest passes",
"layout/forms/test/test_bug348236.html": "",
"layout/forms/test/test_bug378670.html": "TIMED_OUT",
"layout/forms/test/test_bug446663.html": "",
"layout/forms/test/test_bug478219.xhtml": "",
"layout/forms/test/test_bug478219.xhtml": "window.closed not working, bug 907795",
"layout/forms/test/test_bug564115.html": "TIMED_OUT",
"layout/forms/test/test_bug571352.html": "TIMED_OUT",
"layout/forms/test/test_bug572649.html": "TIMED_OUT",

Просмотреть файл

@ -115,7 +115,6 @@
"docshell/test/navigation/test_popup-navigates-children.html": "bug 783589",
"docshell/test/navigation/test_sessionhistory.html": "RANDOM",
"docshell/test/navigation/test_bug344861.html": "",
"docshell/test/test_bug94514.html": "TIMED_OUT",
"docshell/test/test_bug413310.html": "",
"docshell/test/test_bug590573.html": "bug 823022",
"docshell/test/test_bug598895.html": "",
@ -151,7 +150,7 @@
"dom/indexedDB/test/test_webapp_clearBrowserData_oop_inproc.html": "No test app installed",
"dom/network/tests/test_network_basics.html": "",
"dom/permission/tests/test_permission_basics.html": "",
"dom/mobilemessage/tests/test_sms_basics.html": "",
"dom/mobilemessage/tests/test_sms_basics.html": "Bug 909036",
"dom/tests/mochitest/ajax/jquery/test_jQuery.html": "bug 775227",
"dom/tests/mochitest/ajax/offline/test_simpleManifest.html": "TIMED_OUT",
"dom/tests/mochitest/ajax/offline/test_updatingManifest.html": "TIMED_OUT",
@ -244,11 +243,11 @@
"layout/base/tests/test_bug629838.html": "",
"layout/base/tests/test_flush_on_paint.html": "",
"layout/base/tests/test_mozPaintCount.html": "",
"layout/base/tests/test_reftests_with_caret.html": "",
"layout/base/tests/test_reftests_with_caret.html": "1 test failing because of spellcheck, rest passes",
"layout/forms/test/test_bug348236.html": "",
"layout/forms/test/test_bug378670.html": "TIMED_OUT",
"layout/forms/test/test_bug446663.html": "",
"layout/forms/test/test_bug478219.xhtml": "",
"layout/forms/test/test_bug478219.xhtml": "window.closed not working, bug 907795",
"layout/forms/test/test_bug564115.html": "TIMED_OUT",
"layout/forms/test/test_bug571352.html": "TIMED_OUT",
"layout/forms/test/test_bug572649.html": "TIMED_OUT",

Просмотреть файл

@ -11,26 +11,8 @@
"layout/xul" : "",
"dom/tests/mochitest/general/test_focusrings.xul":"",
"layout/base/tests/test_bug465448.xul":"",
"content/base/test/test_bug590870.html":"",
"content/base/test/test_classList.html":"1806 tests, 1 test failing because of xul",
"content/base/test/test_title.html":"",
"content/events/test/test_bug547996-2.xhtml":"16 tests total",
"content/html/content/test/test_bug458037.xhtml":"",
"content/xbl/":"tests that use xbl",
"layout/style/test/test_media_queries_dynamic_xbl.html":"",
"content/base/test/test_xbl_userdata.xhtml":"",
"content/base/test/test_base.xhtml":"",
"content/base/test/test_bug330925.xhtml":"",
"content/base/test/test_bug372086.html":"",
"content/base/test/test_bug419527.xhtml":"",
"content/base/test/test_bug444030.xhtml":"",
"content/events/test/test_bug391568.xhtml":"",
"layout/inspector/tests/test_bug522601.xhtml":"",
"layout/style/test/test_selectors_on_anonymous_content.html":"",
"layout/forms/test/test_bug478219.xhtml":"",
"layout/inspector/tests/test_bug609549.xhtml":"",
"content/xslt/tests/mochitest/test_bug319374.xhtml":"",
"layout/forms/test/test_bug478219.xhtml":"window.closed not working, bug 907795",
"content/media/test/test_bug448534.html": "Timed out, bug 894922? Bug 902677 is for the timing out of a lot of media tests",
"content/media/mediasource/test/test_MediaSource.html": " ReferenceError: MediaSource is not defined",
@ -135,6 +117,8 @@
"dom/tests/mochitest/ajax/offline/test_lowDeviceStorageDuringUpdate.html":"",
"dom/tests/mochitest/ajax/offline/test_missingFile.html":"",
"dom/tests/mochitest/ajax/offline/test_missingManifest.html":"",
"dom/tests/mochitest/ajax/offline/test_noManifest.html":"",
"dom/tests/mochitest/general/test_bug629535.html":"bug 908439",
"dom/tests/mochitest/ajax/offline/test_obsolete.html":"",
"dom/tests/mochitest/ajax/offline/test_offlineIFrame.html":"",
"dom/tests/mochitest/ajax/offline/test_overlap.html":"",
@ -179,8 +163,6 @@
"content/html/content/test/forms/test_input_file_picker.html":"5 failures out of 139 and timing out, bug 901581",
"content/html/content/test/forms/test_validation.html":"374 total, bug 901848, no keygen support",
"content/html/content/test/test_bug209275.xhtml":"timed out, 47 tests, bug 870262, :visited support",
"content/html/content/test/test_bug481335.xhtml":"timed out, bug 870262, :visited support",
"content/html/content/test/test_bug430351.html":"13 failing out of 700, not focusable iframes? bug 902207",
@ -202,27 +184,24 @@
"content/html/content/test/test_iframe_sandbox_plugins.html":"plugins not supported",
"content/html/content/test/test_object_plugin_nav.html":"plugins not supported",
"content/html/document/test/test_bug199692.html":"",
"content/html/document/test/test_bug741266.html":"",
"docshell/test/navigation/test_popup-navigates-children.html":"",
"docshell/test/test_bug590573.html":"",
"dom/devicestorage/ipc/test_ipc.html":"",
"dom/file/test/test_progress_events.html":"",
"dom/file/test/test_request_readyState.html":"",
"dom/file/test/test_stream_tracking.html":"",
"dom/indexedDB/ipc/test_ipc.html":"",
"dom/indexedDB/test/test_lowDiskSpace.html":"",
"dom/tests/mochitest/ajax/offline/test_noManifest.html":"",
"dom/tests/mochitest/general/test_bug629535.html":"",
"content/base/test/test_object.html":"",
"content/base/test/test_CSP_evalscript.html":"",
"content/base/test/test_CSP_evalscript_getCRMFRequest.html":"",
"content/base/test/test_CSP_frameancestors.html":"",
"content/svg/content/test/test_text_selection.html":"",
"content/base/test/test_CSP.html":"",
"content/base/test/test_CrossSiteXHR_origin.html":"",
"content/base/test/test_bug326337.html":"",
"content/base/test/test_bug564863.xhtml":"",
"content/html/document/test/test_bug199692.html":"needs popup to be sized to 600*600",
"content/html/document/test/test_bug741266.html":"needs control of popup window size",
"docshell/test/navigation/test_popup-navigates-children.html":"Needs multiple window.open support, also uses docshelltreenode",
"docshell/test/test_bug590573.html":"queryinterfaces into webnavigation, might suffer from something similar as bug 823022",
"dom/devicestorage/ipc/test_ipc.html":"nested ipc not working",
"dom/indexedDB/ipc/test_ipc.html":"nested ipc not working",
"dom/indexedDB/test/test_lowDiskSpace.html":"this needs probably modification for notifyObserversInParentProcess to be similar as pushPermissions",
"content/base/test/test_object.html":"needs plugin support",
"content/base/test/test_CSP_evalscript.html":"observer not working",
"content/base/test/test_CSP_evalscript_getCRMFRequest.html":"observer not working",
"content/base/test/test_CSP_frameancestors.html":"observer not working",
"content/base/test/test_CSP.html":"observer not working",
"content/base/test/test_CrossSiteXHR_origin.html":"https not working, bug 907770",
"content/base/test/test_bug326337.html":"popup windows don't have specialpowers installed, could be solved with sendmessage/receivemessage",
"content/base/test/test_plugin_freezing.html":"",
"content/base/test/test_bug466409.html":"",
"content/base/test/test_bug482935.html":"",
@ -257,6 +236,7 @@
"content/events/test/test_wheel_default_action.html":"",
"content/base/test/test_bug682592.html":"",
"content/html/document/test/test_bug369370.html":"",
"content/svg/content/test/test_text_selection.html":"Mouse selection not workin on b2g",
"content/svg/content/test/test_SVGAnimatedImageSMILDisabled.html":"",
"content/xml/document/test/test_bug392338.html":"",
"content/base/test/test_bothCSPheaders.html":"",
@ -274,9 +254,7 @@
"docshell/test/navigation/test_bug386782.html":"",
"docshell/test/navigation/test_not-opener.html":"",
"docshell/test/navigation/test_reserved.html":"",
"docshell/test/test_bug369814.html":"",
"docshell/test/test_bug413310.html":"",
"docshell/test/test_bug94514.html":"",
"dom/imptests/html/webgl":"",
"dom/battery/test/test_battery_basics.html":"",
@ -289,6 +267,9 @@
"dom/browser-element/mochitest/test_browserElement_inproc_CloseFromOpener.html":"",
"dom/browser-element/":"",
"dom/file/test/test_progress_events.html":"All of these fail fairly regularly with: UnknownError: The operation failed for reasons unrelated to the database itself and not covered by any other error code. at http://mochi.test:8888/tests/dom/file/test/helpers.js:126",
"dom/file/test/test_request_readyState.html":"",
"dom/file/test/test_stream_tracking.html":"",
"dom/file/test/test_append_read_data.html":"",
"dom/file/test/test_archivereader.html":"",
"dom/file/test/test_archivereader_nonUnicode.html":"",
@ -303,10 +284,11 @@
"dom/file/test/test_truncate.html":"",
"dom/file/test/test_workers.html":"",
"dom/file/test/test_write_read_data.html":"",
"dom/imptests/editing/conformancetest/test_event.html":"",
"dom/imptests/editing/conformancetest/test_runtest.html":"",
"dom/media/tests/mochitest/test_dataChannel_basicAudio.html":"",
"dom/imptests/editing/conformancetest/test_event.html":"1 failure, bug 908879",
"dom/imptests/editing/conformancetest/test_runtest.html":"takes too long",
"dom/media/tests/mochitest/test_dataChannel_basicAudio.html":"bug 908473",
"dom/media/tests/mochitest/test_dataChannel_basicAudioVideo.html":"",
"dom/media/tests/mochitest/test_dataChannel_basicAudioVideoCombined.html":"",
"dom/media/tests/mochitest/test_dataChannel_basicDataOnly.html":"",
@ -334,9 +316,9 @@
"dom/media/tests/mochitest/test_peerConnection_setRemoteOfferInHaveLocalOffer.html":"",
"dom/media/tests/mochitest/test_peerConnection_throwInCallbacks.html":"",
"dom/mobilemessage/tests/test_sms_basics.html":"",
"dom/network/tests/test_networkstats_basics.html":"",
"dom/permission/tests/test_permission_basics.html":"",
"dom/mobilemessage/tests/test_sms_basics.html":"Bug 909036",
"dom/network/tests/test_networkstats_basics.html":"Bug 908935",
"dom/permission/tests/test_permission_basics.html":"Bug 908912 and bug 907770",
"dom/tests/mochitest/bugs/test_bug265203.html":"",
@ -430,7 +412,7 @@
"layout/base/tests/test_mozPaintCount.html":"",
"layout/base/tests/test_scroll_selection_into_view.html":"",
"layout/base/tests/test_bug761572.html":"",
"layout/base/tests/test_reftests_with_caret.html":"",
"layout/base/tests/test_reftests_with_caret.html":"Bug 909043, 1 test failing because of spellcheck",
"layout/base/tests/test_scroll_event_ordering.html":"",
"layout/forms/test/test_bug287446.html":"",
"layout/forms/test/test_bug348236.html":"",
@ -500,11 +482,15 @@
"layout/style/test/test_value_storage.html":"",
"layout/style/test/test_viewport_units.html":"",
"layout/style/test/test_viewport_units.html":"",
"layout/style/test/test_visited_image_loading.html":"",
"layout/style/test/test_visited_image_loading_empty.html":"",
"layout/style/test/test_visited_lying.html" : "timed out",
"layout/style/test/test_visited_pref.html" : "timed out",
"layout/style/test/test_visited_reftests.html":"",
"content/html/content/test/test_bug209275.xhtml":"timed out, 47 tests, bug 870262, :visited support",
"content/html/content/test/test_bug481335.xhtml":"timed out, bug 870262, :visited support",
"layout/style/test/test_visited_image_loading.html":"bug 870262, :visited support",
"layout/style/test/test_visited_image_loading_empty.html":"bug 870262, :visited support",
"layout/style/test/test_visited_lying.html" : "bug 870262, :visited support",
"layout/style/test/test_visited_pref.html" : "bug 870262, :visited support",
"layout/style/test/test_visited_reftests.html":"bug 870262, :visited support",
"layout/tables/test/test_bug541668_table_event_delivery.html":"",
"Harness_sanity/test_sanityEventUtils.html": "bug 688052",

Просмотреть файл

@ -20,6 +20,7 @@ function starttest(){
SpecialPowers.addPermission("pPROMPT", PROMPT_ACTION, document);
SpecialPowers.addPermission("pALLOW", ALLOW_ACTION, document);
SpecialPowers.addPermission("pDENY", DENY_ACTION, document);
SpecialPowers.addPermission("pREMOVE", ALLOW_ACTION, document);
setTimeout(test1, 0);
}
@ -36,6 +37,9 @@ function test1() {
} else if (!SpecialPowers.testPermission('pPROMPT', PROMPT_ACTION, document)) {
dump('/**** prompt not set ****/\n');
setTimeout(test1, 0);
} else if (!SpecialPowers.testPermission('pREMOVE', ALLOW_ACTION, document)) {
dump('/**** remove not set ****/\n');
setTimeout(test1, 0);
} else {
test2();
}
@ -43,20 +47,29 @@ function test1() {
function test2() {
ok(SpecialPowers.testPermission('pUNKNOWN', UNKNOWN_ACTION, document), 'pUNKNOWN value should have UNKOWN permission');
SpecialPowers.pushPermissions([{'type': 'pUNKNOWN', 'allow': true, 'context': document}, {'type': 'pALLOW', 'allow': false, 'context': document}, {'type': 'pDENY', 'allow': true, 'context': document}, {'type': 'pPROMPT', 'allow': true, 'context': document}], test3);
SpecialPowers.pushPermissions([{'type': 'pUNKNOWN', 'allow': true, 'context': document}, {'type': 'pALLOW', 'allow': false, 'context': document}, {'type': 'pDENY', 'allow': true, 'context': document}, {'type': 'pPROMPT', 'allow': true, 'context': document}, {'type': 'pREMOVE', 'remove': true, 'context': document}], test3);
}
function test3() {
ok(SpecialPowers.testPermission('pUNKNOWN', ALLOW_ACTION, document), 'pUNKNOWN value should have ALLOW permission');
ok(SpecialPowers.testPermission('pPROMPT', ALLOW_ACTION, document), 'pUNKNOWN value should have ALLOW permission');
ok(SpecialPowers.testPermission('pPROMPT', ALLOW_ACTION, document), 'pPROMPT value should have ALLOW permission');
ok(SpecialPowers.testPermission('pALLOW', DENY_ACTION, document), 'pALLOW should have DENY permission');
ok(SpecialPowers.testPermission('pDENY', ALLOW_ACTION, document), 'pDENY should have ALLOW permission');
ok(SpecialPowers.testPermission('pREMOVE', UNKNOWN_ACTION, document), 'pREMOVE should have REMOVE permission');
// only pPROMPT (last one) is different, the other stuff is just to see if it doesn't cause test failures
SpecialPowers.pushPermissions([{'type': 'pUNKNOWN', 'allow': true, 'context': document}, {'type': 'pALLOW', 'allow': false, 'context': document}, {'type': 'pDENY', 'allow': true, 'context': document}, {'type': 'pPROMPT', 'allow': false, 'context': document}], test4);
SpecialPowers.pushPermissions([{'type': 'pUNKNOWN', 'allow': true, 'context': document}, {'type': 'pALLOW', 'allow': false, 'context': document}, {'type': 'pDENY', 'allow': true, 'context': document}, {'type': 'pPROMPT', 'allow': false, 'context': document}], test3b);
}
function test3b() {
ok(SpecialPowers.testPermission('pPROMPT', DENY_ACTION, document), 'pPROMPT value should have DENY permission');
SpecialPowers.pushPermissions([{'type': 'pUNKNOWN', 'allow': DENY_ACTION, 'context': document}, {'type': 'pALLOW', 'allow': PROMPT_ACTION, 'context': document}, {'type': 'pDENY', 'allow': PROMPT_ACTION, 'context': document}, {'type': 'pPROMPT', 'allow': ALLOW_ACTION, 'context': document}], test4);
}
function test4() {
ok(SpecialPowers.testPermission('pPROMPT', DENY_ACTION, document), 'pPROMPT value should have DENY permission');
ok(SpecialPowers.testPermission('pUNKNOWN', DENY_ACTION, document), 'pUNKNOWN value should have DENY permission');
ok(SpecialPowers.testPermission('pPROMPT', ALLOW_ACTION, document), 'pPROMPT value should have ALLOW permission');
ok(SpecialPowers.testPermission('pALLOW', PROMPT_ACTION, document), 'pALLOW should have PROMPT permission');
ok(SpecialPowers.testPermission('pDENY', PROMPT_ACTION, document), 'pDENY should have PROMPT permission');
//this should reset all the permissions to before all the pushPermissions calls
SpecialPowers.flushPermissions(test5);
}
@ -67,10 +80,12 @@ function test5() {
ok(SpecialPowers.testPermission('pALLOW', ALLOW_ACTION, document), 'pALLOW should have ALLOW permission');
ok(SpecialPowers.testPermission('pDENY', DENY_ACTION, document), 'pDENY should have DENY permission');
ok(SpecialPowers.testPermission('pPROMPT', PROMPT_ACTION, document), 'pPROMPT should have PROMPT permission');
ok(SpecialPowers.testPermission('pREMOVE', ALLOW_ACTION, document), 'pREMOVE should have ALLOW permission');
SpecialPowers.removePermission("pPROMPT", document);
SpecialPowers.removePermission("pALLOW", document);
SpecialPowers.removePermission("pDENY", document);
SpecialPowers.removePermission("pREMOVE", document);
setTimeout(test6, 0);
}
@ -85,6 +100,9 @@ function test6() {
} else if (!SpecialPowers.testPermission('pPROMPT', UNKNOWN_ACTION, document)) {
dump('/**** prompt still set ****/\n');
setTimeout(test6, 0);
} else if (!SpecialPowers.testPermission('pREMOVE', UNKNOWN_ACTION, document)) {
dump('/**** remove still set ****/\n');
setTimeout(test6, 0);
} else {
SimpleTest.finish();
}

Просмотреть файл

@ -561,7 +561,7 @@ SpecialPowersAPI.prototype = {
[{'type': 'SystemXHR', 'allow': 1, 'context': document},
{'type': 'SystemXHR', 'allow': Ci.nsIPermissionManager.PROMPT_ACTION, 'context': document}]
allow is a boolean and can be true/false or 1/0
Allow can be a boolean value of true/false or ALLOW_ACTION/DENY_ACTION/PROMPT_ACTION/UNKNOWN_ACTION
*/
pushPermissions: function(inPermissions, callback) {
var pendingPermissions = [];
@ -591,7 +591,12 @@ SpecialPowersAPI.prototype = {
if (originalValue == perm) {
continue;
}
pendingPermissions.push({'op': 'add', 'type': permission.type, 'permission': perm, 'value': perm, 'url': url, 'appId': appId, 'isInBrowserElement': isInBrowserElement});
var todo = {'op': 'add', 'type': permission.type, 'permission': perm, 'value': perm, 'url': url, 'appId': appId, 'isInBrowserElement': isInBrowserElement};
if (permission.remove == true)
todo.op = 'remove';
pendingPermissions.push(todo);
/* Push original permissions value or clear into cleanup array */
var cleanupTodo = {'op': 'add', 'type': permission.type, 'permission': perm, 'value': perm, 'url': url, 'appId': appId, 'isInBrowserElement': isInBrowserElement};

Просмотреть файл

@ -160,6 +160,11 @@ let Scheduler = {
if (!("durationMs" in data)) {
return data.ok;
}
// Bug 874425 demonstrates that two successive calls to Date.now()
// can actually produce an interval with negative duration.
// We assume that this is due to an operation that is so short
// that Date.now() is not monotonic, so we round this up to 0.
let durationMs = Math.max(0, data.durationMs);
// Accumulate (or initialize) outExecutionDuration
if (typeof options.outExecutionDuration == "number") {
options.outExecutionDuration += data.durationMs;

Просмотреть файл

@ -902,7 +902,8 @@ DebuggerServerConnection.prototype = {
let actor = this.getActor(aPacket.to);
if (!actor) {
this.transport.send({ from: aPacket.to ? aPacket.to : "root",
error: "noSuchActor" });
error: "noSuchActor",
message: "No such actor for ID: " + aPacket.to });
return;
}

Просмотреть файл

@ -2131,6 +2131,15 @@ this.AddonManagerPrivate = {
getSimpleMeasures: function AMP_getSimpleMeasures() {
return this._simpleMeasures;
},
// Start a timer, record a simple measure of the time interval when
// timer.done() is called
simpleTimer: function(aName) {
let startTime = Date.now();
return {
done: () => AddonManagerPrivate.recordSimpleMeasure(aName, Date.now() - startTime)
};
}
};

Просмотреть файл

@ -94,11 +94,8 @@ const KEY_APP_SYSTEM_LOCAL = "app-system-local";
const KEY_APP_SYSTEM_SHARE = "app-system-share";
const KEY_APP_SYSTEM_USER = "app-system-user";
const UNKNOWN_XPCOM_ABI = "unknownABI";
const XPI_PERMISSION = "install";
const PREFIX_ITEM_URI = "urn:mozilla:item:";
const RDFURI_ITEM_ROOT = "urn:mozilla:item:root"
const RDFURI_INSTALL_MANIFEST_ROOT = "urn:mozilla:install-manifest";
const PREFIX_NS_EM = "http://www.mozilla.org/2004/em-rdf#";
@ -159,6 +156,14 @@ const TYPES = {
dictionary: 64
};
// Keep track of where we are in startup for telemetry
// event happened during XPIDatabase.startup()
const XPI_STARTING = "XPIStarting";
// event happened after startup() but before the final-ui-startup event
const XPI_BEFORE_UI_STARTUP = "BeforeFinalUIStartup";
// event happened after final-ui-startup
const XPI_AFTER_UI_STARTUP = "AfterFinalUIStartup";
const COMPATIBLE_BY_DEFAULT_TYPES = {
extension: true,
dictionary: true
@ -1538,6 +1543,8 @@ var XPIProvider = {
inactiveAddonIDs: [],
// Count of unpacked add-ons
unpackedAddons: 0,
// Keep track of startup phases for telemetry
runPhase: XPI_STARTING,
/**
* Adds or updates a URI mapping for an Addon.id.
@ -1674,6 +1681,7 @@ var XPIProvider = {
*/
startup: function XPI_startup(aAppChanged, aOldAppVersion, aOldPlatformVersion) {
LOG("startup");
this.runPhase = XPI_STARTING;
this.installs = [];
this.installLocations = [];
this.installLocationsByName = {};
@ -1848,9 +1856,19 @@ var XPIProvider = {
}
}, "quit-application-granted", false);
// Detect final-ui-startup for telemetry reporting
Services.obs.addObserver({
observe: function uiStartupObserver(aSubject, aTopic, aData) {
AddonManagerPrivate.recordTimestamp("XPI_finalUIStartup");
XPIProvider.runPhase = XPI_AFTER_UI_STARTUP;
Services.obs.removeObserver(this, "final-ui-startup");
}
}, "final-ui-startup", false);
AddonManagerPrivate.recordTimestamp("XPI_startup_end");
this.extensionsActive = true;
this.runPhase = XPI_BEFORE_UI_STARTUP;
},
/**
@ -1935,7 +1953,7 @@ var XPIProvider = {
var variant = Cc["@mozilla.org/variant;1"].
createInstance(Ci.nsIWritableVariant);
variant.setFromVariant(this.inactiveAddonIDs);
// This *must* be modal as it has to block startup.
var features = "chrome,centerscreen,dialog,titlebar,modal";
var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
@ -1979,7 +1997,7 @@ var XPIProvider = {
Services.appinfo.annotateCrashReport("Add-ons", data);
}
catch (e) { }
const TelemetryPing = Cc["@mozilla.org/base/telemetry-ping;1"].getService(Ci.nsITelemetryPing);
TelemetryPing.setAddOns(data);
},
@ -3143,9 +3161,12 @@ var XPIProvider = {
if (aAppChanged !== false)
this.importPermissions();
// If the application version has changed then the database information
// needs to be updated
let updateDatabase = aAppChanged;
// Keep track of whether and why we need to open and update the database at
// startup time.
let updateReasons = [];
if (aAppChanged) {
updateReasons.push("appChanged");
}
// Load the list of bootstrapped add-ons first so processFileChanges can
// modify it
@ -3160,7 +3181,10 @@ var XPIProvider = {
// changes then we must update the database with the information in the
// install locations
let manifests = {};
updateDatabase = this.processPendingFileChanges(manifests) || updateDatabase;
let updated = this.processPendingFileChanges(manifests);
if (updated) {
updateReasons.push("pendingFileChanges");
}
// This will be true if the previous session made changes that affect the
// active state of add-ons but didn't commit them properly (normally due
@ -3168,33 +3192,41 @@ var XPIProvider = {
let hasPendingChanges = Prefs.getBoolPref(PREF_PENDING_OPERATIONS);
// If the schema appears to have changed then we should update the database
updateDatabase = updateDatabase || DB_SCHEMA != Prefs.getIntPref(PREF_DB_SCHEMA, 0);
if (DB_SCHEMA != Prefs.getIntPref(PREF_DB_SCHEMA, 0)) {
updateReasons.push("schemaChanged");
}
// If the application has changed then check for new distribution add-ons
if (aAppChanged !== false &&
Prefs.getBoolPref(PREF_INSTALL_DISTRO_ADDONS, true))
updateDatabase = this.installDistributionAddons(manifests) || updateDatabase;
{
updated = this.installDistributionAddons(manifests);
if (updated) {
updateReasons.push("installDistributionAddons");
}
}
// Telemetry probe added around getInstallLocationStates() to check perf
let telemetryCaptureTime = new Date();
let telemetryCaptureTime = Date.now();
let state = this.getInstallLocationStates();
let telemetry = Services.telemetry;
telemetry.getHistogramById("CHECK_ADDONS_MODIFIED_MS").add(new Date() - telemetryCaptureTime);
telemetry.getHistogramById("CHECK_ADDONS_MODIFIED_MS").add(Date.now() - telemetryCaptureTime);
if (!updateDatabase) {
// If the state has changed then we must update the database
let cache = Prefs.getCharPref(PREF_INSTALL_CACHE, null);
updateDatabase = cache != JSON.stringify(state);
// If the install directory state has changed then we must update the database
let cache = Prefs.getCharPref(PREF_INSTALL_CACHE, null);
if (cache != JSON.stringify(state)) {
updateReasons.push("directoryState");
}
// If the database doesn't exist and there are add-ons installed then we
// must update the database however if there are no add-ons then there is
// no need to update the database.
let dbFile = FileUtils.getFile(KEY_PROFILEDIR, [FILE_DATABASE], true);
if (!dbFile.exists())
updateDatabase = state.length > 0;
if (!dbFile.exists() && state.length > 0) {
updateReasons.push("needNewDatabase");
}
if (!updateDatabase) {
if (updateReasons.length == 0) {
let bootstrapDescriptors = [this.bootstrappedAddons[b].descriptor
for (b in this.bootstrappedAddons)];
@ -3208,7 +3240,7 @@ var XPIProvider = {
if (bootstrapDescriptors.length > 0) {
WARN("Bootstrap state is invalid (missing add-ons: " + bootstrapDescriptors.toSource() + ")");
updateDatabase = true;
updateReasons.push("missingBootstrapAddon");
}
}
@ -3217,7 +3249,11 @@ var XPIProvider = {
let extensionListChanged = false;
// If the database needs to be updated then open it and then update it
// from the filesystem
if (updateDatabase || hasPendingChanges) {
if (hasPendingChanges) {
updateReasons.push("hasPendingChanges");
}
if (updateReasons.length > 0) {
AddonManagerPrivate.recordSimpleMeasure("XPIDB_startup_load_reasons", updateReasons);
XPIDatabase.syncLoadDB(false);
try {
extensionListChanged = this.processFileChanges(state, manifests,

Просмотреть файл

@ -68,24 +68,6 @@ const DB_BOOL_METADATA = ["visible", "active", "userDisabled", "appDisabled",
"softDisabled", "isForeignInstall",
"hasBinaryComponents", "strictCompatibility"];
const FIELDS_ADDON = "internal_id, id, syncGUID, location, version, type, " +
"internalName, updateURL, updateKey, optionsURL, " +
"optionsType, aboutURL, iconURL, icon64URL, " +
"defaultLocale, visible, active, userDisabled, " +
"appDisabled, pendingUninstall, descriptor, " +
"installDate, updateDate, applyBackgroundUpdates, bootstrap, " +
"skinnable, size, sourceURI, releaseNotesURI, softDisabled, " +
"isForeignInstall, hasBinaryComponents, strictCompatibility";
// Properties that exist in the install manifest
const PROP_METADATA = ["id", "version", "type", "internalName", "updateURL",
"updateKey", "optionsURL", "optionsType", "aboutURL",
"iconURL", "icon64URL"];
const PROP_LOCALE_SINGLE = ["name", "description", "creator", "homepageURL"];
const PROP_LOCALE_MULTI = ["developers", "translators", "contributors"];
const PROP_TARGETAPP = ["id", "minVersion", "maxVersion"];
// Properties to save in JSON file
const PROP_JSON_FIELDS = ["id", "syncGUID", "location", "version", "type",
"internalName", "updateURL", "updateKey", "optionsURL",
@ -549,11 +531,11 @@ this.XPIDatabase = {
* (if false, caller is XPIProvider.checkForChanges() which will rebuild)
*/
syncLoadDB: function XPIDB_syncLoadDB(aRebuildOnError) {
// XXX TELEMETRY report synchronous opens (startup time) vs. delayed opens
this.migrateData = null;
let fstream = null;
let data = "";
try {
let readTimer = AddonManagerPrivate.simpleTimer("XPIDB_syncRead_MS");
LOG("Opening XPI database " + this.jsonFile.path);
fstream = Components.classes["@mozilla.org/network/file-input-stream;1"].
createInstance(Components.interfaces.nsIFileInputStream);
@ -570,11 +552,14 @@ this.XPIDatabase = {
data += str.value;
} while (read != 0);
}
readTimer.done();
this.parseDB(data, aRebuildOnError);
}
catch(e) {
ERROR("Failed to load XPI JSON data from profile", e);
let rebuildTimer = AddonManagerPrivate.simpleTimer("XPIDB_rebuildReadFailed_MS");
this.rebuildDatabase(aRebuildOnError);
rebuildTimer.done();
}
finally {
if (cstream)
@ -582,7 +567,7 @@ this.XPIDatabase = {
}
}
catch (e) {
if (e.result == Cr.NS_ERROR_FILE_NOT_FOUND) {
if (e.result === Cr.NS_ERROR_FILE_NOT_FOUND) {
this.upgradeDB(aRebuildOnError);
}
else {
@ -595,8 +580,10 @@ this.XPIDatabase = {
}
// If an async load was also in progress, resolve that promise with our DB;
// otherwise create a resolved promise
if (this._dbPromise)
if (this._dbPromise) {
AddonManagerPrivate.recordSimpleMeasure("XPIDB_overlapped_load", 1);
this._dbPromise.resolve(this.addonDB);
}
else
this._dbPromise = Promise.resolve(this.addonDB);
},
@ -607,20 +594,27 @@ this.XPIDatabase = {
* If true, synchronously reconstruct the database from installed add-ons
*/
parseDB: function(aData, aRebuildOnError) {
let parseTimer = AddonManagerPrivate.simpleTimer("XPIDB_parseDB_MS");
try {
// dump("Loaded JSON:\n" + aData + "\n");
let inputAddons = JSON.parse(aData);
// Now do some sanity checks on our JSON db
if (!("schemaVersion" in inputAddons) || !("addons" in inputAddons)) {
parseTimer.done();
// Content of JSON file is bad, need to rebuild from scratch
ERROR("bad JSON file contents");
AddonManagerPrivate.recordSimpleMeasure("XPIDB_startupError", "badJSON");
let rebuildTimer = AddonManagerPrivate.simpleTimer("XPIDB_rebuildBadJSON_MS");
this.rebuildDatabase(aRebuildOnError);
rebuildTimer.done();
return;
}
if (inputAddons.schemaVersion != DB_SCHEMA) {
// Handle mismatched JSON schema version. For now, we assume
// compatibility for JSON data, though we throw away any fields we
// don't know about
// don't know about (bug 902956)
AddonManagerPrivate.recordSimpleMeasure("XPIDB_startupError",
"schemaMismatch-" + inputAddons.schemaVersion);
LOG("JSON schema mismatch: expected " + DB_SCHEMA +
", actual " + inputAddons.schemaVersion);
}
@ -631,6 +625,7 @@ this.XPIDatabase = {
let newAddon = new DBAddonInternal(loadedAddon);
addonDB.set(newAddon._key, newAddon);
};
parseTimer.done();
this.addonDB = addonDB;
LOG("Successfully read XPI database");
this.initialized = true;
@ -638,13 +633,18 @@ this.XPIDatabase = {
catch(e) {
// If we catch and log a SyntaxError from the JSON
// parser, the xpcshell test harness fails the test for us: bug 870828
parseTimer.done();
if (e.name == "SyntaxError") {
ERROR("Syntax error parsing saved XPI JSON data");
AddonManagerPrivate.recordSimpleMeasure("XPIDB_startupError", "syntax");
}
else {
ERROR("Failed to load XPI JSON data from profile", e);
AddonManagerPrivate.recordSimpleMeasure("XPIDB_startupError", "other");
}
let rebuildTimer = AddonManagerPrivate.simpleTimer("XPIDB_rebuildReadFailed_MS");
this.rebuildDatabase(aRebuildOnError);
rebuildTimer.done();
}
},
@ -652,14 +652,18 @@ this.XPIDatabase = {
* Upgrade database from earlier (sqlite or RDF) version if available
*/
upgradeDB: function(aRebuildOnError) {
let upgradeTimer = AddonManagerPrivate.simpleTimer("XPIDB_upgradeDB_MS");
try {
let schemaVersion = Services.prefs.getIntPref(PREF_DB_SCHEMA);
if (schemaVersion <= LAST_SQLITE_DB_SCHEMA) {
// we should have an older SQLITE database
this.migrateData = this.getMigrateDataFromSQLITE();
}
// else we've upgraded before but the JSON file is gone, fall through
// and rebuild from scratch
else {
// we've upgraded before but the JSON file is gone, fall through
// and rebuild from scratch
AddonManagerPrivate.recordSimpleMeasure("XPIDB_startupError", "dbMissing");
}
}
catch(e) {
// No schema version pref means either a really old upgrade (RDF) or
@ -668,6 +672,7 @@ this.XPIDatabase = {
}
this.rebuildDatabase(aRebuildOnError);
upgradeTimer.done();
},
/**
@ -675,13 +680,15 @@ this.XPIDatabase = {
* (for example because read permission is denied)
*/
rebuildUnreadableDB: function(aError, aRebuildOnError) {
let rebuildTimer = AddonManagerPrivate.simpleTimer("XPIDB_rebuildUnreadableDB_MS");
WARN("Extensions database " + this.jsonFile.path +
" exists but is not readable; rebuilding in memory", aError);
" exists but is not readable; rebuilding", aError);
// Remember the error message until we try and write at least once, so
// we know at shutdown time that there was a problem
this._loadError = aError;
// XXX TELEMETRY report when this happens?
AddonManagerPrivate.recordSimpleMeasure("XPIDB_startupError", "unreadable");
this.rebuildDatabase(aRebuildOnError);
rebuildTimer.done();
},
/**
@ -692,22 +699,31 @@ this.XPIDatabase = {
* @return Promise<Map> resolves to the Map of loaded JSON data stored
* in this.addonDB; never rejects.
*/
asyncLoadDB: function XPIDB_asyncLoadDB(aDBCallback) {
asyncLoadDB: function XPIDB_asyncLoadDB() {
// Already started (and possibly finished) loading
if (this._dbPromise) {
return this._dbPromise;
}
LOG("Starting async load of XPI database " + this.jsonFile.path);
return this._dbPromise = OS.File.read(this.jsonFile.path).then(
AddonManagerPrivate.recordSimpleMeasure("XPIDB_async_load", XPIProvider.runPhase);
let readOptions = {
outExecutionDuration: 0
};
return this._dbPromise = OS.File.read(this.jsonFile.path, null, readOptions).then(
byteArray => {
LOG("Async JSON file read took " + readOptions.outExecutionDuration + " MS");
AddonManagerPrivate.recordSimpleMeasure("XPIDB_asyncRead_MS",
readOptions.outExecutionDuration);
if (this._addonDB) {
LOG("Synchronous load completed while waiting for async load");
return this.addonDB;
}
LOG("Finished async read of XPI database, parsing...");
let decodeTimer = AddonManagerPrivate.simpleTimer("XPIDB_decode_MS");
let decoder = new TextDecoder();
let data = decoder.decode(byteArray);
decodeTimer.done();
this.parseDB(data, true);
return this.addonDB;
})
@ -973,11 +989,21 @@ this.XPIDatabase = {
this.initialized = false;
if (this._deferredSave) {
AddonManagerPrivate.recordSimpleMeasure(
"XPIDB_saves_total", this._deferredSave.totalSaves);
AddonManagerPrivate.recordSimpleMeasure(
"XPIDB_saves_overlapped", this._deferredSave.overlappedSaves);
AddonManagerPrivate.recordSimpleMeasure(
"XPIDB_saves_late", this._deferredSave.dirty ? 1 : 0);
}
// Make sure any pending writes of the DB are complete, and we
// finish cleaning up, and then call back
this.flush()
.then(null, error => {
ERROR("Flush of XPI database failed", error);
AddonManagerPrivate.recordSimpleMeasure("XPIDB_shutdownFlush_failed", 1);
return 0;
})
.then(count => {
@ -1137,6 +1163,7 @@ this.XPIDatabase = {
// an XPI theme to a lightweight theme before the DB has loaded,
// because we're called from sync XPIProvider.addonChanged
WARN("Synchronous load of XPI database due to getAddonsByType(" + aType + ")");
AddonManagerPrivate.recordSimpleMeasure("XPIDB_lateOpen_byType", XPIProvider.runPhase);
this.syncLoadDB(true);
}
return _filterDB(this.addonDB, aAddon => (aAddon.type == aType));
@ -1152,8 +1179,9 @@ this.XPIDatabase = {
getVisibleAddonForInternalName: function XPIDB_getVisibleAddonForInternalName(aInternalName) {
if (!this.addonDB) {
// This may be called when the DB hasn't otherwise been loaded
// XXX TELEMETRY
WARN("Synchronous load of XPI database due to getVisibleAddonForInternalName");
AddonManagerPrivate.recordSimpleMeasure("XPIDB_lateOpen_forInternalName",
XPIProvider.runPhase);
this.syncLoadDB(true);
}
@ -1225,7 +1253,8 @@ this.XPIDatabase = {
*/
addAddonMetadata: function XPIDB_addAddonMetadata(aAddon, aDescriptor) {
if (!this.addonDB) {
// XXX telemetry. Should never happen on platforms that have a default theme
AddonManagerPrivate.recordSimpleMeasure("XPIDB_lateOpen_addMetadata",
XPIProvider.runPhase);
this.syncLoadDB(false);
}
@ -1371,7 +1400,9 @@ this.XPIDatabase = {
*/
writeAddonsList: function XPIDB_writeAddonsList() {
if (!this.addonDB) {
// Unusual condition, force the DB to load
// force the DB to load
AddonManagerPrivate.recordSimpleMeasure("XPIDB_lateOpen_writeList",
XPIProvider.runPhase);
this.syncLoadDB(true);
}
Services.appinfo.invalidateCachesOnRestart();

Просмотреть файл

@ -998,19 +998,19 @@ function prepare_test(aExpectedEvents, aExpectedInstalls, aNext) {
// Checks if all expected events have been seen and if so calls the callback
function check_test_completed(aArgs) {
if (!gNext)
return;
return undefined;
if (gExpectedInstalls instanceof Array &&
gExpectedInstalls.length > 0)
return;
return undefined;
else for each (let installList in gExpectedInstalls) {
if (installList.length > 0)
return;
return undefined;
}
for (let id in gExpectedEvents) {
if (gExpectedEvents[id].length > 0)
return;
return undefined;
}
return gNext.apply(null, aArgs);
@ -1186,6 +1186,7 @@ if ("nsIWindowsRegKey" in AM_Ci) {
if (value.name == aName)
return value.value;
}
return null;
}
};
@ -1290,7 +1291,7 @@ do_register_cleanup(function addon_cleanup() {
var dirEntries = gTmpD.directoryEntries
.QueryInterface(AM_Ci.nsIDirectoryEnumerator);
var entry;
while (entry = dirEntries.nextFile) {
while ((entry = dirEntries.nextFile)) {
do_throw("Found unexpected file in temporary directory: " + entry.leafName);
}
dirEntries.close();

Просмотреть файл

@ -16,7 +16,7 @@ const IGNORE_PRIVATE = ["AddonAuthor", "AddonCompatibilityOverride",
"registerProvider", "unregisterProvider",
"addStartupChange", "removeStartupChange",
"recordTimestamp", "recordSimpleMeasure",
"getSimpleMeasures"];
"getSimpleMeasures", "simpleTimer"];
function test_functions() {
for (let prop in AddonManager) {

Просмотреть файл

@ -2,6 +2,8 @@
* 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/. */
const DEFAULT_ICON_URL = "chrome://global/skin/icons/webapps-64.png";
/**
* This function receives a list of icon sizes
* and URLs and returns the url string for the biggest icon.
@ -17,95 +19,83 @@
*/
function getBiggestIconURL(aIcons) {
if (!aIcons) {
return "chrome://global/skin/icons/webapps-64.png";
return DEFAULT_ICON_URL;
}
let iconSizes = Object.keys(aIcons);
if (iconSizes.length == 0) {
return "chrome://global/skin/icons/webapps-64.png";
return DEFAULT_ICON_URL;
}
iconSizes.sort(function(a, b) a - b);
return aIcons[iconSizes.pop()];
}
/**
* This function retrieves the icon for an app as specified
* in the iconURI on the shell object.
* Upon completion it will call aShell.processIcon()
*
* @param aShell The shell that specifies the properties
* of the native app. Three properties from this
* shell will be used in this function:
* - iconURI
* - useTmpForIcon
* - processIcon()
*/
function getIconForApp(aShell, callback) {
let iconURI = aShell.iconURI;
let mimeService = Cc["@mozilla.org/mime;1"]
.getService(Ci.nsIMIMEService);
// Download an icon using either a temp file or a pipe.
function downloadIcon(aIconURI) {
let deferred = Promise.defer();
let mimeService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
let mimeType;
try {
let tIndex = iconURI.path.indexOf(";");
if("data" == iconURI.scheme && tIndex != -1) {
mimeType = iconURI.path.substring(0, tIndex);
let tIndex = aIconURI.path.indexOf(";");
if("data" == aIconURI.scheme && tIndex != -1) {
mimeType = aIconURI.path.substring(0, tIndex);
} else {
mimeType = mimeService.getTypeFromURI(iconURI);
}
mimeType = mimeService.getTypeFromURI(aIconURI);
}
} catch(e) {
throw("getIconFromURI - Failed to determine MIME type");
deferred.reject("Failed to determine icon MIME type: " + e);
return deferred.promise;
}
function onIconDownloaded(aStatusCode, aIcon) {
if (Components.isSuccessCode(aStatusCode)) {
deferred.resolve([ mimeType, aIcon ]);
} else {
deferred.reject("Failure downloading icon: " + aStatusCode);
}
}
try {
let listener;
if(aShell.useTmpForIcon) {
let downloadObserver = {
onDownloadComplete: function(downloader, request, cx, aStatus, file) {
// pass downloader just to keep reference around
onIconDownloaded(aShell, mimeType, aStatus, file, callback, downloader);
}
};
#ifdef XP_MACOSX
let downloadObserver = {
onDownloadComplete: function(downloader, request, cx, aStatus, file) {
onIconDownloaded(aStatus, file);
}
};
let tmpIcon = Services.dirsvc.get("TmpD", Ci.nsIFile);
tmpIcon.append("tmpicon." + mimeService.getPrimaryExtension(mimeType, ""));
tmpIcon.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
let tmpIcon = Services.dirsvc.get("TmpD", Ci.nsIFile);
tmpIcon.append("tmpicon." + mimeService.getPrimaryExtension(mimeType, ""));
tmpIcon.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("666", 8));
listener = Cc["@mozilla.org/network/downloader;1"]
.createInstance(Ci.nsIDownloader);
listener.init(downloadObserver, tmpIcon);
} else {
let pipe = Cc["@mozilla.org/pipe;1"]
.createInstance(Ci.nsIPipe);
pipe.init(true, true, 0, 0xffffffff, null);
let listener = Cc["@mozilla.org/network/downloader;1"]
.createInstance(Ci.nsIDownloader);
listener.init(downloadObserver, tmpIcon);
#else
let pipe = Cc["@mozilla.org/pipe;1"]
.createInstance(Ci.nsIPipe);
pipe.init(true, true, 0, 0xffffffff, null);
listener = Cc["@mozilla.org/network/simple-stream-listener;1"]
.createInstance(Ci.nsISimpleStreamListener);
listener.init(pipe.outputStream, {
onStartRequest: function() {},
onStopRequest: function(aRequest, aContext, aStatusCode) {
pipe.outputStream.close();
onIconDownloaded(aShell, mimeType, aStatusCode, pipe.inputStream, callback);
}
});
}
let listener = Cc["@mozilla.org/network/simple-stream-listener;1"]
.createInstance(Ci.nsISimpleStreamListener);
listener.init(pipe.outputStream, {
onStartRequest: function() {},
onStopRequest: function(aRequest, aContext, aStatusCode) {
pipe.outputStream.close();
onIconDownloaded(aStatusCode, pipe.inputStream);
}
});
#endif
let channel = NetUtil.newChannel(iconURI);
let CertUtils = { };
Cu.import("resource://gre/modules/CertUtils.jsm", CertUtils);
let channel = NetUtil.newChannel(aIconURI);
let { BadCertHandler } = Cu.import("resource://gre/modules/CertUtils.jsm", {});
// Pass true to avoid optional redirect-cert-checking behavior.
channel.notificationCallbacks = new CertUtils.BadCertHandler(true);
channel.notificationCallbacks = new BadCertHandler(true);
channel.asyncOpen(listener, null);
} catch(e) {
throw("getIconFromURI - Failure getting icon (" + e + ")");
deferred.reject("Failure initiating download of icon: " + e);
}
}
function onIconDownloaded(aShell, aMimeType, aStatusCode, aIcon, aCallback) {
if (Components.isSuccessCode(aStatusCode)) {
aShell.processIcon(aMimeType, aIcon, aCallback);
} else {
aCallback.call(aShell);
}
return deferred.promise;
}

Просмотреть файл

@ -16,6 +16,7 @@ Cu.import("resource://gre/modules/osfile.jsm");
Cu.import("resource://gre/modules/WebappOSUtils.jsm");
Cu.import("resource://gre/modules/AppsUtils.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/Promise.jsm");
this.WebappsInstaller = {
shell: null,
@ -60,35 +61,27 @@ this.WebappsInstaller = {
*
* @param aData the data provided to the install function
* @param aManifest the manifest data provided by the web app
*
* @returns true on success, false if an error was thrown
*/
install: function(aData, aManifest) {
try {
if (Services.prefs.getBoolPref("browser.mozApps.installer.dry_run")) {
return true;
return Promise.resolve();
}
} catch (ex) {}
this.shell.init(aData, aManifest);
try {
this.shell.install();
} catch (ex) {
Cu.reportError("Error installing app: " + ex);
return false;
}
return this.shell.install().then(() => {
let data = {
"installDir": this.shell.installDir.path,
"app": {
"manifest": aManifest,
"origin": aData.app.origin
}
};
let data = {
"installDir": this.shell.installDir.path,
"app": {
"manifest": aManifest,
"origin": aData.app.origin
}
};
Services.obs.notifyObservers(null, "webapp-installed", JSON.stringify(data));
return true;
Services.obs.notifyObservers(null, "webapp-installed", JSON.stringify(data));
});
}
}
@ -122,6 +115,7 @@ NativeApp.prototype = {
categories: null,
webappJson: null,
runtimeFolder: null,
manifest: null,
/**
* This function reads and parses the data from the app
@ -132,7 +126,8 @@ NativeApp.prototype = {
*
*/
init: function(aData, aManifest) {
let manifest = new ManifestHelper(aManifest, aData.app.origin);
let manifest = this.manifest = new ManifestHelper(aManifest,
aData.app.origin);
let origin = Services.io.newURI(aData.app.origin, null, null);
@ -151,10 +146,16 @@ NativeApp.prototype = {
catch (ex) {}
}
if (manifest.developer && manifest.developer.name) {
let devName = sanitize(manifest.developer.name.substr(0, 128));
if (devName) {
this.developerName = devName;
if (manifest.developer) {
if (manifest.developer.name) {
let devName = sanitize(manifest.developer.name.substr(0, 128));
if (devName) {
this.developerName = devName;
}
}
if (manifest.developer.url) {
this.developerUrl = manifest.developer.url;
}
}
@ -184,7 +185,30 @@ NativeApp.prototype = {
};
this.runtimeFolder = Services.dirsvc.get("GreD", Ci.nsIFile);
}
},
/**
* This function retrieves the icon for an app.
* If the retrieving fails, it uses the default chrome icon.
*/
getIcon: function() {
try {
let [ mimeType, icon ] = yield downloadIcon(this.iconURI);
yield this.processIcon(mimeType, icon);
}
catch(e) {
Cu.reportError("Failure retrieving icon: " + e);
let iconURI = Services.io.newURI(DEFAULT_ICON_URL, null, null);
let [ mimeType, icon ] = yield downloadIcon(iconURI);
yield this.processIcon(mimeType, icon);
// Set the iconURI property so that the user notification will have the
// correct icon.
this.iconURI = iconURI;
}
},
};
#ifdef XP_WIN
@ -218,28 +242,35 @@ NativeApp.prototype = {
*/
function WinNativeApp(aData) {
NativeApp.call(this, aData);
if (aData.isPackage) {
this.size = aData.app.updateManifest.size / 1024;
}
this._init();
}
WinNativeApp.prototype = {
__proto__: NativeApp.prototype,
size: null,
/**
* Install the app in the system
*
*/
install: function() {
try {
this._copyPrebuiltFiles();
this._createShortcutFiles();
this._createConfigFiles();
this._writeSystemKeys();
} catch (ex) {
this._removeInstallation(false);
throw(ex);
}
getIconForApp(this, function() {});
return Task.spawn(function() {
try {
this._copyPrebuiltFiles();
this._createShortcutFiles();
this._createConfigFiles();
this._writeSystemKeys();
yield this.getIcon();
} catch (ex) {
this._removeInstallation(false);
throw(ex);
}
}.bind(this));
},
/**
@ -452,6 +483,28 @@ WinNativeApp.prototype = {
subKey.writeStringValue("DisplayIcon", this.iconFile.path);
}
let date = new Date();
let year = date.getYear().toString();
let month = date.getMonth();
if (month < 10) {
month = "0" + month;
}
let day = date.getDate();
if (day < 10) {
day = "0" + day;
}
subKey.writeStringValue("InstallDate", year + month + day);
if (this.manifest.version) {
subKey.writeStringValue("DisplayVersion", this.manifest.version);
}
if (this.developerName) {
subKey.writeStringValue("Publisher", this.developerName);
}
subKey.writeStringValue("URLInfoAbout", this.developerUrl);
if (this.size) {
subKey.writeIntValue("EstimatedSize", this.size);
}
subKey.writeIntValue("NoModify", 1);
subKey.writeIntValue("NoRepair", 1);
} catch(ex) {
@ -490,14 +543,6 @@ WinNativeApp.prototype = {
shortcut.remove(false);
},
/**
* This variable specifies if the icon retrieval process should
* use a temporary file in the system or a binary stream. This
* is accessed by a common function in WebappsIconHelpers.js and
* is different for each platform.
*/
useTmpForIcon: false,
/**
* Process the icon from the imageStream as retrieved from
* the URL by getIconForApp(). This will save the icon to the
@ -505,28 +550,31 @@ WinNativeApp.prototype = {
*
* @param aMimeType ahe icon mimetype
* @param aImageStream the stream for the image data
* @param aCallback a callback function to be called
* after the process finishes
*/
processIcon: function(aMimeType, aImageStream, aCallback) {
let iconStream;
try {
let imgTools = Cc["@mozilla.org/image/tools;1"]
.createInstance(Ci.imgITools);
let imgContainer = { value: null };
processIcon: function(aMimeType, aImageStream) {
let deferred = Promise.defer();
imgTools.decodeImageData(aImageStream, aMimeType, imgContainer);
iconStream = imgTools.encodeImage(imgContainer.value,
"image/vnd.microsoft.icon",
"format=bmp;bpp=32");
} catch (e) {
throw("processIcon - Failure converting icon (" + e + ")");
let imgTools = Cc["@mozilla.org/image/tools;1"]
.createInstance(Ci.imgITools);
let imgContainer = imgTools.decodeImage(aImageStream, aMimeType);
let iconStream = imgTools.encodeImage(imgContainer,
"image/vnd.microsoft.icon",
"format=bmp;bpp=32");
if (!this.iconFile.parent.exists()) {
this.iconFile.parent.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt("0755", 8));
}
if (!this.iconFile.parent.exists())
this.iconFile.parent.create(Ci.nsIFile.DIRECTORY_TYPE, 0755);
let outputStream = FileUtils.openSafeFileOutputStream(this.iconFile);
NetUtil.asyncCopy(iconStream, outputStream);
NetUtil.asyncCopy(iconStream, outputStream, function(aResult) {
if (Components.isSuccessCode(aResult)) {
deferred.resolve();
} else {
deferred.reject("Failure copying icon: " + aResult);
}
});
return deferred.promise;
}
}
@ -577,15 +625,17 @@ MacNativeApp.prototype = {
},
install: function() {
try {
this._copyPrebuiltFiles();
this._createConfigFiles();
} catch (ex) {
this._removeInstallation(false);
throw(ex);
}
getIconForApp(this, this._moveToApplicationsFolder);
return Task.spawn(function() {
try {
this._copyPrebuiltFiles();
this._createConfigFiles();
yield this.getIcon();
this._moveToApplicationsFolder();
} catch (ex) {
this._removeInstallation(false);
throw(ex);
}
}.bind(this));
},
_removeInstallation: function(keepProfile) {
@ -686,19 +736,11 @@ MacNativeApp.prototype = {
this.appNameAsFilename,
".app");
if (!destinationName) {
return false;
throw("No available filename");
}
this.installDir.moveTo(appDir, destinationName);
},
/**
* This variable specifies if the icon retrieval process should
* use a temporary file in the system or a binary stream. This
* is accessed by a common function in WebappsIconHelpers.js and
* is different for each platform.
*/
useTmpForIcon: true,
/**
* Process the icon from the imageStream as retrieved from
* the URL by getIconForApp(). This will bundle the icon to the
@ -706,29 +748,33 @@ MacNativeApp.prototype = {
*
* @param aMimeType the icon mimetype
* @param aImageStream the stream for the image data
* @param aCallback a callback function to be called
* after the process finishes
*/
processIcon: function(aMimeType, aIcon, aCallback) {
try {
let process = Cc["@mozilla.org/process/util;1"]
.createInstance(Ci.nsIProcess);
let sipsFile = Cc["@mozilla.org/file/local;1"]
.createInstance(Ci.nsILocalFile);
sipsFile.initWithPath("/usr/bin/sips");
processIcon: function(aMimeType, aIcon) {
let deferred = Promise.defer();
process.init(sipsFile);
process.run(true, ["-s",
"format", "icns",
aIcon.path,
"--out", this.iconFile.path,
"-z", "128", "128"],
9);
} catch(e) {
throw(e);
} finally {
aCallback.call(this);
function conversionDone(aSubject, aTopic) {
if (aTopic == "process-finished") {
deferred.resolve();
} else {
deferred.reject("Failure converting icon.");
}
}
let process = Cc["@mozilla.org/process/util;1"].
createInstance(Ci.nsIProcess);
let sipsFile = Cc["@mozilla.org/file/local;1"].
createInstance(Ci.nsILocalFile);
sipsFile.initWithPath("/usr/bin/sips");
process.init(sipsFile);
process.runAsync(["-s",
"format", "icns",
aIcon.path,
"--out", this.iconFile.path,
"-z", "128", "128"],
9, conversionDone);
return deferred.promise;
}
}
@ -786,15 +832,16 @@ LinuxNativeApp.prototype = {
},
install: function() {
try {
this._copyPrebuiltFiles();
this._createConfigFiles();
} catch (ex) {
this._removeInstallation(false);
throw(ex);
}
getIconForApp(this, function() {});
return Task.spawn(function() {
try {
this._copyPrebuiltFiles();
this._createConfigFiles();
yield this.getIcon();
} catch (ex) {
this._removeInstallation(false);
throw(ex);
}
}.bind(this));
},
_removeInstallation: function(keepProfile) {
@ -913,38 +960,32 @@ LinuxNativeApp.prototype = {
writer.writeFile();
},
/**
* This variable specifies if the icon retrieval process should
* use a temporary file in the system or a binary stream. This
* is accessed by a common function in WebappsIconHelpers.js and
* is different for each platform.
*/
useTmpForIcon: false,
/**
* Process the icon from the imageStream as retrieved from
* the URL by getIconForApp().
*
* @param aMimeType ahe icon mimetype
* @param aImageStream the stream for the image data
* @param aCallback a callback function to be called
* after the process finishes
*/
processIcon: function(aMimeType, aImageStream, aCallback) {
let iconStream;
try {
let imgTools = Cc["@mozilla.org/image/tools;1"]
.createInstance(Ci.imgITools);
let imgContainer = { value: null };
processIcon: function(aMimeType, aImageStream) {
let deferred = Promise.defer();
imgTools.decodeImageData(aImageStream, aMimeType, imgContainer);
iconStream = imgTools.encodeImage(imgContainer.value, "image/png");
} catch (e) {
throw("processIcon - Failure converting icon (" + e + ")");
}
let imgTools = Cc["@mozilla.org/image/tools;1"]
.createInstance(Ci.imgITools);
let imgContainer = imgTools.decodeImage(aImageStream, aMimeType);
let iconStream = imgTools.encodeImage(imgContainer, "image/png");
let outputStream = FileUtils.openSafeFileOutputStream(this.iconFile);
NetUtil.asyncCopy(iconStream, outputStream);
NetUtil.asyncCopy(iconStream, outputStream, function(aResult) {
if (Components.isSuccessCode(aResult)) {
deferred.resolve();
} else {
deferred.reject("Failure copying icon: " + aResult);
}
});
return deferred.promise;
}
}

Просмотреть файл

@ -190,6 +190,7 @@ using mozilla::scache::StartupCache;
#include "nsICrashReporter.h"
#define NS_CRASHREPORTER_CONTRACTID "@mozilla.org/toolkit/crash-reporter;1"
#include "nsIPrefService.h"
#include "mozilla/Preferences.h"
#endif
#include "base/command_line.h"
@ -3756,6 +3757,13 @@ XREMain::XRE_mainRun()
mDirProvider.DoStartup();
#ifdef MOZ_CRASHREPORTER
nsCString userAgentLocale;
if (NS_SUCCEEDED(Preferences::GetCString("general.useragent.locale", &userAgentLocale))) {
CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("useragent_locale"), userAgentLocale);
}
#endif
appStartup->GetShuttingDown(&mShuttingDown);
nsCOMPtr<nsICommandLineRunner> cmdLine;

Просмотреть файл

@ -58,6 +58,10 @@ function onLoad() {
// This doesn't capture clicks so content can capture them itself and do
// something different if it doesn't want the default behavior.
gAppBrowser.addEventListener("click", onContentClick, false, true);
if (WebappRT.config.app.manifest.fullscreen) {
enterFullScreen();
}
}
window.addEventListener("load", onLoad, false);
@ -66,6 +70,33 @@ function onUnload() {
}
window.addEventListener("unload", onUnload, false);
// Fullscreen handling.
function enterFullScreen() {
// We call mozRequestFullScreen here so that the app window goes in
// fullscreen mode as soon as it's loaded and not after the <browser>
// content is loaded.
gAppBrowser.mozRequestFullScreen();
// We need to call mozRequestFullScreen on the document element too,
// otherwise the app isn't aware of the fullscreen status.
gAppBrowser.addEventListener("load", function onLoad() {
gAppBrowser.removeEventListener("load", onLoad, true);
gAppBrowser.contentDocument.
documentElement.wrappedJSObject.mozRequestFullScreen();
}, true);
}
#ifndef XP_MACOSX
document.addEventListener('mozfullscreenchange', function() {
if (document.mozFullScreenElement) {
document.getElementById("main-menubar").style.display = "none";
} else {
document.getElementById("main-menubar").style.display = "";
}
}, false);
#endif
/**
* Direct a click on <a target="_blank"> to the user's default browser.
*
@ -216,15 +247,5 @@ nsContextMenu.prototype = {
this.hasPageMenu = PageMenu.maybeBuildAndAttachMenu(document.popupNode,
aXULMenu);
this.shouldDisplay = this.hasPageMenu;
this.showItem("page-menu-separator", this.hasPageMenu);
},
showItem: function(aItemOrID, aShow) {
let item = aItemOrID.constructor == String ?
document.getElementById(aItemOrID) : aItemOrID;
if (item) {
item.hidden = !aShow;
}
}
};

Просмотреть файл

@ -17,6 +17,7 @@
id="default"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
width="1024" height="768"
fullscreenbutton="true"
persist="screenX screenY width height sizemode"
>
@ -157,7 +158,6 @@
<browser type="content-primary" id="content" flex="1" context="contentAreaContextMenu" />
<popupset>
<menuseparator id="page-menu-separator"/>
<menupopup id="contentAreaContextMenu" pagemenu="start"
onpopupshowing="return showContextMenu(event, this)"
onpopuphiding="hideContextMenu(event, this)">

Просмотреть файл

@ -2824,7 +2824,7 @@ AndroidBridge::RequestContentRepaint(const mozilla::layers::FrameMetrics& aFrame
return;
}
CSSToScreenScale resolution = aFrameMetrics.CalculateResolution();
CSSToScreenScale resolution = aFrameMetrics.mZoom;
ScreenRect dp = (aFrameMetrics.mDisplayPort + aFrameMetrics.mScrollOffset) * resolution;
AutoLocalJNIFrame jniFrame(env, 0);

Просмотреть файл

@ -1360,7 +1360,7 @@ public:
// This event shuts down the worker thread and so must be main thread.
MOZ_ASSERT(NS_IsMainThread());
CSSToScreenScale resolution = mFrameMetrics.CalculateResolution();
CSSToScreenScale resolution = mFrameMetrics.mZoom;
CSSRect compositedRect = mFrameMetrics.CalculateCompositedRectInCssPixels();
NS_ConvertASCIItoUTF16 data(nsPrintfCString("{ " \
@ -1490,4 +1490,4 @@ MetroWidget::Observe(nsISupports *subject, const char *topic, const PRUnichar *d
}
}
return NS_OK;
}
}