Merge m-c to inbound on a CLOSED TREE.

This commit is contained in:
Ryan VanderMeulen 2014-03-11 13:25:49 -07:00
Родитель 95184620a9 563ac4400b
Коммит 969b5f09c4
123 изменённых файлов: 4592 добавлений и 1659 удалений

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

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="a351fe62c11737c722ad33aaff438f6ccd00bd4a"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="3005269d4dcabcc7d27eaf72bda44a969873af8c"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="353d422fcbd0b41b76e1262f0992a832420a7567"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d11f524d00cacf5ba0dfbf25e4aa2158b1c3a036"/>

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

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="a351fe62c11737c722ad33aaff438f6ccd00bd4a"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="3005269d4dcabcc7d27eaf72bda44a969873af8c"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="353d422fcbd0b41b76e1262f0992a832420a7567"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="cf1dcc0704c0c1845f8a0a0b44838f7e0c0362c9"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>

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

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="a9e08b91e9cd1f0930f16cfc49ec72f63575d5fe">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="a351fe62c11737c722ad33aaff438f6ccd00bd4a"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="3005269d4dcabcc7d27eaf72bda44a969873af8c"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="353d422fcbd0b41b76e1262f0992a832420a7567"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="cf1dcc0704c0c1845f8a0a0b44838f7e0c0362c9"/>

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

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="a351fe62c11737c722ad33aaff438f6ccd00bd4a"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="3005269d4dcabcc7d27eaf72bda44a969873af8c"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="353d422fcbd0b41b76e1262f0992a832420a7567"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d11f524d00cacf5ba0dfbf25e4aa2158b1c3a036"/>

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

@ -4,6 +4,6 @@
"branch": "",
"revision": ""
},
"revision": "c5bd933fe99317a7e99822f2d9345ae67a3043df",
"revision": "93de9c5bcb90ae32bd3599b185f4bbf67e4529ff",
"repo_path": "/integration/gaia-central"
}

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

@ -17,7 +17,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="a351fe62c11737c722ad33aaff438f6ccd00bd4a"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="3005269d4dcabcc7d27eaf72bda44a969873af8c"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="353d422fcbd0b41b76e1262f0992a832420a7567"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>

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

@ -15,7 +15,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="a351fe62c11737c722ad33aaff438f6ccd00bd4a"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="3005269d4dcabcc7d27eaf72bda44a969873af8c"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="353d422fcbd0b41b76e1262f0992a832420a7567"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>

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

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="a351fe62c11737c722ad33aaff438f6ccd00bd4a"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="3005269d4dcabcc7d27eaf72bda44a969873af8c"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="353d422fcbd0b41b76e1262f0992a832420a7567"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>

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

@ -17,7 +17,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="a351fe62c11737c722ad33aaff438f6ccd00bd4a"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="3005269d4dcabcc7d27eaf72bda44a969873af8c"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="353d422fcbd0b41b76e1262f0992a832420a7567"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>

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

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="a351fe62c11737c722ad33aaff438f6ccd00bd4a"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="3005269d4dcabcc7d27eaf72bda44a969873af8c"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="353d422fcbd0b41b76e1262f0992a832420a7567"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="cf1dcc0704c0c1845f8a0a0b44838f7e0c0362c9"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>

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

@ -17,7 +17,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="a351fe62c11737c722ad33aaff438f6ccd00bd4a"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="3005269d4dcabcc7d27eaf72bda44a969873af8c"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="353d422fcbd0b41b76e1262f0992a832420a7567"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>

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

