Merge m-c to b2ginbound, a=merge
@ -213,6 +213,8 @@ input[type=button] {
|
||||
.newtab-title {
|
||||
left: 0;
|
||||
padding: 0 4px;
|
||||
border: 1px solid #FFFFFF;
|
||||
border-radius: 0px 0px 8px 8px;
|
||||
}
|
||||
|
||||
.newtab-sponsored {
|
||||
@ -340,7 +342,7 @@ input[type=button] {
|
||||
display: -moz-box;
|
||||
position: relative;
|
||||
-moz-box-pack: center;
|
||||
margin: 15px 0px;
|
||||
margin: 40px 0 15px;
|
||||
}
|
||||
|
||||
#newtab-search-container[page-disabled] {
|
||||
|
@ -27,8 +27,6 @@ BROWSER_CHROME_MANIFESTS += [
|
||||
DEFINES['MOZ_APP_VERSION'] = CONFIG['MOZ_APP_VERSION']
|
||||
DEFINES['MOZ_APP_VERSION_DISPLAY'] = CONFIG['MOZ_APP_VERSION_DISPLAY']
|
||||
|
||||
DEFINES['NIGHTLY_BUILD'] = CONFIG['NIGHTLY_BUILD']
|
||||
|
||||
DEFINES['APP_LICENSE_BLOCK'] = '%s/content/overrides/app-license.html' % SRCDIR
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'gtk2', 'gtk3', 'cocoa'):
|
||||
|
@ -360,26 +360,20 @@ html[dir="rtl"] .contact > .dropdown-menu {
|
||||
}
|
||||
|
||||
.contacts-gravatar-promo {
|
||||
position: relative;
|
||||
border: 1px dashed #c1c1c1;
|
||||
border: 1px solid #5cccee;
|
||||
border-radius: 2px;
|
||||
background-color: #fbfbfb;
|
||||
padding: 10px;
|
||||
margin-top: 10px;
|
||||
background-color: #fff;
|
||||
font-size: 1.2rem;
|
||||
margin: 1.5rem;
|
||||
padding: 1.5rem 1rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.contacts-gravatar-promo > p {
|
||||
margin-top: 2px;
|
||||
margin-bottom: 8px;
|
||||
margin-right: 4px;
|
||||
margin-top: 0;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
html[dir="rtl"] .contacts-gravatar-promo > p {
|
||||
margin-right: 0;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.contacts-gravatar-promo > p > a {
|
||||
color: #0295df;
|
||||
text-decoration: none;
|
||||
@ -400,6 +394,44 @@ html[dir="rtl"] .contacts-gravatar-promo > .button-close {
|
||||
left: 8px;
|
||||
}
|
||||
|
||||
.contacts-gravatar-avatars {
|
||||
height: 50px;
|
||||
margin: 1.5rem auto;
|
||||
text-align: center;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.contacts-gravatar-avatars img {
|
||||
margin: 0 1.5rem;
|
||||
vertical-align: middle;
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
/* Adjust the Firefox avatar because it has pointy ears. */
|
||||
.contacts-gravatar-avatars img:last-child {
|
||||
transform: scale(1.08) translateY(-2px);
|
||||
}
|
||||
|
||||
.contacts-gravatar-arrow {
|
||||
border-color: #9b9b9b;
|
||||
border-style: solid solid none none;
|
||||
border-width: 2px;
|
||||
display: inline-block;
|
||||
height: 1.5rem;
|
||||
-moz-margin-start: -.75rem;
|
||||
transform: rotateZ(45deg);
|
||||
vertical-align: middle;
|
||||
width: 1.5rem;
|
||||
}
|
||||
|
||||
html[dir="rtl"] .contacts-gravatar-arrow {
|
||||
transform: rotateZ(225deg);
|
||||
}
|
||||
|
||||
.contacts-gravatar-buttons {
|
||||
padding: 0 .5rem;
|
||||
}
|
||||
|
||||
.contact-controls {
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
@ -265,6 +265,12 @@ html[dir="rtl"] .tab-view li:nth-child(2).selected ~ .slide-bar {
|
||||
font-weight: lighter;
|
||||
}
|
||||
|
||||
/* Don't show the empty contacts image if we're showing gravatar promo. */
|
||||
.contacts-gravatar-promo ~ .contact-list-empty {
|
||||
background-image: none;
|
||||
padding-top: 3%;
|
||||
}
|
||||
|
||||
.contact-list-empty {
|
||||
padding-top: 27%;
|
||||
}
|
||||
|
@ -134,11 +134,17 @@ loop.contacts = (function(_, mozL10n) {
|
||||
onClick: this.handleCloseButtonClick}),
|
||||
React.createElement("p", {dangerouslySetInnerHTML: {__html: message},
|
||||
onClick: this.handleLinkClick}),
|
||||
React.createElement(ButtonGroup, null,
|
||||
React.createElement(Button, {caption: mozL10n.get("gravatars_promo_button_nothanks"),
|
||||
React.createElement("div", {className: "contacts-gravatar-avatars"},
|
||||
React.createElement("img", {src: "loop/shared/img/avatars.svg#orange-avatar"}),
|
||||
React.createElement("span", {className: "contacts-gravatar-arrow"}),
|
||||
React.createElement("img", {src: "loop/shared/img/firefox-avatar.svg"})
|
||||
),
|
||||
React.createElement(ButtonGroup, {additionalClass: "contacts-gravatar-buttons"},
|
||||
React.createElement(Button, {additionalClass: "secondary",
|
||||
caption: mozL10n.get("gravatars_promo_button_nothanks2"),
|
||||
onClick: this.handleCloseButtonClick}),
|
||||
React.createElement(Button, {additionalClass: "button-accept",
|
||||
caption: mozL10n.get("gravatars_promo_button_use"),
|
||||
React.createElement(Button, {additionalClass: "secondary",
|
||||
caption: mozL10n.get("gravatars_promo_button_use2"),
|
||||
onClick: this.handleUseButtonClick})
|
||||
)
|
||||
)
|
||||
@ -666,10 +672,10 @@ loop.contacts = (function(_, mozL10n) {
|
||||
return (
|
||||
React.createElement("div", {className: "contact-list-empty"},
|
||||
React.createElement("p", {className: "panel-text-large"},
|
||||
mozL10n.get("no_contacts_message_heading")
|
||||
mozL10n.get("no_contacts_message_heading2")
|
||||
),
|
||||
React.createElement("p", {className: "panel-text-medium"},
|
||||
mozL10n.get("no_contacts_import_or_add")
|
||||
mozL10n.get("no_contacts_import_or_add2")
|
||||
)
|
||||
)
|
||||
);
|
||||
@ -714,7 +720,7 @@ loop.contacts = (function(_, mozL10n) {
|
||||
busy: this.state.importBusy})})
|
||||
),
|
||||
React.createElement(Button, {additionalClass: "primary",
|
||||
caption: mozL10n.get("new_contact_button"),
|
||||
caption: mozL10n.get("new_contact_button2"),
|
||||
onClick: this.handleAddContactButtonClick})
|
||||
)
|
||||
);
|
||||
@ -726,9 +732,7 @@ loop.contacts = (function(_, mozL10n) {
|
||||
}
|
||||
|
||||
return (
|
||||
React.createElement("div", {className: "content-area"},
|
||||
React.createElement(GravatarPromo, {handleUse: this.handleUseGravatar})
|
||||
)
|
||||
React.createElement(GravatarPromo, {handleUse: this.handleUseGravatar})
|
||||
);
|
||||
},
|
||||
|
||||
|
@ -134,11 +134,17 @@ loop.contacts = (function(_, mozL10n) {
|
||||
onClick={this.handleCloseButtonClick} />
|
||||
<p dangerouslySetInnerHTML={{__html: message}}
|
||||
onClick={this.handleLinkClick}></p>
|
||||
<ButtonGroup>
|
||||
<Button caption={mozL10n.get("gravatars_promo_button_nothanks")}
|
||||
<div className="contacts-gravatar-avatars">
|
||||
<img src="loop/shared/img/avatars.svg#orange-avatar" />
|
||||
<span className="contacts-gravatar-arrow" />
|
||||
<img src="loop/shared/img/firefox-avatar.svg" />
|
||||
</div>
|
||||
<ButtonGroup additionalClass="contacts-gravatar-buttons">
|
||||
<Button additionalClass="secondary"
|
||||
caption={mozL10n.get("gravatars_promo_button_nothanks2")}
|
||||
onClick={this.handleCloseButtonClick}/>
|
||||
<Button additionalClass="button-accept"
|
||||
caption={mozL10n.get("gravatars_promo_button_use")}
|
||||
<Button additionalClass="secondary"
|
||||
caption={mozL10n.get("gravatars_promo_button_use2")}
|
||||
onClick={this.handleUseButtonClick}/>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
@ -666,10 +672,10 @@ loop.contacts = (function(_, mozL10n) {
|
||||
return (
|
||||
<div className="contact-list-empty">
|
||||
<p className="panel-text-large">
|
||||
{mozL10n.get("no_contacts_message_heading")}
|
||||
{mozL10n.get("no_contacts_message_heading2")}
|
||||
</p>
|
||||
<p className="panel-text-medium">
|
||||
{mozL10n.get("no_contacts_import_or_add")}
|
||||
{mozL10n.get("no_contacts_import_or_add2")}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
@ -714,7 +720,7 @@ loop.contacts = (function(_, mozL10n) {
|
||||
busy: this.state.importBusy})} />
|
||||
</Button>
|
||||
<Button additionalClass="primary"
|
||||
caption={mozL10n.get("new_contact_button")}
|
||||
caption={mozL10n.get("new_contact_button2")}
|
||||
onClick={this.handleAddContactButtonClick} />
|
||||
</ButtonGroup>
|
||||
);
|
||||
@ -726,9 +732,7 @@ loop.contacts = (function(_, mozL10n) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="content-area">
|
||||
<GravatarPromo handleUse={this.handleUseGravatar}/>
|
||||
</div>
|
||||
<GravatarPromo handleUse={this.handleUseGravatar} />
|
||||
);
|
||||
},
|
||||
|
||||
|
After Width: | Height: | Size: 11 KiB |
@ -95,6 +95,7 @@ browser.jar:
|
||||
content/browser/loop/shared/img/empty_conversations.svg (content/shared/img/empty_conversations.svg)
|
||||
content/browser/loop/shared/img/empty_search.svg (content/shared/img/empty_search.svg)
|
||||
content/browser/loop/shared/img/avatars.svg (content/shared/img/avatars.svg)
|
||||
content/browser/loop/shared/img/firefox-avatar.svg (content/shared/img/firefox-avatar.svg)
|
||||
|
||||
# Shared scripts
|
||||
content/browser/loop/shared/js/actions.js (content/shared/js/actions.js)
|
||||
|
@ -203,6 +203,9 @@ describe("loop.contacts", function() {
|
||||
var promo = listView.getDOMNode().querySelector(".contacts-gravatar-promo");
|
||||
expect(promo).to.not.equal(null);
|
||||
|
||||
var avatars = listView.getDOMNode().querySelectorAll(".contacts-gravatar-avatars img");
|
||||
expect(avatars).to.have.length(2, "two example avatars are shown");
|
||||
|
||||
checkGravatarContacts(false);
|
||||
});
|
||||
|
||||
@ -238,7 +241,7 @@ describe("loop.contacts", function() {
|
||||
}));
|
||||
|
||||
React.addons.TestUtils.Simulate.click(listView.getDOMNode().querySelector(
|
||||
".contacts-gravatar-promo .button-accept"));
|
||||
".contacts-gravatar-promo .secondary:last-child"));
|
||||
|
||||
sinon.assert.calledTwice(navigator.mozLoop.setLoopPref);
|
||||
|
||||
@ -255,7 +258,7 @@ describe("loop.contacts", function() {
|
||||
}));
|
||||
|
||||
React.addons.TestUtils.Simulate.click(listView.getDOMNode().querySelector(
|
||||
".contacts-gravatar-promo .button-accept"));
|
||||
".contacts-gravatar-promo .secondary:last-child"));
|
||||
|
||||
sinon.assert.calledTwice(navigator.mozLoop.setLoopPref);
|
||||
sinon.assert.calledWithExactly(navigator.mozLoop.setLoopPref, "contacts.gravatars.promo", false);
|
||||
@ -271,7 +274,7 @@ describe("loop.contacts", function() {
|
||||
}));
|
||||
|
||||
React.addons.TestUtils.Simulate.click(listView.getDOMNode().querySelector(
|
||||
".contacts-gravatar-promo .button-close"));
|
||||
".contacts-gravatar-promo .secondary:first-child"));
|
||||
|
||||
var promo = listView.getDOMNode().querySelector(".contacts-gravatar-promo");
|
||||
expect(promo).to.equal(null);
|
||||
@ -285,6 +288,37 @@ describe("loop.contacts", function() {
|
||||
startForm: function() {}
|
||||
}));
|
||||
|
||||
React.addons.TestUtils.Simulate.click(listView.getDOMNode().querySelector(
|
||||
".contacts-gravatar-promo .secondary:first-child"));
|
||||
|
||||
sinon.assert.calledOnce(navigator.mozLoop.setLoopPref);
|
||||
sinon.assert.calledWithExactly(navigator.mozLoop.setLoopPref,
|
||||
"contacts.gravatars.promo", false);
|
||||
});
|
||||
|
||||
it("should hide the gravatars promo box when the 'close' X button is clicked", function() {
|
||||
listView = TestUtils.renderIntoDocument(
|
||||
React.createElement(loop.contacts.ContactsList, {
|
||||
mozLoop: navigator.mozLoop,
|
||||
notifications: notifications,
|
||||
startForm: function() {}
|
||||
}));
|
||||
|
||||
React.addons.TestUtils.Simulate.click(listView.getDOMNode().querySelector(
|
||||
".contacts-gravatar-promo .button-close"));
|
||||
|
||||
var promo = listView.getDOMNode().querySelector(".contacts-gravatar-promo");
|
||||
expect(promo).to.equal(null);
|
||||
});
|
||||
|
||||
it("should set prefs correctly when the 'close' X button is clicked", function() {
|
||||
listView = TestUtils.renderIntoDocument(
|
||||
React.createElement(loop.contacts.ContactsList, {
|
||||
mozLoop: navigator.mozLoop,
|
||||
notifications: notifications,
|
||||
startForm: function() {}
|
||||
}));
|
||||
|
||||
React.addons.TestUtils.Simulate.click(listView.getDOMNode().querySelector(
|
||||
".contacts-gravatar-promo .button-close"));
|
||||
|
||||
@ -326,9 +360,9 @@ describe("loop.contacts", function() {
|
||||
|
||||
it("should display the no contacts strings", function() {
|
||||
sinon.assert.calledWithExactly(mozL10nGetSpy,
|
||||
"no_contacts_message_heading");
|
||||
"no_contacts_message_heading2");
|
||||
sinon.assert.calledWithExactly(mozL10nGetSpy,
|
||||
"no_contacts_import_or_add");
|
||||
"no_contacts_import_or_add2");
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -87,6 +87,8 @@ support-files =
|
||||
doc_pretty-print-3.html
|
||||
doc_pretty-print-on-paused.html
|
||||
doc_promise-get-allocation-stack.html
|
||||
doc_promise-get-fulfillment-stack.html
|
||||
doc_promise-get-rejection-stack.html
|
||||
doc_promise.html
|
||||
doc_random-javascript.html
|
||||
doc_recursion-stack.html
|
||||
@ -349,6 +351,10 @@ skip-if = e10s && debug
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_promises-chrome-allocation-stack.js]
|
||||
skip-if = true # Bug 1177730
|
||||
[browser_dbg_promises-fulfillment-stack.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_promises-rejection-stack.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_reload-preferred-script-01.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_reload-preferred-script-02.js]
|
||||
|
@ -0,0 +1,100 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Test that we can get a stack to a promise's fulfillment point.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "doc_promise-get-fulfillment-stack.html";
|
||||
const { PromisesFront } = require("devtools/server/actors/promises");
|
||||
let events = require("sdk/event/core");
|
||||
|
||||
const TEST_DATA = [
|
||||
{
|
||||
functionDisplayName: "returnPromise/<",
|
||||
line: 19,
|
||||
column: 37
|
||||
},
|
||||
{
|
||||
functionDisplayName: "returnPromise",
|
||||
line: 19,
|
||||
column: 14
|
||||
},
|
||||
{
|
||||
functionDisplayName: "makePromise",
|
||||
line: 14,
|
||||
column: 15
|
||||
},
|
||||
];
|
||||
|
||||
function test() {
|
||||
Task.spawn(function* () {
|
||||
DebuggerServer.init();
|
||||
DebuggerServer.addBrowserActors();
|
||||
|
||||
const [ tab,, panel ] = yield initDebugger(TAB_URL);
|
||||
|
||||
let client = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
yield connect(client);
|
||||
|
||||
let { tabs } = yield listTabs(client);
|
||||
let targetTab = findTab(tabs, TAB_URL);
|
||||
yield attachTab(client, targetTab);
|
||||
|
||||
yield testGetFulfillmentStack(client, targetTab, tab);
|
||||
|
||||
yield close(client);
|
||||
yield closeDebuggerAndFinish(panel);
|
||||
}).then(null, error => {
|
||||
ok(false, "Got an error: " + error.message + "\n" + error.stack);
|
||||
});
|
||||
}
|
||||
|
||||
function* testGetFulfillmentStack(client, form, tab) {
|
||||
let front = PromisesFront(client, form);
|
||||
|
||||
yield front.attach();
|
||||
yield front.listPromises();
|
||||
|
||||
// Get the grip for promise p
|
||||
let onNewPromise = new Promise(resolve => {
|
||||
events.on(front, "new-promises", promises => {
|
||||
for (let p of promises) {
|
||||
if (p.preview.ownProperties.name &&
|
||||
p.preview.ownProperties.name.value === "p") {
|
||||
resolve(p);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
callInTab(tab, "makePromise");
|
||||
|
||||
let grip = yield onNewPromise;
|
||||
ok(grip, "Found our promise p");
|
||||
|
||||
let objectClient = new ObjectClient(client, grip);
|
||||
ok(objectClient, "Got Object Client");
|
||||
|
||||
yield new Promise(resolve => {
|
||||
objectClient.getPromiseFulfillmentStack(response => {
|
||||
ok(response.fulfillmentStack.length, "Got promise allocation stack.");
|
||||
|
||||
for (let i = 0; i < TEST_DATA.length; i++) {
|
||||
let stack = response.fulfillmentStack[i];
|
||||
let data = TEST_DATA[i];
|
||||
is(stack.source.url, TAB_URL, "Got correct source URL.");
|
||||
is(stack.functionDisplayName, data.functionDisplayName,
|
||||
"Got correct function display name.");
|
||||
is(stack.line, data.line, "Got correct stack line number.");
|
||||
is(stack.column, data.column, "Got correct stack column number.");
|
||||
}
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
yield front.detach();
|
||||
}
|
@ -0,0 +1,100 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Test that we can get a stack to a promise's rejection point.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "doc_promise-get-rejection-stack.html";
|
||||
const { PromisesFront } = require("devtools/server/actors/promises");
|
||||
let events = require("sdk/event/core");
|
||||
|
||||
const TEST_DATA = [
|
||||
{
|
||||
functionDisplayName: "returnPromise/<",
|
||||
line: 19,
|
||||
column: 47
|
||||
},
|
||||
{
|
||||
functionDisplayName: "returnPromise",
|
||||
line: 19,
|
||||
column: 14
|
||||
},
|
||||
{
|
||||
functionDisplayName: "makePromise",
|
||||
line: 14,
|
||||
column: 15
|
||||
},
|
||||
];
|
||||
|
||||
function test() {
|
||||
Task.spawn(function* () {
|
||||
DebuggerServer.init();
|
||||
DebuggerServer.addBrowserActors();
|
||||
|
||||
const [ tab,, panel ] = yield initDebugger(TAB_URL);
|
||||
|
||||
let client = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
yield connect(client);
|
||||
|
||||
let { tabs } = yield listTabs(client);
|
||||
let targetTab = findTab(tabs, TAB_URL);
|
||||
yield attachTab(client, targetTab);
|
||||
|
||||
yield testGetRejectionStack(client, targetTab, tab);
|
||||
|
||||
yield close(client);
|
||||
yield closeDebuggerAndFinish(panel);
|
||||
}).then(null, error => {
|
||||
ok(false, "Got an error: " + error.message + "\n" + error.stack);
|
||||
});
|
||||
}
|
||||
|
||||
function* testGetRejectionStack(client, form, tab) {
|
||||
let front = PromisesFront(client, form);
|
||||
|
||||
yield front.attach();
|
||||
yield front.listPromises();
|
||||
|
||||
// Get the grip for promise p
|
||||
let onNewPromise = new Promise(resolve => {
|
||||
events.on(front, "new-promises", promises => {
|
||||
for (let p of promises) {
|
||||
if (p.preview.ownProperties.name &&
|
||||
p.preview.ownProperties.name.value === "p") {
|
||||
resolve(p);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
callInTab(tab, "makePromise");
|
||||
|
||||
let grip = yield onNewPromise;
|
||||
ok(grip, "Found our promise p");
|
||||
|
||||
let objectClient = new ObjectClient(client, grip);
|
||||
ok(objectClient, "Got Object Client");
|
||||
|
||||
yield new Promise(resolve => {
|
||||
objectClient.getPromiseRejectionStack(response => {
|
||||
ok(response.rejectionStack.length, "Got promise allocation stack.");
|
||||
|
||||
for (let i = 0; i < TEST_DATA.length; i++) {
|
||||
let stack = response.rejectionStack[i];
|
||||
let data = TEST_DATA[i];
|
||||
is(stack.source.url, TAB_URL, "Got correct source URL.");
|
||||
is(stack.functionDisplayName, data.functionDisplayName,
|
||||
"Got correct function display name.");
|
||||
is(stack.line, data.line, "Got correct stack line number.");
|
||||
is(stack.column, data.column, "Got correct stack column number.");
|
||||
}
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
yield front.detach();
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Promise test page</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
function makePromise() {
|
||||
var p = returnPromise();
|
||||
p.name = "p";
|
||||
}
|
||||
|
||||
function returnPromise() {
|
||||
return new Promise(resolve => resolve("hello"));
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -0,0 +1,24 @@
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Promise test page</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
function makePromise() {
|
||||
var p = returnPromise();
|
||||
p.name = "p";
|
||||
}
|
||||
|
||||
function returnPromise() {
|
||||
return new Promise((resolve, reject) => reject("hello"));
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -2,18 +2,22 @@
|
||||
* 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/. */
|
||||
|
||||
let { interfaces: Ci } = Components;
|
||||
|
||||
addMessageListener("Eyedropper:RequestContentScreenshot", sendContentScreenshot);
|
||||
|
||||
function sendContentScreenshot() {
|
||||
let canvas = content.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
|
||||
let scale = content.getInterface(Ci.nsIDOMWindowUtils).fullZoom;
|
||||
let width = content.innerWidth;
|
||||
let height = content.innerHeight;
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
canvas.width = width * scale;
|
||||
canvas.height = height * scale;
|
||||
canvas.mozOpaque = true;
|
||||
|
||||
let ctx = canvas.getContext("2d");
|
||||
|
||||
ctx.scale(scale, scale);
|
||||
ctx.drawWindow(content, content.scrollX, content.scrollY, width, height, "#fff");
|
||||
|
||||
sendAsyncMessage("Eyedropper:Screenshot", canvas.toDataURL());
|
||||
|
@ -362,7 +362,7 @@ const Formatters = {
|
||||
|
||||
GCFields: function (marker) {
|
||||
let fields = Object.create(null);
|
||||
let cause = marker.cause;
|
||||
let cause = marker.causeName;
|
||||
let label = L10N.getStr(`marker.gcreason.label.${cause}`) || cause;
|
||||
|
||||
fields[L10N.getStr("marker.field.causeName")] = label;
|
||||
|
@ -90,7 +90,7 @@ const EVENTS = {
|
||||
UI_STOP_RECORDING: "Performance:UI:StopRecording",
|
||||
|
||||
// Emitted by the PerformanceView on import button click
|
||||
UI_RECORDING_IMPORTED: "Performance:UI:ImportRecording",
|
||||
UI_IMPORT_RECORDING: "Performance:UI:ImportRecording",
|
||||
// Emitted by the RecordingsView on export button click
|
||||
UI_EXPORT_RECORDING: "Performance:UI:ExportRecording",
|
||||
|
||||
@ -226,7 +226,7 @@ let PerformanceController = {
|
||||
ToolbarView.on(EVENTS.PREF_CHANGED, this._onPrefChanged);
|
||||
PerformanceView.on(EVENTS.UI_START_RECORDING, this.startRecording);
|
||||
PerformanceView.on(EVENTS.UI_STOP_RECORDING, this.stopRecording);
|
||||
PerformanceView.on(EVENTS.UI_RECORDING_IMPORTED, this.importRecording);
|
||||
PerformanceView.on(EVENTS.UI_IMPORT_RECORDING, this.importRecording);
|
||||
PerformanceView.on(EVENTS.UI_CLEAR_RECORDINGS, this.clearRecordings);
|
||||
RecordingsView.on(EVENTS.UI_EXPORT_RECORDING, this.exportRecording);
|
||||
RecordingsView.on(EVENTS.RECORDING_SELECTED, this._onRecordingSelectFromView);
|
||||
@ -244,7 +244,7 @@ let PerformanceController = {
|
||||
ToolbarView.off(EVENTS.PREF_CHANGED, this._onPrefChanged);
|
||||
PerformanceView.off(EVENTS.UI_START_RECORDING, this.startRecording);
|
||||
PerformanceView.off(EVENTS.UI_STOP_RECORDING, this.stopRecording);
|
||||
PerformanceView.off(EVENTS.UI_RECORDING_IMPORTED, this.importRecording);
|
||||
PerformanceView.off(EVENTS.UI_IMPORT_RECORDING, this.importRecording);
|
||||
PerformanceView.off(EVENTS.UI_CLEAR_RECORDINGS, this.clearRecordings);
|
||||
RecordingsView.off(EVENTS.UI_EXPORT_RECORDING, this.exportRecording);
|
||||
RecordingsView.off(EVENTS.RECORDING_SELECTED, this._onRecordingSelectFromView);
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
let test = Task.async(function*() {
|
||||
var { target, panel, toolbox } = yield initPerformance(SIMPLE_URL);
|
||||
var { EVENTS, PerformanceController, DetailsView, DetailsSubview } = panel.panelWin;
|
||||
var { EVENTS, PerformanceController, PerformanceView, DetailsView, DetailsSubview } = panel.panelWin;
|
||||
|
||||
// Enable allocations to test the memory-calltree and memory-flamegraph.
|
||||
Services.prefs.setBoolPref(ALLOCATIONS_PREF, true);
|
||||
@ -49,7 +49,7 @@ let test = Task.async(function*() {
|
||||
|
||||
let rerendered = waitForWidgetsRendered(panel);
|
||||
let imported = once(PerformanceController, EVENTS.RECORDING_IMPORTED);
|
||||
yield PerformanceController.importRecording("", file);
|
||||
PerformanceView.emit(EVENTS.UI_IMPORT_RECORDING, file);
|
||||
|
||||
yield imported;
|
||||
ok(true, "The recording data appears to have been successfully imported.");
|
||||
|
@ -39,10 +39,10 @@ add_task(function () {
|
||||
equal(fields[1].label, "Phase:", "getMarkerFields() correctly returns fields via function (3)");
|
||||
equal(fields[1].value, "Target", "getMarkerFields() correctly returns fields via function (4)");
|
||||
|
||||
fields = Utils.getMarkerFields({ name: "GarbageCollection", cause: "ALLOC_TRIGGER" });
|
||||
fields = Utils.getMarkerFields({ name: "GarbageCollection", causeName: "ALLOC_TRIGGER" });
|
||||
equal(fields[0].value, "Too Many Allocations", "Uses L10N for GC reasons");
|
||||
|
||||
fields = Utils.getMarkerFields({ name: "GarbageCollection", cause: "NOT_A_GC_REASON" });
|
||||
fields = Utils.getMarkerFields({ name: "GarbageCollection", causeName: "NOT_A_GC_REASON" });
|
||||
equal(fields[0].value, "NOT_A_GC_REASON", "Defaults to enum for GC reasons when not L10N'd");
|
||||
|
||||
equal(Utils.getMarkerFields({ name: "Javascript", causeName: "Some Platform Field" })[0].value, "(Gecko)",
|
||||
|
@ -88,9 +88,9 @@ settings_menu_button_tooltip=Settings
|
||||
## the search field.
|
||||
contacts_search_placesholder2=Search…
|
||||
|
||||
## LOCALIZATION NOTE (new_contact_button): This is the button to open the
|
||||
## LOCALIZATION NOTE (new_contact_button2): This is the button to open the
|
||||
## new contact sub-panel.
|
||||
new_contact_button=New Contact
|
||||
new_contact_button2=Add new contact
|
||||
## LOCALIZATION NOTE (contact_form_*_placeholder):
|
||||
## These are the placeholders for the inputs for entering or editing a contact
|
||||
## Click the 'New Contact' button to see the fields.
|
||||
@ -131,12 +131,12 @@ import_contacts_success_message={{total}} contact was successfully imported.;{{t
|
||||
## LOCALIZATION NOTE(sync_contacts_button): This button is displayed in place of
|
||||
## importing_contacts_button once contacts have been imported once.
|
||||
sync_contacts_button=Sync Contacts
|
||||
## LOCALIZATION NOTE(no_contacts_message_heading): Title shown when user has no
|
||||
## LOCALIZATION NOTE(no_contacts_message_heading2): Title shown when user has no
|
||||
## contacts in his address book
|
||||
no_contacts_message_heading=No contacts yet
|
||||
## LOCALIZATION NOTE(no_contacts_import_or_add): Subheading inviting the user
|
||||
no_contacts_message_heading2=No contacts yet.
|
||||
## LOCALIZATION NOTE(no_contacts_import_or_add2): Subheading inviting the user
|
||||
## to add people to his contact list
|
||||
no_contacts_import_or_add=Import or add someone
|
||||
no_contacts_import_or_add2=Add someone!
|
||||
## LOCALIZATION NOTE(no_conversations_message_heading): Title shown when user
|
||||
## has no conversations available.
|
||||
no_conversations_message_heading=There are no conversations yet
|
||||
@ -208,8 +208,8 @@ video_call_menu_button=Video Conversation
|
||||
gravatars_promo_message=You can automatically add profile icons to your contacts \
|
||||
by sharing their email addresses with Gravatar. {{learn_more}}.
|
||||
gravatars_promo_message_learnmore=Learn more
|
||||
gravatars_promo_button_nothanks=No Thanks
|
||||
gravatars_promo_button_use=Use Profile Icons
|
||||
gravatars_promo_button_nothanks2=No, thanks
|
||||
gravatars_promo_button_use2=Use profile icons
|
||||
|
||||
# Conversation Window Strings
|
||||
|
||||
|
@ -167,7 +167,10 @@
|
||||
}
|
||||
|
||||
.newtab-site:hover .newtab-title {
|
||||
color: #222;
|
||||
color: white;
|
||||
background-color: black;
|
||||
border: 1px solid black;
|
||||
border-top: 1px solid white;
|
||||
}
|
||||
|
||||
.newtab-site[pinned] .newtab-title {
|
||||
|
@ -76,7 +76,7 @@ struct EventRadiusPrefs
|
||||
bool mRegistered;
|
||||
bool mTouchOnly;
|
||||
bool mRepositionEventCoords;
|
||||
bool mTouchClusterDetectionDisabled;
|
||||
bool mTouchClusterDetectionEnabled;
|
||||
uint32_t mLimitReadableSize;
|
||||
};
|
||||
|
||||
@ -125,8 +125,8 @@ GetPrefsFor(EventClassID aEventClassID)
|
||||
nsPrintfCString repositionPref("ui.%s.radius.reposition", prefBranch);
|
||||
Preferences::AddBoolVarCache(&prefs->mRepositionEventCoords, repositionPref.get(), false);
|
||||
|
||||
nsPrintfCString touchClusterPref("ui.zoomedview.disabled", prefBranch);
|
||||
Preferences::AddBoolVarCache(&prefs->mTouchClusterDetectionDisabled, touchClusterPref.get(), true);
|
||||
nsPrintfCString touchClusterPref("ui.zoomedview.enabled", prefBranch);
|
||||
Preferences::AddBoolVarCache(&prefs->mTouchClusterDetectionEnabled, touchClusterPref.get(), false);
|
||||
|
||||
nsPrintfCString limitReadableSizePref("ui.zoomedview.limitReadableSize", prefBranch);
|
||||
Preferences::AddUintVarCache(&prefs->mLimitReadableSize, limitReadableSizePref.get(), 8);
|
||||
@ -451,7 +451,7 @@ GetClosest(nsIFrame* aRoot, const nsPoint& aPointRelativeToRootFrame,
|
||||
static bool
|
||||
IsElementClickableAndReadable(nsIFrame* aFrame, WidgetGUIEvent* aEvent, const EventRadiusPrefs* aPrefs)
|
||||
{
|
||||
if (aPrefs->mTouchClusterDetectionDisabled) {
|
||||
if (!aPrefs->mTouchClusterDetectionEnabled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -578,7 +578,7 @@ FindFrameTargetedByInputEvent(WidgetGUIEvent* aEvent,
|
||||
restrictToDescendants, clickableAncestor, candidates,
|
||||
&elementsInCluster);
|
||||
if (closestClickable) {
|
||||
if ((!prefs->mTouchClusterDetectionDisabled && elementsInCluster > 1) ||
|
||||
if ((prefs->mTouchClusterDetectionEnabled && elementsInCluster > 1) ||
|
||||
(!IsElementClickableAndReadable(closestClickable, aEvent, prefs))) {
|
||||
if (aEvent->mClass == eMouseEventClass) {
|
||||
WidgetMouseEventBase* mouseEventBase = aEvent->AsMouseEventBase();
|
||||
|
@ -414,7 +414,7 @@ pref("font.size.inflation.minTwips", 0);
|
||||
// When true, zooming will be enabled on all sites, even ones that declare user-scalable=no.
|
||||
pref("browser.ui.zoom.force-user-scalable", false);
|
||||
|
||||
pref("ui.zoomedview.disabled", false);
|
||||
pref("ui.zoomedview.enabled", true);
|
||||
pref("ui.zoomedview.limitReadableSize", 8); // value in layer pixels
|
||||
pref("ui.zoomedview.defaultZoomFactor", 2);
|
||||
pref("ui.zoomedview.simplified", true); // Do not display all the zoomed view controls
|
||||
|
@ -777,7 +777,7 @@ public class BrowserApp extends GeckoApp
|
||||
}
|
||||
|
||||
if (HardwareUtils.isTablet()) {
|
||||
mTabStrip = (Refreshable) (((ViewStub) findViewById(R.id.new_tablet_tab_strip)).inflate());
|
||||
mTabStrip = (Refreshable) (((ViewStub) findViewById(R.id.tablet_tab_strip)).inflate());
|
||||
}
|
||||
|
||||
((GeckoApp.MainLayout) mMainLayout).setTouchEventInterceptor(new HideOnTouchListener());
|
||||
|
@ -1962,6 +1962,8 @@ public abstract class GeckoApp
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
RestrictedProfiles.update(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -311,7 +311,7 @@ res/raw/browsersearch.json: .locales.deps ;
|
||||
res/raw/suggestedsites.json: .locales.deps ;
|
||||
|
||||
all_resources = \
|
||||
$(CURDIR)/AndroidManifest.xml \
|
||||
$(abspath $(CURDIR)/AndroidManifest.xml) \
|
||||
$(android_res_files) \
|
||||
$(ANDROID_GENERATED_RESFILES) \
|
||||
$(NULL)
|
||||
@ -420,8 +420,8 @@ endef
|
||||
# .aapt.deps: $(all_resources)
|
||||
$(eval $(call aapt_command,.aapt.deps,$(all_resources),gecko.ap_,generated/,./))
|
||||
|
||||
# .aapt.nodeps: $(CURDIR)/AndroidManifest.xml FORCE
|
||||
$(eval $(call aapt_command,.aapt.nodeps,$(CURDIR)/AndroidManifest.xml FORCE,gecko-nodeps.ap_,gecko-nodeps/,gecko-nodeps/))
|
||||
# .aapt.nodeps: $(abspath $(CURDIR)/AndroidManifest.xml) FORCE
|
||||
$(eval $(call aapt_command,.aapt.nodeps,$(abspath $(CURDIR)/AndroidManifest.xml) FORCE,gecko-nodeps.ap_,gecko-nodeps/,gecko-nodeps/))
|
||||
|
||||
# Override the Java settings with some specific android settings
|
||||
include $(topsrcdir)/config/android-common.mk
|
||||
@ -446,6 +446,7 @@ $(abspath $(DIST)/fennec/$(OMNIJAR_NAME)): FORCE
|
||||
$(MAKE) -C ../app
|
||||
$(MAKE) -C ../themes/core
|
||||
$(MAKE) -C ../installer stage-package
|
||||
$(MKDIR) -p $(@D)
|
||||
rsync --update $(DIST)/fennec/$(notdir $(OMNIJAR_NAME)) $@
|
||||
$(RM) $(DIST)/fennec/$(notdir $(OMNIJAR_NAME))
|
||||
|
||||
|
@ -43,7 +43,7 @@ public class RestrictedProfiles {
|
||||
|
||||
if (isGuestProfile(context)) {
|
||||
return new GuestProfileConfiguration();
|
||||
} else if(isRestrictedProfile(context)) {
|
||||
} else if (isRestrictedProfile(context)) {
|
||||
return new RestrictedProfileConfiguration(context);
|
||||
} else {
|
||||
return new DefaultConfiguration();
|
||||
@ -51,6 +51,10 @@ public class RestrictedProfiles {
|
||||
}
|
||||
|
||||
private static boolean isGuestProfile(Context context) {
|
||||
if (configuration != null) {
|
||||
return configuration instanceof GuestProfileConfiguration;
|
||||
}
|
||||
|
||||
GeckoAppShell.GeckoInterface geckoInterface = GeckoAppShell.getGeckoInterface();
|
||||
if (geckoInterface != null) {
|
||||
return geckoInterface.getProfile().inGuestMode();
|
||||
@ -61,6 +65,10 @@ public class RestrictedProfiles {
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
|
||||
public static boolean isRestrictedProfile(Context context) {
|
||||
if (configuration != null) {
|
||||
return configuration instanceof RestrictedProfileConfiguration;
|
||||
}
|
||||
|
||||
if (Versions.preJBMR2) {
|
||||
// Early versions don't support restrictions at all
|
||||
return false;
|
||||
@ -81,6 +89,10 @@ public class RestrictedProfiles {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void update(Context context) {
|
||||
getConfiguration(context).update();
|
||||
}
|
||||
|
||||
private static Restriction geckoActionToRestriction(int action) {
|
||||
for (Restriction rest : Restriction.values()) {
|
||||
if (rest.id == action) {
|
||||
|
@ -415,8 +415,8 @@ size. -->
|
||||
|
||||
<!-- Localization note (pref_prevent_magnifying_glass): Label for setting that controls
|
||||
whether or not the magnifying glass is disabled. -->
|
||||
<!ENTITY pref_prevent_magnifying_glass "Disable magnifying glass ">
|
||||
<!ENTITY pref_prevent_magnifying_glass_summary "Prevent magnifying glass for use in resolving ambiguous screen touches">
|
||||
<!ENTITY pref_magnifying_glass_enabled "Magnify small areas">
|
||||
<!ENTITY pref_magnifying_glass_enabled_summary "Enlarge links and form fields when tapping near them">
|
||||
|
||||
<!-- Localization note (pref_scroll_title_bar2): Label for setting that controls
|
||||
whether or not the dynamic toolbar is enabled. -->
|
||||
|
@ -117,7 +117,7 @@ OnSharedPreferenceChangeListener
|
||||
private static final String PREFS_CRASHREPORTER_ENABLED = "datareporting.crashreporter.submitEnabled";
|
||||
private static final String PREFS_MENU_CHAR_ENCODING = "browser.menu.showCharacterEncoding";
|
||||
private static final String PREFS_MP_ENABLED = "privacy.masterpassword.enabled";
|
||||
private static final String PREFS_DISABLE_ZOOMED_VIEW = "ui.zoomedview.disabled";
|
||||
private static final String PREFS_ZOOMED_VIEW_ENABLED = "ui.zoomedview.enabled";
|
||||
private static final String PREFS_UPDATER_AUTODOWNLOAD = "app.update.autodownload";
|
||||
private static final String PREFS_UPDATER_URL = "app.update.url.android";
|
||||
private static final String PREFS_GEO_REPORTING = NON_PREF_PREFIX + "app.geo.reportdata";
|
||||
@ -735,7 +735,7 @@ OnSharedPreferenceChangeListener
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
} else if (PREFS_DISABLE_ZOOMED_VIEW.equals(key)) {
|
||||
} else if (PREFS_ZOOMED_VIEW_ENABLED.equals(key)) {
|
||||
// Only enable the ZoomedView / magnifying pref on Nightly.
|
||||
if (!AppConstants.NIGHTLY_BUILD) {
|
||||
preferences.removePreference(pref);
|
||||
|
@ -32,10 +32,10 @@
|
||||
android:color="@color/background_normal_lwt" />
|
||||
<item android:state_pressed="true"
|
||||
gecko:state_light="true"
|
||||
android:color="@color/new_tablet_highlight_lwt" />
|
||||
android:color="@color/tablet_highlight_lwt" />
|
||||
<item android:state_pressed="true"
|
||||
gecko:state_dark="true"
|
||||
android:color="@color/new_tablet_highlight_dark_lwt" />
|
||||
android:color="@color/tablet_highlight_dark_lwt" />
|
||||
|
||||
<item android:state_checked="true"
|
||||
android:color="@color/toolbar_grey" />
|
||||
|
@ -10,13 +10,13 @@
|
||||
android:state_pressed="true"
|
||||
android:state_enabled="true">
|
||||
|
||||
<inset android:insetTop="@dimen/new_tablet_browser_toolbar_menu_item_inset_vertical"
|
||||
android:insetBottom="@dimen/new_tablet_browser_toolbar_menu_item_inset_vertical"
|
||||
android:insetLeft="@dimen/new_tablet_browser_toolbar_menu_item_inset_horizontal"
|
||||
android:insetRight="@dimen/new_tablet_browser_toolbar_menu_item_inset_horizontal">
|
||||
<inset android:insetTop="@dimen/tablet_browser_toolbar_menu_item_inset_vertical"
|
||||
android:insetBottom="@dimen/tablet_browser_toolbar_menu_item_inset_vertical"
|
||||
android:insetLeft="@dimen/tablet_browser_toolbar_menu_item_inset_horizontal"
|
||||
android:insetRight="@dimen/tablet_browser_toolbar_menu_item_inset_horizontal">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@color/text_and_tabs_tray_grey"/>
|
||||
<corners android:radius="@dimen/new_tablet_browser_toolbar_menu_item_corner_radius"/>
|
||||
<corners android:radius="@dimen/tablet_browser_toolbar_menu_item_corner_radius"/>
|
||||
</shape>
|
||||
</inset>
|
||||
|
||||
@ -26,13 +26,13 @@
|
||||
android:state_focused="true"
|
||||
android:state_pressed="false">
|
||||
|
||||
<inset android:insetTop="@dimen/new_tablet_browser_toolbar_menu_item_inset_vertical"
|
||||
android:insetBottom="@dimen/new_tablet_browser_toolbar_menu_item_inset_vertical"
|
||||
android:insetLeft="@dimen/new_tablet_browser_toolbar_menu_item_inset_horizontal"
|
||||
android:insetRight="@dimen/new_tablet_browser_toolbar_menu_item_inset_horizontal">
|
||||
<inset android:insetTop="@dimen/tablet_browser_toolbar_menu_item_inset_vertical"
|
||||
android:insetBottom="@dimen/tablet_browser_toolbar_menu_item_inset_vertical"
|
||||
android:insetLeft="@dimen/tablet_browser_toolbar_menu_item_inset_horizontal"
|
||||
android:insetRight="@dimen/tablet_browser_toolbar_menu_item_inset_horizontal">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@color/placeholder_active_grey"/>
|
||||
<corners android:radius="@dimen/new_tablet_browser_toolbar_menu_item_corner_radius"/>
|
||||
<corners android:radius="@dimen/tablet_browser_toolbar_menu_item_corner_radius"/>
|
||||
</shape>
|
||||
</inset>
|
||||
|
||||
@ -41,13 +41,13 @@
|
||||
<item android:state_pressed="true"
|
||||
android:state_enabled="true">
|
||||
|
||||
<inset android:insetTop="@dimen/new_tablet_browser_toolbar_menu_item_inset_vertical"
|
||||
android:insetBottom="@dimen/new_tablet_browser_toolbar_menu_item_inset_vertical"
|
||||
android:insetLeft="@dimen/new_tablet_browser_toolbar_menu_item_inset_horizontal"
|
||||
android:insetRight="@dimen/new_tablet_browser_toolbar_menu_item_inset_horizontal">
|
||||
<inset android:insetTop="@dimen/tablet_browser_toolbar_menu_item_inset_vertical"
|
||||
android:insetBottom="@dimen/tablet_browser_toolbar_menu_item_inset_vertical"
|
||||
android:insetLeft="@dimen/tablet_browser_toolbar_menu_item_inset_horizontal"
|
||||
android:insetRight="@dimen/tablet_browser_toolbar_menu_item_inset_horizontal">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@color/toolbar_grey_pressed"/>
|
||||
<corners android:radius="@dimen/new_tablet_browser_toolbar_menu_item_corner_radius"/>
|
||||
<corners android:radius="@dimen/tablet_browser_toolbar_menu_item_corner_radius"/>
|
||||
</shape>
|
||||
</inset>
|
||||
|
||||
@ -56,13 +56,13 @@
|
||||
<item android:state_focused="true"
|
||||
android:state_pressed="false">
|
||||
|
||||
<inset android:insetTop="@dimen/new_tablet_browser_toolbar_menu_item_inset_vertical"
|
||||
android:insetBottom="@dimen/new_tablet_browser_toolbar_menu_item_inset_vertical"
|
||||
android:insetLeft="@dimen/new_tablet_browser_toolbar_menu_item_inset_horizontal"
|
||||
android:insetRight="@dimen/new_tablet_browser_toolbar_menu_item_inset_horizontal">
|
||||
<inset android:insetTop="@dimen/tablet_browser_toolbar_menu_item_inset_vertical"
|
||||
android:insetBottom="@dimen/tablet_browser_toolbar_menu_item_inset_vertical"
|
||||
android:insetLeft="@dimen/tablet_browser_toolbar_menu_item_inset_horizontal"
|
||||
android:insetRight="@dimen/tablet_browser_toolbar_menu_item_inset_horizontal">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@color/new_tablet_highlight_focused"/>
|
||||
<corners android:radius="@dimen/new_tablet_browser_toolbar_menu_item_corner_radius"/>
|
||||
<solid android:color="@color/tablet_highlight_focused"/>
|
||||
<corners android:radius="@dimen/tablet_browser_toolbar_menu_item_corner_radius"/>
|
||||
</shape>
|
||||
</inset>
|
||||
|
||||
|
@ -9,13 +9,13 @@
|
||||
<item android:state_pressed="true"
|
||||
android:state_enabled="true">
|
||||
|
||||
<inset android:insetTop="@dimen/new_tablet_tab_strip_button_inset"
|
||||
android:insetBottom="@dimen/new_tablet_tab_strip_button_inset"
|
||||
android:insetLeft="@dimen/new_tablet_tab_strip_button_inset"
|
||||
android:insetRight="@dimen/new_tablet_tab_strip_button_inset">
|
||||
<inset android:insetTop="@dimen/tablet_tab_strip_button_inset"
|
||||
android:insetBottom="@dimen/tablet_tab_strip_button_inset"
|
||||
android:insetLeft="@dimen/tablet_tab_strip_button_inset"
|
||||
android:insetRight="@dimen/tablet_tab_strip_button_inset">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@color/highlight_dark"/>
|
||||
<corners android:radius="@dimen/new_tablet_browser_toolbar_menu_item_corner_radius"/>
|
||||
<corners android:radius="@dimen/tablet_browser_toolbar_menu_item_corner_radius"/>
|
||||
</shape>
|
||||
</inset>
|
||||
|
||||
@ -24,13 +24,13 @@
|
||||
<item android:state_focused="true"
|
||||
android:state_pressed="false">
|
||||
|
||||
<inset android:insetTop="@dimen/new_tablet_tab_strip_button_inset"
|
||||
android:insetBottom="@dimen/new_tablet_tab_strip_button_inset"
|
||||
android:insetLeft="@dimen/new_tablet_tab_strip_button_inset"
|
||||
android:insetRight="@dimen/new_tablet_tab_strip_button_inset">
|
||||
<inset android:insetTop="@dimen/tablet_tab_strip_button_inset"
|
||||
android:insetBottom="@dimen/tablet_tab_strip_button_inset"
|
||||
android:insetLeft="@dimen/tablet_tab_strip_button_inset"
|
||||
android:insetRight="@dimen/tablet_tab_strip_button_inset">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@color/new_tablet_highlight_focused"/>
|
||||
<corners android:radius="@dimen/new_tablet_browser_toolbar_menu_item_corner_radius"/>
|
||||
<solid android:color="@color/tablet_highlight_focused"/>
|
||||
<corners android:radius="@dimen/tablet_browser_toolbar_menu_item_corner_radius"/>
|
||||
</shape>
|
||||
</inset>
|
||||
|
||||
|
@ -24,7 +24,7 @@
|
||||
<!-- focused state -->
|
||||
<item android:state_focused="true"
|
||||
android:state_pressed="false"
|
||||
android:drawable="@color/new_tablet_highlight_focused"/>
|
||||
android:drawable="@color/tablet_highlight_focused"/>
|
||||
|
||||
<!-- private browsing mode -->
|
||||
<item gecko:state_private="true"
|
||||
|
@ -11,7 +11,7 @@
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignLeft="@+id/back"
|
||||
android:layout_toLeftOf="@id/menu_items"
|
||||
android:layout_marginLeft="@dimen/new_tablet_nav_button_width_half"
|
||||
android:layout_marginLeft="@dimen/tablet_nav_button_width_half"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:duplicateParentState="true"
|
||||
@ -51,14 +51,14 @@
|
||||
android:src="@drawable/ic_menu_forward"
|
||||
android:background="@drawable/url_bar_nav_button"
|
||||
android:alpha="0"
|
||||
android:layout_width="@dimen/new_tablet_nav_button_width_plus_half"
|
||||
android:layout_marginLeft="@dimen/new_tablet_nav_button_width_half"
|
||||
android:layout_width="@dimen/tablet_nav_button_width_plus_half"
|
||||
android:layout_marginLeft="@dimen/tablet_nav_button_width_half"
|
||||
android:paddingLeft="18dp"/>
|
||||
|
||||
<org.mozilla.gecko.toolbar.BackButton android:id="@id/back"
|
||||
style="@style/UrlBar.ImageButton"
|
||||
android:layout_width="@dimen/new_tablet_nav_button_width"
|
||||
android:layout_height="@dimen/new_tablet_nav_button_width"
|
||||
android:layout_width="@dimen/tablet_nav_button_width"
|
||||
android:layout_height="@dimen/tablet_nav_button_width"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginLeft="12dp"
|
||||
android:layout_alignParentLeft="true"
|
||||
|
@ -17,7 +17,7 @@
|
||||
<org.mozilla.gecko.widget.ThemedImageButton
|
||||
android:id="@+id/add_tab"
|
||||
style="@style/UrlBar.ImageButton"
|
||||
android:layout_width="@dimen/new_tablet_tab_strip_height"
|
||||
android:layout_width="@dimen/tablet_tab_strip_height"
|
||||
android:src="@drawable/tab_strip_add_tab"
|
||||
android:contentDescription="@string/new_tab"
|
||||
android:layout_marginRight="9dp"
|
||||
|
@ -7,7 +7,7 @@
|
||||
the close button within the TabStripItemView -->
|
||||
<org.mozilla.gecko.tabs.TabStripItemView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="@dimen/new_tablet_tab_strip_item_width"
|
||||
android:layout_width="@dimen/tablet_tab_strip_item_width"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingLeft="28dp"
|
||||
android:paddingRight="12dp"/>
|
||||
|
@ -12,6 +12,6 @@
|
||||
android:src="@drawable/tabs_panel_nav_back"
|
||||
android:contentDescription="@string/back"
|
||||
android:background="@drawable/action_bar_button_inverse"
|
||||
gecko:dividerVerticalPadding="@dimen/new_tablet_tab_panel_divider_vertical_padding"
|
||||
gecko:dividerVerticalPadding="@dimen/tablet_tab_panel_divider_vertical_padding"
|
||||
gecko:rightDivider="@drawable/tab_indicator_divider"/>
|
||||
|
||||
|
@ -16,9 +16,9 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:duplicateParentState="true"
|
||||
android:paddingLeft="@dimen/new_tablet_tab_highlight_stroke_width"
|
||||
android:paddingRight="@dimen/new_tablet_tab_highlight_stroke_width"
|
||||
android:paddingBottom="@dimen/new_tablet_tab_highlight_stroke_width">
|
||||
android:paddingLeft="@dimen/tablet_tab_highlight_stroke_width"
|
||||
android:paddingRight="@dimen/tablet_tab_highlight_stroke_width"
|
||||
android:paddingBottom="@dimen/tablet_tab_highlight_stroke_width">
|
||||
|
||||
<org.mozilla.gecko.widget.FadedSingleColorTextView
|
||||
android:id="@+id/title"
|
||||
@ -62,13 +62,13 @@
|
||||
android:id="@+id/wrapper"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/new_tablet_tab_highlight_stroke_width"
|
||||
android:padding="@dimen/tablet_tab_highlight_stroke_width"
|
||||
android:background="@drawable/tab_thumbnail"
|
||||
android:duplicateParentState="true">
|
||||
|
||||
<org.mozilla.gecko.widget.ThumbnailView android:id="@+id/thumbnail"
|
||||
android:layout_width="@dimen/new_tablet_tab_thumbnail_width"
|
||||
android:layout_height="@dimen/new_tablet_tab_thumbnail_height"
|
||||
android:layout_width="@dimen/tablet_tab_thumbnail_width"
|
||||
android:layout_height="@dimen/tablet_tab_thumbnail_height"
|
||||
/>
|
||||
|
||||
</org.mozilla.gecko.widget.TabThumbnailWrapper>
|
||||
|
@ -23,7 +23,7 @@
|
||||
<RelativeLayout android:id="@+id/gecko_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_below="@+id/new_tablet_tab_strip"
|
||||
android:layout_below="@+id/tablet_tab_strip"
|
||||
android:layout_above="@+id/find_in_page">
|
||||
|
||||
<include layout="@layout/shared_ui_components"/>
|
||||
@ -98,11 +98,11 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ViewStub android:id="@+id/new_tablet_tab_strip"
|
||||
android:inflatedId="@id/new_tablet_tab_strip"
|
||||
<ViewStub android:id="@+id/tablet_tab_strip"
|
||||
android:inflatedId="@id/tablet_tab_strip"
|
||||
android:layout="@layout/tab_strip"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/new_tablet_tab_strip_height"
|
||||
android:layout_height="@dimen/tablet_tab_strip_height"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<org.mozilla.gecko.widget.GeckoViewFlipper
|
||||
|
@ -28,7 +28,7 @@
|
||||
android:layout_height="match_parent"
|
||||
android:tabStripEnabled="false"
|
||||
android:divider="@drawable/tab_indicator_divider"
|
||||
android:dividerPadding="@dimen/new_tablet_tab_panel_divider_vertical_padding"
|
||||
android:dividerPadding="@dimen/tablet_tab_panel_divider_vertical_padding"
|
||||
android:layout="@layout/tabs_panel_indicator"/>
|
||||
|
||||
<View android:layout_width="0dip"
|
||||
|
@ -9,5 +9,5 @@
|
||||
<dimen name="home_remote_tabs_top_padding">16dp</dimen>
|
||||
<dimen name="page_group_height">64dp</dimen>
|
||||
|
||||
<dimen name="new_tablet_tab_panel_grid_padding">48dp</dimen>
|
||||
<dimen name="tablet_tab_panel_grid_padding">48dp</dimen>
|
||||
</resources>
|
||||
|
@ -6,7 +6,7 @@
|
||||
<resources>
|
||||
|
||||
<style name="UrlBar.ImageButton" parent="UrlBar.ImageButtonBase">
|
||||
<item name="android:layout_width">@dimen/new_tablet_browser_toolbar_menu_item_width</item>
|
||||
<item name="android:layout_width">@dimen/tablet_browser_toolbar_menu_item_width</item>
|
||||
</style>
|
||||
|
||||
<style name="UrlBar.ImageButton.TabCount">
|
||||
@ -54,8 +54,8 @@
|
||||
* The reload button contains whitespace at the top of the image to lower it -->
|
||||
<item name="android:paddingTop">19dp</item>
|
||||
<item name="android:paddingBottom">21dp</item>
|
||||
<item name="android:paddingLeft">@dimen/new_tablet_browser_toolbar_menu_item_padding_horizontal</item>
|
||||
<item name="android:paddingRight">@dimen/new_tablet_browser_toolbar_menu_item_padding_horizontal</item>
|
||||
<item name="android:paddingLeft">@dimen/tablet_browser_toolbar_menu_item_padding_horizontal</item>
|
||||
<item name="android:paddingRight">@dimen/tablet_browser_toolbar_menu_item_padding_horizontal</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.MenuItemSecondaryActionBar" parent="Widget.MenuItemSecondaryActionBarBase">
|
||||
|
@ -5,6 +5,6 @@
|
||||
|
||||
<resources>
|
||||
|
||||
<dimen name="new_tablet_tab_panel_grid_padding">64dp</dimen>
|
||||
<dimen name="tablet_tab_panel_grid_padding">64dp</dimen>
|
||||
|
||||
</resources>
|
||||
|
@ -6,6 +6,6 @@
|
||||
<resources>
|
||||
|
||||
<dimen name="panel_grid_view_column_width">250dp</dimen>
|
||||
<dimen name="new_tablet_tab_panel_grid_padding">48dp</dimen>
|
||||
<dimen name="tablet_tab_panel_grid_padding">48dp</dimen>
|
||||
|
||||
</resources>
|
||||
|
@ -47,13 +47,13 @@
|
||||
<color name="highlight_dark_focused">#1AFFFFFF</color>
|
||||
|
||||
<!-- Synced w/ toolbar_grey_pressed -->
|
||||
<color name="new_tablet_highlight_lwt">#AAD7D7DC</color>
|
||||
<color name="tablet_highlight_lwt">#AAD7D7DC</color>
|
||||
|
||||
<!-- Synced w/ tabs_tray_grey_pressed -->
|
||||
<color name="new_tablet_highlight_dark_lwt">#AA45494E</color>
|
||||
<color name="tablet_highlight_dark_lwt">#AA45494E</color>
|
||||
|
||||
<!-- (bug 1077195) Focused state values are temporary. -->
|
||||
<color name="new_tablet_highlight_focused">#C0C9D0</color>
|
||||
<color name="tablet_highlight_focused">#C0C9D0</color>
|
||||
|
||||
<!-- highlight on shaped button: 20% white over text_and_tabs_tray_grey -->
|
||||
<color name="highlight_shaped">#FF696D71</color>
|
||||
|
@ -28,25 +28,25 @@
|
||||
<dimen name="browser_toolbar_shadow_size">2dp</dimen>
|
||||
|
||||
<!-- If you update one of these values, update the others. -->
|
||||
<dimen name="new_tablet_nav_button_width">42dp</dimen>
|
||||
<dimen name="new_tablet_nav_button_width_half">21dp</dimen>
|
||||
<dimen name="new_tablet_nav_button_width_plus_half">63dp</dimen>
|
||||
<dimen name="tablet_nav_button_width">42dp</dimen>
|
||||
<dimen name="tablet_nav_button_width_half">21dp</dimen>
|
||||
<dimen name="tablet_nav_button_width_plus_half">63dp</dimen>
|
||||
|
||||
<!-- This is the system default for the vertical padding for the divider of the TabWidget.
|
||||
Used to mimic the divider padding on the tablet tabs panel back button. -->
|
||||
<dimen name="new_tablet_tab_panel_divider_vertical_padding">12dp</dimen>
|
||||
<dimen name="tablet_tab_panel_divider_vertical_padding">12dp</dimen>
|
||||
|
||||
<dimen name="new_tablet_tab_strip_height">48dp</dimen>
|
||||
<dimen name="new_tablet_tab_strip_item_width">208dp</dimen>
|
||||
<dimen name="new_tablet_tab_strip_item_margin">-28dp</dimen>
|
||||
<dimen name="new_tablet_tab_strip_fading_edge_size">15dp</dimen>
|
||||
<dimen name="new_tablet_browser_toolbar_menu_item_width">56dp</dimen>
|
||||
<dimen name="tablet_tab_strip_height">48dp</dimen>
|
||||
<dimen name="tablet_tab_strip_item_width">208dp</dimen>
|
||||
<dimen name="tablet_tab_strip_item_margin">-28dp</dimen>
|
||||
<dimen name="tablet_tab_strip_fading_edge_size">15dp</dimen>
|
||||
<dimen name="tablet_browser_toolbar_menu_item_width">56dp</dimen>
|
||||
<!-- Padding combines with an 18dp image to form the menu item width and height. -->
|
||||
<dimen name="new_tablet_browser_toolbar_menu_item_padding_horizontal">19dp</dimen>
|
||||
<dimen name="new_tablet_browser_toolbar_menu_item_inset_vertical">5dp</dimen>
|
||||
<dimen name="new_tablet_browser_toolbar_menu_item_inset_horizontal">3dp</dimen>
|
||||
<dimen name="new_tablet_browser_toolbar_menu_item_corner_radius">5dp</dimen>
|
||||
<dimen name="new_tablet_tab_strip_button_inset">5dp</dimen>
|
||||
<dimen name="tablet_browser_toolbar_menu_item_padding_horizontal">19dp</dimen>
|
||||
<dimen name="tablet_browser_toolbar_menu_item_inset_vertical">5dp</dimen>
|
||||
<dimen name="tablet_browser_toolbar_menu_item_inset_horizontal">3dp</dimen>
|
||||
<dimen name="tablet_browser_toolbar_menu_item_corner_radius">5dp</dimen>
|
||||
<dimen name="tablet_tab_strip_button_inset">5dp</dimen>
|
||||
|
||||
<!-- Dimensions used by Favicons and FaviconView -->
|
||||
<dimen name="favicon_bg">32dp</dimen>
|
||||
@ -145,14 +145,14 @@
|
||||
<dimen name="validation_message_height">50dp</dimen>
|
||||
<dimen name="validation_message_margin_top">6dp</dimen>
|
||||
|
||||
<dimen name="new_tablet_tab_thumbnail_width">168dp</dimen>
|
||||
<dimen name="new_tablet_tab_thumbnail_height">140dp</dimen>
|
||||
<dimen name="new_tablet_tab_panel_column_width">178dp</dimen>
|
||||
<dimen name="new_tablet_tab_panel_grid_padding">19dp</dimen>
|
||||
<dimen name="new_tablet_tab_panel_grid_vspacing">21dp</dimen>
|
||||
<dimen name="new_tablet_tab_panel_grid_padding_top">24dp</dimen>
|
||||
<dimen name="tablet_tab_thumbnail_width">168dp</dimen>
|
||||
<dimen name="tablet_tab_thumbnail_height">140dp</dimen>
|
||||
<dimen name="tablet_tab_panel_column_width">178dp</dimen>
|
||||
<dimen name="tablet_tab_panel_grid_padding">19dp</dimen>
|
||||
<dimen name="tablet_tab_panel_grid_vspacing">21dp</dimen>
|
||||
<dimen name="tablet_tab_panel_grid_padding_top">24dp</dimen>
|
||||
|
||||
<dimen name="new_tablet_tab_highlight_stroke_width">5dp</dimen>
|
||||
<dimen name="tablet_tab_highlight_stroke_width">5dp</dimen>
|
||||
|
||||
<!-- PageActionButtons dimensions -->
|
||||
<dimen name="page_action_button_width">32dp</dimen>
|
||||
|
@ -208,9 +208,9 @@
|
||||
<item name="android:scrollbarStyle">outsideOverlay</item>
|
||||
<item name="android:gravity">center</item>
|
||||
<item name="android:numColumns">auto_fit</item>
|
||||
<item name="android:columnWidth">@dimen/new_tablet_tab_panel_column_width</item>
|
||||
<item name="android:columnWidth">@dimen/tablet_tab_panel_column_width</item>
|
||||
<item name="android:horizontalSpacing">2dp</item>
|
||||
<item name="android:verticalSpacing">@dimen/new_tablet_tab_panel_grid_vspacing</item>
|
||||
<item name="android:verticalSpacing">@dimen/tablet_tab_panel_grid_vspacing</item>
|
||||
<item name="android:drawSelectorOnTop">true</item>
|
||||
<item name="android:clipToPadding">false</item>
|
||||
</style>
|
||||
@ -688,8 +688,8 @@
|
||||
<item name="android:layout_weight">1</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:layout_gravity">center_vertical</item>
|
||||
<item name="android:ellipsize">none</item>
|
||||
<item name="android:maxLines">3</item>
|
||||
<item name="android:ellipsize">end</item>
|
||||
<item name="android:maxLines">1</item>
|
||||
<item name="android:clickable">false</item>
|
||||
<item name="android:focusable">false</item>
|
||||
</style>
|
||||
|
@ -33,9 +33,9 @@
|
||||
android:title="@string/pref_zoom_force_enabled"
|
||||
android:summary="@string/pref_zoom_force_enabled_summary" />
|
||||
|
||||
<CheckBoxPreference android:key="ui.zoomedview.disabled"
|
||||
android:title="@string/pref_prevent_magnifying_glass"
|
||||
android:summary="@string/pref_prevent_magnifying_glass_summary" />
|
||||
<CheckBoxPreference android:key="ui.zoomedview.enabled"
|
||||
android:title="@string/pref_magnifying_glass_enabled"
|
||||
android:summary="@string/pref_magnifying_glass_enabled_summary" />
|
||||
|
||||
<PreferenceCategory android:title="@string/pref_category_input_options"
|
||||
android:key="@string/pref_category_input_options">
|
||||
|
@ -24,4 +24,7 @@ public class DefaultConfiguration implements RestrictionConfiguration {
|
||||
public boolean isRestricted() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {}
|
||||
}
|
||||
|
@ -72,4 +72,7 @@ public class GuestProfileConfiguration implements RestrictionConfiguration {
|
||||
public boolean isRestricted() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {}
|
||||
}
|
||||
|
@ -6,16 +6,19 @@
|
||||
package org.mozilla.gecko.restrictions;
|
||||
|
||||
import org.mozilla.gecko.AboutPages;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.StrictMode;
|
||||
import android.os.UserManager;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
|
||||
public class RestrictedProfileConfiguration implements RestrictionConfiguration {
|
||||
static List<Restriction> DEFAULT_RESTRICTIONS = Arrays.asList(
|
||||
Restriction.DISALLOW_INSTALL_EXTENSION,
|
||||
@ -31,22 +34,36 @@ public class RestrictedProfileConfiguration implements RestrictionConfiguration
|
||||
);
|
||||
|
||||
private Context context;
|
||||
private Bundle cachedRestrictions;
|
||||
private boolean isCacheInvalid = true;
|
||||
|
||||
public RestrictedProfileConfiguration(Context context) {
|
||||
this.context = context.getApplicationContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAllowed(Restriction restriction) {
|
||||
boolean isAllowed = !getAppRestrictions(context).getBoolean(restriction.name, DEFAULT_RESTRICTIONS.contains(restriction));
|
||||
|
||||
if (isAllowed) {
|
||||
// If this restriction is not enforced by the app setup then check wether this is a restriction that is
|
||||
// enforced by the system.
|
||||
isAllowed = !getUserRestrictions(context).getBoolean(restriction.name, false);
|
||||
public synchronized boolean isAllowed(Restriction restriction) {
|
||||
if (isCacheInvalid || !ThreadUtils.isOnUiThread()) {
|
||||
cachedRestrictions = readRestrictions();
|
||||
isCacheInvalid = false;
|
||||
}
|
||||
|
||||
return isAllowed;
|
||||
return !cachedRestrictions.getBoolean(restriction.name, DEFAULT_RESTRICTIONS.contains(restriction));
|
||||
}
|
||||
|
||||
private Bundle readRestrictions() {
|
||||
final UserManager mgr = (UserManager) context.getSystemService(Context.USER_SERVICE);
|
||||
|
||||
StrictMode.ThreadPolicy policy = StrictMode.allowThreadDiskReads();
|
||||
|
||||
try {
|
||||
Bundle restrictions = new Bundle();
|
||||
restrictions.putAll(mgr.getApplicationRestrictions(context.getPackageName()));
|
||||
restrictions.putAll(mgr.getUserRestrictions());
|
||||
return restrictions;
|
||||
} finally {
|
||||
StrictMode.setThreadPolicy(policy);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -72,15 +89,8 @@ public class RestrictedProfileConfiguration implements RestrictionConfiguration
|
||||
return true;
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
|
||||
private Bundle getAppRestrictions(final Context context) {
|
||||
final UserManager mgr = (UserManager) context.getSystemService(Context.USER_SERVICE);
|
||||
return mgr.getApplicationRestrictions(context.getPackageName());
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
|
||||
private Bundle getUserRestrictions(final Context context) {
|
||||
final UserManager mgr = (UserManager) context.getSystemService(Context.USER_SERVICE);
|
||||
return mgr.getUserRestrictions();
|
||||
@Override
|
||||
public synchronized void update() {
|
||||
isCacheInvalid = true;
|
||||
}
|
||||
}
|
||||
|
@ -23,4 +23,9 @@ public interface RestrictionConfiguration {
|
||||
* Is this user restricted in any way?
|
||||
*/
|
||||
boolean isRestricted();
|
||||
|
||||
/**
|
||||
* Update restrictions if needed.
|
||||
*/
|
||||
void update();
|
||||
}
|
||||
|
@ -379,8 +379,8 @@
|
||||
<string name="pref_titlebar_mode_title">&pref_titlebar_mode_title;</string>
|
||||
<string name="pref_titlebar_mode_url">&pref_titlebar_mode_url;</string>
|
||||
|
||||
<string name="pref_prevent_magnifying_glass">&pref_prevent_magnifying_glass;</string>
|
||||
<string name="pref_prevent_magnifying_glass_summary">&pref_prevent_magnifying_glass_summary;</string>
|
||||
<string name="pref_magnifying_glass_enabled">&pref_magnifying_glass_enabled;</string>
|
||||
<string name="pref_magnifying_glass_enabled_summary">&pref_magnifying_glass_enabled_summary;</string>
|
||||
|
||||
<string name="pref_scroll_title_bar2">&pref_scroll_title_bar2;</string>
|
||||
<string name="pref_scroll_title_bar_summary">&pref_scroll_title_bar_summary;</string>
|
||||
|
@ -20,12 +20,12 @@ import org.mozilla.gecko.sync.repositories.android.ClientsDatabaseAccessor;
|
||||
import org.mozilla.gecko.sync.repositories.domain.ClientRecord;
|
||||
import org.mozilla.gecko.tabqueue.TabQueueDispatcher;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.support.v4.app.NotificationManagerCompat;
|
||||
|
||||
/**
|
||||
* Process commands received from Sync clients.
|
||||
@ -264,26 +264,27 @@ public class CommandProcessor {
|
||||
}
|
||||
|
||||
final String ns = Context.NOTIFICATION_SERVICE;
|
||||
final NotificationManager notificationManager = (NotificationManager) context.getSystemService(ns);
|
||||
|
||||
// Create a Notification.
|
||||
final int icon = R.drawable.flat_icon;
|
||||
String notificationTitle = context.getString(R.string.sync_new_tab);
|
||||
if (title != null) {
|
||||
notificationTitle = notificationTitle.concat(": " + title);
|
||||
}
|
||||
|
||||
final long when = System.currentTimeMillis();
|
||||
Notification notification = new Notification(icon, notificationTitle, when);
|
||||
notification.flags = Notification.FLAG_AUTO_CANCEL;
|
||||
|
||||
// Set pending intent associated with the notification.
|
||||
Intent notificationIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(uri));
|
||||
notificationIntent.putExtra(TabQueueDispatcher.SKIP_TAB_QUEUE_FLAG, true);
|
||||
PendingIntent contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0);
|
||||
notification.setLatestEventInfo(context, notificationTitle, uri, contentIntent);
|
||||
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
|
||||
builder.setSmallIcon(R.drawable.flat_icon);
|
||||
builder.setContentTitle(notificationTitle);
|
||||
builder.setWhen(System.currentTimeMillis());
|
||||
builder.setAutoCancel(true);
|
||||
builder.setContentIntent(contentIntent);
|
||||
builder.setContentText(uri);
|
||||
builder.setContentIntent(contentIntent);
|
||||
|
||||
// Send notification.
|
||||
notificationManager.notify(currentId.getAndIncrement(), notification);
|
||||
final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
|
||||
notificationManager.notify(currentId.getAndIncrement(), builder.build());
|
||||
}
|
||||
}
|
||||
|
@ -70,14 +70,14 @@ public class TabStripView extends TwoWayView {
|
||||
divider.getPadding(dividerPadding);
|
||||
|
||||
final int itemMargin =
|
||||
resources.getDimensionPixelSize(R.dimen.new_tablet_tab_strip_item_margin);
|
||||
resources.getDimensionPixelSize(R.dimen.tablet_tab_strip_item_margin);
|
||||
setItemMargin(itemMargin);
|
||||
|
||||
animatorListener = new TabAnimatorListener();
|
||||
|
||||
fadingEdgePaint = new Paint();
|
||||
fadingEdgeSize =
|
||||
resources.getDimensionPixelOffset(R.dimen.new_tablet_tab_strip_fading_edge_size);
|
||||
resources.getDimensionPixelOffset(R.dimen.tablet_tab_strip_fading_edge_size);
|
||||
|
||||
adapter = new TabStripAdapter(context);
|
||||
setAdapter(adapter);
|
||||
|
@ -88,10 +88,10 @@ class TabsGridLayout extends GridView
|
||||
setClipToPadding(false);
|
||||
|
||||
final Resources resources = getResources();
|
||||
mColumnWidth = resources.getDimensionPixelSize(R.dimen.new_tablet_tab_panel_column_width);
|
||||
mColumnWidth = resources.getDimensionPixelSize(R.dimen.tablet_tab_panel_column_width);
|
||||
|
||||
final int padding = resources.getDimensionPixelSize(R.dimen.new_tablet_tab_panel_grid_padding);
|
||||
final int paddingTop = resources.getDimensionPixelSize(R.dimen.new_tablet_tab_panel_grid_padding_top);
|
||||
final int padding = resources.getDimensionPixelSize(R.dimen.tablet_tab_panel_grid_padding);
|
||||
final int paddingTop = resources.getDimensionPixelSize(R.dimen.tablet_tab_panel_grid_padding_top);
|
||||
|
||||
// Lets set double the top padding on the bottom so that the last row shows up properly!
|
||||
// Your demise, GridView, cannot come fast enough.
|
||||
@ -142,7 +142,7 @@ class TabsGridLayout extends GridView
|
||||
|
||||
final int removedHeight = getChildAt(0).getMeasuredHeight();
|
||||
final int verticalSpacing =
|
||||
getResources().getDimensionPixelOffset(R.dimen.new_tablet_tab_panel_grid_vspacing);
|
||||
getResources().getDimensionPixelOffset(R.dimen.tablet_tab_panel_grid_vspacing);
|
||||
|
||||
ValueAnimator paddingAnimator = ValueAnimator.ofInt(getPaddingBottom() + removedHeight + verticalSpacing, getPaddingBottom());
|
||||
paddingAnimator.setDuration(ANIM_TIME_MS * 2);
|
||||
|
@ -36,7 +36,7 @@ class BrowserToolbarTablet extends BrowserToolbarTabletBase {
|
||||
super(context, attrs);
|
||||
|
||||
forwardButtonTranslationWidth =
|
||||
getResources().getDimensionPixelOffset(R.dimen.new_tablet_nav_button_width);
|
||||
getResources().getDimensionPixelOffset(R.dimen.tablet_nav_button_width);
|
||||
|
||||
// The forward button is initially expanded (in the layout file)
|
||||
// so translate it for start of the expansion animation; future
|
||||
|
@ -71,7 +71,7 @@ abstract class NavButton extends ShapedButton {
|
||||
stateList.addState(PRIVATE_PRESSED_STATE_SET, getColorDrawable(R.color.placeholder_active_grey));
|
||||
stateList.addState(PRESSED_ENABLED_STATE_SET, getColorDrawable(R.color.toolbar_grey_pressed));
|
||||
stateList.addState(PRIVATE_FOCUSED_STATE_SET, getColorDrawable(R.color.text_and_tabs_tray_grey));
|
||||
stateList.addState(FOCUSED_STATE_SET, getColorDrawable(R.color.new_tablet_highlight_focused));
|
||||
stateList.addState(FOCUSED_STATE_SET, getColorDrawable(R.color.tablet_highlight_focused));
|
||||
stateList.addState(PRIVATE_STATE_SET, getColorDrawable(R.color.tabs_tray_grey_pressed));
|
||||
stateList.addState(EMPTY_STATE_SET, drawable);
|
||||
|
||||
|
@ -30,6 +30,7 @@ import android.net.Uri;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.net.wifi.WifiManager.WifiLock;
|
||||
import android.os.Environment;
|
||||
import android.support.v4.app.NotificationManagerCompat;
|
||||
import android.support.v4.net.ConnectivityManagerCompat;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.support.v4.app.NotificationCompat.Builder;
|
||||
@ -77,7 +78,7 @@ public class UpdateService extends IntentService {
|
||||
|
||||
private SharedPreferences mPrefs;
|
||||
|
||||
private NotificationManager mNotificationManager;
|
||||
private NotificationManagerCompat mNotificationManager;
|
||||
private ConnectivityManager mConnectivityManager;
|
||||
private Builder mBuilder;
|
||||
|
||||
@ -142,7 +143,7 @@ public class UpdateService extends IntentService {
|
||||
super.onCreate();
|
||||
|
||||
mPrefs = getSharedPreferences(PREFS_NAME, 0);
|
||||
mNotificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
mNotificationManager = NotificationManagerCompat.from(this);
|
||||
mConnectivityManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
mWifiLock = ((WifiManager)getSystemService(Context.WIFI_SERVICE))
|
||||
.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, PREFS_NAME);
|
||||
@ -300,19 +301,19 @@ public class UpdateService extends IntentService {
|
||||
sendCheckUpdateResult(CheckUpdateResult.AVAILABLE);
|
||||
|
||||
// We aren't autodownloading here, so prompt to start the update
|
||||
Notification notification = new Notification(R.drawable.ic_status_logo, null, System.currentTimeMillis());
|
||||
|
||||
Intent notificationIntent = new Intent(UpdateServiceHelper.ACTION_DOWNLOAD_UPDATE);
|
||||
notificationIntent.setClass(this, UpdateService.class);
|
||||
|
||||
PendingIntent contentIntent = PendingIntent.getService(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
notification.flags = Notification.FLAG_AUTO_CANCEL;
|
||||
|
||||
notification.setLatestEventInfo(this, getResources().getString(R.string.updater_start_title),
|
||||
getResources().getString(R.string.updater_start_select),
|
||||
contentIntent);
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
|
||||
builder.setSmallIcon(R.drawable.ic_status_logo);
|
||||
builder.setWhen(System.currentTimeMillis());
|
||||
builder.setAutoCancel(true);
|
||||
builder.setContentTitle(getString(R.string.updater_start_title));
|
||||
builder.setContentText(getString(R.string.updater_start_select));
|
||||
builder.setContentIntent(contentIntent);
|
||||
|
||||
mNotificationManager.notify(NOTIFICATION_ID, notification);
|
||||
mNotificationManager.notify(NOTIFICATION_ID, builder.build());
|
||||
|
||||
return;
|
||||
}
|
||||
@ -332,20 +333,22 @@ public class UpdateService extends IntentService {
|
||||
applyUpdate(pkg);
|
||||
} else {
|
||||
// Prompt to apply the update
|
||||
Notification notification = new Notification(R.drawable.ic_status_logo, null, System.currentTimeMillis());
|
||||
|
||||
Intent notificationIntent = new Intent(UpdateServiceHelper.ACTION_APPLY_UPDATE);
|
||||
notificationIntent.setClass(this, UpdateService.class);
|
||||
notificationIntent.putExtra(UpdateServiceHelper.EXTRA_PACKAGE_PATH_NAME, pkg.getAbsolutePath());
|
||||
|
||||
PendingIntent contentIntent = PendingIntent.getService(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
notification.flags = Notification.FLAG_AUTO_CANCEL;
|
||||
|
||||
notification.setLatestEventInfo(this, getResources().getString(R.string.updater_apply_title),
|
||||
getResources().getString(R.string.updater_apply_select),
|
||||
contentIntent);
|
||||
|
||||
mNotificationManager.notify(NOTIFICATION_ID, notification);
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
|
||||
builder.setSmallIcon(R.drawable.ic_status_logo);
|
||||
builder.setWhen(System.currentTimeMillis());
|
||||
builder.setAutoCancel(true);
|
||||
builder.setContentTitle(getString(R.string.updater_apply_title));
|
||||
builder.setContentText(getString(R.string.updater_apply_select));
|
||||
builder.setContentIntent(contentIntent);
|
||||
|
||||
mNotificationManager.notify(NOTIFICATION_ID, builder.build());
|
||||
}
|
||||
}
|
||||
|
||||
@ -475,18 +478,18 @@ public class UpdateService extends IntentService {
|
||||
}
|
||||
|
||||
private void showDownloadFailure() {
|
||||
Notification notification = new Notification(R.drawable.ic_status_logo, null, System.currentTimeMillis());
|
||||
|
||||
Intent notificationIntent = new Intent(UpdateServiceHelper.ACTION_CHECK_FOR_UPDATE);
|
||||
notificationIntent.setClass(this, UpdateService.class);
|
||||
|
||||
PendingIntent contentIntent = PendingIntent.getService(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
|
||||
notification.setLatestEventInfo(this, getResources().getString(R.string.updater_downloading_title_failed),
|
||||
getResources().getString(R.string.updater_downloading_retry),
|
||||
contentIntent);
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
|
||||
builder.setSmallIcon(R.drawable.ic_status_logo);
|
||||
builder.setWhen(System.currentTimeMillis());
|
||||
builder.setContentTitle(getString(R.string.updater_downloading_title_failed));
|
||||
builder.setContentText(getString(R.string.updater_downloading_retry));
|
||||
builder.setContentIntent(contentIntent);
|
||||
|
||||
mNotificationManager.notify(NOTIFICATION_ID, notification);
|
||||
mNotificationManager.notify(NOTIFICATION_ID, builder.build());
|
||||
}
|
||||
|
||||
private boolean deleteUpdatePackage(String path) {
|
||||
|
@ -50,6 +50,37 @@ dependencies {
|
||||
androidTestCompile 'com.jayway.android.robotium:robotium-solo:4.3.1'
|
||||
}
|
||||
|
||||
task syncOmnijarFromDistDir(type: Sync) {
|
||||
into("${project.buildDir}/generated/omnijar")
|
||||
from("${topobjdir}/dist/fennec/assets") {
|
||||
include 'omni.ja'
|
||||
}
|
||||
}
|
||||
|
||||
task checkLibsExistInDistDir<< {
|
||||
if (syncLibsFromDistDir.source.empty) {
|
||||
throw new GradleException("Required JNI libraries not found in ${topobjdir}/dist/fennec/lib. Have you built and packaged?")
|
||||
}
|
||||
}
|
||||
|
||||
task syncLibsFromDistDir(type: Sync, dependsOn: checkLibsExistInDistDir) {
|
||||
into("${project.buildDir}/generated/jniLibs")
|
||||
from("${topobjdir}/dist/fennec/lib")
|
||||
}
|
||||
|
||||
task checkAssetsExistInDistDir<< {
|
||||
if (syncAssetsFromDistDir.source.empty) {
|
||||
throw new GradleException("Required assets not found in ${topobjdir}/dist/fennec/assets. Have you built and packaged?")
|
||||
}
|
||||
}
|
||||
|
||||
task syncAssetsFromDistDir(type: Sync, dependsOn: checkAssetsExistInDistDir) {
|
||||
into("${project.buildDir}/generated/assets")
|
||||
from("${topobjdir}/dist/fennec/assets") {
|
||||
exclude 'omni.ja'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* We want to expose the JSM files and chrome content to IDEs; the omnijar
|
||||
* project does this. In addition, the :omnijar:buildOmnijar task builds a new
|
||||
@ -71,8 +102,15 @@ android.applicationVariants.all { variant ->
|
||||
}
|
||||
|
||||
def buildOmnijarTask = project(':omnijar').tasks.getByName('buildOmnijar')
|
||||
syncOmnijarFromDistDir.dependsOn buildOmnijarTask
|
||||
def generateAssetsTask = tasks.findByName("generate${name.capitalize()}Assets")
|
||||
generateAssetsTask.dependsOn buildOmnijarTask
|
||||
generateAssetsTask.dependsOn syncOmnijarFromDistDir
|
||||
generateAssetsTask.dependsOn syncLibsFromDistDir
|
||||
generateAssetsTask.dependsOn syncAssetsFromDistDir
|
||||
|
||||
android.sourceSets.debug.assets.srcDir syncOmnijarFromDistDir.destinationDir
|
||||
android.sourceSets.debug.assets.srcDir syncAssetsFromDistDir.destinationDir
|
||||
android.sourceSets.debug.jniLibs.srcDir syncLibsFromDistDir.destinationDir
|
||||
}
|
||||
|
||||
apply plugin: 'spoon'
|
||||
|
@ -77,10 +77,6 @@ dependencies {
|
||||
compile project(':thirdparty')
|
||||
}
|
||||
|
||||
android.libraryVariants.all { variant ->
|
||||
variant.checkManifest.dependsOn rootProject.generateCodeAndResources
|
||||
}
|
||||
|
||||
apply plugin: 'idea'
|
||||
|
||||
idea {
|
||||
|
@ -59,3 +59,17 @@ task generateCodeAndResources(type:Exec) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
subprojects {
|
||||
if (!hasProperty('android')) {
|
||||
return
|
||||
}
|
||||
android.applicationVariants.all {
|
||||
checkManifest.dependsOn rootProject.generateCodeAndResources
|
||||
}
|
||||
android.libraryVariants.all {
|
||||
checkManifest.dependsOn rootProject.generateCodeAndResources
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,21 +21,30 @@ android {
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
srcDir "${topsrcdir}/mobile/android/base/adjust"
|
||||
srcDir "${project.buildDir}/generated/source/java"
|
||||
|
||||
srcDir 'src/adjust/java'
|
||||
if (mozconfig.substs.MOZ_INSTALL_TRACKING) {
|
||||
exclude 'StubAdjustHelper.java'
|
||||
exclude 'org/mozilla/gecko/adjust/StubAdjustHelper.java'
|
||||
} else {
|
||||
exclude 'AdjustHelper.java'
|
||||
exclude 'org/mozilla/gecko/adjust/AdjustHelper.java'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
android.libraryVariants.all { variant ->
|
||||
variant.checkManifest.dependsOn rootProject.generateCodeAndResources
|
||||
task syncGeneratedSources(type: Sync) {
|
||||
into("${project.buildDir}/generated/source/java")
|
||||
from("${topobjdir}/mobile/android/base/generated/preprocessed")
|
||||
}
|
||||
|
||||
android.libraryVariants.all { variant ->
|
||||
// variant does not expose its generate sources task.
|
||||
def name = variant.buildType.name
|
||||
def generateSourcesTask = tasks.findByName("generate${name.capitalize()}Sources")
|
||||
generateSourcesTask.dependsOn syncGeneratedSources
|
||||
}
|
||||
|
||||
dependencies {
|
||||
if (mozconfig.substs.MOZ_INSTALL_TRACKING) {
|
||||
|
@ -17,10 +17,26 @@ android {
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
res {
|
||||
srcDir "${project.buildDir}/generated/source/preprocessed_resources"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task syncPreprocessedResources(type: Sync) {
|
||||
into("${project.buildDir}/generated/source/preprocessed_resources")
|
||||
from("${topobjdir}/mobile/android/base/res")
|
||||
}
|
||||
|
||||
android.libraryVariants.all { variant ->
|
||||
variant.checkManifest.dependsOn rootProject.generateCodeAndResources
|
||||
// variant does not expose its generate debug res values task.
|
||||
def name = variant.buildType.name
|
||||
def generateResValuesTask = tasks.findByName("generate${name.capitalize()}ResValues")
|
||||
generateResValuesTask.dependsOn syncPreprocessedResources
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
@ -95,11 +95,10 @@ class MachCommands(MachCommandBase):
|
||||
|
||||
srcdir('preprocessed_code/build.gradle', 'mobile/android/gradle/preprocessed_code/build.gradle')
|
||||
srcdir('preprocessed_code/src/main/AndroidManifest.xml', 'mobile/android/gradle/preprocessed_code/AndroidManifest.xml')
|
||||
objdir('preprocessed_code/src/main/java', 'mobile/android/base/generated/preprocessed')
|
||||
srcdir('preprocessed_code/src/adjust/java/org/mozilla/gecko/adjust', 'mobile/android/base/adjust')
|
||||
|
||||
srcdir('preprocessed_resources/build.gradle', 'mobile/android/gradle/preprocessed_resources/build.gradle')
|
||||
srcdir('preprocessed_resources/src/main/AndroidManifest.xml', 'mobile/android/gradle/preprocessed_resources/AndroidManifest.xml')
|
||||
objdir('preprocessed_resources/src/main/res', 'mobile/android/base/res')
|
||||
|
||||
srcdir('thirdparty/build.gradle', 'mobile/android/gradle/thirdparty/build.gradle')
|
||||
srcdir('thirdparty/src/main/AndroidManifest.xml', 'mobile/android/gradle/thirdparty/AndroidManifest.xml')
|
||||
@ -120,8 +119,6 @@ class MachCommands(MachCommandBase):
|
||||
objdir('app/src/androidTest/AndroidManifest.xml', 'build/mobile/robocop/AndroidManifest.xml')
|
||||
srcdir('app/src/androidTest/res', 'build/mobile/robocop/res')
|
||||
srcdir('app/src/androidTest/assets', 'mobile/android/tests/browser/robocop/assets')
|
||||
objdir('app/src/debug/assets', 'dist/fennec/assets')
|
||||
objdir('app/src/debug/jniLibs', 'dist/fennec/lib')
|
||||
# Test code.
|
||||
srcdir('app/src/robocop_harness/org/mozilla/gecko', 'build/mobile/robocop')
|
||||
srcdir('app/src/robocop/org/mozilla/gecko/tests', 'mobile/android/tests/browser/robocop')
|
||||
|
Before Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 143 B |
Before Width: | Height: | Size: 144 B |
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 564 B |
Before Width: | Height: | Size: 562 B |
@ -51,10 +51,7 @@ chrome.jar:
|
||||
skin/images/textfield.png (images/textfield.png)
|
||||
|
||||
skin/images/5stars.png (images/5stars.png)
|
||||
skin/images/addons-32.png (images/addons-32.png)
|
||||
skin/images/amo-logo.png (images/amo-logo.png)
|
||||
skin/images/arrowleft-16.png (images/arrowleft-16.png)
|
||||
skin/images/arrowright-16.png (images/arrowright-16.png)
|
||||
skin/images/arrowdown-16.png (images/arrowdown-16.png)
|
||||
skin/images/arrowup-16.png (images/arrowup-16.png)
|
||||
skin/images/blocked-warning.png (images/blocked-warning.png)
|
||||
@ -65,7 +62,6 @@ chrome.jar:
|
||||
skin/images/checkbox_unchecked_disabled.png (images/checkbox_unchecked_disabled.png)
|
||||
skin/images/checkbox_unchecked_pressed.png (images/checkbox_unchecked_pressed.png)
|
||||
skin/images/chevron.png (images/chevron.png)
|
||||
skin/images/default-app-icon.png (images/default-app-icon.png)
|
||||
skin/images/dropmarker.svg (images/dropmarker.svg)
|
||||
skin/images/dropmarker-right.svg (images/dropmarker-right.svg)
|
||||
skin/images/errorpage-warning.png (images/errorpage-warning.png)
|
||||
@ -73,8 +69,6 @@ chrome.jar:
|
||||
skin/images/fullscreen-hdpi.png (images/fullscreen-hdpi.png)
|
||||
skin/images/grey-caution.svg (images/grey-caution.svg)
|
||||
skin/images/certerror-warning.png (images/certerror-warning.png)
|
||||
skin/images/errorpage-larry-white.png (images/errorpage-larry-white.png)
|
||||
skin/images/errorpage-larry-black.png (images/errorpage-larry-black.png)
|
||||
skin/images/throbber.png (images/throbber.png)
|
||||
skin/images/search-clear-30.png (images/search-clear-30.png)
|
||||
skin/images/play-hdpi.png (images/play-hdpi.png)
|
||||
|
@ -2420,6 +2420,36 @@ ObjectClient.prototype = {
|
||||
return aPacket;
|
||||
}
|
||||
}),
|
||||
|
||||
/**
|
||||
* Request the stack to the promise's fulfillment point.
|
||||
*/
|
||||
getPromiseFulfillmentStack: DebuggerClient.requester({
|
||||
type: "fulfillmentStack"
|
||||
}, {
|
||||
before: function(packet) {
|
||||
if (this._grip.class !== "Promise") {
|
||||
throw new Error("getPromiseFulfillmentStack is only valid for " +
|
||||
"promise grips.");
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
}),
|
||||
|
||||
/**
|
||||
* Request the stack to the promise's rejection point.
|
||||
*/
|
||||
getPromiseRejectionStack: DebuggerClient.requester({
|
||||
type: "rejectionStack"
|
||||
}, {
|
||||
before: function(packet) {
|
||||
if (this._grip.class !== "Promise") {
|
||||
throw new Error("getPromiseRejectionStack is only valid for " +
|
||||
"promise grips.");
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -119,6 +119,8 @@ HELPER_SHEET += ":-moz-devtools-highlighted { outline: 2px dashed #F06!important
|
||||
loader.lazyRequireGetter(this, "DevToolsUtils",
|
||||
"devtools/toolkit/DevToolsUtils");
|
||||
|
||||
loader.lazyRequireGetter(this, "AsyncUtils", "devtools/toolkit/async-utils");
|
||||
|
||||
loader.lazyGetter(this, "DOMParser", function() {
|
||||
return Cc["@mozilla.org/xmlextras/domparser;1"].createInstance(Ci.nsIDOMParser);
|
||||
});
|
||||
@ -624,16 +626,12 @@ var NodeActor = exports.NodeActor = protocol.ActorClass({
|
||||
* transfered in the longstring back to the client will be that much smaller
|
||||
*/
|
||||
getImageData: method(function(maxDim) {
|
||||
// imageToImageData may fail if the node isn't an image
|
||||
try {
|
||||
let imageData = imageToImageData(this.rawNode, maxDim);
|
||||
return promise.resolve({
|
||||
return imageToImageData(this.rawNode, maxDim).then(imageData => {
|
||||
return {
|
||||
data: LongStringActor(this.conn, imageData.data),
|
||||
size: imageData.size
|
||||
});
|
||||
} catch(e) {
|
||||
return promise.reject(new Error("Image not available"));
|
||||
}
|
||||
};
|
||||
});
|
||||
}, {
|
||||
request: {maxDim: Arg(0, "nullable:number")},
|
||||
response: RetVal("imageData")
|
||||
@ -3645,39 +3643,16 @@ var InspectorActor = exports.InspectorActor = protocol.ActorClass({
|
||||
* transfered in the longstring back to the client will be that much smaller
|
||||
*/
|
||||
getImageDataFromURL: method(function(url, maxDim) {
|
||||
let deferred = promise.defer();
|
||||
let img = new this.window.Image();
|
||||
|
||||
// On load, get the image data and send the response
|
||||
img.onload = () => {
|
||||
// imageToImageData throws an error if the image is missing
|
||||
try {
|
||||
let imageData = imageToImageData(img, maxDim);
|
||||
deferred.resolve({
|
||||
data: LongStringActor(this.conn, imageData.data),
|
||||
size: imageData.size
|
||||
});
|
||||
} catch (e) {
|
||||
deferred.reject(new Error("Image " + url+ " not available"));
|
||||
}
|
||||
}
|
||||
|
||||
// If the URL doesn't point to a resource, reject
|
||||
img.onerror = () => {
|
||||
deferred.reject(new Error("Image " + url+ " not available"));
|
||||
}
|
||||
|
||||
// If the request hangs for too long, kill it to avoid queuing up other requests
|
||||
// to the same actor, except if we're running tests
|
||||
if (!DevToolsUtils.testing) {
|
||||
this.window.setTimeout(() => {
|
||||
deferred.reject(new Error("Image " + url + " could not be retrieved in time"));
|
||||
}, IMAGE_FETCHING_TIMEOUT);
|
||||
}
|
||||
|
||||
img.src = url;
|
||||
|
||||
return deferred.promise;
|
||||
// imageToImageData waits for the image to load.
|
||||
return imageToImageData(img, maxDim).then(imageData => {
|
||||
return {
|
||||
data: LongStringActor(this.conn, imageData.data),
|
||||
size: imageData.size
|
||||
};
|
||||
});
|
||||
}, {
|
||||
request: {url: Arg(0), maxDim: Arg(1, "nullable:number")},
|
||||
response: RetVal("imageData")
|
||||
@ -3922,20 +3897,84 @@ function allAnonymousContentTreeWalkerFilter(aNode) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an image DOMNode, return the image data-uri.
|
||||
* @param {DOMNode} node The image node
|
||||
* @param {Number} maxDim Optionally pass a maximum size you want the longest
|
||||
* side of the image to be resized to before getting the image data.
|
||||
* @return {Object} An object containing the data-uri and size-related information
|
||||
* {data: "...", size: {naturalWidth: 400, naturalHeight: 300, resized: true}}
|
||||
* @throws an error if the node isn't an image or if the image is missing
|
||||
* Returns a promise that is settled once the given HTMLImageElement has
|
||||
* finished loading.
|
||||
*
|
||||
* @param {HTMLImageElement} image - The image element.
|
||||
* @param {Number} timeout - Maximum amount of time the image is allowed to load
|
||||
* before the waiting is aborted. Ignored if DevToolsUtils.testing is set.
|
||||
*
|
||||
* @return {Promise} that is fulfilled once the image has loaded. If the image
|
||||
* fails to load or the load takes too long, the promise is rejected.
|
||||
*/
|
||||
function imageToImageData(node, maxDim) {
|
||||
let isImg = node.tagName.toLowerCase() === "img";
|
||||
let isCanvas = node.tagName.toLowerCase() === "canvas";
|
||||
function ensureImageLoaded(image, timeout) {
|
||||
let { HTMLImageElement } = image.ownerDocument.defaultView;
|
||||
if (!(image instanceof HTMLImageElement)) {
|
||||
return promise.reject("image must be an HTMLImageELement");
|
||||
}
|
||||
|
||||
if (image.complete) {
|
||||
// The image has already finished loading.
|
||||
return promise.resolve();
|
||||
}
|
||||
|
||||
// This image is still loading.
|
||||
let onLoad = AsyncUtils.listenOnce(image, "load");
|
||||
|
||||
// Reject if loading fails.
|
||||
let onError = AsyncUtils.listenOnce(image, "error").then(() => {
|
||||
return promise.reject("Image '" + image.src + "' failed to load.");
|
||||
});
|
||||
|
||||
// Don't timeout when testing. This is never settled.
|
||||
let onAbort = new promise(() => {});
|
||||
|
||||
if (!DevToolsUtils.testing) {
|
||||
// Tests are not running. Reject the promise after given timeout.
|
||||
onAbort = DevToolsUtils.waitForTime(timeout).then(() => {
|
||||
return promise.reject("Image '" + image.src + "' took too long to load.");
|
||||
});
|
||||
}
|
||||
|
||||
// See which happens first.
|
||||
return promise.race([onLoad, onError, onAbort]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an <img> or <canvas> element, return the image data-uri. If @param node
|
||||
* is an <img> element, the method waits a while for the image to load before
|
||||
* the data is generated. If the image does not finish loading in a reasonable
|
||||
* time (IMAGE_FETCHING_TIMEOUT milliseconds) the process aborts.
|
||||
*
|
||||
* @param {HTMLImageElement|HTMLCanvasElement} node - The <img> or <canvas>
|
||||
* element, or Image() object. Other types cause the method to reject.
|
||||
* @param {Number} maxDim - Optionally pass a maximum size you want the longest
|
||||
* side of the image to be resized to before getting the image data.
|
||||
|
||||
* @return {Promise} A promise that is fulfilled with an object containing the
|
||||
* data-uri and size-related information:
|
||||
* { data: "...",
|
||||
* size: {
|
||||
* naturalWidth: 400,
|
||||
* naturalHeight: 300,
|
||||
* resized: true }
|
||||
* }.
|
||||
*
|
||||
* If something goes wrong, the promise is rejected.
|
||||
*/
|
||||
let imageToImageData = Task.async(function* (node, maxDim) {
|
||||
let { HTMLCanvasElement, HTMLImageElement } = node.ownerDocument.defaultView;
|
||||
|
||||
let isImg = node instanceof HTMLImageElement;
|
||||
let isCanvas = node instanceof HTMLCanvasElement;
|
||||
|
||||
if (!isImg && !isCanvas) {
|
||||
return null;
|
||||
throw "node is not a <canvas> or <img> element.";
|
||||
}
|
||||
|
||||
if (isImg) {
|
||||
// Ensure that the image is ready.
|
||||
yield ensureImageLoaded(node, IMAGE_FETCHING_TIMEOUT);
|
||||
}
|
||||
|
||||
// Get the image resize ratio if a maxDim was provided
|
||||
@ -3973,7 +4012,7 @@ function imageToImageData(node, maxDim) {
|
||||
resized: resizeRatio !== 1
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
loader.lazyGetter(this, "DOMUtils", function () {
|
||||
return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
|
||||
|
@ -577,8 +577,68 @@ ObjectActor.prototype = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Helper function for onAllocationStack which fetches the source location
|
||||
* for a SavedFrame stack.
|
||||
* Handle a protocol request to get the fulfillment stack of a promise.
|
||||
*/
|
||||
onFulfillmentStack: function() {
|
||||
if (this.obj.class != "Promise") {
|
||||
return { error: "objectNotPromise",
|
||||
message: "'fulfillmentStack' request is only valid for " +
|
||||
"object grips with a 'Promise' class." };
|
||||
}
|
||||
|
||||
let rawPromise = this.obj.unsafeDereference();
|
||||
let stack = PromiseDebugging.getFullfillmentStack(rawPromise);
|
||||
let fulfillmentStacks = [];
|
||||
|
||||
while (stack) {
|
||||
if (stack.source) {
|
||||
let source = this._getSourceOriginalLocation(stack);
|
||||
|
||||
if (source) {
|
||||
fulfillmentStacks.push(source);
|
||||
}
|
||||
}
|
||||
stack = stack.parent;
|
||||
}
|
||||
|
||||
return Promise.all(fulfillmentStacks).then(stacks => {
|
||||
return { fulfillmentStack: stacks };
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle a protocol request to get the rejection stack of a promise.
|
||||
*/
|
||||
onRejectionStack: function() {
|
||||
if (this.obj.class != "Promise") {
|
||||
return { error: "objectNotPromise",
|
||||
message: "'rejectionStack' request is only valid for " +
|
||||
"object grips with a 'Promise' class." };
|
||||
}
|
||||
|
||||
let rawPromise = this.obj.unsafeDereference();
|
||||
let stack = PromiseDebugging.getRejectionStack(rawPromise);
|
||||
let rejectionStacks = [];
|
||||
|
||||
while (stack) {
|
||||
if (stack.source) {
|
||||
let source = this._getSourceOriginalLocation(stack);
|
||||
|
||||
if (source) {
|
||||
rejectionStacks.push(source);
|
||||
}
|
||||
}
|
||||
stack = stack.parent;
|
||||
}
|
||||
|
||||
return Promise.all(rejectionStacks).then(stacks => {
|
||||
return { rejectionStack: stacks };
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Helper function for fetching the source location of a SavedFrame stack.
|
||||
*
|
||||
* @param SavedFrame stack
|
||||
* The promise allocation stack frame
|
||||
* @return object
|
||||
@ -625,7 +685,9 @@ ObjectActor.prototype.requestTypes = {
|
||||
"release": ObjectActor.prototype.onRelease,
|
||||
"scope": ObjectActor.prototype.onScope,
|
||||
"dependentPromises": ObjectActor.prototype.onDependentPromises,
|
||||
"allocationStack": ObjectActor.prototype.onAllocationStack
|
||||
"allocationStack": ObjectActor.prototype.onAllocationStack,
|
||||
"fulfillmentStack": ObjectActor.prototype.onFulfillmentStack,
|
||||
"rejectionStack": ObjectActor.prototype.onRejectionStack
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -8,6 +8,7 @@ support-files =
|
||||
director-helpers.js
|
||||
hello-actor.js
|
||||
inspector_getImageData.html
|
||||
inspector-delay-image-response.sjs
|
||||
inspector-helpers.js
|
||||
inspector-styles-data.css
|
||||
inspector-styles-data.html
|
||||
@ -57,6 +58,10 @@ skip-if = buildapp == 'mulet'
|
||||
[test_inspector-dead-nodes.html]
|
||||
[test_inspector_getImageData.html]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[test_inspector_getImageDataFromURL.html]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[test_inspector_getImageData-wait-for-load.html]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[test_inspector_getNodeFromActor.html]
|
||||
[test_inspector-hide.html]
|
||||
[test_inspector-insert.html]
|
||||
|
@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Adapted from https://dxr.mozilla.org/mozilla-central/rev/
|
||||
* 4e883591bb5dff021c108d3e30198a99547eed1e/layout/reftests/backgrounds/
|
||||
* delay-image-response.sjs
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
// A 1x1 PNG image.
|
||||
// Source: https://commons.wikimedia.org/wiki/File:1x1.png (Public Domain)
|
||||
const IMAGE = atob("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAA" +
|
||||
"ACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII=");
|
||||
|
||||
// To avoid GC.
|
||||
let timer = null;
|
||||
|
||||
function handleRequest(request, response) {
|
||||
let query = {};
|
||||
request.queryString.split("&").forEach(function(val) {
|
||||
let [name, value] = val.split("=");
|
||||
query[name] = unescape(value);
|
||||
});
|
||||
|
||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||
response.setHeader("Content-Type", "image/png", false);
|
||||
|
||||
// If there is no delay, we write the image and leave.
|
||||
if (!("delay" in query)) {
|
||||
response.write(IMAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
// If there is a delay, we create a timer which, when it fires, will write
|
||||
// image and leave.
|
||||
response.processAsync();
|
||||
const nsITimer = Components.interfaces.nsITimer;
|
||||
|
||||
timer = Components.classes["@mozilla.org/timer;1"].createInstance(nsITimer);
|
||||
timer.initWithCallback(function() {
|
||||
response.write(IMAGE);
|
||||
response.finish();
|
||||
}, query.delay, nsITimer.TYPE_ONE_SHOT);
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<body>
|
||||
<img class="custom">
|
||||
<img class="big-horizontal" src="large-image.jpg" style="width:500px;" />
|
||||
<canvas class="big-vertical" style="width:500px;"></canvas>
|
||||
<img class="small" src="small-image.gif"></img>
|
||||
|
@ -0,0 +1,136 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
Tests for InspectorActor.getImageData() in following cases:
|
||||
* Image takes too long to load (the method rejects after a timeout).
|
||||
* Image is loading when the method is called and the load finishes before
|
||||
timeout.
|
||||
* Image fails to load.
|
||||
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1192536
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1192536</title>
|
||||
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
<script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
|
||||
<script type="application/javascript;version=1.8">
|
||||
|
||||
const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
|
||||
const wasTesting = DevToolsUtils.testing;
|
||||
SimpleTest.registerCleanupFunction(() => DevToolsUtils.testing = wasTesting);
|
||||
|
||||
const PATH = "http://mochi.test:8888/chrome/toolkit/devtools/server/tests/mochitest/";
|
||||
const BASE_IMAGE = PATH + "inspector-delay-image-response.sjs";
|
||||
const DELAYED_IMAGE = BASE_IMAGE + "?delay=300";
|
||||
const TIMEOUT_IMAGE = BASE_IMAGE + "?delay=50000";
|
||||
const NONEXISTENT_IMAGE = PATH + "this-does-not-exist.png";
|
||||
|
||||
window.onload = function() {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
runNextTest();
|
||||
}
|
||||
|
||||
var gImg = null;
|
||||
var gNodeFront = null;
|
||||
var gWalker = null;
|
||||
|
||||
addTest(function setup() {
|
||||
let url = document.getElementById("inspectorContent").href;
|
||||
attachURL(url, function(err, client, tab, doc) {
|
||||
let {InspectorFront} = require("devtools/server/actors/inspector");
|
||||
let inspector = InspectorFront(client, tab);
|
||||
|
||||
promiseDone(inspector.getWalker().then(walker => {
|
||||
gWalker = walker;
|
||||
return walker.querySelector(gWalker.rootNode, "img.custom").then(img => {
|
||||
gNodeFront = img;
|
||||
gImg = doc.querySelector("img.custom");
|
||||
|
||||
ok(gNodeFront, "Got the image NodeFront.");
|
||||
ok(gImg, "Got the image Node.");
|
||||
});
|
||||
}).then(runNextTest));
|
||||
});
|
||||
});
|
||||
|
||||
addTest(function testTimeout() {
|
||||
info("Testing that the method aborts if the image takes too long to load.");
|
||||
|
||||
// imageToImageData() only times out when DTU.testing is not set.
|
||||
DevToolsUtils.testing = false;
|
||||
|
||||
gImg.src = TIMEOUT_IMAGE;
|
||||
|
||||
info("Calling getImageData().");
|
||||
ensureRejects(gNodeFront.getImageData(), "Timeout image").then(runNextTest);
|
||||
});
|
||||
|
||||
addTest(function testNonExistentImage() {
|
||||
info("Testing that non-existent image causes a rejection.");
|
||||
|
||||
// This test shouldn't hit the timeout.
|
||||
DevToolsUtils.testing = true;
|
||||
|
||||
gImg.src = NONEXISTENT_IMAGE;
|
||||
|
||||
info("Calling getImageData().");
|
||||
ensureRejects(gNodeFront.getImageData(), "Non-existent image").then(runNextTest);
|
||||
});
|
||||
|
||||
addTest(function testDelayedImage() {
|
||||
info("Testing that the method waits for an image to load.");
|
||||
|
||||
// This test shouldn't hit the timeout.
|
||||
DevToolsUtils.testing = true;
|
||||
|
||||
gImg.src = DELAYED_IMAGE;
|
||||
|
||||
info("Calling getImageData().");
|
||||
checkImageData(gNodeFront.getImageData()).then(runNextTest);
|
||||
});
|
||||
|
||||
addTest(function cleanup() {
|
||||
delete gImg;
|
||||
delete gNodeFront
|
||||
delete gWalker;
|
||||
runNextTest();
|
||||
});
|
||||
|
||||
/**
|
||||
* Asserts that the given promise rejects.
|
||||
*/
|
||||
function ensureRejects(promise, desc) {
|
||||
return promise.then(() => {
|
||||
ok(false, desc + ": promise resolved unexpectedly.");
|
||||
}, () => {
|
||||
ok(true, desc + ": promise rejected as expected.");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for the call to getImageData() the resolve and checks that the image
|
||||
* size is reported correctly.
|
||||
*/
|
||||
function checkImageData(promise, { width, height } = { width: 1, height: 1 }) {
|
||||
return promise.then(({ size }) => {
|
||||
is(size.naturalWidth, width, "The width is correct.");
|
||||
is(size.naturalHeight, height, "The height is correct.");
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1192536">Mozilla Bug 1192536</a>
|
||||
<a id="inspectorContent" target="_blank" href="inspector_getImageData.html">Test Document</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -95,10 +95,27 @@ addTest(function testSmallImage() {
|
||||
});
|
||||
});
|
||||
|
||||
addTest(function testNonImgOrCanvasElements() {
|
||||
gWalker.querySelector(gWalker.rootNode, "body").then(body => {
|
||||
ensureRejects(body.getImageData(), "Invalid element").then(runNextTest);
|
||||
});
|
||||
});
|
||||
|
||||
addTest(function cleanup() {
|
||||
delete gWalker;
|
||||
runNextTest();
|
||||
});
|
||||
|
||||
/**
|
||||
* Asserts that the given promise rejects.
|
||||
*/
|
||||
function ensureRejects(promise, desc) {
|
||||
return promise.then(() => {
|
||||
ok(false, desc + ": promise resolved unexpectedly.");
|
||||
}, () => {
|
||||
ok(true, desc + ": promise rejected as expected.");
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
@ -0,0 +1,114 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
Tests for InspectorActor.getImageDataFromURL() in following cases:
|
||||
* Normal case, image loads after a small delay.
|
||||
* Image takes too long to load (the method rejects after a timeout).
|
||||
* Image fails to load.
|
||||
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1192536
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1192536</title>
|
||||
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
<script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
|
||||
<script type="application/javascript;version=1.8">
|
||||
|
||||
const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
|
||||
const wasTesting = DevToolsUtils.testing;
|
||||
SimpleTest.registerCleanupFunction(() => DevToolsUtils.testing = wasTesting);
|
||||
|
||||
const PATH = "http://mochi.test:8888/chrome/toolkit/devtools/server/tests/mochitest/";
|
||||
const BASE_IMAGE = PATH + "inspector-delay-image-response.sjs";
|
||||
const DELAYED_IMAGE = BASE_IMAGE + "?delay=300";
|
||||
const TIMEOUT_IMAGE = BASE_IMAGE + "?delay=50000";
|
||||
const NONEXISTENT_IMAGE = PATH + "this-does-not-exist.png";
|
||||
|
||||
window.onload = function() {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
runNextTest();
|
||||
}
|
||||
|
||||
var gInspector = null;
|
||||
|
||||
addTest(function setup() {
|
||||
let url = document.getElementById("inspectorContent").href;
|
||||
attachURL(url, function(err, client, tab, doc) {
|
||||
let {InspectorFront} = require("devtools/server/actors/inspector");
|
||||
gInspector = InspectorFront(client, tab);
|
||||
runNextTest();
|
||||
});
|
||||
});
|
||||
|
||||
addTest(function testTimeout() {
|
||||
info("Testing that the method aborts if the image takes too long to load.");
|
||||
|
||||
// imageToImageData() only times out when DTU.testing is not set.
|
||||
DevToolsUtils.testing = false;
|
||||
|
||||
ensureRejects(gInspector.getImageDataFromURL(TIMEOUT_IMAGE),
|
||||
"Image that loads for too long").then(runNextTest);
|
||||
});
|
||||
|
||||
addTest(function testNonExistentImage() {
|
||||
info("Testing that non-existent image causes a rejection.");
|
||||
|
||||
// This test shouldn't hit the timeout.
|
||||
DevToolsUtils.testing = true;
|
||||
|
||||
ensureRejects(gInspector.getImageDataFromURL(NONEXISTENT_IMAGE),
|
||||
"Non-existent image").then(runNextTest);
|
||||
});
|
||||
|
||||
addTest(function testNormalImage() {
|
||||
info("Testing that the method waits for an image to load.");
|
||||
|
||||
// This test shouldn't hit the timeout.
|
||||
DevToolsUtils.testing = true;
|
||||
|
||||
checkImageData(gInspector.getImageDataFromURL(DELAYED_IMAGE)).then(runNextTest);
|
||||
});
|
||||
|
||||
addTest(function cleanup() {
|
||||
delete gInspector;
|
||||
runNextTest();
|
||||
});
|
||||
|
||||
/**
|
||||
* Asserts that the given promise rejects.
|
||||
*/
|
||||
function ensureRejects(promise, desc) {
|
||||
return promise.then(() => {
|
||||
ok(false, desc + ": promise resolved unexpectedly.");
|
||||
}, () => {
|
||||
ok(true, desc + ": promise rejected as expected.");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for the call to getImageData() the resolve and checks that the image
|
||||
* size is reported correctly.
|
||||
*/
|
||||
function checkImageData(promise, { width, height } = { width: 1, height: 1 }) {
|
||||
return promise.then(({ size }) => {
|
||||
is(size.naturalWidth, width, "The width is correct.");
|
||||
is(size.naturalHeight, height, "The height is correct.");
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1192536">Mozilla Bug 1192536</a>
|
||||
<a id="inspectorContent" target="_blank" href="inspector_getImageData.html">Test Document</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -143,6 +143,13 @@ this.AppConstants = Object.freeze({
|
||||
false,
|
||||
#endif
|
||||
|
||||
MOZ_VERIFY_MAR_SIGNATURE:
|
||||
#ifdef MOZ_VERIFY_MAR_SIGNATURE
|
||||
true,
|
||||
#else
|
||||
false,
|
||||
#endif
|
||||
|
||||
MOZ_MAINTENANCE_SERVICE:
|
||||
#ifdef MOZ_MAINTENANCE_SERVICE
|
||||
true,
|
||||
|
@ -7663,9 +7663,17 @@ DirectoryInstallLocation.prototype = {
|
||||
getTrashDir: function DirInstallLocation_getTrashDir() {
|
||||
let trashDir = this._directory.clone();
|
||||
trashDir.append(DIR_TRASH);
|
||||
if (trashDir.exists())
|
||||
recursiveRemove(trashDir);
|
||||
trashDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
|
||||
let trashDirExists = trashDir.exists();
|
||||
try {
|
||||
if (trashDirExists)
|
||||
recursiveRemove(trashDir);
|
||||
trashDirExists = false;
|
||||
} catch (e) {
|
||||
logger.warn("Failed to remove trash directory", e);
|
||||
}
|
||||
if (!trashDirExists)
|
||||
trashDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
|
||||
|
||||
return trashDir;
|
||||
},
|
||||
|
||||
|
35
toolkit/mozapps/extensions/test/xpcshell/test_bug1180901.js
Normal file
@ -0,0 +1,35 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
function run_test() {
|
||||
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
|
||||
startupManager();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
let profileDir = OS.Constants.Path.profileDir;
|
||||
let trashDir = OS.Path.join(profileDir, "extensions", "trash");
|
||||
let testFile = OS.Path.join(trashDir, "test.txt");
|
||||
|
||||
yield OS.File.makeDir(trashDir, {
|
||||
from: profileDir,
|
||||
ignoreExisting: true
|
||||
});
|
||||
|
||||
let trashDirExists = yield OS.File.exists(trashDir);
|
||||
ok(trashDirExists, "trash directory should have been created");
|
||||
|
||||
let file = yield OS.File.open(testFile, {create: true}, {winShare: 0});
|
||||
let fileExists = yield OS.File.exists(testFile);
|
||||
ok(fileExists, "test.txt should have been created in " + trashDir);
|
||||
|
||||
yield promiseInstallAllFiles([do_get_addon("test_install1")]);
|
||||
yield promiseRestartManager();
|
||||
fileExists = yield OS.File.exists(testFile);
|
||||
ok(fileExists, "test.txt still exists");
|
||||
yield file.close();
|
||||
yield OS.File.removeDir(OS.Path.join(OS.Constants.Path.profileDir, "extensions"));
|
||||
yield promiseShutdownManager();
|
||||
});
|
@ -288,3 +288,5 @@ run-sequentially = Uses global XCurProcD dir.
|
||||
[test_sourceURI.js]
|
||||
[test_webextension.js]
|
||||
[test_bootstrap_globals.js]
|
||||
[test_bug1180901.js]
|
||||
skip-if = os != "win"
|
||||
|
@ -28,4 +28,5 @@ skip-if = appname != "firefox"
|
||||
[test_XPIStates.js]
|
||||
|
||||
|
||||
|
||||
[include:xpcshell-shared.ini]
|
||||
|
@ -3872,6 +3872,13 @@ Downloader.prototype = {
|
||||
}
|
||||
|
||||
LOG("Downloader:_verifyDownload downloaded size == expected size.");
|
||||
|
||||
// The hash check is not necessary when mar signatures are used to verify
|
||||
// the downloaded mar file.
|
||||
if (AppConstants.MOZ_VERIFY_MAR_SIGNATURE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let fileStream = Cc["@mozilla.org/network/file-input-stream;1"].
|
||||
createInstance(Ci.nsIFileInputStream);
|
||||
fileStream.init(destination, FileUtils.MODE_RDONLY, FileUtils.PERMS_FILE, 0);
|
||||
|
@ -8,7 +8,7 @@
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
|
||||
|
||||
<window title="Update Wizard pages: update check, basic, download, and errors (partial patch with an invalid hash)"
|
||||
<window title="Update Wizard pages: update check, basic, download, and errors (partial patch with an invalid size)"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
onload="runTestDefault();">
|
||||
<script type="application/javascript"
|
||||
@ -35,7 +35,7 @@ function runTest() {
|
||||
debugDump("entering");
|
||||
|
||||
let url = URL_HTTP_UPDATE_XML + "?showDetails=1&partialPatchOnly=1" +
|
||||
"&invalidPartialHash=1" + getVersionParams();
|
||||
"&invalidPartialSize=1" + getVersionParams();
|
||||
setUpdateURLOverride(url);
|
||||
|
||||
gUP.checkForUpdates();
|
||||
|
@ -8,7 +8,7 @@
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
|
||||
|
||||
<window title="Update Wizard pages: update check, basic, download, and errors (complete patch with an invalid hash)"
|
||||
<window title="Update Wizard pages: update check, basic, download, and errors (complete patch with an invalid size)"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
onload="runTestDefault();">
|
||||
<script type="application/javascript"
|
||||
@ -35,7 +35,7 @@ function runTest() {
|
||||
debugDump("entering");
|
||||
|
||||
let url = URL_HTTP_UPDATE_XML + "?showDetails=1&completePatchOnly=1" +
|
||||
"&invalidCompleteHash=1" + getVersionParams();
|
||||
"&invalidCompleteSize=1" + getVersionParams();
|
||||
setUpdateURLOverride(url);
|
||||
|
||||
gUP.checkForUpdates();
|
||||
|
@ -8,7 +8,7 @@
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
|
||||
|
||||
<window title="Update Wizard pages: update check, basic, download, and errors (partial and complete patches with invalid hashes)"
|
||||
<window title="Update Wizard pages: update check, basic, download, and errors (partial and complete patches with invalid sizes)"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
onload="runTestDefault();">
|
||||
<script type="application/javascript"
|
||||
@ -34,8 +34,8 @@ const TESTS = [ {
|
||||
function runTest() {
|
||||
debugDump("entering");
|
||||
|
||||
let url = URL_HTTP_UPDATE_XML + "?showDetails=1&invalidPartialHash=1" +
|
||||
"&invalidCompleteHash=1" + getVersionParams();
|
||||
let url = URL_HTTP_UPDATE_XML + "?showDetails=1&invalidPartialSize=1" +
|
||||
"&invalidCompleteSize=1" + getVersionParams();
|
||||
setUpdateURLOverride(url);
|
||||
|
||||
gUP.checkForUpdates();
|
||||
|
@ -8,7 +8,7 @@
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
|
||||
|
||||
<window title="Update Wizard pages: update check, basic, download, and finished (partial patch with an invalid hash and successful complete patch)"
|
||||
<window title="Update Wizard pages: update check, basic, download, and finished (partial patch with an invalid size and successful complete patch)"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
onload="runTestDefault();">
|
||||
<script type="application/javascript"
|
||||
@ -34,7 +34,7 @@ const TESTS = [ {
|
||||
function runTest() {
|
||||
debugDump("entering");
|
||||
|
||||
let url = URL_HTTP_UPDATE_XML + "?showDetails=1&invalidPartialHash=1" +
|
||||
let url = URL_HTTP_UPDATE_XML + "?showDetails=1&invalidPartialSize=1" +
|
||||
getVersionParams();
|
||||
setUpdateURLOverride(url);
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
|
||||
|
||||
<window title="Update Wizard pages: errors (partial patch with an invalid hash)"
|
||||
<window title="Update Wizard pages: errors (partial patch with an invalid size)"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
onload="runTestDefault();">
|
||||
<script type="application/javascript"
|
||||
@ -27,7 +27,7 @@ const TESTS = [ {
|
||||
function runTest() {
|
||||
debugDump("entering");
|
||||
|
||||
let patches = getLocalPatchString("partial", null, null, "1234", null, null,
|
||||
let patches = getLocalPatchString("partial", null, null, null, "1234", null,
|
||||
STATE_DOWNLOADING);
|
||||
let updates = getLocalUpdateString(patches, null, null, null,
|
||||
Services.appinfo.version,
|
||||
|
@ -8,7 +8,7 @@
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
|
||||
|
||||
<window title="Update Wizard pages: errors (complete patch with an invalid hash)"
|
||||
<window title="Update Wizard pages: errors (complete patch with an invalid size)"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
onload="runTestDefault();">
|
||||
<script type="application/javascript"
|
||||
@ -27,7 +27,7 @@ const TESTS = [ {
|
||||
function runTest() {
|
||||
debugDump("entering");
|
||||
|
||||
let patches = getLocalPatchString("complete", null, null, "1234", null, null,
|
||||
let patches = getLocalPatchString("complete", null, null, null, "1234", null,
|
||||
STATE_DOWNLOADING);
|
||||
let updates = getLocalUpdateString(patches, null, null, null,
|
||||
Services.appinfo.version,
|
||||
|
@ -8,7 +8,7 @@
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
|
||||
|
||||
<window title="Update Wizard pages: errors (partial and complete patches with invalid hashes)"
|
||||
<window title="Update Wizard pages: errors (partial and complete patches with invalid sizes)"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
onload="runTestDefault();">
|
||||
<script type="application/javascript"
|
||||
@ -27,9 +27,9 @@ const TESTS = [ {
|
||||
function runTest() {
|
||||
debugDump("entering");
|
||||
|
||||
let patches = getLocalPatchString("partial", null, null, "1234", null, null,
|
||||
let patches = getLocalPatchString("partial", null, null, null, "1234", null,
|
||||
STATE_DOWNLOADING) +
|
||||
getLocalPatchString("complete", null, null, "1234", null,
|
||||
getLocalPatchString("complete", null, null, null, "1234",
|
||||
"false");
|
||||
let updates = getLocalUpdateString(patches, null, null, null,
|
||||
Services.appinfo.version,
|
||||
|
@ -8,7 +8,7 @@
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
|
||||
|
||||
<window title="Update Wizard pages: finishedBackground (partial patch with an invalid hash and successful complete patch)"
|
||||
<window title="Update Wizard pages: finishedBackground (partial patch with an invalid size and successful complete patch)"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
onload="runTestDefault();">
|
||||
<script type="application/javascript"
|
||||
@ -27,7 +27,7 @@ const TESTS = [ {
|
||||
function runTest() {
|
||||
debugDump("entering");
|
||||
|
||||
let patches = getLocalPatchString("partial", null, null, "1234", null, null,
|
||||
let patches = getLocalPatchString("partial", null, null, null, "1234", null,
|
||||
STATE_DOWNLOADING) +
|
||||
getLocalPatchString("complete", null, null, null, null,
|
||||
"false");
|
||||
|
@ -42,7 +42,7 @@ function runTest() {
|
||||
let patches = getLocalPatchString("partial", null, null, null, null, null,
|
||||
STATE_PENDING) +
|
||||
getLocalPatchString("complete", slowDownloadURL, "MD5",
|
||||
"1234cd43a1c77e30191c53a329a3f99d", null,
|
||||
null, "1234",
|
||||
"false");
|
||||
let updates = getLocalUpdateString(patches, null, null, null,
|
||||
Services.appinfo.version,
|
||||
|
@ -112,18 +112,18 @@ function handleRequest(aRequest, aResponse) {
|
||||
return;
|
||||
}
|
||||
|
||||
let hash;
|
||||
let size;
|
||||
let patches = "";
|
||||
if (!params.partialPatchOnly) {
|
||||
hash = SHA512_HASH_SIMPLE_MAR + (params.invalidCompleteHash ? "e" : "");
|
||||
size = SIZE_SIMPLE_MAR + (params.invalidCompleteSize ? "1" : "");
|
||||
patches += getRemotePatchString("complete", SERVICE_URL, "SHA512",
|
||||
hash, SIZE_SIMPLE_MAR);
|
||||
SHA512_HASH_SIMPLE_MAR, size);
|
||||
}
|
||||
|
||||
if (!params.completePatchOnly) {
|
||||
hash = SHA512_HASH_SIMPLE_MAR + (params.invalidPartialHash ? "e" : "");
|
||||
size = SIZE_SIMPLE_MAR + (params.invalidPartialSize ? "1" : "");
|
||||
patches += getRemotePatchString("partial", SERVICE_URL, "SHA512",
|
||||
hash, SIZE_SIMPLE_MAR);
|
||||
SHA512_HASH_SIMPLE_MAR, size);
|
||||
}
|
||||
|
||||
let type = params.type ? params.type : "major";
|
||||
|
@ -23,7 +23,13 @@ function run_test() {
|
||||
// The mock XMLHttpRequest is MUCH faster
|
||||
overrideXHR(callHandleEvent);
|
||||
standardInit();
|
||||
do_execute_soon(run_test_pt1);
|
||||
// Only perform the non hash check tests when mar signing is enabled since the
|
||||
// update service doesn't perform hash checks when mar signing is enabled.
|
||||
if (IS_MAR_CHECKS_ENABLED) {
|
||||
do_execute_soon(run_test_pt11);
|
||||
} else {
|
||||
do_execute_soon(run_test_pt1);
|
||||
}
|
||||
}
|
||||
|
||||
// The HttpServer must be stopped before calling do_test_finished
|
||||
|