@ -9,9 +9,17 @@ const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/FxAccounts.jsm");
let fxAccountsCommon = {};
Cu.import("resource://gre/modules/FxAccountsCommon.js", fxAccountsCommon);
const PREF_LAST_FXA_USER = "identity.fxaccounts.lastSignedInUserHash";
const PREF_SYNC_SHOW_CUSTOMIZATION = "services.sync.ui.showCustomizationDialog";
const OBSERVER_TOPICS = [
fxAccountsCommon.ONVERIFIED_NOTIFICATION,
fxAccountsCommon.ONLOGOUT_NOTIFICATION,
];
function log(msg) {
//dump("FXA: " + msg + "\n");
};
@ -150,6 +158,11 @@ let wrapper = {
xps.whenLoaded().then(() => {
return fxAccounts.setSignedInUser(accountData);
}).then(() => {
// If the user data is verified, we want it to immediately look like
// they are signed in without waiting for messages to bounce around.
if (accountData.verified) {
showManage();
}
this.injectData("message", { status: "login" });
// until we sort out a better UX, just leave the jelly page in place.
// If the account email is not yet verified, it will tell the user to
@ -249,23 +262,36 @@ function openPrefs() {
}
function init() {
if (window.location.href.contains("action=signin")) {
show("remote");
wrapper.init(fxAccounts.getAccountsSignInURI());
} else if (window.location.href.contains("action=signup")) {
show("remote");
wrapper.init();
} else if (window.location.href.contains("action=reauth")) {
fxAccounts.promiseAccountsForceSigninURI().then(url => {
show("remote");
wrapper.init(url);
});
} else {
// Check if we have a local account
fxAccounts.getSignedInUser().then(user => {
fxAccounts.getSignedInUser().then(user => {
if (window.location.href.contains("action=signin")) {
if (user) {
show("stage");
show("manage");
// asking to sign-in when already signed in just shows manage.
showManage();
} else {
show("remote");
wrapper.init(fxAccounts.getAccountsSignInURI());
}
} else if (window.location.href.contains("action=signup")) {
if (user) {
// asking to sign-up when already signed in just shows manage.
showManage();
} else {
show("remote");
wrapper.init();
}
} else if (window.location.href.contains("action=reauth")) {
// ideally we would only show this when we know the user is in a
// "must reauthenticate" state - but we don't.
// As the email address will be included in the URL returned from
// promiseAccountsForceSigninURI, just always show it.
fxAccounts.promiseAccountsForceSigninURI().then(url => {
show("remote");
wrapper.init(url);
});
} else {
// No action specified
if (user) {
showManage();
let sb = Services.strings.createBundle("chrome://browser/locale/syncSetup.properties");
document.title = sb.GetStringFromName("manage.pageTitle");
} else {
@ -274,8 +300,8 @@ function init() {
// load the remote frame in the background
wrapper.init();
}
});
}
}
});
}
function show(id) {
@ -285,7 +311,38 @@ function hide(id) {
document.getElementById(id).style.display = 'none';
}
function showManage() {
show("stage");
show("manage");
hide("remote");
hide("intro");
}
document.addEventListener("DOMContentLoaded", function onload() {
document.removeEventListener("DOMContentLoaded", onload, true);
init();
}, true);
function initObservers() {
function observe(subject, topic, data) {
log("about:accounts observed " + topic);
if (topic == fxAccountsCommon.ONLOGOUT_NOTIFICATION) {
// All about:account windows get changed to action=signin on logout.
window.location = "about:accounts?action=signin";
return;
}
// must be onverified - just about:accounts is loaded.
window.location = "about:accounts";
}
for (let topic of OBSERVER_TOPICS) {
Services.obs.addObserver(observe, topic, false);
}
window.addEventListener("unload", function(event) {
log("about:accounts unloading")
for (let topic of OBSERVER_TOPICS) {
Services.obs.removeObserver(observe, topic);
}
});
}
initObservers();

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

@ -24,6 +24,7 @@ let gFxAccounts = {
// Do all this dance to lazy-load FxAccountsCommon.
delete this.topics;
return this.topics = [
"weave:service:ready",
"weave:service:sync:start",
"weave:service:login:error",
FxAccountsCommon.ONLOGIN_NOTIFICATION,

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

@ -901,7 +901,7 @@ SocialSidebar = {
let popup = event.target;
let providerMenuSeps = popup.getElementsByClassName("social-provider-menu");
if (providerMenuSeps[0].previousSibling.nodeName == "menuseparator")
SocialSidebar._populateProviderMenu(providerMenuSeps[0]);
SocialSidebar.populateProviderMenu(providerMenuSeps[0]);
},
clearProviderMenus: function() {
@ -916,7 +916,7 @@ SocialSidebar = {
}
},
_populateProviderMenu: function(providerMenuSep) {
populateProviderMenu: function(providerMenuSep) {
let menu = providerMenuSep.parentNode;
// selectable providers are inserted before the provider-menu seperator,
// remove any menuitems in that area

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

@ -768,10 +768,6 @@ toolbarbutton[type="badged"] {
-moz-binding: url("chrome://browser/content/urlbarBindings.xml#toolbarbutton-badged");
}
toolbarpaletteitem[place="palette"] > toolbarbutton[type="badged"] > .toolbarbutton-badge-container {
padding: 5px;
}
toolbarbutton[type="socialmark"] {
-moz-binding: url("chrome://browser/content/socialmarks.xml#toolbarbutton-marks");
}
@ -779,11 +775,9 @@ toolbarbutton[type="socialmark"] {
toolbarbutton[type="badged"] > .toolbarbutton-badge-container > .toolbarbutton-icon,
toolbarbutton[type="socialmark"] > .toolbarbutton-icon {
max-width: 16px;
max-height: 16px;
}
toolbarpaletteitem[place="palette"] > toolbarbutton[type="badged"] > .toolbarbutton-badge-container > .toolbarbutton-icon {
max-width: 32px;
max-height: 32px;
}
panelview > .social-panel-frame {

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

@ -1166,7 +1166,7 @@
#ifndef XP_UNIX
<svg:clipPath id="windows-keyhole-forward-clip-path" clipPathUnits="objectBoundingBox">
<svg:path d="M 0,0 C 0.16,0.11 0.28,0.29 0.28,0.5 0.28,0.71 0.16,0.89 0,1 L 1,1 1,0 0,0 z"/>
<svg:path d="m 0,0 c .3,.25 .3,.75, 0,1 l 1,0 0,-1 z"/>
</svg:clipPath>
<svg:clipPath id="windows-urlbar-back-button-clip-path" clipPathUnits="userSpaceOnUse">
<svg:path d="m 0,-5 l 0,7.8 c 2.5,3.2 4,6.2 4,10.2 c 0,4 -1.5,7 -4,10 l 0,22l10000,0 l 0,-50 l -10000,0 z"/>

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

@ -12,7 +12,9 @@
<xul:panel anonid="panel" hidden="true" type="arrow" class="social-panel"/>
<xul:image class="toolbarbutton-icon" xbl:inherits="validate,src=image,label"/>
<xul:label class="toolbarbutton-text" crop="right" flex="1"
xbl:inherits="value=label,accesskey,crop"/>
xbl:inherits="value=label,accesskey,crop,wrap"/>
<xul:label class="toolbarbutton-multiline-text" flex="1"
xbl:inherits="xbl:text=label,accesskey,wrap"/>
</content>
<implementation implements="nsIDOMEventListener, nsIObserver">
<field name="inMenuPanel">false</field>

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

@ -83,6 +83,8 @@ support-files =
skip-if = (os == "linux" || os == "mac") && debug # bug 970052, bug 970053
[browser_aboutAccounts.js]
skip-if = os == "linux" # Bug 958026
support-files =
content_aboutAccounts.js
[browser_aboutHealthReport.js]
skip-if = os == "linux" # Bug 924307
[browser_aboutHome.js]

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

@ -9,22 +9,35 @@ XPCOMUtils.defineLazyModuleGetter(this, "Task",
XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts",
"resource://gre/modules/FxAccounts.jsm");
const CHROME_BASE = "chrome://mochitests/content/browser/browser/base/content/test/general/";
// Preference helpers.
let changedPrefs = new Set();
function setPref(name, value) {
changedPrefs.add(name);
Services.prefs.setCharPref(name, value);
}
registerCleanupFunction(function() {
// Ensure we don't pollute prefs for next tests.
Services.prefs.clearUserPref("identity.fxaccounts.remote.uri");
for (let name of changedPrefs) {
Services.prefs.clearUserPref(name);
}
});
let gTests = [
{
desc: "Test the remote commands",
setup: function ()
{
Services.prefs.setCharPref("identity.fxaccounts.remote.uri",
"https://example.com/browser/browser/base/content/test/general/accounts_testRemoteCommands.html");
teardown: function* () {
gBrowser.removeCurrentTab();
yield fxAccounts.signOut();
},
run: function ()
run: function* ()
{
setPref("identity.fxaccounts.remote.uri",
"https://example.com/browser/browser/base/content/test/general/accounts_testRemoteCommands.html");
yield promiseNewTabLoadEvent("about:accounts");
let deferred = Promise.defer();
let results = 0;
@ -47,11 +60,135 @@ let gTests = [
ok(false, "Failed to get all commands");
deferred.reject();
}
return deferred.promise.then(() => fxAccounts.signOut());
yield deferred.promise;
}
},
{
desc: "Test action=signin - no user logged in",
teardown: () => gBrowser.removeCurrentTab(),
run: function* ()
{
// When this loads with no user logged-in, we expect the "normal" URL
const expected_url = "https://example.com/?is_sign_in";
setPref("identity.fxaccounts.remote.signin.uri", expected_url);
let [tab, url] = yield promiseNewTabWithIframeLoadEvent("about:accounts?action=signin");
is(url, expected_url, "action=signin got the expected URL");
// we expect the remote iframe to be shown.
yield checkVisibilities(tab, {
stage: false, // parent of 'manage' and 'intro'
manage: false,
intro: false, // this is "get started"
remote: true
});
}
},
{
desc: "Test action=signin - user logged in",
teardown: function* () {
gBrowser.removeCurrentTab();
yield signOut();
},
run: function* ()
{
// When this loads with a user logged-in, we expect the normal URL to
// have been ignored and the "manage" page to be shown.
const expected_url = "https://example.com/?is_sign_in";
setPref("identity.fxaccounts.remote.signin.uri", expected_url);
yield setSignedInUser();
let tab = yield promiseNewTabLoadEvent("about:accounts?action=signin");
// about:accounts initializes after fetching the current user from Fxa -
// so we also request it - by the time we get it we know it should have
// done its thing.
yield fxAccounts.getSignedInUser();
// we expect "manage" to be shown.
yield checkVisibilities(tab, {
stage: true, // parent of 'manage' and 'intro'
manage: true,
intro: false, // this is "get started"
remote: false
});
}
},
{
desc: "Test action=signup - no user logged in",
teardown: () => gBrowser.removeCurrentTab(),
run: function* ()
{
const expected_url = "https://example.com/?is_sign_up";
setPref("identity.fxaccounts.remote.uri", expected_url);
let [tab, url] = yield promiseNewTabWithIframeLoadEvent("about:accounts?action=signup");
is(url, expected_url, "action=signup got the expected URL");
// we expect the remote iframe to be shown.
yield checkVisibilities(tab, {
stage: false, // parent of 'manage' and 'intro'
manage: false,
intro: false, // this is "get started"
remote: true
});
},
},
{
desc: "Test action=signup - user logged in",
teardown: () => gBrowser.removeCurrentTab(),
run: function* ()
{
const expected_url = "https://example.com/?is_sign_up";
setPref("identity.fxaccounts.remote.uri", expected_url);
yield setSignedInUser();
let tab = yield promiseNewTabLoadEvent("about:accounts?action=signup");
yield fxAccounts.getSignedInUser();
// we expect "manage" to be shown.
yield checkVisibilities(tab, {
stage: true, // parent of 'manage' and 'intro'
manage: true,
intro: false, // this is "get started"
remote: false
});
},
},
{
desc: "Test action=reauth",
teardown: function* () {
gBrowser.removeCurrentTab();
yield signOut();
},
run: function* ()
{
const expected_url = "https://example.com/?is_force_auth";
setPref("identity.fxaccounts.remote.force_auth.uri", expected_url);
let userData = {
email: "foo@example.com",
uid: "1234@lcip.org",
assertion: "foobar",
sessionToken: "dead",
kA: "beef",
kB: "cafe",
verified: true
};
yield setSignedInUser();
let [tab, url] = yield promiseNewTabWithIframeLoadEvent("about:accounts?action=reauth");
// The current user will be appended to the url
let expected = expected_url + "&email=foo%40example.com";
is(url, expected, "action=reauth got the expected URL");
},
},
{
desc: "Test observers about:accounts",
teardown: function() {
gBrowser.removeCurrentTab();
},
run: function* () {
setPref("identity.fxaccounts.remote.uri", "https://example.com/");
yield setSignedInUser();
let tab = yield promiseNewTabLoadEvent("about:accounts");
// sign the user out - the tab should have action=signin
yield signOut();
// wait for the new load.
yield promiseOneMessage(tab, "test:document:load");
is(tab.linkedBrowser.contentDocument.location.href, "about:accounts?action=signin");
}
},
]; // gTests
function test()
@ -61,32 +198,91 @@ function test()
Task.spawn(function () {
for (let test of gTests) {
info(test.desc);
test.setup();
yield promiseNewTabLoadEvent("about:accounts");
yield test.run();
gBrowser.removeCurrentTab();
try {
yield test.run();
} finally {
yield test.teardown();
}
}
finish();
});
}
function promiseOneMessage(tab, messageName) {
let mm = tab.linkedBrowser.messageManager;
let deferred = Promise.defer();
mm.addMessageListener(messageName, function onmessage(message) {
mm.removeMessageListener(messageName, onmessage);
deferred.resolve(message);
});
return deferred.promise;
}
function promiseNewTabLoadEvent(aUrl)
{
let tab = gBrowser.selectedTab = gBrowser.addTab(aUrl);
let browser = tab.linkedBrowser;
let mm = browser.messageManager;
// give it an e10s-friendly content script to help with our tests.
mm.loadFrameScript(CHROME_BASE + "content_aboutAccounts.js", true);
// and wait for it to tell us about the load.
return promiseOneMessage(tab, "test:document:load").then(
() => tab
);
}
// Returns a promise which is resolved with the iframe's URL after a new
// tab is created and the iframe in that tab loads.
function promiseNewTabWithIframeLoadEvent(aUrl) {
let deferred = Promise.defer();
let tab = gBrowser.selectedTab = gBrowser.addTab(aUrl);
let browser = tab.linkedBrowser;
let mm = browser.messageManager;
browser.addEventListener("load", function onLoad(event) {
let iframe = browser.contentDocument.getElementById("remote");
if (iframe && event.target == iframe.contentDocument) {
browser.removeEventListener("load", onLoad, true);
deferred.resolve();
}
}, true);
// give it an e10s-friendly content script to help with our tests.
mm.loadFrameScript(CHROME_BASE + "content_aboutAccounts.js", true);
// and wait for it to tell us about the iframe load.
mm.addMessageListener("test:iframe:load", function onFrameLoad(message) {
mm.removeMessageListener("test:iframe:load", onFrameLoad);
deferred.resolve([tab, message.data.url]);
});
return deferred.promise;
}
function checkVisibilities(tab, data) {
let ids = Object.keys(data);
let mm = tab.linkedBrowser.messageManager;
let deferred = Promise.defer();
mm.addMessageListener("test:check-visibilities-response", function onResponse(message) {
mm.removeMessageListener("test:check-visibilities-response", onResponse);
for (let id of ids) {
is(message.data[id], data[id], "Element '" + id + "' has correct visibility");
}
deferred.resolve();
});
mm.sendAsyncMessage("test:check-visibilities", {ids: ids});
return deferred.promise;
}
// watch out - these will fire observers which if you aren't careful, may
// interfere with the tests.
function setSignedInUser(data) {
if (!data) {
data = {
email: "foo@example.com",
uid: "1234@lcip.org",
assertion: "foobar",
sessionToken: "dead",
kA: "beef",
kB: "cafe",
verified: true
}
}
return fxAccounts.setSignedInUser(data);
}
function signOut() {
return fxAccounts.signOut();
}

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

@ -0,0 +1,48 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// This file is loaded as a "content script" for browser_aboutAccounts tests
"use strict";
addEventListener("load", function load(event) {
if (event.target != content.document) {
return;
}
// content.document.removeEventListener("load", load, true);
sendAsyncMessage("test:document:load");
}, true);
addEventListener("DOMContentLoaded", function domContentLoaded(event) {
removeEventListener("DOMContentLoaded", domContentLoaded, true);
let iframe = content.document.getElementById("remote");
iframe.addEventListener("load", function iframeLoaded(event) {
if (iframe.contentWindow.location.href == "about:blank" ||
event.target != iframe) {
return;
}
iframe.removeEventListener("load", iframeLoaded, true);
sendAsyncMessage("test:iframe:load", {url: iframe.getAttribute("src")});
}, true);
}, true);
// Return the visibility state of a list of ids.
addMessageListener("test:check-visibilities", function (message) {
let result = {};
for (let id of message.data.ids) {
let elt = content.document.getElementById(id);
if (elt) {
let displayStyle = content.window.getComputedStyle(elt).display;
if (displayStyle == 'none') {
result[id] = false;
} else if (displayStyle == 'block') {
result[id] = true;
} else {
result[id] = "strange: " + displayStyle; // tests should fail!
}
} else {
result[id] = "doesn't exist: " + id;
}
}
sendAsyncMessage("test:check-visibilities-response", result);
});

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

@ -2196,12 +2196,14 @@
extends="chrome://global/content/bindings/toolbarbutton.xml#toolbarbutton">
<content>
<children includes="observes|template|menupopup|panel|tooltip"/>
<xul:hbox class="toolbarbutton-badge-container" align="start" pack="end" flex="1">
<xul:hbox class="toolbarbutton-badge-container" align="start" pack="end">
<xul:hbox class="toolbarbutton-badge" xbl:inherits="badge"/>
<xul:image class="toolbarbutton-icon" xbl:inherits="validate,src=image,label"/>
</xul:hbox>
<xul:label class="toolbarbutton-text" crop="right" flex="1"
xbl:inherits="value=label,accesskey,crop"/>
xbl:inherits="value=label,accesskey,crop,wrap"/>
<xul:label class="toolbarbutton-multiline-text" flex="1"
xbl:inherits="xbl:text=label,accesskey,wrap"/>
</content>
</binding>

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

@ -153,6 +153,11 @@
<vbox id="PanelUI-developerItems" class="panel-subview-body"/>
</panelview>
<panelview id="PanelUI-sidebar" flex="1">
<label value="&appMenuSidebars.label;" class="panel-subview-header"/>
<vbox id="PanelUI-sidebarItems" class="panel-subview-body"/>
</panelview>
<panelview id="PanelUI-characterEncodingView" flex="1">
<label value="&charsetMenu.label;" class="panel-subview-header"/>
<vbox class="panel-subview-body">

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

@ -320,6 +320,74 @@ const CustomizableWidgets = [{
parent.appendChild(items);
}
}, {
id: "sidebar-button",
type: "view",
viewId: "PanelUI-sidebar",
onViewShowing: function(aEvent) {
// Largely duplicated from the developer-button above with a couple minor
// alterations.
// Populate the subview with whatever menuitems are in the
// sidebar menu. We skip menu elements, because the menu panel has no way
// of dealing with those right now.
let doc = aEvent.target.ownerDocument;
let win = doc.defaultView;
let items = doc.getElementById("PanelUI-sidebarItems");
let menu = doc.getElementById("viewSidebarMenu");
// First clear any existing menuitems then populate. Social sidebar
// options may not have been added yet, so we do that here. Add it to the
// standard menu first, then copy all sidebar options to the panel.
win.SocialSidebar.clearProviderMenus();
let providerMenuSeps = menu.getElementsByClassName("social-provider-menu");
if (providerMenuSeps.length > 0)
win.SocialSidebar.populateProviderMenu(providerMenuSeps[0]);
let attrs = ["oncommand", "onclick", "label", "key", "disabled",
"command", "observes", "hidden", "class", "origin",
"image", "checked"];
let fragment = doc.createDocumentFragment();
let itemsToDisplay = [...menu.children];
for (let node of itemsToDisplay) {
if (node.hidden)
continue;
let item;
if (node.localName == "menuseparator") {
item = doc.createElementNS(kNSXUL, "menuseparator");
} else if (node.localName == "menuitem") {
item = doc.createElementNS(kNSXUL, "toolbarbutton");
} else {
continue;
}
for (let attr of attrs) {
let attrVal = node.getAttribute(attr);
if (attrVal)
item.setAttribute(attr, attrVal);
}
if (node.localName == "menuitem")
item.classList.add("subviewbutton");
fragment.appendChild(item);
}
items.appendChild(fragment);
},
onViewHiding: function(aEvent) {
let doc = aEvent.target.ownerDocument;
let items = doc.getElementById("PanelUI-sidebarItems");
let parent = items.parentNode;
// We'll take the container out of the document before cleaning it out
// to avoid reflowing each time we remove something.
parent.removeChild(items);
while (items.firstChild) {
items.firstChild.remove();
}
parent.appendChild(items);
}
}, {
id: "add-ons-button",
shortcutId: "key_openAddons",
tooltiptext: "add-ons-button.tooltiptext2",

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

@ -214,8 +214,6 @@ CustomizeMode.prototype = {
customizeButton.setAttribute("label", customizeButton.getAttribute("exitLabel"));
customizeButton.setAttribute("enterTooltiptext", customizeButton.getAttribute("tooltiptext"));
customizeButton.setAttribute("tooltiptext", customizeButton.getAttribute("exitTooltiptext"));
document.getElementById("PanelUI-help").setAttribute("disabled", true);
document.getElementById("PanelUI-quit").setAttribute("disabled", true);
this._transitioning = true;
@ -246,6 +244,9 @@ CustomizeMode.prototype = {
window.gNavToolbox.addEventListener("toolbarvisibilitychange", this);
document.getElementById("PanelUI-help").setAttribute("disabled", true);
document.getElementById("PanelUI-quit").setAttribute("disabled", true);
this._updateResetButton();
this._updateUndoResetButton();
@ -336,7 +337,6 @@ CustomizeMode.prototype = {
this.document.removeEventListener("keypress", this);
this.window.PanelUI.menuButton.removeEventListener("mousedown", this);
this.window.PanelUI.menuButton.open = false;
this.window.PanelUI.beginBatchUpdate();
@ -425,6 +425,7 @@ CustomizeMode.prototype = {
CustomizableUI.dispatchToolboxEvent("customizationending", {}, window);
window.PanelUI.setMainView(window.PanelUI.mainView);
window.PanelUI.menuButton.open = false;
window.PanelUI.menuButton.disabled = false;
let customizeButton = document.getElementById("PanelUI-customize");

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

@ -1961,10 +1961,14 @@ VariableBubbleView.prototype = {
/**
* The mousemove listener for the source editor.
*/
_onMouseMove: function({ clientX: x, clientY: y }) {
_onMouseMove: function({ clientX: x, clientY: y, buttons: btns }) {
// Prevent the variable inspection popup from showing when the thread client
// is not paused, or while a popup is already visible.
if (gThreadClient && gThreadClient.state != "paused" || !this._tooltip.isHidden()) {
// is not paused, or while a popup is already visible, or when the user tries
// to select text in the editor.
if (gThreadClient && gThreadClient.state != "paused"
|| !this._tooltip.isHidden()
|| (DebuggerView.editor.somethingSelected()
&& btns > 0)) {
clearNamedTimeout("editor-mouse-move");
return;
}

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

@ -265,6 +265,7 @@ support-files =
[browser_dbg_variables-view-popup-11.js]
[browser_dbg_variables-view-popup-12.js]
[browser_dbg_variables-view-popup-13.js]
[browser_dbg_variables-view-popup-14.js]
[browser_dbg_variables-view-reexpand-01.js]
[browser_dbg_variables-view-reexpand-02.js]
[browser_dbg_variables-view-webidl.js]

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

@ -0,0 +1,49 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests that the variable inspection popup is hidden when
* selecting text in the editor.
*/
const TAB_URL = EXAMPLE_URL + "doc_frame-parameters.html";
function test() {
Task.spawn(function*() {
let [tab, debuggee, panel] = yield initDebugger(TAB_URL);
let win = panel.panelWin;
let bubble = win.DebuggerView.VariableBubble;
// Allow this generator function to yield first.
executeSoon(() => debuggee.start());
yield waitForSourceAndCaretAndScopes(panel, ".html", 24);
// Select some text.
let cursor = win.DebuggerView.editor.getOffset({ line: 15, ch: 12 });
let [ anchor, head ] = win.DebuggerView.editor.getPosition(
cursor,
cursor + 3
);
win.DebuggerView.editor.setSelection(anchor, head);
// Try to Inspect variable during selection.
let popupOpened = yield intendOpenVarPopup(panel, { line: 15, ch: 12 }, true);
// Ensure the bubble is not there
ok(!popupOpened,
"The popup is not opened");
ok(!bubble._markedText,
"The marked text in the editor is not there.");
// Try to Inspect variable after selection.
popupOpened = yield intendOpenVarPopup(panel, { line: 15, ch: 12 }, false);
// Ensure the bubble is not there
ok(popupOpened,
"The popup is opened");
ok(bubble._markedText,
"The marked text in the editor is there.");
yield resumeDebuggerThenCloseAndFinish(panel);
});
}

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

@ -605,6 +605,41 @@ function openVarPopup(aPanel, aCoords, aWaitForFetchedProperties) {
return promise.all([popupShown, fetchedProperties]).then(waitForTick);
}
// Simulates the mouse hovering a variable in the debugger
// Takes in account the position of the cursor in the text, if the text is
// selected and if a button is currently pushed (aButtonPushed > 0).
// The function returns a promise which returns true if the popup opened or
// false if it didn't
function intendOpenVarPopup(aPanel, aPosition, aButtonPushed) {
let bubble = aPanel.panelWin.DebuggerView.VariableBubble;
let editor = aPanel.panelWin.DebuggerView.editor;
let tooltip = bubble._tooltip;
let { left, top } = editor.getCoordsFromPosition(aPosition);
const eventDescriptor = {
clientX: left,
clientY: top,
buttons: aButtonPushed
};
bubble._onMouseMove(eventDescriptor);
const deferred = promise.defer();
window.setTimeout(
function() {
if(tooltip.isEmpty()) {
deferred.resolve(false);
} else {
deferred.resolve(true);
}
},
tooltip.defaultShowDelay + 1000
);
return deferred.promise;
}
function hideVarPopup(aPanel) {
let bubble = aPanel.panelWin.DebuggerView.VariableBubble;
let tooltip = bubble._tooltip.panel;
@ -661,7 +696,6 @@ function filterTraces(aPanel, f) {
.children;
return Array.filter(traces, f);
}
function attachAddonActorForUrl(aClient, aUrl) {
let deferred = promise.defer();

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

@ -11,12 +11,15 @@ function test() {
addTab("about:blank", function() {
let target = TargetFactory.forTab(gBrowser.selectedTab);
toolIDs = gDevTools.getToolDefinitionArray()
.filter(def => def.isTargetSupported(target))
.map(def => def.id);
idIndex = 0;
gDevTools.showToolbox(target, toolIDs[0], Toolbox.HostType.BOTTOM)
.then(testShortcuts);
target.makeRemote().then(() => {
toolIDs = gDevTools.getToolDefinitionArray()
.filter(def => def.isTargetSupported(target))
.map(def => def.id);
gDevTools.showToolbox(target, toolIDs[0], Toolbox.HostType.BOTTOM)
.then(testShortcuts);
});
});
}

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

@ -3,6 +3,10 @@
function test() {
addTab().then(function(data) {
data.target.makeRemote().then(performChecks.bind(null, data));
}).then(null, console.error);
function performChecks(data) {
let toolIds = gDevTools.getToolDefinitionArray()
.filter(def => def.isTargetSupported(data.target))
.map(def => def.id);
@ -32,5 +36,5 @@ function test() {
};
open(0);
}).then(null, console.error);
}
}

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

@ -253,21 +253,20 @@ Toolbox.prototype = {
this._addZoomKeys();
this._loadInitialZoom();
// Load the toolbox-level actor fronts and utilities now
this._target.makeRemote().then(() => {
this._telemetry.toolOpened("toolbox");
this._telemetry.toolOpened("toolbox");
this.selectTool(this._defaultToolId).then(panel => {
this.emit("ready");
deferred.resolve();
});
this.selectTool(this._defaultToolId).then(panel => {
this.emit("ready");
deferred.resolve();
});
};
iframe.setAttribute("src", this._URL);
let domHelper = new DOMHelpers(iframe.contentWindow);
domHelper.onceDOMReady(domReady);
// Load the toolbox-level actor fronts and utilities now
this._target.makeRemote().then(() => {
iframe.setAttribute("src", this._URL);
let domHelper = new DOMHelpers(iframe.contentWindow);
domHelper.onceDOMReady(domReady);
});
return deferred.promise;
});
@ -465,7 +464,7 @@ Toolbox.prototype = {
fireCustomKey: function(toolId) {
let toolDefinition = gDevTools.getToolDefinition(toolId);
if (toolDefinition.onkey &&
if (toolDefinition.onkey &&
((this.currentToolId === toolId) ||
(toolId == "webconsole" && this.splitConsole))) {
toolDefinition.onkey(this.getCurrentPanel(), this);
@ -1093,27 +1092,17 @@ Toolbox.prototype = {
* Returns a promise that resolves when the fronts are initialized
*/
initInspector: function() {
let deferred = promise.defer();
if (!this._inspector) {
this._inspector = InspectorFront(this._target.client, this._target.form);
this._inspector.getWalker().then(walker => {
this._walker = walker;
if (!this._initInspector) {
this._initInspector = Task.spawn(function*() {
this._inspector = InspectorFront(this._target.client, this._target.form);
this._walker = yield this._inspector.getWalker();
this._selection = new Selection(this._walker);
if (this.highlighterUtils.isRemoteHighlightable) {
this._inspector.getHighlighter().then(highlighter => {
this._highlighter = highlighter;
deferred.resolve();
});
} else {
deferred.resolve();
this._highlighter = yield this._inspector.getHighlighter();
}
});
} else {
deferred.resolve();
}.bind(this));
}
return deferred.promise;
return this._initInspector;
},
/**

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

@ -239,7 +239,8 @@ Tools.netMonitor = {
inMenu: true,
isTargetSupported: function(target) {
return !target.isApp;
let root = target.client.mainRoot;
return root.traits.networkMonitor || !target.isApp;
},
build: function(iframeWindow, toolbox) {

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

@ -78,7 +78,11 @@ const EVENTS = {
// Fired when charts have been displayed in the PerformanceStatisticsView.
PLACEHOLDER_CHARTS_DISPLAYED: "NetMonitor:PlaceholderChartsDisplayed",
PRIMED_CACHE_CHART_DISPLAYED: "NetMonitor:PrimedChartsDisplayed",
EMPTY_CACHE_CHART_DISPLAYED: "NetMonitor:EmptyChartsDisplayed"
EMPTY_CACHE_CHART_DISPLAYED: "NetMonitor:EmptyChartsDisplayed",
// Fired once the NetMonitorController establishes a connection to the debug
// target.
CONNECTED: "connected",
};
// Descriptions for what this frontend is currently doing.
@ -200,7 +204,10 @@ let NetMonitorController = {
this._startMonitoringTab(client, form, deferred.resolve);
}
return deferred.promise;
return deferred.promise.then((result) => {
window.emit(EVENTS.CONNECTED);
return result;
});
},
/**
@ -362,6 +369,25 @@ let NetMonitorController = {
return promise.reject(new Error("Invalid activity type"));
},
/**
* Getter that tells if the server supports sending custom network requests.
* @type boolean
*/
get supportsCustomRequest() {
return this.webConsoleClient &&
(this.webConsoleClient.traits.customNetworkRequest ||
!this._target.isApp);
},
/**
* Getter that tells if the server can do network performance statistics.
* @type boolean
*/
get supportsPerfStats() {
return this.tabClient &&
(this.tabClient.traits.reconfigure || !this._target.isApp);
},
_startup: null,
_shutdown: null,
_connection: null,
@ -493,6 +519,11 @@ NetworkEventsHandler.prototype = {
* The message received from the server.
*/
_onNetworkEvent: function(aType, aPacket) {
if (aPacket.from != this.webConsoleClient.actor) {
// Skip events from different console actors.
return;
}
let { actor, startedDateTime, method, url, isXHR } = aPacket.eventActor;
NetMonitorView.RequestsMenu.addRequest(actor, startedDateTime, method, url, isXHR);
window.emit(EVENTS.NETWORK_EVENT);
@ -508,6 +539,10 @@ NetworkEventsHandler.prototype = {
*/
_onNetworkEventUpdate: function(aType, aPacket) {
let actor = aPacket.from;
if (!NetMonitorView.RequestsMenu.getItemByValue(actor)) {
// Skip events from unknown actors.
return;
}
switch (aPacket.updateType) {
case "requestHeaders":

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

@ -374,16 +374,32 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
$("#request-menu-context-newtab").addEventListener("command", this._onContextNewTabCommand, false);
$("#request-menu-context-copy-url").addEventListener("command", this._onContextCopyUrlCommand, false);
$("#request-menu-context-copy-image-as-data-uri").addEventListener("command", this._onContextCopyImageAsDataUriCommand, false);
$("#request-menu-context-resend").addEventListener("command", this._onContextResendCommand, false);
$("#request-menu-context-perf").addEventListener("command", this._onContextPerfCommand, false);
$("#requests-menu-perf-notice-button").addEventListener("command", this._onContextPerfCommand, false);
$("#requests-menu-network-summary-button").addEventListener("command", this._onContextPerfCommand, false);
$("#requests-menu-network-summary-label").addEventListener("click", this._onContextPerfCommand, false);
window.once("connected", this._onConnect.bind(this));
},
$("#custom-request-send-button").addEventListener("click", this.sendCustomRequestEvent, false);
$("#custom-request-close-button").addEventListener("click", this.closeCustomRequestEvent, false);
$("#headers-summary-resend").addEventListener("click", this.cloneSelectedRequestEvent, false);
_onConnect: function() {
if (NetMonitorController.supportsCustomRequest) {
$("#request-menu-context-resend").addEventListener("command", this._onContextResendCommand, false);
$("#custom-request-send-button").addEventListener("click", this.sendCustomRequestEvent, false);
$("#custom-request-close-button").addEventListener("click", this.closeCustomRequestEvent, false);
$("#headers-summary-resend").addEventListener("click", this.cloneSelectedRequestEvent, false);
} else {
$("#request-menu-context-resend").hidden = true;
$("#headers-summary-resend").hidden = true;
}
if (NetMonitorController.supportsPerfStats) {
$("#request-menu-context-perf").addEventListener("command", this._onContextPerfCommand, false);
$("#requests-menu-perf-notice-button").addEventListener("command", this._onContextPerfCommand, false);
$("#requests-menu-network-summary-button").addEventListener("command", this._onContextPerfCommand, false);
$("#requests-menu-network-summary-label").addEventListener("click", this._onContextPerfCommand, false);
} else {
$("#notice-perf-message").hidden = true;
$("#request-menu-context-perf").hidden = true;
$("#requests-menu-network-summary-button").hidden = true;
$("#requests-menu-network-summary-label").hidden = true;
}
},
/**
@ -546,8 +562,12 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
*/
sendCustomRequest: function() {
let selected = this.selectedItem.attachment;
let data = Object.create(selected);
let data = {
method: selected.method,
url: selected.url,
httpVersion: selected.httpVersion,
};
if (selected.requestHeaders) {
data.headers = selected.requestHeaders.headers;
}
@ -1534,7 +1554,8 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
let selectedItem = this.selectedItem;
let resendElement = $("#request-menu-context-resend");
resendElement.hidden = !selectedItem || selectedItem.attachment.isCustom;
resendElement.hidden = !NetMonitorController.supportsCustomRequest ||
!selectedItem || selectedItem.attachment.isCustom;
let copyUrlElement = $("#request-menu-context-copy-url");
copyUrlElement.hidden = !selectedItem;

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

@ -90,6 +90,10 @@ exports.RuleViewTool = RuleViewTool;
RuleViewTool.prototype = {
onSelect: function RVT_onSelect(aEvent) {
if (!this.view) {
// Skip the event if RuleViewTool has been destroyed.
return;
}
this.view.setPageStyle(this.inspector.pageStyle);
if (!this.inspector.selection.isConnected() ||
@ -158,6 +162,10 @@ exports.ComputedViewTool = ComputedViewTool;
ComputedViewTool.prototype = {
onSelect: function CVT_onSelect(aEvent)
{
if (!this.view) {
// Skip the event if ComputedViewTool has been destroyed.
return;
}
this.view.setPageStyle(this.inspector.pageStyle);
if (!this.inspector.selection.isConnected() ||

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

@ -9,6 +9,8 @@ const {Cc, Ci, Cu} = require("chrome");
loader.lazyImporter(this, "VariablesView", "resource:///modules/devtools/VariablesView.jsm");
loader.lazyImporter(this, "escapeHTML", "resource:///modules/devtools/VariablesView.jsm");
loader.lazyImporter(this, "gDevTools", "resource:///modules/devtools/gDevTools.jsm");
loader.lazyImporter(this, "Task","resource://gre/modules/Task.jsm");
const Heritage = require("sdk/core/heritage");
const XHTML_NS = "http://www.w3.org/1999/xhtml";
@ -131,7 +133,7 @@ ConsoleOutput.prototype = {
* @type DOMDocument
*/
get document() {
return this.owner.document;
return this.owner ? this.owner.document : null;
},
/**
@ -150,6 +152,14 @@ ConsoleOutput.prototype = {
return this.owner.webConsoleClient;
},
/**
* Getter for the current toolbox debuggee target.
* @type Target
*/
get toolboxTarget() {
return this.owner.owner.target;
},
/**
* Release an actor.
*
@ -507,6 +517,15 @@ Messages.BaseMessage.prototype = {
{
this.output.openLink(event.target.href);
},
destroy: function()
{
// Destroy all widgets that have registered themselves in this.widgets
for (let widget of this.widgets) {
widget.destroy();
}
this.widgets.clear();
}
}; // Messages.BaseMessage.prototype
@ -2017,6 +2036,7 @@ Widgets.ObjectRenderers.add({
case Ci.nsIDOMNode.TEXT_NODE:
case Ci.nsIDOMNode.COMMENT_NODE:
case Ci.nsIDOMNode.DOCUMENT_FRAGMENT_NODE:
case Ci.nsIDOMNode.ELEMENT_NODE:
return true;
default:
return false;
@ -2045,6 +2065,9 @@ Widgets.ObjectRenderers.add({
case Ci.nsIDOMNode.DOCUMENT_FRAGMENT_NODE:
this._renderDocumentFragmentNode();
break;
case Ci.nsIDOMNode.ELEMENT_NODE:
this._renderElementNode();
break;
default:
throw new Error("Unsupported nodeType: " + preview.nodeType);
}
@ -2138,6 +2161,168 @@ Widgets.ObjectRenderers.add({
this._text(" ]");
},
_renderElementNode: function()
{
let doc = this.document;
let {attributes, nodeName} = this.objectActor.preview;
this.element = this.el("span." + "kind-" + this.objectActor.preview.kind + ".elementNode");
let openTag = this.el("span.cm-tag");
openTag.textContent = "<";
this.element.appendChild(openTag);
let tagName = this._anchor(nodeName, {
className: "cm-tag",
appendTo: openTag
});
if (this.options.concise) {
if (attributes.id) {
tagName.appendChild(this.el("span.cm-attribute", "#" + attributes.id));
}
if (attributes.class) {
tagName.appendChild(this.el("span.cm-attribute", "." + attributes.class.split(" ").join(".")));
}
} else {
for (let name of Object.keys(attributes)) {
let attr = this._renderAttributeNode(" " + name, attributes[name]);
this.element.appendChild(attr);
}
}
let closeTag = this.el("span.cm-tag");
closeTag.textContent = ">";
this.element.appendChild(closeTag);
// Register this widget in the owner message so that it gets destroyed when
// the message is destroyed.
this.message.widgets.add(this);
this.linkToInspector();
},
/**
* If the DOMNode being rendered can be highlit in the page, this function
* will attach mouseover/out event listeners to do so, and the inspector icon
* to open the node in the inspector.
* @return a promise (always the same) that resolves when the node has been
* linked to the inspector, or rejects if it wasn't (either if no toolbox
* could be found to access the inspector, or if the node isn't present in the
* inspector, i.e. if the node is in a DocumentFragment or not part of the
* tree, or not of type Ci.nsIDOMNode.ELEMENT_NODE).
*/
linkToInspector: function()
{
if (this._linkedToInspector) {
return this._linkedToInspector;
}
this._linkedToInspector = Task.spawn(function*() {
// Checking the node type
if (this.objectActor.preview.nodeType !== Ci.nsIDOMNode.ELEMENT_NODE) {
throw null;
}
// Checking the presence of a toolbox
let target = this.message.output.toolboxTarget;
this.toolbox = gDevTools.getToolbox(target);
if (!this.toolbox) {
throw null;
}
// Checking that the inspector supports the node
yield this.toolbox.initInspector();
this._nodeFront = yield this.toolbox.walker.getNodeActorFromObjectActor(this.objectActor.actor);
if (!this._nodeFront) {
throw null;
}
// At this stage, the message may have been cleared already
if (!this.document) {
throw null;
}
this.highlightDomNode = this.highlightDomNode.bind(this);
this.element.addEventListener("mouseover", this.highlightDomNode, false);
this.unhighlightDomNode = this.unhighlightDomNode.bind(this);
this.element.addEventListener("mouseout", this.unhighlightDomNode, false);
this._openInspectorNode = this._anchor("", {
className: "open-inspector",
onClick: this.openNodeInInspector.bind(this)
});
this._openInspectorNode.title = l10n.getStr("openNodeInInspector");
}.bind(this));
return this._linkedToInspector;
},
/**
* Highlight the DOMNode corresponding to the ObjectActor in the page.
* @return a promise that resolves when the node has been highlighted, or
* rejects if the node cannot be highlighted (detached from the DOM)
*/
highlightDomNode: function()
{
return Task.spawn(function*() {
yield this.linkToInspector();
let isAttached = yield this.toolbox.walker.isInDOMTree(this._nodeFront);
if (isAttached) {
yield this.toolbox.highlighterUtils.highlightNodeFront(this._nodeFront);
} else {
throw null;
}
}.bind(this));
},
/**
* Unhighlight a previously highlit node
* @see highlightDomNode
* @return a promise that resolves when the highlighter has been hidden
*/
unhighlightDomNode: function()
{
return this.linkToInspector().then(() => {
return this.toolbox.highlighterUtils.unhighlight();
});
},
/**
* Open the DOMNode corresponding to the ObjectActor in the inspector panel
* @return a promise that resolves when the inspector has been switched to
* and the node has been selected, or rejects if the node cannot be selected
* (detached from the DOM). Note that in any case, the inspector panel will
* be switched to.
*/
openNodeInInspector: function()
{
return Task.spawn(function*() {
yield this.linkToInspector();
yield this.toolbox.selectTool("inspector");
let isAttached = yield this.toolbox.walker.isInDOMTree(this._nodeFront);
if (isAttached) {
let onReady = this.toolbox.inspector.once("inspector-updated");
yield this.toolbox.selection.setNodeFront(this._nodeFront, "console");
yield onReady;
} else {
throw null;
}
}.bind(this));
},
destroy: function()
{
if (this.toolbox && this._nodeFront) {
this.element.removeEventListener("mouseover", this.highlightDomNode, false);
this.element.removeEventListener("mouseout", this.unhighlightDomNode, false);
this._openInspectorNode.removeEventListener("mousedown", this.openNodeInInspector, true);
this.toolbox = null;
this._nodeFront = null;
}
},
}); // Widgets.ObjectRenderers.byKind.DOMNode
/**

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

@ -69,6 +69,7 @@ support-files =
test-console-output-02.html
test-console-output-03.html
test-console-output-04.html
test-console-output-dom-elements.html
test-console-output-events.html
test-consoleiframes.html
test-data.json
@ -266,6 +267,11 @@ run-if = os == "mac"
[browser_webconsole_output_02.js]
[browser_webconsole_output_03.js]
[browser_webconsole_output_04.js]
[browser_webconsole_output_05.js]
[browser_webconsole_output_dom_elements_01.js]
[browser_webconsole_output_dom_elements_02.js]
[browser_webconsole_output_dom_elements_03.js]
[browser_webconsole_output_dom_elements_04.js]
[browser_webconsole_output_events.js]
[browser_console_variables_view_highlighter.js]
[browser_webconsole_start_netmon_first.js]

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

@ -7,7 +7,6 @@
const TEST_URI = "data:text/html;charset=utf8,test for console output - 01";
let dateNow = Date.now();
let {DebuggerServer} = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
let LONG_STRING_LENGTH = DebuggerServer.LONG_STRING_LENGTH;
@ -76,70 +75,6 @@ let inputTests = [
output: "/foobar/",
inspectable: true,
},
// 9
{
input: "/foo?b*\\s\"ar/igym",
output: "/foo?b*\\s\"ar/gimy",
printOutput: "/foo?b*\\s\"ar/gimy",
inspectable: true,
},
// 10
{
input: "null",
output: "null",
},
// 11
{
input: "undefined",
output: "undefined",
},
// 12
{
input: "true",
output: "true",
},
// 13
{
input: "new Boolean(false)",
output: "false",
inspectable: true,
},
// 14
{
input: "new Date(" + dateNow + ")",
output: "Date " + (new Date(dateNow)).toISOString(),
printOutput: (new Date(dateNow)).toString(),
inspectable: true,
},
// 15
{
input: "new Date('test')",
output: "Invalid Date",
printOutput: "Invalid Date",
inspectable: true,
variablesViewLabel: "Invalid Date",
},
// 16
{
input: "new Number(43)",
output: "43",
inspectable: true,
},
// 17
{
input: "new String('hello world')",
output: '"hello world"',
inspectable: true,
},
];
longString = initialString = null;
@ -150,11 +85,9 @@ function test() {
DebuggerServer.LONG_STRING_INITIAL_LENGTH = LONG_STRING_INITIAL_LENGTH;
});
addTab(TEST_URI);
browser.addEventListener("load", function onLoad() {
browser.removeEventListener("load", onLoad, true);
openConsole().then((hud) => {
return checkOutputForInputs(hud, inputTests);
}).then(finishTest);
}, true);
Task.spawn(function*() {
let {tab} = yield loadTab(TEST_URI);
let hud = yield openConsole(tab);
return checkOutputForInputs(hud, inputTests);
}).then(finishTest);
}

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

@ -29,7 +29,7 @@ let inputTests = [
// 2
{
input: "testDocumentFragment()",
output: 'DocumentFragment [ <div#foo1>, <div#foo3> ]',
output: 'DocumentFragment [ <div#foo1.bar>, <div#foo3> ]',
printOutput: "[object DocumentFragment]",
inspectable: true,
variablesViewLabel: "DocumentFragment[2]",

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

@ -0,0 +1,84 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// Test the webconsole output for various types of objects.
const TEST_URI = "data:text/html;charset=utf8,test for console output - 05";
let dateNow = Date.now();
let inputTests = [
// 0
{
input: "/foo?b*\\s\"ar/igym",
output: "/foo?b*\\s\"ar/gimy",
printOutput: "/foo?b*\\s\"ar/gimy",
inspectable: true,
},
// 1
{
input: "null",
output: "null",
},
// 2
{
input: "undefined",
output: "undefined",
},
// 3
{
input: "true",
output: "true",
},
// 4
{
input: "new Boolean(false)",
output: "false",
inspectable: true,
},
// 5
{
input: "new Date(" + dateNow + ")",
output: "Date " + (new Date(dateNow)).toISOString(),
printOutput: (new Date(dateNow)).toString(),
inspectable: true,
},
// 6
{
input: "new Date('test')",
output: "Invalid Date",
printOutput: "Invalid Date",
inspectable: true,
variablesViewLabel: "Invalid Date",
},
// 7
{
input: "new Number(43)",
output: "43",
inspectable: true,
},
// 8
{
input: "new String('hello world')",
output: '"hello world"',
inspectable: true,
},
];
function test() {
Task.spawn(function*() {
let {tab} = yield loadTab(TEST_URI);
let hud = yield openConsole(tab);
return checkOutputForInputs(hud, inputTests);
}).then(finishTest);
}

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

@ -0,0 +1,99 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// Test the webconsole output for various types of DOM Nodes.
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console-output-dom-elements.html";
let inputTests = [
{
input: "testBodyNode()",
output: '<body id="body-id" class="body-class">',
printOutput: "[object HTMLBodyElement]",
inspectable: true,
noClick: true,
inspectorIcon: true
},
{
input: "testDocumentElement()",
output: '<html lang="en-US" dir="ltr">',
printOutput: "[object HTMLHtmlElement]",
inspectable: true,
noClick: true,
inspectorIcon: true
},
{
input: "testDocument()",
output: 'HTMLDocument \u2192 ' + TEST_URI,
printOutput: "[object HTMLDocument]",
inspectable: true,
noClick: true,
inspectorIcon: false
},
{
input: "testNode()",
output: '<p some-attribute="some-value">',
printOutput: "[object HTMLParagraphElement]",
inspectable: true,
noClick: true,
inspectorIcon: true
},
{
input: "testNodeList()",
output: 'NodeList [ <html>, <head>, <meta>, <title>, <body#body-id.body-class>, <p>, <iframe>, <script> ]',
printOutput: "[object NodeList]",
inspectable: true,
noClick: true,
inspectorIcon: true
},
{
input: "testNodeInIframe()",
output: '<p>',
printOutput: "[object HTMLParagraphElement]",
inspectable: true,
noClick: true,
inspectorIcon: true
},
{
input: "testDocumentFragment()",
output: 'DocumentFragment [ <span.foo>, <div#fragdiv> ]',
printOutput: "[object DocumentFragment]",
inspectable: true,
noClick: true,
inspectorIcon: false
},
{
input: "testNodeInDocumentFragment()",
output: '<span class="foo" data-lolz="hehe">',
printOutput: "[object HTMLSpanElement]",
inspectable: true,
noClick: true,
inspectorIcon: false
},
{
input: "testUnattachedNode()",
output: '<p class="such-class" data-data="such-data">',
printOutput: "[object HTMLParagraphElement]",
inspectable: true,
noClick: true,
inspectorIcon: false
}
];
function test() {
Task.spawn(function*() {
let {tab} = yield loadTab(TEST_URI);
let hud = yield openConsole(tab);
yield checkOutputForInputs(hud, inputTests);
}).then(finishTest);
}

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

@ -0,0 +1,98 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// Test the inspector links in the webconsole output for DOM Nodes actually
// open the inspector and select the right node
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console-output-dom-elements.html";
const TEST_DATA = [
{
// The first test shouldn't be returning the body element as this is the
// default selected node, so re-selecting it won't fire the inspector-updated
// event
input: "testNode()",
output: '<p some-attribute="some-value">'
},
{
input: "testBodyNode()",
output: '<body id="body-id" class="body-class">'
},
{
input: "testNodeInIframe()",
output: '<p>'
},
{
input: "testDocumentElement()",
output: '<html lang="en-US" dir="ltr">'
}
];
function test() {
Task.spawn(function*() {
let {tab} = yield loadTab(TEST_URI);
let hud = yield openConsole(tab);
let toolbox = gDevTools.getToolbox(hud.target);
// Loading the inspector panel at first, to make it possible to listen for
// new node selections
yield toolbox.selectTool("inspector");
let inspector = toolbox.getCurrentPanel();
yield toolbox.selectTool("webconsole");
info("Iterating over the test data");
for (let data of TEST_DATA) {
let [result] = yield jsEval(data.input, hud, {text: data.output});
let {widget, msg} = yield getWidgetAndMessage(result);
let inspectorIcon = msg.querySelector(".open-inspector");
ok(inspectorIcon, "Inspector icon found in the ElementNode widget");
info("Clicking on the inspector icon and waiting for the inspector to be selected");
let onInspectorSelected = toolbox.once("inspector-selected");
let onInspectorUpdated = inspector.once("inspector-updated");
let onNewNode = toolbox.selection.once("new-node");
EventUtils.synthesizeMouseAtCenter(inspectorIcon, {},
inspectorIcon.ownerDocument.defaultView);
yield onInspectorSelected;
yield onInspectorUpdated;
yield onNewNode;
ok(true, "Inspector selected and new node got selected");
let rawNode = content.wrappedJSObject[data.input.replace(/\(\)/g, "")]();
is(inspector.selection.node.wrappedJSObject, rawNode,
"The current inspector selection is correct");
info("Switching back to the console");
yield toolbox.selectTool("webconsole");
}
}).then(finishTest);
}
function jsEval(input, hud, message) {
info("Executing '" + input + "' in the web console");
hud.jsterm.clearOutput();
hud.jsterm.execute(input);
return waitForMessages({
webconsole: hud,
messages: [message]
});
}
function* getWidgetAndMessage(result) {
info("Getting the output ElementNode widget");
let msg = [...result.matched][0];
let widget = [...msg._messageObject.widgets][0];
ok(widget, "ElementNode widget found in the output");
info("Waiting for the ElementNode widget to be linked to the inspector");
yield widget.linkToInspector();
return {widget: widget, msg: msg};
}

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

@ -0,0 +1,67 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// Test that inspector links in webconsole outputs for DOM Nodes highlight
// the actual DOM Nodes on hover
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console-output-dom-elements.html";
function test() {
Task.spawn(function*() {
let {tab} = yield loadTab(TEST_URI);
let hud = yield openConsole(tab);
let toolbox = gDevTools.getToolbox(hud.target);
// Loading the inspector panel at first, to make it possible to listen for
// new node selections
yield toolbox.loadTool("inspector");
let inspector = toolbox.getPanel("inspector");
info("Executing 'testNode()' in the web console to output a DOM Node");
let [result] = yield jsEval("testNode()", hud, {
text: '<p some-attribute="some-value">'
});
let elementNodeWidget = yield getWidget(result);
let nodeFront = yield hoverOverWidget(elementNodeWidget, toolbox);
let attrs = nodeFront.attributes;
is(nodeFront.tagName, "P", "The correct node was highlighted");
is(attrs[0].name, "some-attribute", "The correct node was highlighted");
is(attrs[0].value, "some-value", "The correct node was highlighted");
}).then(finishTest);
}
function jsEval(input, hud, message) {
hud.jsterm.execute(input);
return waitForMessages({
webconsole: hud,
messages: [message]
});
}
function* getWidget(result) {
info("Getting the output ElementNode widget");
let msg = [...result.matched][0];
let elementNodeWidget = [...msg._messageObject.widgets][0];
ok(elementNodeWidget, "ElementNode widget found in the output");
info("Waiting for the ElementNode widget to be linked to the inspector");
yield elementNodeWidget.linkToInspector();
return elementNodeWidget;
}
function* hoverOverWidget(widget, toolbox) {
info("Hovering over the output to highlight the node");
let onHighlight = toolbox.once("node-highlight");
EventUtils.sendMouseEvent({type: "mouseover"}, widget.element,
widget.element.ownerDocument.defaultView);
let nodeFront = yield onHighlight;
ok(true, "The highlighter was shown on a node");
return nodeFront;
}

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

@ -0,0 +1,106 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// Test that inspector links in the webconsole output for DOM Nodes do not try
// to highlight or select nodes once they have been detached
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console-output-dom-elements.html";
const TEST_DATA = [
{
// The first test shouldn't be returning the body element as this is the
// default selected node, so re-selecting it won't fire the inspector-updated
// event
input: "testNode()",
output: '<p some-attribute="some-value">'
},
{
input: "testBodyNode()",
output: '<body id="body-id" class="body-class">'
},
{
input: "testNodeInIframe()",
output: '<p>'
},
{
input: "testDocumentElement()",
output: '<html lang="en-US" dir="ltr">'
}
];
const PREF = "devtools.webconsole.persistlog";
function test() {
Services.prefs.setBoolPref(PREF, true);
registerCleanupFunction(() => Services.prefs.clearUserPref(PREF));
Task.spawn(function*() {
let {tab} = yield loadTab(TEST_URI);
let hud = yield openConsole(tab);
let toolbox = gDevTools.getToolbox(hud.target);
info("Executing the test data");
let widgets = [];
for (let data of TEST_DATA) {
let [result] = yield jsEval(data.input, hud, {text: data.output});
let {widget} = yield getWidgetAndMessage(result);
widgets.push(widget);
}
info("Reloading the page");
yield reloadPage();
info("Iterating over the ElementNode widgets");
for (let widget of widgets) {
// Verify that openNodeInInspector rejects since the associated dom node
// doesn't exist anymore
yield widget.openNodeInInspector().then(() => {
ok(false, "The openNodeInInspector promise resolved");
}, () => {
ok(true, "The openNodeInInspector promise rejected as expected");
});
yield toolbox.selectTool("webconsole");
// Verify that highlightDomNode rejects too, for the same reason
yield widget.highlightDomNode().then(() => {
ok(false, "The highlightDomNode promise resolved");
}, () => {
ok(true, "The highlightDomNode promise rejected as expected");
});
}
}).then(finishTest);
}
function jsEval(input, hud, message) {
info("Executing '" + input + "' in the web console");
hud.jsterm.execute(input);
return waitForMessages({
webconsole: hud,
messages: [message]
});
}
function* getWidgetAndMessage(result) {
info("Getting the output ElementNode widget");
let msg = [...result.matched][0];
let widget = [...msg._messageObject.widgets][0];
ok(widget, "ElementNode widget found in the output");
info("Waiting for the ElementNode widget to be linked to the inspector");
yield widget.linkToInspector();
return {widget: widget, msg: msg};
}
function reloadPage() {
let def = promise.defer();
gBrowser.selectedBrowser.addEventListener("load", function onload() {
gBrowser.selectedBrowser.removeEventListener("load", onload, true);
def.resolve();
}, true);
content.location.reload();
return def.promise;
}

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

@ -1314,6 +1314,10 @@ function whenDelayedStartupFinished(aWindow, aCallback)
* - variablesViewLabel: string|RegExp, optional, the expected variables
* view label when the object is inspected. If this is not provided, then
* |output| is used.
*
* - inspectorIcon: boolean, when true, the test runner expects the
* result widget to contain an inspectorIcon element (className
* open-inspector).
*/
function checkOutputForInputs(hud, inputTests)
{
@ -1338,12 +1342,12 @@ function checkOutputForInputs(hud, inputTests)
yield checkJSEval(entry);
}
function checkConsoleLog(entry)
function* checkConsoleLog(entry)
{
hud.jsterm.clearOutput();
hud.jsterm.execute("console.log(" + entry.input + ")");
return waitForMessages({
let [result] = yield waitForMessages({
webconsole: hud,
messages: [{
name: "console.log() output: " + entry.output,
@ -1352,6 +1356,11 @@ function checkOutputForInputs(hud, inputTests)
severity: SEVERITY_LOG,
}],
});
if (typeof entry.inspectorIcon == "boolean") {
let msg = [...result.matched][0];
yield checkLinkToInspector(entry, msg);
}
}
function checkPrintOutput(entry)
@ -1385,10 +1394,13 @@ function checkOutputForInputs(hud, inputTests)
}],
});
let msg = [...result.matched][0];
if (!entry.noClick) {
let msg = [...result.matched][0];
yield checkObjectClick(entry, msg);
}
if (typeof entry.inspectorIcon == "boolean") {
yield checkLinkToInspector(entry, msg);
}
}
function checkObjectClick(entry, msg)
@ -1413,6 +1425,29 @@ function checkOutputForInputs(hud, inputTests)
return promise.resolve(null);
}
function checkLinkToInspector(entry, msg)
{
let elementNodeWidget = [...msg._messageObject.widgets][0];
if (!elementNodeWidget) {
ok(!entry.inspectorIcon, "The message has no ElementNode widget");
return;
}
return elementNodeWidget.linkToInspector().then(() => {
// linkToInspector resolved, check for the .open-inspector element
if (entry.inspectorIcon) {
ok(msg.querySelectorAll(".open-inspector").length,
"The ElementNode widget is linked to the inspector");
} else {
ok(!msg.querySelectorAll(".open-inspector").length,
"The ElementNode widget isn't linked to the inspector");
}
}, () => {
// linkToInspector promise rejected, node not linked to inspector
ok(!entry.inspectorIcon, "The ElementNode widget isn't linked to the inspector");
});
}
function onVariablesViewOpen(entry, deferred, event, view, options)
{
let label = entry.variablesViewLabel || entry.output;

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

@ -0,0 +1,68 @@
<!DOCTYPE HTML>
<html dir="ltr" lang="en-US">
<head>
<meta charset="utf-8">
<title>Test the web console output - 05</title>
<!--
- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/
-->
</head>
<body class="body-class" id="body-id">
<p some-attribute="some-value">hello world!</p>
<iframe src="data:text/html,<p>hello from iframe</p>"></iframe>
<script type="text/javascript">
function testBodyNode() {
return document.body;
}
function testDocumentElement() {
return document.documentElement;
}
function testDocument() {
return document;
}
function testNode() {
return document.querySelector("p");
}
function testNodeList() {
return document.querySelectorAll("*");
}
function testNodeInIframe() {
return document.querySelector("iframe").contentWindow.document.querySelector("p");
}
function testDocumentFragment() {
var frag = document.createDocumentFragment();
var span = document.createElement("span");
span.className = 'foo';
span.dataset.lolz = 'hehe';
var div = document.createElement('div')
div.id = 'fragdiv';
frag.appendChild(span);
frag.appendChild(div);
return frag;
}
function testNodeInDocumentFragment() {
var frag = testDocumentFragment();
return frag.firstChild;
}
function testUnattachedNode() {
var p = document.createElement("p");
p.className = "such-class";
p.dataset.data = "such-data";
return p;
}
</script>
</body>
</html>

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

@ -2362,6 +2362,10 @@ WebConsoleFrame.prototype = {
*/
removeOutputMessage: function WCF_removeOutputMessage(aNode)
{
if (aNode._messageObject) {
aNode._messageObject.destroy();
}
if (aNode._objectActors) {
for (let actor of aNode._objectActors) {
this._releaseObject(actor);

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

@ -214,6 +214,11 @@ emptyPropertiesList=No properties to display
# example: 3 repeats
messageRepeats.tooltip2=#1 repeat;#1 repeats
# LOCALIZATION NOTE (openNodeInInspector): the text that is displayed in a
# tooltip when hovering over the inspector icon next to a DOM Node in the console
# output
openNodeInInspector=Click to select the node in the inspector
# LOCALIZATION NOTE (cdFunctionInvalidArgument): the text that is displayed when
# cd() is invoked with an invalid argument.
cdFunctionInvalidArgument=Cannot cd() to the given window. Invalid argument.

Двоичные данные
browser/themes/linux/Toolbar-inverted.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 8.8 KiB

После

Ширина:  |  Высота:  |  Размер: 8.9 KiB

Двоичные данные
browser/themes/linux/Toolbar.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 14 KiB

После

Ширина:  |  Высота:  |  Размер: 14 KiB

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

@ -29,6 +29,18 @@
-moz-margin-start: 3px;
}
/* subviewbutton entries for social sidebars have images that come from external
/* sources, and are not guaranteed to be the size we want, so force the size on
/* those icons. */
toolbarbutton.social-provider-menuitem > .toolbarbutton-icon {
width: 16px;
height: 16px;
}
.subviewbutton:-moz-any([image],[targetURI],.cui-withicon, .restoreallitem, .bookmark-item)[checked="true"] > .toolbarbutton-icon {
visibility: hidden;
}
.PanelUI-subView toolbarseparator,
.PanelUI-subView menuseparator,
.cui-widget-panelview menuseparator,

Двоичные данные
browser/themes/linux/menuPanel.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 23 KiB

После

Ширина:  |  Высота:  |  Размер: 23 KiB

Двоичные данные
browser/themes/osx/Toolbar-inverted.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 29 KiB

После

Ширина:  |  Высота:  |  Размер: 30 KiB

Двоичные данные
browser/themes/osx/Toolbar-inverted@2x.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 71 KiB

После

Ширина:  |  Высота:  |  Размер: 73 KiB

Двоичные данные
browser/themes/osx/Toolbar.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 23 KiB

После

Ширина:  |  Высота:  |  Размер: 24 KiB

Двоичные данные
browser/themes/osx/Toolbar@2x.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 63 KiB

После

Ширина:  |  Высота:  |  Размер: 63 KiB

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

@ -724,6 +724,10 @@ toolbar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker > .dropmarker-ic
-moz-image-region: rect(18px, 666px, 36px, 648px);
}
#sidebar-button@toolbarButtonPressed@ {
-moz-image-region: rect(18px, 684px, 36px, 666px);
}
/**
* OSX has a unique set of icons when fullscreen is in the checked state.
*/
@ -1027,6 +1031,14 @@ toolbar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker > .dropmarker-ic
-moz-image-region: rect(36px, 1332px, 72px, 1296px);
}
#sidebar-button[cui-areatype="toolbar"] {
-moz-image-region: rect(0, 1368px, 36px, 1332px);
}
#sidebar-button[cui-areatype="toolbar"]:hover:active:not([disabled="true"]) {
-moz-image-region: rect(36px, 1368px, 72px, 1332px);
}
:-moz-any(@primaryToolbarButtons@) > .toolbarbutton-icon,
:-moz-any(@primaryToolbarButtons@) > .toolbarbutton-menubutton-button > .toolbarbutton-icon {
width: 18px;
@ -1160,6 +1172,11 @@ toolbar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker > .dropmarker-ic
-moz-image-region: rect(0px, 1600px, 64px, 1536px);
}
#sidebar-button[cui-areatype="menu-panel"],
toolbarpaletteitem[place="palette"] > #sidebar-button {
-moz-image-region: rect(0px, 1728px, 64px, 1664px);
}
/* Footer and wide panel control icons */
#edit-controls@inAnyPanel@ > toolbarbutton,
#zoom-controls@inAnyPanel@ > toolbarbutton,
@ -3979,7 +3996,7 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
position: relative;
}
.toolbarbutton-badge-container > .toolbarbutton-icon {
margin: 7px 2px;
margin: 4px 2px;
}
.toolbarbutton-badge[badge]:not([badge=""]) {

Двоичные данные
browser/themes/osx/menuPanel.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 26 KiB

После

Ширина:  |  Высота:  |  Размер: 26 KiB

Двоичные данные
browser/themes/osx/menuPanel@2x.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 60 KiB

После

Ширина:  |  Высота:  |  Размер: 59 KiB

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

@ -1,5 +1,5 @@
%filter substitution
%define primaryToolbarButtons #back-button, #forward-button, #home-button, #print-button, #downloads-button, #bookmarks-menu-button, #new-tab-button, #new-window-button, #cut-button, #copy-button, #paste-button, #fullscreen-button, #zoom-out-button, #zoom-reset-button, #zoom-in-button, #sync-button, #feed-button, #tabview-button, #webrtc-status-button, #social-share-button, #open-file-button, #find-button, #developer-button, #preferences-button, #privatebrowsing-button, #save-page-button, #switch-to-metro-button, #add-ons-button, #history-panelmenu, #nav-bar-overflow-button, #PanelUI-menu-button, #characterencoding-button, #email-link-button
%define primaryToolbarButtons #back-button, #forward-button, #home-button, #print-button, #downloads-button, #bookmarks-menu-button, #new-tab-button, #new-window-button, #cut-button, #copy-button, #paste-button, #fullscreen-button, #zoom-out-button, #zoom-reset-button, #zoom-in-button, #sync-button, #feed-button, #tabview-button, #webrtc-status-button, #social-share-button, #open-file-button, #find-button, #developer-button, #preferences-button, #privatebrowsing-button, #save-page-button, #switch-to-metro-button, #add-ons-button, #history-panelmenu, #nav-bar-overflow-button, #PanelUI-menu-button, #characterencoding-button, #email-link-button, #sidebar-button
%define inAnyPanel :-moz-any(:not([cui-areatype="toolbar"]), [overflowedItem=true])
%define nestedButtons #zoom-out-button, #zoom-in-button, #cut-button, #copy-button, #paste-button

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

@ -17,13 +17,13 @@
pointer-events: none;
}
#main-window[customize-entered] .customization-target:not(#PanelUI-contents)::before,
#main-window[customize-entered] .customization-target:not(:-moz-any(#PanelUI-contents, #TabsToolbar, #toolbar-menubar))::before,
#PanelUI-contents > .panel-customization-placeholder {
-moz-outline-radius: 2.5px;
outline: 1px dashed transparent;
}
#main-window[customize-entered] .customization-target:not(#PanelUI-contents)::before {
#main-window[customize-entered] .customization-target:not(:-moz-any(#PanelUI-contents, #TabsToolbar, #toolbar-menubar))::before {
/* Prevent jumping of tabs when switching a window between inactive and active (bug 853415). */
-moz-box-ordinal-group: 0;
content: "";
@ -45,16 +45,16 @@
}
/* The parents of the outline pseudo-elements need to be positioned so that the outline is positioned relative to it. */
#main-window[customize-entered] .customization-target:not(#PanelUI-contents):hover,
#main-window[customize-entered] .customization-target[customizing-dragovertarget]:not(#PanelUI-contents),
#main-window[customize-entered] .customization-target:not(:-moz-any(#PanelUI-contents, #TabsToolbar, #toolbar-menubar)):hover,
#main-window[customize-entered] .customization-target[customizing-dragovertarget]:not(:-moz-any(#PanelUI-contents, #TabsToolbar, #toolbar-menubar)),
#main-window[customize-entered] #nav-bar-customization-target.customization-target {
position: relative;
}
/* Most target outlines are shown on hover and drag over but the panel menu uses
placeholders instead. */
#main-window[customize-entered] .customization-target:not(#PanelUI-contents):hover::before,
#main-window[customize-entered] .customization-target[customizing-dragovertarget]:not(#PanelUI-contents)::before,
#main-window[customize-entered] .customization-target:not(:-moz-any(#PanelUI-contents, #TabsToolbar, #toolbar-menubar)):hover::before,
#main-window[customize-entered] .customization-target[customizing-dragovertarget]:not(:-moz-any(#PanelUI-contents, #TabsToolbar, #toolbar-menubar))::before,
/* nav-bar and panel outlines are always shown */
#nav-bar[showoutline=true] > #nav-bar-customization-target.customization-target::before {
outline-color: rgb(102,102,102);

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

@ -295,6 +295,7 @@ toolbaritem[cui-areatype="menu-panel"][sdkstylewidget="true"]:not(.panel-wide-it
.panelUI-grid .toolbarbutton-1 > .toolbarbutton-menubutton-button > .toolbarbutton-icon,
.panelUI-grid .toolbarbutton-1 > .toolbarbutton-icon,
.panelUI-grid .toolbarbutton-1 > .toolbarbutton-badge-container,
.customization-palette .toolbarbutton-1 > .toolbarbutton-menubutton-button > .toolbarbutton-icon,
.customization-palette .toolbarbutton-1 > .toolbarbutton-icon,
.panelUI-grid #bookmarks-toolbar-placeholder > .toolbarbutton-icon,
@ -317,6 +318,16 @@ toolbaritem[cui-areatype="menu-panel"][sdkstylewidget="true"]:not(.panel-wide-it
margin: 4px calc(@menuPanelButtonWidth@ / 2 - 23px);
}
/* above we treat the container as the icon for the margins, that is so the
/* badge itself is positioned correctly. Here we make sure that the icon itself
/* has the minum size we want, but no padding/margin. */
.panelUI-grid .toolbarbutton-1 > .toolbarbutton-badge-container > .toolbarbutton-icon {
min-width: 32px;
min-height: 32px;
margin: 0;
padding: 0;
}
toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
-moz-box-flex: 1;
}

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

@ -381,6 +381,23 @@ a {
text-decoration: underline;
}
/* Open DOMNode in inspector button */
.open-inspector {
background: url("chrome://browser/skin/devtools/vview-open-inspector.png") no-repeat 0 0;
padding-left: 16px;
margin-left: 5px;
cursor: pointer;
}
.elementNode:hover .open-inspector,
.open-inspector:hover {
background-position: -32px 0;
}
.open-inspector:active {
background-position: -16px 0;
}
/* Replace these values with CSS variables as available */
.theme-dark .jsterm-input-container {
background-color: #252c33; /* tabToolbarBackgroundColor */

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

@ -126,6 +126,15 @@ toolbarpaletteitem[place="palette"] > #email-link-button {
-moz-image-region: rect(0, 800px, 32px, 768px);
}
#sidebar-button[cui-areatype="menu-panel"],
toolbarpaletteitem[place="palette"] > #sidebar-button {
-moz-image-region: rect(0, 864px, 32px, 832px);
}
#sidebar-button[cui-areatype="menu-panel"][panel-multiview-anchor=true] {
-moz-image-region: rect(32px, 864px, 64px, 832px);
}
/* Wide panel control icons */
#edit-controls@inAnyPanel@ > toolbarbutton,

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

@ -64,10 +64,6 @@
-moz-image-region: rect(0px, 306px, 18px, 288px);
}
#email-link-button[cui-areatype="toolbar"] {
-moz-image-region: rect(0, 666px, 18px, 648px);
}
#characterencoding-button[cui-areatype="toolbar"]{
-moz-image-region: rect(0, 324px, 18px, 306px);
}
@ -143,3 +139,11 @@
#tabview-button {
-moz-image-region: rect(0, 648px, 18px, 630px);
}
#email-link-button[cui-areatype="toolbar"] {
-moz-image-region: rect(0, 666px, 18px, 648px);
}
#sidebar-button[cui-areatype="toolbar"] {
-moz-image-region: rect(0, 684px, 18px, 666px);
}

Двоичные данные
browser/themes/windows/Toolbar-aero.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 13 KiB

После

Ширина:  |  Высота:  |  Размер: 13 KiB

Двоичные данные
browser/themes/windows/Toolbar-inverted.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 8.8 KiB

После

Ширина:  |  Высота:  |  Размер: 8.9 KiB

Двоичные данные
browser/themes/windows/Toolbar-lunaSilver.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 14 KiB

После

Ширина:  |  Высота:  |  Размер: 14 KiB

Двоичные данные
browser/themes/windows/Toolbar.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 9.2 KiB

После

Ширина:  |  Высота:  |  Размер: 9.7 KiB

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

@ -318,6 +318,36 @@
}
}
/* Win8 and beyond. */
@media not all and (-moz-os-version: windows-vista) {
@media not all and (-moz-os-version: windows-win7) {
#urlbar:not(:-moz-lwtheme),
.searchbar-textbox:not(:-moz-lwtheme) {
background-color: hsla(0,0%,100%,.9);
border: 1px solid transparent;
border-color: hsla(210,54%,20%,.25) hsla(210,54%,20%,.27) hsla(210,54%,20%,.3);
box-shadow: 0 1px 0 hsla(0,0%,0%,.01) inset,
0 1px 0 hsla(0,0%,100%,.1);
transition-property: border-color, background-color;
transition-duration: 200ms;
}
#urlbar:not(:-moz-lwtheme)[focused],
.searchbar-textbox:not(:-moz-lwtheme)[focused] {
background-color: hsla(0,0%,100%,1);
border-color: #4595e5;
}
/* Introducing an additional hover state for the Bookmark button */
#nav-bar .toolbarbutton-1[buttonover] > .toolbarbutton-menubutton-button:hover > .toolbarbutton-icon {
background-color: hsla(210,4%,10%,.08);
-moz-border-end: 1px solid;
-moz-padding-end: 5px;
border-color: hsla(210,4%,10%,.1);
}
}
}
/* ::::: fullscreen window controls ::::: */
#window-controls {

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

@ -538,19 +538,38 @@ toolbar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker:-moz-lwtheme-bri
#nav-bar .toolbarbutton-1 > .toolbarbutton-text,
#nav-bar .toolbarbutton-1 > .toolbarbutton-badge-container,
#nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-button > .toolbarbutton-icon,
#nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon {
#nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon,
@conditionalForwardWithUrlbar@ > .toolbarbutton-1:-moz-any([disabled],:not([open]):not([disabled]):not(:active)) > .toolbarbutton-icon {
padding: 2px 6px;
background: hsla(210,32%,93%,0) padding-box;
border-radius: 2px;
border: 1px solid;
border-color: hsla(210,54%,20%,0) hsla(210,54%,20%,0) hsla(210,54%,20%,0);
box-shadow: 0 1px hsla(0,0%,100%,0) inset,
0 1px hsla(210,54%,20%,0),
0 0 2px hsla(210,54%,20%,0);
transition-property: background-color, border-color, box-shadow;
border-color: transparent;
transition-property: background-color, border-color;
transition-duration: 150ms;
}
%ifdef WINDOWS_AERO
@media (-moz-os-version: windows-vista),
(-moz-os-version: windows-win7) {
%endif
/* < Win8 */
#nav-bar .toolbarbutton-1 > .toolbarbutton-icon,
#nav-bar .toolbarbutton-1 > .toolbarbutton-text,
#nav-bar .toolbarbutton-1 > .toolbarbutton-badge-container,
#nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-button > .toolbarbutton-icon {
background-color: hsla(210,32%,93%,0);
background-origin: padding-box;
border-radius: 2px;
border-color: hsla(210,54%,20%,0) hsla(210,54%,20%,0) hsla(210,54%,20%,0);
box-shadow: 0 1px hsla(0,0%,100%,0) inset,
0 1px hsla(210,54%,20%,0),
0 0 2px hsla(210,54%,20%,0);
transition-property: background-color, border-color, box-shadow;
transition-duration: 150ms;
}
%ifdef WINDOWS_AERO
}
%endif
#nav-bar .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-icon,
#nav-bar .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-badge-container,
#nav-bar .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-menubutton-button > .toolbarbutton-icon {
@ -599,26 +618,17 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon {
content: "";
display: -moz-box;
width: 1px;
height: 18px;
height: 16px;
-moz-margin-end: -1px;
background-image: linear-gradient(hsla(210,54%,20%,.2) 0, hsla(210,54%,20%,.2) 18px);
background-image: linear-gradient(hsla(210,54%,20%,.2) 0, hsla(210,54%,20%,.2) 16px);
background-clip: padding-box;
background-position: center;
background-repeat: no-repeat;
background-size: 1px 18px;
box-shadow: 0 0 0 1px hsla(0,0%,100%,.2);
background-size: 1px 16px;
}
#nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-button > .toolbarbutton-icon:-moz-locale-dir(ltr),
#nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon:-moz-locale-dir(rtl) {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
#nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-button > .toolbarbutton-icon:-moz-locale-dir(rtl),
#nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon:-moz-locale-dir(ltr) {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
@conditionalForwardWithUrlbar@ > .toolbarbutton-1:-moz-any([disabled],:not([open]):not([disabled]):not(:active)) > .toolbarbutton-icon {
border-color: hsla(210,4%,10%,.1);
}
#nav-bar .toolbarbutton-1:not([disabled=true]):-moz-any(:hover,[open]) > .toolbarbutton-menubutton-button > .toolbarbutton-icon,
@ -626,48 +636,114 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon {
#nav-bar .toolbarbutton-1:not([disabled=true]):not([checked]):not([open]):not(:active):hover > .toolbarbutton-icon,
#nav-bar .toolbarbutton-1:not([disabled=true]):not([checked]):not([open]):not(:active):hover > .toolbarbutton-text,
#nav-bar .toolbarbutton-1:not([disabled=true]):not([checked]):not([open]):not(:active):hover > .toolbarbutton-badge-container,
@conditionalForwardWithUrlbar@ > .toolbarbutton-1:-moz-any([disabled=true],:not([open]):not([disabled=true]):not(:active)) > .toolbarbutton-icon {
background-image: linear-gradient(hsla(0,0%,100%,.6), hsla(0,0%,100%,.1));
border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.2) hsla(210,54%,20%,.25);
box-shadow: 0 1px hsla(0,0%,100%,.3) inset,
0 1px hsla(210,54%,20%,.03),
0 0 2px hsla(210,54%,20%,.1);
@conditionalForwardWithUrlbar@ > #forward-button:not([open]):not(:active):not([disabled]):hover > .toolbarbutton-icon,
#nav-bar .toolbarbutton-1:not([buttonover]):not([open]):not(:active):hover > .toolbarbutton-menubutton-dropmarker:not([disabled]) > .dropmarker-icon {
background-color: hsla(210,4%,10%,.08);
border-color: hsla(210,4%,10%,.1);
}
#nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-button:not([disabled=true]):not([open]):not(:active):hover > .toolbarbutton-icon,
#nav-bar .toolbarbutton-1:not([buttonover]):not([open]):not(:active):hover > .toolbarbutton-menubutton-dropmarker:not([disabled=true]) > .dropmarker-icon,
@conditionalForwardWithUrlbar@ > #forward-button:not([open]):not(:active):not([disabled=true]):hover > .toolbarbutton-icon {
border-color: hsla(210,54%,20%,.3) hsla(210,54%,20%,.35) hsla(210,54%,20%,.4);
background-color: hsla(210,48%,96%,.75);
box-shadow: 0 0 1px hsla(210,54%,20%,.03),
0 0 2px hsla(210,54%,20%,.1);
%ifdef WINDOWS_AERO
@media (-moz-os-version: windows-vista),
(-moz-os-version: windows-win7) {
%endif
/* < Win8 */
#nav-bar .toolbarbutton-1:not(:hover):not(:active):not([open]) > .toolbarbutton-menubutton-dropmarker::before,
#nav-bar .toolbaritem-combined-buttons > .toolbarbutton-1:-moz-any(:not(:hover):not([open]),[disabled]) + .toolbarbutton-1:-moz-any(:not(:hover):not([open]),[disabled])::before {
content: "";
display: -moz-box;
width: 1px;
height: 18px;
-moz-margin-end: -1px;
background-image: linear-gradient(hsla(210,54%,20%,.2) 0, hsla(210,54%,20%,.2) 18px);
background-clip: padding-box;
background-position: center;
background-repeat: no-repeat;
background-size: 1px 18px;
box-shadow: 0 0 0 1px hsla(0,0%,100%,.2);
}
#nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-button > .toolbarbutton-icon:-moz-locale-dir(ltr),
#nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon:-moz-locale-dir(rtl) {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
#nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-button > .toolbarbutton-icon:-moz-locale-dir(rtl),
#nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon:-moz-locale-dir(ltr) {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
#nav-bar .toolbarbutton-1:not([disabled]):-moz-any(:hover,[open]) > .toolbarbutton-menubutton-button > .toolbarbutton-icon,
#nav-bar .toolbarbutton-1:not([disabled]):hover > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon,
#nav-bar .toolbarbutton-1:not([disabled]):not([checked]):not([open]):not(:active):hover > .toolbarbutton-icon,
#nav-bar .toolbarbutton-1:not([disabled]):not([checked]):not([open]):not(:active):hover > .toolbarbutton-text,
#nav-bar .toolbarbutton-1:not([disabled]):not([checked]):not([open]):not(:active):hover > .toolbarbutton-badge-container,
@conditionalForwardWithUrlbar@ > .toolbarbutton-1:-moz-any([disabled],:not([open]):not([disabled]):not(:active)) > .toolbarbutton-icon {
background-image: linear-gradient(hsla(0,0%,100%,.6), hsla(0,0%,100%,.1));
background-color: transparent;
border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.2) hsla(210,54%,20%,.25);
box-shadow: 0 1px hsla(0,0%,100%,.3) inset,
0 1px hsla(210,54%,20%,.03),
0 0 2px hsla(210,54%,20%,.1);
}
#nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-button:not([disabled]):not([open]):not(:active):hover > .toolbarbutton-icon,
#nav-bar .toolbarbutton-1:not([buttonover]):not([open]):not(:active):hover > .toolbarbutton-menubutton-dropmarker:not([disabled]) > .dropmarker-icon,
@conditionalForwardWithUrlbar@ > #forward-button:not([open]):not(:active):not([disabled]):hover > .toolbarbutton-icon {
border-color: hsla(210,54%,20%,.3) hsla(210,54%,20%,.35) hsla(210,54%,20%,.4);
background-color: hsla(210,48%,96%,.75);
box-shadow: 0 0 1px hsla(210,54%,20%,.03),
0 0 2px hsla(210,54%,20%,.1);
}
%ifdef WINDOWS_AERO
}
%endif
#nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-button:not([disabled=true]):hover:active > .toolbarbutton-icon,
#nav-bar .toolbarbutton-1[open] > .toolbarbutton-menubutton-dropmarker:not([disabled=true]) > .dropmarker-icon,
#nav-bar .toolbarbutton-1:not([disabled=true]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-icon,
#nav-bar .toolbarbutton-1:not([disabled=true]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-text,
#nav-bar .toolbarbutton-1:not([disabled=true]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-badge-container {
background-image: linear-gradient(hsla(0,0%,100%,.6), hsla(0,0%,100%,.1));
background-color: hsla(210,54%,20%,.15);
border-color: hsla(210,54%,20%,.3) hsla(210,54%,20%,.35) hsla(210,54%,20%,.4);
box-shadow: 0 1px 1px hsla(210,54%,20%,.1) inset,
0 0 1px hsla(210,54%,20%,.2) inset,
/* allows windows-keyhole-forward-clip-path to be used for non-hover as well as hover: */
0 1px 0 hsla(210,54%,20%,0),
0 0 2px hsla(210,54%,20%,0);
text-shadow: none;
transition: none;
background-color: hsla(210,4%,10%,.12);
border-top-color: hsla(210,4%,10%,.2);
box-shadow: 0 1px 0 0 hsla(210,4%,10%,.1) inset;
transition-duration: 10ms;
}
#nav-bar .toolbarbutton-1:-moz-any(:hover,[open]) > .toolbarbutton-menubutton-dropmarker:not([disabled=true]) > .dropmarker-icon {
-moz-border-start-color: hsla(210,54%,20%,.35);
}
%ifdef WINDOWS_AERO
@media (-moz-os-version: windows-vista),
(-moz-os-version: windows-win7) {
%endif
/* < Win8 */
#nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-button:not([disabled]):hover:active > .toolbarbutton-icon,
#nav-bar .toolbarbutton-1[open] > .toolbarbutton-menubutton-dropmarker:not([disabled]) > .dropmarker-icon,
#nav-bar .toolbarbutton-1:not([disabled]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-icon,
#nav-bar .toolbarbutton-1:not([disabled]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-text,
#nav-bar .toolbarbutton-1:not([disabled]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-badge-container {
background-image: linear-gradient(hsla(0,0%,100%,.6), hsla(0,0%,100%,.1));
background-color: hsla(210,54%,20%,.15);
border-color: hsla(210,54%,20%,.3) hsla(210,54%,20%,.35) hsla(210,54%,20%,.4);
box-shadow: 0 1px 1px hsla(210,54%,20%,.1) inset,
0 0 1px hsla(210,54%,20%,.2) inset,
/* allows windows-keyhole-forward-clip-path to be used for non-hover as well as hover: */
0 1px 0 hsla(210,54%,20%,0),
0 0 2px hsla(210,54%,20%,0);
text-shadow: none;
transition: none;
}
#nav-bar .toolbarbutton-1[checked]:not(:active):hover > .toolbarbutton-icon {
background-color: rgba(90%,90%,90%,.4);
transition: background-color .4s;
#nav-bar .toolbarbutton-1:-moz-any(:hover,[open]) > .toolbarbutton-menubutton-dropmarker:not([disabled]) > .dropmarker-icon {
-moz-border-start-color: hsla(210,54%,20%,.35);
}
#nav-bar .toolbarbutton-1[checked]:not(:active):hover > .toolbarbutton-icon {
background-color: rgba(90%,90%,90%,.4);
transition: background-color .4s;
}
%ifdef WINDOWS_AERO
}
%endif
#TabsToolbar .toolbarbutton-1,
#TabsToolbar .toolbarbutton-1 > .toolbarbutton-menubutton-button,
@ -702,15 +778,27 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon {
}
#forward-button > .toolbarbutton-icon {
background-clip: padding-box !important;
/*mask: url(keyhole-forward-mask.svg#mask); XXX: this regresses twinopen */
clip-path: url(chrome://browser/content/browser.xul#windows-keyhole-forward-clip-path) !important;
margin-left: -6px !important;
margin-left: -7px !important;
border-left-style: none !important;
border-radius: 0 !important;
padding-left: 7px !important;
padding-right: 3px !important;
}
%ifdef WINDOWS_AERO
@media (-moz-os-version: windows-vista),
(-moz-os-version: windows-win7) {
%endif
#forward-button > .toolbarbutton-icon {
margin-left: -6px !important;
}
%ifdef WINDOWS_AERO
}
%endif
@conditionalForwardWithUrlbar@:not([switchingtabs]) > #forward-button {
transition: opacity @forwardTransitionLength@ ease-out;
}
@ -743,40 +831,65 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon {
#back-button > .toolbarbutton-icon {
border-radius: 10000px !important;
background-clip: padding-box !important;
background-color: hsla(210,25%,98%,.08) !important;
padding: 5px !important;
border: none !important;
background-image: linear-gradient(hsla(0,0%,100%,.6), hsla(0,0%,100%,.1)) !important;
box-shadow: 0 1px 0 hsla(0,0%,100%,.3) inset,
0 0 0 1px hsla(0,0%,100%,.3) inset,
0 0 0 1px hsla(210,54%,20%,.25),
0 1px 0 hsla(210,54%,20%,.35) !important;
transition-property: background-color, box-shadow !important;
border-color: hsla(210,4%,10%,.25) !important;
transition-property: background-color, border-color !important;
transition-duration: 250ms !important;
}
#back-button:not([disabled="true"]):not([open="true"]):not(:active):hover > .toolbarbutton-icon {
background-color: hsla(210,48%,96%,.75) !important;
box-shadow: 0 1px 0 hsla(0,0%,100%,.3) inset,
0 0 0 1px hsla(0,0%,100%,.3) inset,
0 0 0 1px hsla(210,54%,20%,.3),
0 1px 0 hsla(210,54%,20%,.4),
0 0 4px hsla(210,54%,20%,.2) !important;
background-color: hsla(210,4%,10%,.08) !important;
box-shadow: none !important;
}
#back-button:not([disabled="true"]):hover:active > .toolbarbutton-icon,
#back-button[open="true"] > .toolbarbutton-icon {
background-color: hsla(210,54%,20%,.15) !important;
box-shadow: 0 1px 1px hsla(210,54%,20%,.1) inset,
0 0 1px hsla(210,54%,20%,.2) inset,
0 0 0 1px hsla(210,54%,20%,.4),
0 1px 0 hsla(210,54%,20%,.2) !important;
transition: none;
background-color: hsla(210,4%,10%,.12) !important;
box-shadow: 0 1px 0 0 hsla(210,80%,20%,.1) inset !important;
}
#main-window:not([customizing]) #back-button[disabled] > .toolbarbutton-icon {
box-shadow: 0 0 0 1px hsla(210,54%,20%,.55),
0 1px 0 hsla(210,54%,20%,.65) !important;
transition: none;
%ifdef WINDOWS_AERO
@media (-moz-os-version: windows-vista),
(-moz-os-version: windows-win7) {
%endif
#back-button > .toolbarbutton-icon {
border: none !important;
background-image: linear-gradient(hsla(0,0%,100%,.6), hsla(0,0%,100%,.1)) !important;
box-shadow: 0 1px 0 hsla(0,0%,100%,.3) inset,
0 0 0 1px hsla(0,0%,100%,.3) inset,
0 0 0 1px hsla(210,54%,20%,.25),
0 1px 0 hsla(210,54%,20%,.35) !important;
transition-property: background-color, box-shadow !important;
}
#back-button:not([disabled="true"]):not([open="true"]):not(:active):hover > .toolbarbutton-icon {
background-color: hsla(210,48%,96%,.75) !important;
box-shadow: 0 1px 0 hsla(0,0%,100%,.3) inset,
0 0 0 1px hsla(0,0%,100%,.3) inset,
0 0 0 1px hsla(210,54%,20%,.3),
0 1px 0 hsla(210,54%,20%,.4),
0 0 4px hsla(210,54%,20%,.2) !important;
}
#back-button:not([disabled="true"]):hover:active > .toolbarbutton-icon,
#back-button[open="true"] > .toolbarbutton-icon {
background-color: hsla(210,54%,20%,.15) !important;
box-shadow: 0 1px 1px hsla(210,54%,20%,.1) inset,
0 0 1px hsla(210,54%,20%,.2) inset,
0 0 0 1px hsla(210,54%,20%,.4),
0 1px 0 hsla(210,54%,20%,.2) !important;
transition: none;
}
#main-window:not([customizing]) #back-button[disabled] > .toolbarbutton-icon {
box-shadow: 0 0 0 1px hsla(210,54%,20%,.55),
0 1px 0 hsla(210,54%,20%,.65) !important;
transition: none;
}
%ifdef WINDOWS_AERO
}
%endif
#back-button:-moz-locale-dir(rtl) > .toolbarbutton-icon,
#forward-button:-moz-locale-dir(rtl) {
@ -899,9 +1012,21 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon {
padding: 0;
background-clip: padding-box;
border: 1px solid ThreeDShadow;
border-radius: 2px;
}
%ifdef WINDOWS_AERO
@media (-moz-os-version: windows-vista),
(-moz-os-version: windows-win7) {
%endif
/* < Win8 */
#urlbar,
.searchbar-textbox {
border-radius: 2px;
}
%ifdef WINDOWS_AERO
}
%endif
#urlbar {
-moz-padding-end: 2px;
}

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

@ -47,6 +47,18 @@
-moz-margin-start: 3px;
}
/* subviewbutton entries for social sidebars have images that come from external
/* sources, and are not guaranteed to be the size we want, so force the size on
/* those icons. */
toolbarbutton.social-provider-menuitem > .toolbarbutton-icon {
width: 16px;
height: 16px;
}
.subviewbutton:-moz-any([image],[targetURI],.cui-withicon, .restoreallitem, .bookmark-item)[checked="true"] > .toolbarbutton-icon {
visibility: hidden;
}
%ifdef WINDOWS_AERO
/* Win8 and beyond. */
@media not all and (-moz-os-version: windows-vista) {

Двоичные данные
browser/themes/windows/menuPanel-aero.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 28 KiB

После

Ширина:  |  Высота:  |  Размер: 28 KiB

Двоичные данные
browser/themes/windows/menuPanel.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 23 KiB

После

Ширина:  |  Высота:  |  Размер: 23 KiB

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

@ -9,12 +9,13 @@ let Promise =
let bluetoothManager;
/* Get mozSettings value specified by @aKey.
/**
* Get mozSettings value specified by @aKey.
*
* Resolve if that mozSettings value is retrieved successfully, reject
* otherwise.
*
* Forfill params:
* Fulfill params:
* The corresponding mozSettings value of the key.
* Reject params: (none)
*
@ -39,11 +40,12 @@ function getSettings(aKey) {
return deferred.promise;
}
/* Set mozSettings values.
/**
* Set mozSettings values.
*
* Resolve if that mozSettings value is set successfully, reject otherwise.
*
* Forfill params: (none)
* Fulfill params: (none)
* Reject params: (none)
*
* @param aSettings
@ -67,12 +69,13 @@ function setSettings(aSettings) {
return deferred.promise;
}
/* Get mozSettings value of 'bluetooth.enabled'.
/**
* Get mozSettings value of 'bluetooth.enabled'.
*
* Resolve if that mozSettings value is retrieved successfully, reject
* otherwise.
*
* Forfill params:
* Fulfill params:
* A boolean value.
* Reject params: (none)
*
@ -82,11 +85,12 @@ function getBluetoothEnabled() {
return getSettings("bluetooth.enabled");
}
/* Set mozSettings value of 'bluetooth.enabled'.
/**
* Set mozSettings value of 'bluetooth.enabled'.
*
* Resolve if that mozSettings value is set successfully, reject otherwise.
*
* Forfill params: (none)
* Fulfill params: (none)
* Reject params: (none)
*
* @param aEnabled
@ -100,10 +104,11 @@ function setBluetoothEnabled(aEnabled) {
return setSettings(obj);
}
/* Push required permissions and test if |navigator.mozBluetooth| exists.
/**
* Push required permissions and test if |navigator.mozBluetooth| exists.
* Resolve if it does, reject otherwise.
*
* Forfill params:
* Fulfill params:
* bluetoothManager -- an reference to navigator.mozBluetooth.
* Reject params: (none)
*
@ -151,11 +156,12 @@ function ensureBluetoothManager(aPermissions) {
return deferred.promise;
}
/* Wait for one named BluetoothManager event.
/**
* Wait for one named BluetoothManager event.
*
* Resolve if that named event occurs. Never reject.
*
* Forfill params: the DOMEvent passed.
* Fulfill params: the DOMEvent passed.
*
* @return A deferred promise.
*/
@ -172,12 +178,13 @@ function waitForManagerEvent(aEventName) {
return deferred.promise;
}
/* Convenient function for setBluetoothEnabled and waitForManagerEvent
/**
* Convenient function for setBluetoothEnabled and waitForManagerEvent
* combined.
*
* Resolve if that named event occurs. Reject if we can't set settings.
*
* Forfill params: the DOMEvent passed.
* Fulfill params: the DOMEvent passed.
* Reject params: (none)
*
* @return A deferred promise.
@ -198,11 +205,12 @@ function setBluetoothEnabledAndWait(aEnabled) {
return Promise.all(promises);
}
/* Get default adapter.
/**
* Get default adapter.
*
* Resolve if that default adapter is got, reject otherwise.
*
* Forfill params: a BluetoothAdapter instance.
* Fulfill params: a BluetoothAdapter instance.
* Reject params: a DOMError, or null if if there is no adapter ready yet.
*
* @return A deferred promise.
@ -237,7 +245,8 @@ function getDefaultAdapter() {
return deferred.promise;
}
/* Flush permission settings and call |finish()|.
/**
* Flush permission settings and call |finish()|.
*/
function cleanUp() {
SpecialPowers.flushPermissions(function() {

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

@ -0,0 +1,399 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
const {Cc: Cc, Ci: Ci, Cr: Cr, Cu: Cu} = SpecialPowers;
const SETTINGS_KEY_DATA_ENABLED = "ril.data.enabled";
const SETTINGS_KEY_DATA_ROAMING_ENABLED = "ril.data.roaming_enabled";
const SETTINGS_KEY_DATA_APN_SETTINGS = "ril.data.apnSettings";
let Promise = Cu.import("resource://gre/modules/Promise.jsm").Promise;
let _pendingEmulatorCmdCount = 0;
/**
* Send emulator command with safe guard.
*
* We should only call |finish()| after all emulator command transactions
* end, so here comes with the pending counter. Resolve when the emulator
* gives positive response, and reject otherwise.
*
* Fulfill params:
* result -- an array of emulator response lines.
* Reject params:
* result -- an array of emulator response lines.
*
* @param aCommand
* A string command to be passed to emulator through its telnet console.
*
* @return A deferred promise.
*/
function runEmulatorCmdSafe(aCommand) {
let deferred = Promise.defer();
++_pendingEmulatorCmdCount;
runEmulatorCmd(aCommand, function(aResult) {
--_pendingEmulatorCmdCount;
ok(true, "Emulator response: " + JSON.stringify(aResult));
if (Array.isArray(aResult) && aResult[0] === "OK") {
deferred.resolve(aResult);
} else {
deferred.reject(aResult);
}
});
return deferred.promise;
}
/**
* Get mozSettings value specified by @aKey.
*
* Resolve if that mozSettings value is retrieved successfully, reject
* otherwise.
*
* Fulfill params:
* The corresponding mozSettings value of the key.
* Reject params: (none)
*
* @param aKey
* A string.
* @param aAllowError [optional]
* A boolean value. If set to true, an error response won't be treated
* as test failure. Default: false.
*
* @return A deferred promise.
*/
function getSettings(aKey, aAllowError) {
let deferred = Promise.defer();
let request = navigator.mozSettings.createLock().get(aKey);
request.addEventListener("success", function(aEvent) {
ok(true, "getSettings(" + aKey + ") - success");
deferred.resolve(aEvent.target.result[aKey]);
});
request.addEventListener("error", function() {
ok(aAllowError, "getSettings(" + aKey + ") - error");
deferred.reject();
});
return deferred.promise;
}
/**
* Set mozSettings values.
*
* Resolve if that mozSettings value is set successfully, reject otherwise.
*
* Fulfill params: (none)
* Reject params: (none)
*
* @param aSettings
* An object of format |{key1: value1, key2: value2, ...}|.
* @param aAllowError [optional]
* A boolean value. If set to true, an error response won't be treated
* as test failure. Default: false.
*
* @return A deferred promise.
*/
function setSettings(aSettings, aAllowError) {
let deferred = Promise.defer();
let request = navigator.mozSettings.createLock().set(aSettings);
request.addEventListener("success", function() {
ok(true, "setSettings(" + JSON.stringify(aSettings) + ")");
deferred.resolve();
});
request.addEventListener("error", function() {
ok(aAllowError, "setSettings(" + JSON.stringify(aSettings) + ")");
deferred.reject();
});
return deferred.promise;
}
/**
* Set mozSettings value with only one key.
*
* Resolve if that mozSettings value is set successfully, reject otherwise.
*
* Fulfill params: (none)
* Reject params: (none)
*
* @param aKey
* A string key.
* @param aValue
* An object value.
* @param aAllowError [optional]
* A boolean value. If set to true, an error response won't be treated
* as test failure. Default: false.
*
* @return A deferred promise.
*/
function setSettings1(aKey, aValue, aAllowError) {
let settings = {};
settings[aKey] = aValue;
return setSettings(settings, aAllowError);
}
/**
* Convenient MozSettings getter for SETTINGS_KEY_DATA_ENABLED.
*/
function getDataEnabled(aAllowError) {
return getSettings(SETTINGS_KEY_DATA_ENABLED, aAllowError);
}
/**
* Convenient MozSettings setter for SETTINGS_KEY_DATA_ENABLED.
*/
function setDataEnabled(aEnabled, aAllowError) {
return setSettings1(SETTINGS_KEY_DATA_ENABLED, aEnabled, aAllowError);
}
/**
* Convenient MozSettings getter for SETTINGS_KEY_DATA_ROAMING_ENABLED.
*/
function getDataRoamingEnabled(aAllowError) {
return getSettings(SETTINGS_KEY_DATA_ROAMING_ENABLED, aAllowError);
}
/**
* Convenient MozSettings setter for SETTINGS_KEY_DATA_ROAMING_ENABLED.
*/
function setDataRoamingEnabled(aEnabled, aAllowError) {
return setSettings1(SETTINGS_KEY_DATA_ROAMING_ENABLED, aEnabled, aAllowError);
}
/**
* Convenient MozSettings getter for SETTINGS_KEY_DATA_APN_SETTINGS.
*/
function getDataApnSettings(aAllowError) {
return getSettings(SETTINGS_KEY_DATA_APN_SETTINGS, aAllowError);
}
/**
* Convenient MozSettings setter for SETTINGS_KEY_DATA_APN_SETTINGS.
*/
function setDataApnSettings(aApnSettings, aAllowError) {
return setSettings1(SETTINGS_KEY_DATA_APN_SETTINGS, aApnSettings, aAllowError);
}
let mobileConnection;
/**
* Push required permissions and test if
* |navigator.mozMobileConnections[<aServiceId>]| exists. Resolve if it does,
* reject otherwise.
*
* Fulfill params:
* mobileConnection -- an reference to navigator.mozMobileMessage.
*
* Reject params: (none)
*
* @param aAdditonalPermissions [optional]
* An array of permission strings other than "mobileconnection" to be
* pushed. Default: empty string.
* @param aServiceId [optional]
* A numeric DSDS service id. Default: 0.
*
* @return A deferred promise.
*/
function ensureMobileConnection(aAdditionalPermissions, aServiceId) {
let deferred = Promise.defer();
aAdditionalPermissions = aAdditionalPermissions || [];
aServiceId = aServiceId || 0;
if (aAdditionalPermissions.indexOf("mobileconnection") < 0) {
aAdditionalPermissions.push("mobileconnection");
}
let permissions = [];
for (let perm of aAdditionalPermissions) {
permissions.push({ "type": perm, "allow": 1, "context": document });
}
SpecialPowers.pushPermissions(permissions, function() {
ok(true, "permissions pushed: " + JSON.stringify(permissions));
// Permission changes can't change existing Navigator.prototype
// objects, so grab our objects from a new Navigator.
let ifr = document.createElement("iframe");
ifr.addEventListener("load", function load() {
ifr.removeEventListener("load", load);
mobileConnection =
ifr.contentWindow.navigator.mozMobileConnections[aServiceId];
if (mobileConnection) {
log("navigator.mozMobileConnections[" + aServiceId + "] is instance of " +
mobileConnection.constructor);
} else {
log("navigator.mozMobileConnections[" + aServiceId + "] is undefined");
}
if (mobileConnection instanceof MozMobileConnection) {
deferred.resolve(mobileConnection);
} else {
deferred.reject();
}
});
document.body.appendChild(ifr);
});
return deferred.promise;
}
/**
* Wait for one named MobileConnection event.
*
* Resolve if that named event occurs. Never reject.
*
* Fulfill params: the DOMEvent passed.
*
* @param aEventName
* A string event name.
*
* @return A deferred promise.
*/
function waitForManagerEvent(aEventName) {
let deferred = Promise.defer();
mobileConnection.addEventListener(aEventName, function onevent(aEvent) {
mobileConnection.removeEventListener(aEventName, onevent);
ok(true, "MobileConnection event '" + aEventName + "' got.");
deferred.resolve(aEvent);
});
return deferred.promise;
}
/**
* Set data connection enabling state and wait for "datachange" event.
*
* Resolve if data connection state changed to the expected one. Never reject.
*
* Fulfill params: (none)
*
* @param aEnabled
* A boolean state.
*
* @return A deferred promise.
*/
function setDataEnabledAndWait(aEnabled) {
let deferred = Promise.defer();
let promises = [];
promises.push(waitForManagerEvent("datachange"));
promises.push(setDataEnabled(aEnabled));
Promise.all(promises).then(function keepWaiting() {
// To ignore some transient states, we only resolve that deferred promise
// when the |connected| state equals to the expected one and never rejects.
let connected = mobileConnection.data.connected;
if (connected == aEnabled) {
deferred.resolve();
return;
}
return waitForManagerEvent("datachange").then(keepWaiting);
});
return deferred.promise;
}
/**
* Set voice/data roaming emulation and wait for state change.
*
* Fulfill params: (none)
*
* @param aRoaming
* A boolean state.
*
* @return A deferred promise.
*/
function setEmulatorRoamingAndWait(aRoaming) {
function doSetAndWait(aWhich, aRoaming) {
let promises = [];
promises.push(waitForManagerEvent(aWhich + "change"));
let cmd = "gsm " + aWhich + " " + (aRoaming ? "roaming" : "home");
promises.push(runEmulatorCmdSafe(cmd));
return Promise.all(promises)
.then(() => is(mobileConnection[aWhich].roaming, aRoaming,
aWhich + ".roaming"));
}
// Set voice registration state first and then data registration state.
return doSetAndWait("voice", aRoaming)
.then(() => doSetAndWait("data", aRoaming));
}
let _networkManager;
/**
* Get internal NetworkManager service.
*/
function getNetworkManager() {
if (!_networkManager) {
_networkManager = Cc["@mozilla.org/network/manager;1"]
.getService(Ci.nsINetworkManager);
ok(_networkManager, "NetworkManager");
}
return _networkManager;
}
/**
* Flush permission settings and call |finish()|.
*/
function cleanUp() {
waitFor(function() {
SpecialPowers.flushPermissions(function() {
// Use ok here so that we have at least one test run.
ok(true, "permissions flushed");
finish();
});
}, function() {
return _pendingEmulatorCmdCount === 0;
});
}
/**
* Basic test routine helper for mobile connection tests.
*
* This helper does nothing but clean-ups.
*
* @param aTestCaseMain
* A function that takes no parameter.
*/
function startTestBase(aTestCaseMain) {
Promise.resolve()
.then(aTestCaseMain)
.then(cleanUp, function() {
ok(false, 'promise rejects during test.');
cleanUp();
});
}
/**
* Common test routine helper for mobile connection tests.
*
* This function ensures global |mobileConnection| variable is available during
* the process and performs clean-ups as well.
*
* @param aTestCaseMain
* A function that takes one parameter -- mobileConnection.
* @param aAdditonalPermissions [optional]
* An array of permission strings other than "mobileconnection" to be
* pushed. Default: empty string.
* @param aServiceId [optional]
* A numeric DSDS service id. Default: 0.
*/
function startTestCommon(aTestCaseMain, aAdditionalPermissions, aServiceId) {
startTestBase(function() {
return ensureMobileConnection(aAdditionalPermissions, aServiceId)
.then(aTestCaseMain);
});
}

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

@ -23,3 +23,5 @@ disabled = Bug 808783
[test_mobile_last_known_network.js]
[test_mobile_icc_change.js]
[test_mobile_connections_array_uninitialized.js]
[test_mobile_data_ipv6.js]
disabled = Bug 978071

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

@ -0,0 +1,112 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
MARIONETTE_TIMEOUT = 60000;
MARIONETTE_HEAD_JS = "head.js";
/**
* Test resulting IP address format with given APN settings.
*
* This test utility function performs following steps:
*
* 1) set "ril.data.apnSettings" to a given settings object,
* 2) enable data connection and wait for a "datachange" event,
* 3) check the IP address type of the active network interface,
* 4) disable data connection.
*
* Fulfill params: (none)
* Reject params: (none)
*
* @param aApnSettings
* An APN settings value.
* @param aIsIPv6
* A boolean value indicating whether we're expecting an IPv6 address.
*
* @return A deferred promise.
*/
function doTest(aApnSettings, aIsIPv6) {
return setDataApnSettings([])
.then(() => setDataApnSettings(aApnSettings))
.then(() => setDataEnabledAndWait(true))
.then(function() {
let nm = getNetworkManager();
let active = nm.active;
ok(active, "Active network interface");
log(" Interface: " + active.name);
log(" Address: " + active.ip);
if (aIsIPv6) {
ok(active.ip.indexOf(":") > 0, "IPv6 address");
} else {
ok(active.ip.indexOf(":") < 0, "IPv4 address");
}
})
.then(() => setDataEnabledAndWait(false));
}
function doTestHome(aApnSettings, aProtocol) {
log("Testing \"" + aProtocol + "\"@HOME... ");
// aApnSettings is a double-array of per PDP context settings. The first
// index is a DSDS service ID, and the second one is the index of pre-defined
// PDP context settings of a specified radio interface. We use 0 for both as
// default here.
aApnSettings[0][0].protocol = aProtocol;
delete aApnSettings[0][0].roaming_protocol;
return doTest(aApnSettings, aProtocol === "IPV6");
}
function doTestRoaming(aApnSettings, aRoaminProtocol) {
log("Testing \"" + aRoaminProtocol + "\"@ROMAING... ");
// aApnSettings is a double-array of per PDP context settings. The first
// index is a DSDS service ID, and the second one is the index of pre-defined
// PDP context settings of a specified radio interface. We use 0 for both as
// default here.
delete aApnSettings[0][0].protocol;
aApnSettings[0][0].roaming_protocol = aRoaminProtocol;
return doTest(aApnSettings, aRoaminProtocol === "IPV6");
}
startTestCommon(function() {
let origApnSettings;
return setDataRoamingEnabled(true)
.then(getDataApnSettings)
.then(function(aResult) {
// If already set, then save original APN settings.
origApnSettings = JSON.parse(JSON.stringify(aResult));
return aResult;
}, function() {
// Return our own default value.
return [[{ "carrier": "T-Mobile US",
"apn": "epc.tmobile.com",
"mmsc": "http://mms.msg.eng.t-mobile.com/mms/wapenc",
"types": ["default", "supl", "mms"] }]];
})
.then(function(aApnSettings) {
return Promise.resolve()
.then(() => doTestHome(aApnSettings, "NoSuchProtocol"))
.then(() => doTestHome(aApnSettings, "IP"))
.then(() => doTestHome(aApnSettings, "IPV6"))
.then(() => setEmulatorRoamingAndWait(true))
.then(() => doTestRoaming(aApnSettings, "NoSuchProtocol"))
.then(() => doTestRoaming(aApnSettings, "IP"))
.then(() => doTestRoaming(aApnSettings, "IPV6"))
.then(() => setEmulatorRoamingAndWait(false));
})
.then(() => setDataRoamingEnabled(false))
.then(function() {
if (origApnSettings) {
return setDataApnSettings(origApnSettings);
}
});
}, ["settings-read", "settings-write"]);

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

@ -5,7 +5,8 @@ const {Cc: Cc, Ci: Ci, Cr: Cr, Cu: Cu} = SpecialPowers;
let Promise = Cu.import("resource://gre/modules/Promise.jsm").Promise;
/* Push required permissions and test if |navigator.mozMobileMessage| exists.
/**
* Push required permissions and test if |navigator.mozMobileMessage| exists.
* Resolve if it does, reject otherwise.
*
* Fulfill params:
@ -44,7 +45,8 @@ function ensureMobileMessage() {
return deferred.promise;
}
/* Send a SMS message to a single receiver. Resolve if it succeeds, reject
/**
* Send a SMS message to a single receiver. Resolve if it succeeds, reject
* otherwise.
*
* Fulfill params:
@ -72,7 +74,8 @@ function sendSmsWithSuccess(aReceiver, aText) {
return deferred.promise;
}
/* Send a MMS message with specified parameters. Resolve if it fails, reject
/**
* Send a MMS message with specified parameters. Resolve if it fails, reject
* otherwise.
*
* Fulfill params:
@ -100,7 +103,8 @@ function sendMmsWithFailure(aMmsParameters) {
return deferred.promise;
}
/* Retrieve messages from database.
/**
* Retrieve messages from database.
*
* Fulfill params:
* messages -- an array of {Sms,Mms}Message instances.
@ -136,7 +140,8 @@ function getMessages(aFilter, aReverse) {
return deferred.promise;
}
/* Retrieve all messages from database.
/**
* Retrieve all messages from database.
*
* Fulfill params:
* messages -- an array of {Sms,Mms}Message instances.
@ -150,7 +155,8 @@ function getAllMessages() {
return getMessages(null, false);
}
/* Retrieve all threads from database.
/**
* Retrieve all threads from database.
*
* Fulfill params:
* threads -- an array of MozMobileMessageThread instances.
@ -179,7 +185,8 @@ function getAllThreads() {
return deferred.promise;
}
/* Retrieve a single specified thread from database.
/**
* Retrieve a single specified thread from database.
*
* Fulfill params:
* thread -- a MozMobileMessageThread instance.
@ -204,7 +211,8 @@ function getThreadById(aThreadId) {
});
}
/* Delete messages specified from database.
/**
* Delete messages specified from database.
*
* Fulfill params:
* result -- an array of boolean values indicating whether delesion was
@ -234,7 +242,8 @@ function deleteMessagesById(aMessageIds) {
return deferred.promise;
}
/* Delete messages specified from database.
/**
* Delete messages specified from database.
*
* Fulfill params:
* result -- an array of boolean values indicating whether delesion was
@ -252,7 +261,8 @@ function deleteMessages(aMessages) {
return deleteMessagesById(ids);
}
/* Delete all messages from database.
/**
* Delete all messages from database.
*
* Fulfill params:
* ids -- an array of numeric values identifying those deleted
@ -269,7 +279,8 @@ function deleteAllMessages() {
let pendingEmulatorCmdCount = 0;
/* Send emulator command with safe guard.
/**
* Send emulator command with safe guard.
*
* We should only call |finish()| after all emulator command transactions
* end, so here comes with the pending counter. Resolve when the emulator
@ -301,7 +312,8 @@ function runEmulatorCmdSafe(aCommand) {
return deferred.promise;
}
/* Send simple text SMS to emulator.
/**
* Send simple text SMS to emulator.
*
* Fulfill params:
* result -- an array of emulator response lines.
@ -316,7 +328,8 @@ function sendTextSmsToEmulator(aFrom, aText) {
return runEmulatorCmdSafe(command);
}
/* Send raw SMS TPDU to emulator.
/**
* Send raw SMS TPDU to emulator.
*
* Fulfill params:
* result -- an array of emulator response lines.
@ -331,7 +344,8 @@ function sendRawSmsToEmulator(aPdu) {
return runEmulatorCmdSafe(command);
}
/* Name space for MobileMessageDB.jsm. Only initialized after first call to
/**
* Name space for MobileMessageDB.jsm. Only initialized after first call to
* newMobileMessageDB.
*/
let MMDB;
@ -348,7 +362,8 @@ function newMobileMessageDB() {
return mmdb;
}
/* Initialize a MobileMessageDB. Resolve if initialized with success, reject
/**
* Initialize a MobileMessageDB. Resolve if initialized with success, reject
* otherwise.
*
* Fulfill params: a MobileMessageDB instance.
@ -378,7 +393,8 @@ function initMobileMessageDB(aMmdb, aDbName, aDbVersion) {
return deferred.promise;
}
/* Close a MobileMessageDB.
/**
* Close a MobileMessageDB.
*
* @return The passed MobileMessageDB instance.
*/
@ -387,7 +403,8 @@ function closeMobileMessageDB(aMmdb) {
return aMmdb;
}
/* Create a new array of id attribute of input messages.
/**
* Create a new array of id attribute of input messages.
*
* @param aMessages an array of {Sms,Mms}Message instances.
*
@ -404,7 +421,8 @@ function messagesToIds(aMessages) {
// A reference to a nsIUUIDGenerator service.
let uuidGenerator;
/* Generate a new UUID.
/**
* Generate a new UUID.
*
* @return A UUID string.
*/
@ -418,7 +436,8 @@ function newUUID() {
return uuidGenerator.generateUUID().toString();
}
/* Flush permission settings and call |finish()|.
/**
* Flush permission settings and call |finish()|.
*/
function cleanUp() {
waitFor(function() {
@ -433,6 +452,14 @@ function cleanUp() {
});
}
/**
* Basic test routine helper for mobile message tests.
*
* This helper does nothing but clean-ups.
*
* @param aTestCaseMain
* A function that takes no parameter.
*/
function startTestBase(aTestCaseMain) {
Promise.resolve()
.then(aTestCaseMain)
@ -442,6 +469,15 @@ function startTestBase(aTestCaseMain) {
});
}
/**
* Common test routine helper for mobile message tests.
*
* This function ensures global |manager| variable is available during the
* process and performs clean-ups as well.
*
* @param aTestCaseMain
* A function that takes no parameter.
*/
function startTestCommon(aTestCaseMain) {
startTestBase(function() {
return ensureMobileMessage()

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

@ -52,6 +52,10 @@ let RILQUIRKS_DATA_REGISTRATION_ON_DEMAND =
let RILQUIRKS_RADIO_OFF_WO_CARD =
libcutils.property_get("ro.moz.ril.radio_off_wo_card", "false") == "true";
// Ril quirk to enable IPv6 protocol/roaming protocol in APN settings.
let RILQUIRKS_HAVE_IPV6 =
libcutils.property_get("ro.moz.ril.ipv6", "false") == "true";
const RADIOINTERFACELAYER_CID =
Components.ID("{2d831c8d-6017-435b-a80c-e5d422810cea}");
const RADIOINTERFACE_CID =
@ -4525,12 +4529,25 @@ RILNetworkInterface.prototype = {
}
authType = RIL.RIL_DATACALL_AUTH_TO_GECKO.indexOf(RIL.GECKO_DATACALL_AUTH_DEFAULT);
}
let pdpType = RIL.GECKO_DATACALL_PDP_TYPE_IP;
if (RILQUIRKS_HAVE_IPV6) {
pdpType = !radioInterface.rilContext.data.roaming
? this.apnSetting.protocol
: this.apnSetting.roaming_protocol;
if (RIL.RIL_DATACALL_PDP_TYPES.indexOf(pdpType) < 0) {
if (DEBUG) {
this.debug("Invalid pdpType '" + pdpType + "', using '" +
RIL.GECKO_DATACALL_PDP_TYPE_DEFAULT + "'");
}
pdpType = RIL.GECKO_DATACALL_PDP_TYPE_DEFAULT;
}
}
radioInterface.setupDataCall(radioTechnology,
this.apnSetting.apn,
this.apnSetting.user,
this.apnSetting.password,
authType,
"IP");
pdpType);
this.connecting = true;
},

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

@ -2361,6 +2361,15 @@ this.RIL_DATACALL_AUTH_TO_GECKO = [
GECKO_DATACALL_AUTH_PAP_OR_CHAP // DATACALL_AUTH_PAP_OR_CHAP
];
this.GECKO_DATACALL_PDP_TYPE_IP = "IP";
this.GECKO_DATACALL_PDP_TYPE_IPV6 = "IPV6";
this.GECKO_DATACALL_PDP_TYPE_DEFAULT = GECKO_DATACALL_PDP_TYPE_IP;
this.RIL_DATACALL_PDP_TYPES = [
GECKO_DATACALL_PDP_TYPE_IP,
GECKO_DATACALL_PDP_TYPE_IPV6,
// TODO: Bug 978711 - Support IPV4V6
];
this.DATACALL_PROFILE_DEFAULT = 0;
this.DATACALL_PROFILE_TETHERED = 1;
this.DATACALL_PROFILE_OEM_BASE = 1000;

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

@ -3962,8 +3962,9 @@ RilObject.prototype = {
}
currentDataCall.gw = updatedDataCall.gw;
if (updatedDataCall.dns) {
currentDataCall.dns[0] = updatedDataCall.dns[0];
currentDataCall.dns[1] = updatedDataCall.dns[1];
currentDataCall.dns = updatedDataCall.dns.slice();
} else {
currentDataCall.dns = [];
}
currentDataCall.rilMessageType = "datacallstatechange";
this.sendChromeMessage(currentDataCall);

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

@ -16,6 +16,7 @@ import java.net.URL;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
@ -678,10 +679,7 @@ public abstract class GeckoApp
Intent intent = GeckoAppShell.getOpenURIIntent(sAppContext, message.optString("url"),
message.optString("mime"), message.optString("action"), message.optString("title"));
String[] handlers = GeckoAppShell.getHandlersForIntent(intent);
ArrayList<String> appList = new ArrayList<String>(handlers.length);
for (int i = 0; i < handlers.length; i++) {
appList.add(handlers[i]);
}
List<String> appList = Arrays.asList(handlers);
JSONObject handlersJSON = new JSONObject();
handlersJSON.put("apps", new JSONArray(appList));
EventDispatcher.sendResponse(message, handlersJSON);

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

@ -9,7 +9,9 @@ import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Map;
import java.util.TreeMap;
import org.json.simple.JSONObject;
import org.json.simple.parser.ParseException;
import org.mozilla.apache.commons.codec.binary.Base64;
import org.mozilla.apache.commons.codec.binary.StringUtils;
@ -28,6 +30,7 @@ import org.mozilla.gecko.sync.Utils;
public class JSONWebTokenUtils {
public static final long DEFAULT_CERTIFICATE_DURATION_IN_MILLISECONDS = 60 * 60 * 1000;
public static final long DEFAULT_ASSERTION_DURATION_IN_MILLISECONDS = 60 * 60 * 1000;
public static final long DEFAULT_FUTURE_EXPIRES_AT_IN_MILLISECONDS = 9999999999999L;
public static final String DEFAULT_CERTIFICATE_ISSUER = "127.0.0.1";
public static final String DEFAULT_ASSERTION_ISSUER = "127.0.0.1";
@ -70,8 +73,12 @@ public class JSONWebTokenUtils {
return payload;
}
protected static String getPayloadString(String payloadString, String issuer,
long issuedAt, String audience, long expiresAt) throws NonObjectJSONException,
/**
* Public for testing.
*/
@SuppressWarnings("unchecked")
public static String getPayloadString(String payloadString, String audience, String issuer,
Long issuedAt, long expiresAt) throws NonObjectJSONException,
IOException, ParseException {
ExtendedJSONObject payload;
if (payloadString != null) {
@ -79,13 +86,16 @@ public class JSONWebTokenUtils {
} else {
payload = new ExtendedJSONObject();
}
payload.put("iss", issuer);
payload.put("iat", issuedAt);
if (audience != null) {
payload.put("aud", audience);
}
payload.put("iss", issuer);
if (issuedAt != null) {
payload.put("iat", issuedAt);
}
payload.put("exp", expiresAt);
return payload.toJSONString();
// TreeMap so that keys are sorted. A small attempt to keep output stable over time.
return JSONObject.toJSONString(new TreeMap<Object, Object>(payload.object));
}
protected static String getCertificatePayloadString(VerifyingPublicKey publicKeyToSign, String email) throws NonObjectJSONException, IOException, ParseException {
@ -100,33 +110,42 @@ public class JSONWebTokenUtils {
public static String createCertificate(VerifyingPublicKey publicKeyToSign, String email,
String issuer, long issuedAt, long expiresAt, SigningPrivateKey privateKey) throws NonObjectJSONException, IOException, ParseException, GeneralSecurityException {
String certificatePayloadString = getCertificatePayloadString(publicKeyToSign, email);
String payloadString = getPayloadString(certificatePayloadString, issuer, issuedAt, null, expiresAt);
String payloadString = getPayloadString(certificatePayloadString, null, issuer, issuedAt, expiresAt);
return JSONWebTokenUtils.encode(payloadString, privateKey);
}
public static String createCertificate(VerifyingPublicKey publicKeyToSign, String email, SigningPrivateKey privateKey) throws NonObjectJSONException, IOException, ParseException, GeneralSecurityException {
String issuer = DEFAULT_CERTIFICATE_ISSUER;
long issuedAt = System.currentTimeMillis();
long durationInMilliseconds = DEFAULT_CERTIFICATE_DURATION_IN_MILLISECONDS;
return createCertificate(publicKeyToSign, email, issuer, issuedAt, issuedAt + durationInMilliseconds, privateKey);
}
/**
* Create a Browser ID assertion.
*
* @param privateKeyToSignWith
* private key to sign assertion with.
* @param certificate
* to include in assertion; no attempt is made to ensure the
* certificate is valid, or corresponds to the private key, or any
* other condition.
* @param audience
* to produce assertion for.
* @param issuer
* to produce assertion for.
* @param issuedAt
* timestamp for assertion, in milliseconds since the epoch; if null,
* no timestamp is included.
* @param expiresAt
* expiration timestamp for assertion, in milliseconds since the epoch.
* @return assertion.
* @throws NonObjectJSONException
* @throws IOException
* @throws ParseException
* @throws GeneralSecurityException
*/
public static String createAssertion(SigningPrivateKey privateKeyToSignWith, String certificate, String audience,
String issuer, long issuedAt, long durationInMilliseconds) throws NonObjectJSONException, IOException, ParseException, GeneralSecurityException {
long expiresAt = issuedAt + durationInMilliseconds;
String issuer, Long issuedAt, long expiresAt) throws NonObjectJSONException, IOException, ParseException, GeneralSecurityException {
String emptyAssertionPayloadString = "{}";
String payloadString = getPayloadString(emptyAssertionPayloadString, issuer, issuedAt, audience, expiresAt);
String payloadString = getPayloadString(emptyAssertionPayloadString, audience, issuer, issuedAt, expiresAt);
String signature = JSONWebTokenUtils.encode(payloadString, privateKeyToSignWith);
return certificate + "~" + signature;
}
public static String createAssertion(SigningPrivateKey privateKeyToSignWith, String certificate, String audience) throws NonObjectJSONException, IOException, ParseException, GeneralSecurityException {
String issuer = DEFAULT_ASSERTION_ISSUER;
long issuedAt = System.currentTimeMillis();
long durationInMilliseconds = DEFAULT_ASSERTION_DURATION_IN_MILLISECONDS;
return createAssertion(privateKeyToSignWith, certificate, audience, issuer, issuedAt, durationInMilliseconds);
}
/**
* For debugging only!
*

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

@ -40,18 +40,17 @@ public class MockMyIDTokenFactory {
* sign username@mockmyid.com
* @param issuedAt
* timestamp for certificate, in milliseconds since the epoch.
* @param durationInMilliseconds
* lifespan of certificate, in milliseconds.
* @param expiresAt
* expiration timestamp for certificate, in milliseconds since the epoch.
* @return encoded certificate string.
* @throws Exception
*/
public String createMockMyIDCertificate(final VerifyingPublicKey publicKeyToSign, String username,
final long issuedAt, final long durationInMilliseconds)
final long issuedAt, final long expiresAt)
throws Exception {
if (!username.endsWith("@mockmyid.com")) {
username = username + "@mockmyid.com";
}
long expiresAt = issuedAt + durationInMilliseconds;
SigningPrivateKey mockMyIdPrivateKey = getMockMyIDPrivateKey();
return JSONWebTokenUtils.createCertificate(publicKeyToSign, username, "mockmyid.com", issuedAt, expiresAt, mockMyIdPrivateKey);
}
@ -69,8 +68,9 @@ public class MockMyIDTokenFactory {
*/
public String createMockMyIDCertificate(final VerifyingPublicKey publicKeyToSign, final String username)
throws Exception {
return createMockMyIDCertificate(publicKeyToSign, username,
System.currentTimeMillis(), JSONWebTokenUtils.DEFAULT_CERTIFICATE_DURATION_IN_MILLISECONDS );
long ciat = System.currentTimeMillis();
long cexp = ciat + JSONWebTokenUtils.DEFAULT_CERTIFICATE_DURATION_IN_MILLISECONDS;
return createMockMyIDCertificate(publicKeyToSign, username, ciat, cexp);
}
/**
@ -84,23 +84,24 @@ public class MockMyIDTokenFactory {
* sign username@mockmyid.com.
* @param certificateIssuedAt
* timestamp for certificate, in milliseconds since the epoch.
* @param certificateDurationInMilliseconds
* lifespan of certificate, in milliseconds.
* @param certificateExpiresAt
* expiration timestamp for certificate, in milliseconds since the epoch.
* @param assertionIssuedAt
* timestamp for assertion, in milliseconds since the epoch.
* @param assertionDurationInMilliseconds
* lifespan of assertion, in milliseconds.
* timestamp for assertion, in milliseconds since the epoch; if null,
* no timestamp is included.
* @param assertionExpiresAt
* expiration timestamp for assertion, in milliseconds since the epoch.
* @return encoded assertion string.
* @throws Exception
*/
public String createMockMyIDAssertion(BrowserIDKeyPair keyPair, String username, String audience,
long certificateIssuedAt, long certificateDurationInMilliseconds,
long assertionIssuedAt, long assertionDurationInMilliseconds)
long certificateIssuedAt, long certificateExpiresAt,
Long assertionIssuedAt, long assertionExpiresAt)
throws Exception {
String certificate = createMockMyIDCertificate(keyPair.getPublic(), username,
certificateIssuedAt, certificateDurationInMilliseconds);
certificateIssuedAt, certificateExpiresAt);
return JSONWebTokenUtils.createAssertion(keyPair.getPrivate(), certificate, audience,
JSONWebTokenUtils.DEFAULT_ASSERTION_ISSUER, assertionIssuedAt, assertionDurationInMilliseconds);
JSONWebTokenUtils.DEFAULT_ASSERTION_ISSUER, assertionIssuedAt, assertionExpiresAt);
}
/**
@ -117,9 +118,11 @@ public class MockMyIDTokenFactory {
*/
public String createMockMyIDAssertion(BrowserIDKeyPair keyPair, String username, String audience)
throws Exception {
long now = System.currentTimeMillis();
long ciat = System.currentTimeMillis();
long cexp = ciat + JSONWebTokenUtils.DEFAULT_CERTIFICATE_DURATION_IN_MILLISECONDS;
long aiat = ciat + 1;
long aexp = aiat + JSONWebTokenUtils.DEFAULT_ASSERTION_DURATION_IN_MILLISECONDS;
return createMockMyIDAssertion(keyPair, username, audience,
now, JSONWebTokenUtils.DEFAULT_CERTIFICATE_DURATION_IN_MILLISECONDS,
now + 1, JSONWebTokenUtils.DEFAULT_ASSERTION_DURATION_IN_MILLISECONDS);
ciat, cexp, aiat, aexp);
}
}

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

@ -10,6 +10,7 @@ import java.net.URI;
import java.net.URISyntaxException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.mozilla.gecko.background.common.log.Logger;
@ -118,9 +119,9 @@ public class BrowserIDRemoteVerifierClient implements BrowserIDVerifierClient {
r.delegate = new RemoteVerifierResourceDelegate(r, delegate);
List<NameValuePair> nvps = new ArrayList<NameValuePair>();
nvps.add(new BasicNameValuePair("audience", audience));
nvps.add(new BasicNameValuePair("assertion", assertion));
List<NameValuePair> nvps = Arrays.asList(new NameValuePair[] {
new BasicNameValuePair("audience", audience),
new BasicNameValuePair("assertion", assertion) });
try {
r.post(new UrlEncodedFormEntity(nvps, "UTF-8"));

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

@ -30,8 +30,10 @@ import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
public class Favicons {
@ -396,9 +398,9 @@ public class Favicons {
// Load and cache the built-in favicon in each of its sizes.
// TODO: don't open the zip twice!
ArrayList<Bitmap> toInsert = new ArrayList<Bitmap>(2);
toInsert.add(loadBrandingBitmap(context, "favicon64.png"));
toInsert.add(loadBrandingBitmap(context, "favicon32.png"));
List<Bitmap> toInsert = Arrays.asList(loadBrandingBitmap(context, "favicon64.png"),
loadBrandingBitmap(context, "favicon32.png"));
putFaviconsInMemCache(BUILT_IN_FAVICON_URL, toInsert.iterator(), true);
}

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

@ -14,6 +14,8 @@ public class FxAccountConstants {
public static final String DEFAULT_AUTH_SERVER_ENDPOINT = "https://api.accounts.firefox.com/v1";
public static final String DEFAULT_TOKEN_SERVER_ENDPOINT = "https://token.services.mozilla.com/1.0/sync/1.5";
public static final String STAGE_TOKEN_SERVER_ENDPOINT = "https://token.stage.mozaws.net/1.0/sync/1.5";
// For extra debugging. Not final so it can be changed from Fennec, or from
// an add-on.
public static boolean LOG_PERSONAL_INFORMATION = false;

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

@ -56,8 +56,11 @@ public class Married extends TokensAndKeysState {
delegate.handleTransition(new LogMessage("staying married"), this);
}
public String generateAssertion(String audience, String issuer, long issuedAt, long durationInMilliseconds) throws NonObjectJSONException, IOException, ParseException, GeneralSecurityException {
String assertion = JSONWebTokenUtils.createAssertion(keyPair.getPrivate(), certificate, audience, issuer, issuedAt, durationInMilliseconds);
public String generateAssertion(String audience, String issuer) throws NonObjectJSONException, IOException, ParseException, GeneralSecurityException {
// We generate assertions with no iat and an exp after 2050 to avoid
// invalid-timestamp errors from the token server.
final long expiresAt = JSONWebTokenUtils.DEFAULT_FUTURE_EXPIRES_AT_IN_MILLISECONDS;
String assertion = JSONWebTokenUtils.createAssertion(keyPair.getPrivate(), certificate, audience, issuer, null, expiresAt);
if (!FxAccountConstants.LOG_PERSONAL_INFORMATION) {
return assertion;
}

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

@ -347,9 +347,9 @@ public class FxAccountSyncAdapter extends AbstractThreadedSyncAdapter {
// skew adjustment that the HawkAuthHeaderProvider uses to adjust its
// timestamps. Eventually we might want this to adapt within the scope of a
// global session.
final SkewHandler tokenServerSkewHandler = SkewHandler.getSkewHandlerForHostname(storageHostname);
final long tokenServerSkew = tokenServerSkewHandler.getSkewInSeconds();
final AuthHeaderProvider authHeaderProvider = new HawkAuthHeaderProvider(token.id, token.key.getBytes("UTF-8"), false, tokenServerSkew);
final SkewHandler storageServerSkewHandler = SkewHandler.getSkewHandlerForHostname(storageHostname);
final long storageServerSkew = storageServerSkewHandler.getSkewInSeconds();
final AuthHeaderProvider authHeaderProvider = new HawkAuthHeaderProvider(token.id, token.key.getBytes("UTF-8"), false, storageServerSkew);
final Context context = getContext();
final SyncConfiguration syncConfig = new SyncConfiguration(token.uid, authHeaderProvider, sharedPrefs, syncKeyBundle);
@ -503,11 +503,7 @@ public class FxAccountSyncAdapter extends AbstractThreadedSyncAdapter {
}
final Married married = (Married) state;
SkewHandler skewHandler = SkewHandler.getSkewHandlerFromEndpointString(tokenServerEndpoint);
final long now = System.currentTimeMillis();
final long issuedAtMillis = now + skewHandler.getSkewInMillis();
final long assertionDurationMillis = this.getAssertionDurationInMilliseconds();
final String assertion = married.generateAssertion(audience, JSONWebTokenUtils.DEFAULT_ASSERTION_ISSUER, issuedAtMillis, assertionDurationMillis);
final String assertion = married.generateAssertion(audience, JSONWebTokenUtils.DEFAULT_ASSERTION_ISSUER);
/*
* At this point we're in the correct state to sync, and we're ready to fetch

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

@ -14,6 +14,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Scanner;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
@ -789,9 +790,9 @@ public class BrowserHealthRecorder implements HealthRecorder, GeckoEventListener
new MeasurementFields() {
@Override
public Iterable<FieldSpec> getFields() {
ArrayList<FieldSpec> out = new ArrayList<FieldSpec>(2);
out.add(new FieldSpec("normal", Field.TYPE_JSON_DISCRETE));
out.add(new FieldSpec("abnormal", Field.TYPE_JSON_DISCRETE));
List<FieldSpec> out = Arrays.asList(
new FieldSpec("normal", Field.TYPE_JSON_DISCRETE),
new FieldSpec("abnormal", Field.TYPE_JSON_DISCRETE));
return out;
}
});

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

@ -27,14 +27,15 @@ public abstract class CustomListPreference extends Preference implements View.On
public static final int INDEX_SET_DEFAULT_BUTTON = 0;
// Dialog item labels.
protected final String[] mDialogItems;
private String[] mDialogItems;
// Dialog displayed when this element is tapped.
protected AlertDialog mDialog;
// Cache label to avoid repeated use of the resource system.
public final String LABEL_IS_DEFAULT;
public final String LABEL_SET_AS_DEFAULT;
protected final String LABEL_IS_DEFAULT;
protected final String LABEL_SET_AS_DEFAULT;
protected final String LABEL_REMOVE;
protected boolean mIsDefault;
@ -68,8 +69,7 @@ public abstract class CustomListPreference extends Preference implements View.On
// Fetch these strings now, instead of every time we ever want to relabel a button.
LABEL_IS_DEFAULT = res.getString(R.string.pref_default);
LABEL_SET_AS_DEFAULT = res.getString(R.string.pref_dialog_set_default);
mDialogItems = getDialogStrings();
LABEL_REMOVE = res.getString(R.string.pref_dialog_remove);
}
/**
@ -97,10 +97,17 @@ public abstract class CustomListPreference extends Preference implements View.On
}
}
private String[] getCachedDialogItems() {
if (mDialogItems == null) {
mDialogItems = createDialogItems();
}
return mDialogItems;
}
/**
* Returns the strings to be displayed in the dialog.
*/
abstract protected String[] getDialogStrings();
abstract protected String[] createDialogItems();
/**
* Display a dialog for this preference, when the preference is clicked.
@ -108,7 +115,7 @@ public abstract class CustomListPreference extends Preference implements View.On
public void showDialog() {
final AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle(getTitle().toString());
builder.setItems(mDialogItems, new DialogInterface.OnClickListener() {
builder.setItems(getCachedDialogItems(), new DialogInterface.OnClickListener() {
// Forward relevant events to the container class for handling.
@Override
public void onClick(DialogInterface dialog, int indexClicked) {

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

@ -16,16 +16,22 @@ import android.widget.TextView;
public class PanelsPreference extends CustomListPreference {
protected String LOGTAG = "PanelsPreference";
private static final int INDEX_SHOW_BUTTON = 1;
private static final int INDEX_REMOVE_BUTTON = 2;
/**
* Index of the context menu button for controlling display options.
* For (removable) Dynamic panels, this button removes the panel.
* For built-in panels, this button toggles showing or hiding the panel.
*/
private static final int INDEX_DISPLAY_BUTTON = 1;
private String LABEL_HIDE;
private String LABEL_SHOW;
protected boolean mIsHidden = false;
private boolean mIsRemovable;
public PanelsPreference(Context context, CustomListCategory parentCategory) {
public PanelsPreference(Context context, CustomListCategory parentCategory, boolean isRemovable) {
super(context, parentCategory);
mIsRemovable = isRemovable;
}
@Override
@ -49,15 +55,17 @@ public class PanelsPreference extends CustomListPreference {
}
@Override
protected String[] getDialogStrings() {
protected String[] createDialogItems() {
if (mIsRemovable) {
return new String[] { LABEL_SET_AS_DEFAULT, LABEL_REMOVE };
}
// Built-in panels can't be removed, so use show/hide options.
Resources res = getContext().getResources();
LABEL_HIDE = res.getString(R.string.pref_panels_hide);
LABEL_SHOW = res.getString(R.string.pref_panels_show);
// XXX: Don't provide the "Remove" string for now, because we only support built-in
// panels, which can only be disabled.
return new String[] { LABEL_SET_AS_DEFAULT,
LABEL_HIDE };
return new String[] { LABEL_SET_AS_DEFAULT, LABEL_HIDE };
}
@Override
@ -81,12 +89,15 @@ public class PanelsPreference extends CustomListPreference {
mParentCategory.setDefault(this);
break;
case INDEX_SHOW_BUTTON:
((PanelsPreferenceCategory) mParentCategory).setHidden(this, !mIsHidden);
break;
case INDEX_REMOVE_BUTTON:
mParentCategory.uninstall(this);
case INDEX_DISPLAY_BUTTON:
// Handle display options for the panel.
if (mIsRemovable) {
// For removable panels, the button displays text for removing the panel.
mParentCategory.uninstall(this);
} else {
// Otherwise, the button toggles between text for showing or hiding the panel.
((PanelsPreferenceCategory) mParentCategory).setHidden(this, !mIsHidden);
}
break;
default:
@ -99,8 +110,10 @@ public class PanelsPreference extends CustomListPreference {
super.configureShownDialog();
// Handle Show/Hide buttons.
final TextView hideButton = (TextView) mDialog.getListView().getChildAt(INDEX_SHOW_BUTTON);
hideButton.setText(mIsHidden ? LABEL_SHOW : LABEL_HIDE);
if (!mIsRemovable) {
final TextView hideButton = (TextView) mDialog.getListView().getChildAt(INDEX_DISPLAY_BUTTON);
hideButton.setText(mIsHidden ? LABEL_SHOW : LABEL_HIDE);
}
}
public void setHidden(boolean toHide) {

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

@ -86,8 +86,10 @@ public class PanelsPreferenceCategory extends CustomListCategory {
private void displayHomeConfig(HomeConfig.State configState) {
for (PanelConfig panelConfig : configState) {
final boolean isRemovable = panelConfig.isDynamic();
// Create and add the pref.
final PanelsPreference pref = new PanelsPreference(getContext(), PanelsPreferenceCategory.this);
final PanelsPreference pref = new PanelsPreference(getContext(), PanelsPreferenceCategory.this, isRemovable);
pref.setTitle(panelConfig.getTitle());
pref.setKey(panelConfig.getId());
// XXX: Pull icon from PanelInfo.

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

@ -14,7 +14,6 @@ import org.mozilla.gecko.widget.FaviconView;
import android.app.AlertDialog;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.text.SpannableString;
@ -66,10 +65,9 @@ public class SearchEnginePreference extends CustomListPreference {
* Returns the strings to be displayed in the dialog.
*/
@Override
protected String[] getDialogStrings() {
Resources res = getContext().getResources();
protected String[] createDialogItems() {
return new String[] { LABEL_SET_AS_DEFAULT,
res.getString(R.string.pref_dialog_remove) };
LABEL_REMOVE };
}
@Override

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

@ -13,6 +13,7 @@ import android.util.Log;
import org.mozilla.gecko.mozglue.RobocopTarget;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map.Entry;
/*
@ -200,9 +201,7 @@ public class SQLiteBridge {
if (!TextUtils.isEmpty(whereClause)) {
sb.append(" WHERE ");
sb.append(whereClause);
for (int i = 0; i < whereArgs.length; i++) {
valueNames.add(whereArgs[i]);
}
valueNames.addAll(Arrays.asList(whereArgs));
}
String[] binds = new String[valueNames.size()];

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

@ -5,6 +5,7 @@
package org.mozilla.gecko.sync.repositories.android;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
@ -313,9 +314,9 @@ public class FormHistoryRepositorySession extends
}
};
ArrayList<Callable<Cursor>> callableCursors = new ArrayList<Callable<Cursor>>();
callableCursors.add(regularCallable);
callableCursors.add(deletedCallable);
@SuppressWarnings("unchecked")
List<Callable<Cursor>> callableCursors = Arrays.asList(regularCallable, deletedCallable);
fetchHelper(delegate, sharedEnd, callableCursors);
}
@ -348,9 +349,9 @@ public class FormHistoryRepositorySession extends
}
};
ArrayList<Callable<Cursor>> callableCursors = new ArrayList<Callable<Cursor>>();
callableCursors.add(regularCallable);
callableCursors.add(deletedCallable);
@SuppressWarnings("unchecked")
List<Callable<Cursor>> callableCursors = Arrays.asList(regularCallable, deletedCallable);
fetchHelper(delegate, sharedEnd, callableCursors);
}

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

@ -5,6 +5,7 @@
package org.mozilla.gecko.sync.setup.activities;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import android.content.Intent;
@ -50,10 +51,7 @@ public class SendTabData {
// For URL, take first URL from EXTRA_TEXT, EXTRA_SUBJECT, and EXTRA_TITLE
// (in that order).
List<String> strings = new ArrayList<String>();
strings.add(text);
strings.add(subject);
strings.add(title);
List<String> strings = Arrays.asList(text, subject, title);
String theUri = new WebURLFinder(strings).bestWebURL();
return new SendTabData(theTitle, theUri);

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

@ -44,6 +44,9 @@ abstract class UITest extends ActivityInstrumentationTestCase2<Activity>
private static final String LAUNCHER_ACTIVITY = TestConstants.ANDROID_PACKAGE_NAME + ".App";
private static final String TARGET_PACKAGE_ID = "org.mozilla.gecko";
private static final String JUNIT_FAILURE_MSG = "A JUnit method was called. Make sure " +
"you are using AssertionHelper to make assertions. Try `fAssert*(...);`";
private final static Class<Activity> sLauncherActivityClass;
private Activity mActivity;
@ -226,4 +229,63 @@ abstract class UITest extends ActivityInstrumentationTestCase2<Activity>
return intent;
}
/**
* Throws an Exception. Called from overridden JUnit methods to ensure JUnit assertions
* are not accidentally used over AssertionHelper assertions (the latter of which contains
* additional logging facilities for use in our test harnesses).
*/
private static void junit() {
throw new UnsupportedOperationException(JUNIT_FAILURE_MSG);
}
// Note: inexplicably, javac does not think we're overriding these methods,
// so we can't use the @Override annotation.
public static void assertEquals(short e, short a) { junit(); }
public static void assertEquals(String m, int e, int a) { junit(); }
public static void assertEquals(String m, short e, short a) { junit(); }
public static void assertEquals(char e, char a) { junit(); }
public static void assertEquals(String m, String e, String a) { junit(); }
public static void assertEquals(int e, int a) { junit(); }
public static void assertEquals(String m, double e, double a, double delta) { junit(); }
public static void assertEquals(String m, long e, long a) { junit(); }
public static void assertEquals(byte e, byte a) { junit(); }
public static void assertEquals(Object e, Object a) { junit(); }
public static void assertEquals(boolean e, boolean a) { junit(); }
public static void assertEquals(String m, float e, float a, float delta) { junit(); }
public static void assertEquals(String m, boolean e, boolean a) { junit(); }
public static void assertEquals(String e, String a) { junit(); }
public static void assertEquals(float e, float a, float delta) { junit(); }
public static void assertEquals(String m, byte e, byte a) { junit(); }
public static void assertEquals(double e, double a, double delta) { junit(); }
public static void assertEquals(String m, char e, char a) { junit(); }
public static void assertEquals(String m, Object e, Object a) { junit(); }
public static void assertEquals(long e, long a) { junit(); }
public static void assertFalse(String m, boolean c) { junit(); }
public static void assertFalse(boolean c) { junit(); }
public static void assertNotNull(String m, Object o) { junit(); }
public static void assertNotNull(Object o) { junit(); }
public static void assertNotSame(Object e, Object a) { junit(); }
public static void assertNotSame(String m, Object e, Object a) { junit(); }
public static void assertNull(Object o) { junit(); }
public static void assertNull(String m, Object o) { junit(); }
public static void assertSame(Object e, Object a) { junit(); }
public static void assertSame(String m, Object e, Object a) { junit(); }
public static void assertTrue(String m, boolean c) { junit(); }
public static void assertTrue(boolean c) { junit(); }
public static void fail(String m) { junit(); }
public static void fail() { junit(); }
public static void failNotEquals(String m, Object e, Object a) { junit(); }
public static void failNotSame(String m, Object e, Object a) { junit(); }
public static void failSame(String m) { junit(); }
public static String format(String m, Object e, Object a) { junit(); return null; }
}

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

@ -75,10 +75,10 @@ public class testInputConnection extends UITest {
assertTextAndSelectionAt("Can finish composition", ic, "foobar", 6);
// Test getTextBeforeCursor
assertEquals("Can retrieve text before cursor", "bar", ic.getTextBeforeCursor(3, 0));
fAssertEquals("Can retrieve text before cursor", "bar", ic.getTextBeforeCursor(3, 0));
// Test getTextAfterCursor
assertEquals("Can retrieve text after cursor", "", ic.getTextAfterCursor(3, 0));
fAssertEquals("Can retrieve text after cursor", "", ic.getTextAfterCursor(3, 0));
}
}
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше