Merge fx-team to m-c.
|
@ -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() {
|
||||
fxAccounts.getSignedInUser().then(user => {
|
||||
if (window.location.href.contains("action=signin")) {
|
||||
if (user) {
|
||||
// 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 {
|
||||
// Check if we have a local account
|
||||
fxAccounts.getSignedInUser().then(user => {
|
||||
// No action specified
|
||||
if (user) {
|
||||
show("stage");
|
||||
show("manage");
|
||||
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");
|
||||
|
||||
try {
|
||||
yield test.run();
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
} 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,13 +11,16 @@ function test() {
|
|||
|
||||
addTab("about:blank", function() {
|
||||
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||
idIndex = 0;
|
||||
|
||||
target.makeRemote().then(() => {
|
||||
toolIDs = gDevTools.getToolDefinitionArray()
|
||||
.filter(def => def.isTargetSupported(target))
|
||||
.map(def => def.id);
|
||||
idIndex = 0;
|
||||
gDevTools.showToolbox(target, toolIDs[0], Toolbox.HostType.BOTTOM)
|
||||
.then(testShortcuts);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function testShortcuts(aToolbox, aIndex) {
|
||||
|
|
|
@ -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.selectTool(this._defaultToolId).then(panel => {
|
||||
this.emit("ready");
|
||||
deferred.resolve();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// 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;
|
||||
});
|
||||
|
@ -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) {
|
||||
if (!this._initInspector) {
|
||||
this._initInspector = Task.spawn(function*() {
|
||||
this._inspector = InspectorFront(this._target.client, this._target.form);
|
||||
this._inspector.getWalker().then(walker => {
|
||||
this._walker = walker;
|
||||
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);
|
||||
|
||||
window.once("connected", this._onConnect.bind(this));
|
||||
},
|
||||
|
||||
_onConnect: function() {
|
||||
if (NetMonitorController.supportsCustomRequest) {
|
||||
$("#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);
|
||||
|
||||
$("#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) => {
|
||||
Task.spawn(function*() {
|
||||
let {tab} = yield loadTab(TEST_URI);
|
||||
let hud = yield openConsole(tab);
|
||||
return checkOutputForInputs(hud, inputTests);
|
||||
}).then(finishTest);
|
||||
}, true);
|
||||
}
|
||||
|
|
|
@ -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)
|
|||
}],
|
||||
});
|
||||
|
||||
if (!entry.noClick) {
|
||||
let msg = [...result.matched][0];
|
||||
if (!entry.noClick) {
|
||||
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,11 +538,27 @@ 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: 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),
|
||||
|
@ -550,6 +566,9 @@ toolbar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker:-moz-lwtheme-bri
|
|||
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,
|
||||
|
@ -596,6 +615,40 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon {
|
|||
|
||||
#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=true]) + .toolbarbutton-1:-moz-any(:not(:hover):not([open]),[disabled=true])::before {
|
||||
content: "";
|
||||
display: -moz-box;
|
||||
width: 1px;
|
||||
height: 16px;
|
||||
-moz-margin-end: -1px;
|
||||
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 16px;
|
||||
}
|
||||
|
||||
@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,
|
||||
#nav-bar .toolbarbutton-1:not([disabled=true]):hover > .toolbarbutton-menubutton-dropmarker > .dropmarker-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@ > #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);
|
||||
}
|
||||
|
||||
%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;
|
||||
|
@ -621,33 +674,53 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon {
|
|||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
#nav-bar .toolbarbutton-1:not([disabled=true]):-moz-any(:hover,[open]) > .toolbarbutton-menubutton-button > .toolbarbutton-icon,
|
||||
#nav-bar .toolbarbutton-1:not([disabled=true]):hover > .toolbarbutton-menubutton-dropmarker > .dropmarker-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 {
|
||||
#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=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 {
|
||||
#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-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;
|
||||
}
|
||||
|
||||
%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);
|
||||
|
@ -660,7 +733,7 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon {
|
|||
transition: none;
|
||||
}
|
||||
|
||||
#nav-bar .toolbarbutton-1:-moz-any(:hover,[open]) > .toolbarbutton-menubutton-dropmarker:not([disabled=true]) > .dropmarker-icon {
|
||||
#nav-bar .toolbarbutton-1:-moz-any(:hover,[open]) > .toolbarbutton-menubutton-dropmarker:not([disabled]) > .dropmarker-icon {
|
||||
-moz-border-start-color: hsla(210,54%,20%,.35);
|
||||
}
|
||||
|
||||
|
@ -668,6 +741,9 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .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,7 +831,29 @@ 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-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,4%,10%,.08) !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
#back-button[open="true"] > .toolbarbutton-icon {
|
||||
background-color: hsla(210,4%,10%,.12) !important;
|
||||
box-shadow: 0 1px 0 0 hsla(210,80%,20%,.1) inset !important;
|
||||
}
|
||||
|
||||
%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,
|
||||
|
@ -777,6 +887,9 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon {
|
|||
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,8 +1012,20 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon {
|
|||
padding: 0;
|
||||
background-clip: padding-box;
|
||||
border: 1px solid ThreeDShadow;
|
||||
}
|
||||
|
||||
%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 |
|
@ -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:
|
||||
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,9 +110,11 @@ public class PanelsPreference extends CustomListPreference {
|
|||
super.configureShownDialog();
|
||||
|
||||
// Handle Show/Hide buttons.
|
||||
final TextView hideButton = (TextView) mDialog.getListView().getChildAt(INDEX_SHOW_BUTTON);
|
||||
if (!mIsRemovable) {
|
||||
final TextView hideButton = (TextView) mDialog.getListView().getChildAt(INDEX_DISPLAY_BUTTON);
|
||||
hideButton.setText(mIsHidden ? LABEL_SHOW : LABEL_HIDE);
|
||||
}
|
||||
}
|
||||
|
||||
public void setHidden(boolean toHide) {
|
||||
if (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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7100,7 +7100,7 @@ var WebappsUI = {
|
|||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
DOMApplicationRegistry.denyInstall(aData);
|
||||
}
|
||||
|
|
|
@ -5,13 +5,15 @@
|
|||
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
||||
|
||||
const APK_MIME_TYPE = "application/vnd.android.package-archive";
|
||||
const PREF_BD_USEDOWNLOADDIR = "browser.download.useDownloadDir";
|
||||
const URI_GENERIC_ICON_DOWNLOAD = "drawable://alert_download";
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Prompt.jsm");
|
||||
Cu.import("resource://gre/modules/FileUtils.jsm");
|
||||
Cu.import("resource://gre/modules/HelperApps.jsm");
|
||||
Cu.import("resource://gre/modules/Prompt.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// HelperApp Launcher Dialog
|
||||
|
@ -23,7 +25,110 @@ HelperAppLauncherDialog.prototype = {
|
|||
classID: Components.ID("{e9d277a0-268a-4ec2-bb8c-10fdf3e44611}"),
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIHelperAppLauncherDialog]),
|
||||
|
||||
getNativeWindow: function () {
|
||||
try {
|
||||
let win = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
if (win && win.NativeWindow) {
|
||||
return win.NativeWindow;
|
||||
}
|
||||
} catch (e) {
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns false if `url` represents a local or special URL that we don't
|
||||
* wish to ever download.
|
||||
*
|
||||
* Returns true otherwise.
|
||||
*/
|
||||
_canDownload: function (url, alreadyResolved=false) {
|
||||
Services.console.logStringMessage("_canDownload: " + url);
|
||||
// The common case.
|
||||
if (url.schemeIs("http") ||
|
||||
url.schemeIs("https") ||
|
||||
url.schemeIs("ftp")) {
|
||||
Services.console.logStringMessage("_canDownload: true\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
// The less-common opposite case.
|
||||
if (url.schemeIs("chrome") ||
|
||||
url.schemeIs("jar") ||
|
||||
url.schemeIs("resource") ||
|
||||
url.schemeIs("wyciwyg")) {
|
||||
Services.console.logStringMessage("_canDownload: false\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// For all other URIs, try to resolve them to an inner URI, and check that.
|
||||
if (!alreadyResolved) {
|
||||
let ioSvc = Cc["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
|
||||
let innerURI = ioSvc.newChannelFromURI(url).URI;
|
||||
if (!url.equals(innerURI)) {
|
||||
Services.console.logStringMessage("_canDownload: recursing.\n");
|
||||
return this._canDownload(innerURI, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (url.schemeIs("file")) {
|
||||
// If it's in our app directory or profile directory, we never ever
|
||||
// want to do anything with it, including saving to disk or passing the
|
||||
// file to another application.
|
||||
let file = url.QueryInterface(Ci.nsIFileURL).file;
|
||||
|
||||
// TODO: pref blacklist?
|
||||
|
||||
let appRoot = FileUtils.getFile("XREExeF", []);
|
||||
if (appRoot.contains(file, true)) {
|
||||
Services.console.logStringMessage("_canDownload: appRoot.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
let profileRoot = FileUtils.getFile("ProfD", []);
|
||||
if (profileRoot.contains(file, true)) {
|
||||
Services.console.logStringMessage("_canDownload: prof dir.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
Services.console.logStringMessage("_canDownload: safe.\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Anything else is fine to download.
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if `launcher` represents a download for which we wish
|
||||
* to prompt.
|
||||
*/
|
||||
_shouldPrompt: function (launcher) {
|
||||
let mimeType = this._getMimeTypeFromLauncher(launcher);
|
||||
|
||||
// Straight equality: nsIMIMEInfo normalizes.
|
||||
return APK_MIME_TYPE == mimeType;
|
||||
},
|
||||
|
||||
show: function hald_show(aLauncher, aContext, aReason) {
|
||||
if (!this._canDownload(aLauncher.source)) {
|
||||
aLauncher.cancel(Cr.NS_BINDING_ABORTED);
|
||||
|
||||
let win = this.getNativeWindow();
|
||||
if (!win) {
|
||||
// Oops.
|
||||
Services.console.logStringMessage("Refusing download, but can't show a toast.");
|
||||
return;
|
||||
}
|
||||
|
||||
Services.console.logStringMessage("Refusing download of non-downloadable file.");
|
||||
let bundle = Services.strings.createBundle("chrome://browser/locale/handling.properties");
|
||||
let failedText = bundle.GetStringFromName("protocol.failed");
|
||||
win.toast.show(failedText, "long");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let bundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
|
||||
|
||||
let defaultHandler = new Object();
|
||||
|
@ -31,13 +136,13 @@ HelperAppLauncherDialog.prototype = {
|
|||
mimeType: aLauncher.MIMEInfo.MIMEType,
|
||||
});
|
||||
|
||||
// Add a fake intent for save to disk at the top of the list
|
||||
// Add a fake intent for save to disk at the top of the list.
|
||||
apps.unshift({
|
||||
name: bundle.GetStringFromName("helperapps.saveToDisk"),
|
||||
packageName: "org.mozilla.gecko.Download",
|
||||
iconUri: "drawable://icon",
|
||||
launch: function() {
|
||||
// Reset the preferredAction here
|
||||
// Reset the preferredAction here.
|
||||
aLauncher.MIMEInfo.preferredAction = Ci.nsIMIMEInfo.saveToDisk;
|
||||
aLauncher.saveToDisk(null, false);
|
||||
return true;
|
||||
|
@ -65,7 +170,14 @@ HelperAppLauncherDialog.prototype = {
|
|||
}
|
||||
}
|
||||
|
||||
if (apps.length > 1) {
|
||||
// If there's only one choice, and we don't want to prompt, go right ahead
|
||||
// and choose that app automatically.
|
||||
if (!this._shouldPrompt(aLauncher) && (apps.length === 1)) {
|
||||
callback(apps[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, let's go through the prompt.
|
||||
HelperApps.prompt(apps, {
|
||||
title: bundle.GetStringFromName("helperapps.pick"),
|
||||
buttons: [
|
||||
|
@ -73,24 +185,23 @@ HelperAppLauncherDialog.prototype = {
|
|||
bundle.GetStringFromName("helperapps.useJustOnce")
|
||||
]
|
||||
}, (data) => {
|
||||
if (data.button < 0)
|
||||
if (data.button < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
callback(apps[data.icongrid0]);
|
||||
|
||||
if (data.button == 0)
|
||||
if (data.button === 0) {
|
||||
this._setPreferredApp(aLauncher, apps[data.icongrid0]);
|
||||
});
|
||||
} else {
|
||||
callback(apps[0]);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_getPrefName: function getPrefName(mimetype) {
|
||||
return "browser.download.preferred." + mimetype.replace("\\", ".");
|
||||
},
|
||||
|
||||
_getMimeTypeFromLauncher: function getMimeTypeFromLauncher(launcher) {
|
||||
_getMimeTypeFromLauncher: function (launcher) {
|
||||
let mime = launcher.MIMEInfo.MIMEType;
|
||||
if (!mime)
|
||||
mime = ContentAreaUtils.getMIMETypeForURI(launcher.source) || "";
|
||||
|
@ -105,7 +216,7 @@ HelperAppLauncherDialog.prototype = {
|
|||
try {
|
||||
return Services.prefs.getCharPref(this._getPrefName(mime));
|
||||
} catch(ex) {
|
||||
Services.console.logStringMessage("Error getting pref for " + mime + " " + ex);
|
||||
Services.console.logStringMessage("Error getting pref for " + mime + ".");
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
|
|
@ -5,15 +5,23 @@ package org.mozilla.gecko.background.nativecode.test;
|
|||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.mozilla.gecko.background.nativecode.NativeCrypto;
|
||||
import org.mozilla.gecko.sync.Utils;
|
||||
|
||||
// Test vectors from
|
||||
// SHA-256: <https://github.com/ircmaxell/PHP-PasswordLib/blob/master/test/Data/Vectors/pbkdf2-draft-josefsson-sha256.test-vectors>
|
||||
// <https://gitorious.org/scrypt/nettle-scrypt/blobs/37c0d5288e991604fe33dba2f1724986a8dddf56/testsuite/pbkdf2-test.c>
|
||||
/*
|
||||
* Tests the Java wrapper over native implementations of crypto code. Test vectors from:
|
||||
* * PBKDF2SHA256:
|
||||
* - <https://github.com/ircmaxell/PHP-PasswordLib/blob/master/test/Data/Vectors/pbkdf2-draft-josefsson-sha256.test-vectors>
|
||||
* - <https://gitorious.org/scrypt/nettle-scrypt/blobs/37c0d5288e991604fe33dba2f1724986a8dddf56/testsuite/pbkdf2-test.c>
|
||||
* * SHA-1:
|
||||
* - <http://oauth.googlecode.com/svn/code/c/liboauth/src/sha1.c>
|
||||
*/
|
||||
public class TestNativeCrypto extends TestCase {
|
||||
|
||||
public final void testPBKDF2SHA256A() throws UnsupportedEncodingException, GeneralSecurityException {
|
||||
|
@ -72,6 +80,71 @@ public class TestNativeCrypto extends TestCase {
|
|||
checkPBKDF2SHA256("password", "salt", 80000, 32, null);
|
||||
}
|
||||
|
||||
public final void testPBKDF2SHA256InvalidLenArg() throws UnsupportedEncodingException, GeneralSecurityException {
|
||||
final String p = "password";
|
||||
final String s = "salt";
|
||||
final int c = 1;
|
||||
final int dkLen = -1; // Should always be positive.
|
||||
|
||||
try {
|
||||
NativeCrypto.pbkdf2SHA256(p.getBytes("US-ASCII"), s.getBytes("US-ASCII"), c, dkLen);
|
||||
fail("Expected sha256 to throw with negative dkLen argument.");
|
||||
} catch (IllegalArgumentException e) { } // Expected.
|
||||
}
|
||||
|
||||
public final void testSHA1() throws UnsupportedEncodingException {
|
||||
final String[] inputs = new String[] {
|
||||
"abc",
|
||||
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
|
||||
"" // To be filled in below.
|
||||
};
|
||||
final String baseStr = "01234567";
|
||||
final int repetitions = 80;
|
||||
final StringBuilder builder = new StringBuilder(baseStr.length() * repetitions);
|
||||
for (int i = 0; i < 80; ++i) {
|
||||
builder.append(baseStr);
|
||||
}
|
||||
inputs[2] = builder.toString();
|
||||
|
||||
final String[] expecteds = new String[] {
|
||||
"a9993e364706816aba3e25717850c26c9cd0d89d",
|
||||
"84983e441c3bd26ebaae4aa1f95129e5e54670f1",
|
||||
"dea356a2cddd90c7a7ecedc5ebb563934f460452"
|
||||
};
|
||||
|
||||
for (int i = 0; i < inputs.length; ++i) {
|
||||
final byte[] input = inputs[i].getBytes("US-ASCII");
|
||||
final String expected = expecteds[i];
|
||||
|
||||
final byte[] actual = NativeCrypto.sha1(input);
|
||||
assertNotNull("Hashed value is non-null", actual);
|
||||
assertExpectedBytes(expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to ensure the output of our SHA1 algo is the same as MessageDigest's. This is important
|
||||
* because we intend to replace MessageDigest in FHR with this SHA-1 algo (bug 959652).
|
||||
*/
|
||||
public final void testSHA1AgainstMessageDigest() throws UnsupportedEncodingException,
|
||||
NoSuchAlgorithmException {
|
||||
final String[] inputs = {
|
||||
"password",
|
||||
"saranghae",
|
||||
"aoeusnthaoeusnthaoeusnth \0 12345098765432109876_!"
|
||||
};
|
||||
|
||||
final MessageDigest digest = MessageDigest.getInstance("SHA-1");
|
||||
for (final String input : inputs) {
|
||||
final byte[] inputBytes = input.getBytes("US-ASCII");
|
||||
|
||||
final byte[] mdBytes = digest.digest(inputBytes);
|
||||
final byte[] ourBytes = NativeCrypto.sha1(inputBytes);
|
||||
assertTrue("MessageDigest hash is the same as NativeCrypto SHA-1 hash",
|
||||
Arrays.equals(ourBytes, mdBytes));
|
||||
}
|
||||
}
|
||||
|
||||
private void checkPBKDF2SHA256(String p, String s, int c, int dkLen,
|
||||
final String expectedStr)
|
||||
throws GeneralSecurityException, UnsupportedEncodingException {
|
||||
|
|
|
@ -516,6 +516,7 @@ this.BrowserIDManager.prototype = {
|
|||
this._shouldHaveSyncKeyBundle = true;
|
||||
this._syncKeyBundle = null;
|
||||
Weave.Status.login = this._authFailureReason;
|
||||
Services.obs.notifyObservers(null, "weave:service:login:error", null);
|
||||
throw err;
|
||||
});
|
||||
},
|
||||
|
|
|
@ -462,6 +462,11 @@ this.CrashManager.prototype = Object.freeze({
|
|||
_getStore: function () {
|
||||
return Task.spawn(function* () {
|
||||
if (!this._store) {
|
||||
yield OS.File.makeDir(this._storeDir, {
|
||||
ignoreExisting: true,
|
||||
unixMode: OS.Constants.libc.S_IRWXU,
|
||||
});
|
||||
|
||||
let store = new CrashStore(this._storeDir, this._telemetryStoreSizeKey);
|
||||
yield store.load();
|
||||
|
||||
|
|
|
@ -134,9 +134,14 @@ this.getManager = function () {
|
|||
const dirMode = OS.Constants.libc.S_IRWXU;
|
||||
let baseFile = OS.Constants.Path.profileDir;
|
||||
|
||||
function makeDir() {
|
||||
function makeDir(create=true) {
|
||||
return Task.spawn(function* () {
|
||||
let path = OS.Path.join(baseFile, "dummy-dir-" + DUMMY_DIR_COUNT++);
|
||||
|
||||
if (!create) {
|
||||
return path;
|
||||
}
|
||||
|
||||
dump("Creating directory: " + path + "\n");
|
||||
yield OS.File.makeDir(path, {unixMode: dirMode});
|
||||
|
||||
|
@ -148,7 +153,10 @@ this.getManager = function () {
|
|||
let submittedD = yield makeDir();
|
||||
let eventsD1 = yield makeDir();
|
||||
let eventsD2 = yield makeDir();
|
||||
let storeD = yield makeDir();
|
||||
|
||||
// Store directory is created at run-time if needed. Ensure those code
|
||||
// paths are triggered.
|
||||
let storeD = yield makeDir(false);
|
||||
|
||||
let m = new TestingCrashManager({
|
||||
pendingDumpsDir: pendingD,
|
||||
|
|
|
@ -363,19 +363,22 @@ this.Download.prototype = {
|
|||
|
||||
// This function propagates progress from the DownloadSaver object, unless
|
||||
// it comes in late from a download attempt that was replaced by a new one.
|
||||
// If the cancellation process for the download has started, then the update
|
||||
// is ignored.
|
||||
function DS_setProgressBytes(aCurrentBytes, aTotalBytes, aHasPartialData)
|
||||
{
|
||||
if (this._currentAttempt == currentAttempt || !this._currentAttempt) {
|
||||
if (this._currentAttempt == currentAttempt) {
|
||||
this._setBytes(aCurrentBytes, aTotalBytes, aHasPartialData);
|
||||
}
|
||||
}
|
||||
|
||||
// This function propagates download properties from the DownloadSaver
|
||||
// object, unless it comes in late from a download attempt that was
|
||||
// replaced by a new one.
|
||||
// replaced by a new one. If the cancellation process for the download has
|
||||
// started, then the update is ignored.
|
||||
function DS_setProperties(aOptions)
|
||||
{
|
||||
if (this._currentAttempt && this._currentAttempt != currentAttempt) {
|
||||
if (this._currentAttempt != currentAttempt) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2053,10 +2056,16 @@ this.DownloadLegacySaver.prototype = {
|
|||
Cu.reportError(e2);
|
||||
}
|
||||
}
|
||||
// In case the operation failed, ensure we stop downloading data. Since
|
||||
// we never re-enter this function, deferCanceled is always available.
|
||||
this.deferCanceled.resolve();
|
||||
throw ex;
|
||||
} finally {
|
||||
// We don't need the reference to the request anymore.
|
||||
// We don't need the reference to the request anymore. We must also set
|
||||
// deferCanceled to null in order to free any indirect references it
|
||||
// may hold to the request.
|
||||
this.request = null;
|
||||
this.deferCanceled = null;
|
||||
// Allow the download to restart through a DownloadCopySaver.
|
||||
this.firstExecutionFinished = true;
|
||||
}
|
||||
|
@ -2073,8 +2082,12 @@ this.DownloadLegacySaver.prototype = {
|
|||
return this.copySaver.cancel.apply(this.copySaver, arguments);
|
||||
}
|
||||
|
||||
// Cancel the operation as soon as the object is connected.
|
||||
// If the download hasn't stopped already, resolve deferCanceled so that the
|
||||
// operation is canceled as soon as a cancellation handler is registered.
|
||||
// Note that the handler might not have been registered yet.
|
||||
if (this.deferCanceled) {
|
||||
this.deferCanceled.resolve();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -99,7 +99,8 @@ DownloadLegacyTransfer.prototype = {
|
|||
// To handle asynchronous cancellation properly, we should hook up the
|
||||
// handler only after we have been notified that the main request
|
||||
// started. We will wait until the main request stopped before
|
||||
// notifying that the download has been canceled.
|
||||
// notifying that the download has been canceled. Since the request has
|
||||
// not completed yet, deferCanceled is guaranteed to be set.
|
||||
return download.saver.deferCanceled.promise.then(() => {
|
||||
// Only cancel if the object executing the download is still running.
|
||||
if (this._cancelable && !this._componentFailed) {
|
||||
|
@ -224,11 +225,8 @@ DownloadLegacyTransfer.prototype = {
|
|||
aDownload.tryToKeepPartialData = true;
|
||||
}
|
||||
|
||||
// Start the download before allowing it to be controlled.
|
||||
aDownload.start().then(null, function () {
|
||||
// In case the operation failed, ensure we stop downloading data.
|
||||
aDownload.saver.deferCanceled.resolve();
|
||||
});
|
||||
// Start the download before allowing it to be controlled. Ignore errors.
|
||||
aDownload.start().then(null, () => {});
|
||||
|
||||
// Start processing all the other events received through nsITransfer.
|
||||
this._deferDownload.resolve(aDownload);
|
||||
|
|
|
@ -449,7 +449,8 @@ DebuggerClient.prototype = {
|
|||
let cachedTab = this._tabClients.get(aTabActor);
|
||||
let cachedResponse = {
|
||||
cacheEnabled: cachedTab.cacheEnabled,
|
||||
javascriptEnabled: cachedTab.javascriptEnabled
|
||||
javascriptEnabled: cachedTab.javascriptEnabled,
|
||||
traits: cachedTab.traits,
|
||||
};
|
||||
setTimeout(() => aOnResponse(cachedResponse, cachedTab), 0);
|
||||
return;
|
||||
|
@ -519,7 +520,7 @@ DebuggerClient.prototype = {
|
|||
if (this._consoleClients.has(aConsoleActor)) {
|
||||
consoleClient = this._consoleClients.get(aConsoleActor);
|
||||
} else {
|
||||
consoleClient = new WebConsoleClient(this, aConsoleActor);
|
||||
consoleClient = new WebConsoleClient(this, aResponse);
|
||||
this._consoleClients.set(aConsoleActor, consoleClient);
|
||||
}
|
||||
}
|
||||
|
@ -984,6 +985,7 @@ function TabClient(aClient, aForm) {
|
|||
this.cacheEnabled = aForm.cacheEnabled;
|
||||
this.thread = null;
|
||||
this.request = this.client.request;
|
||||
this.traits = aForm.traits || {};
|
||||
}
|
||||
|
||||
TabClient.prototype = {
|
||||
|
|
|
@ -24,6 +24,7 @@ function ContentActor(connection, chromeGlobal)
|
|||
{
|
||||
TabActor.call(this, connection, chromeGlobal);
|
||||
this._chromeGlobal = chromeGlobal;
|
||||
this.traits.reconfigure = false;
|
||||
}
|
||||
|
||||
ContentActor.prototype = Object.create(TabActor.prototype);
|
||||
|
|
|
@ -83,7 +83,7 @@ let HighlighterActor = protocol.ActorClass({
|
|||
* outline highlighter for instance does not scrollIntoView
|
||||
*/
|
||||
showBoxModel: method(function(node, options={}) {
|
||||
if (this._isNodeValidForHighlighting(node.rawNode)) {
|
||||
if (node && this._isNodeValidForHighlighting(node.rawNode)) {
|
||||
this._boxModelHighlighter.show(node.rawNode, options);
|
||||
} else {
|
||||
this._boxModelHighlighter.hide();
|
||||
|
|
|
@ -2050,14 +2050,54 @@ var WalkerActor = protocol.ActorClass({
|
|||
this.releaseNode(documentActor, { force: true });
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if a node is attached to the DOM tree of the current page.
|
||||
* @param {nsIDomNode} rawNode
|
||||
* @return {Boolean} false if the node is removed from the tree or within a
|
||||
* document fragment
|
||||
*/
|
||||
_isInDOMTree: function(rawNode) {
|
||||
let walker = documentWalker(rawNode, this.rootWin);
|
||||
let current = walker.currentNode;
|
||||
|
||||
// Reaching the top of tree
|
||||
while (walker.parentNode()) {
|
||||
current = walker.currentNode;
|
||||
}
|
||||
|
||||
// The top of the tree is a fragment or is not rootDoc, hence rawNode isn't
|
||||
// attached
|
||||
if (current.nodeType === Ci.nsIDOMNode.DOCUMENT_FRAGMENT_NODE ||
|
||||
current !== this.rootDoc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Otherwise the top of the tree is rootDoc, hence rawNode is in rootDoc
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* @see _isInDomTree
|
||||
*/
|
||||
isInDOMTree: method(function(node) {
|
||||
return node ? this._isInDOMTree(node.rawNode) : false;
|
||||
}, {
|
||||
request: { node: Arg(0, "domnode") },
|
||||
response: { attached: RetVal("boolean") }
|
||||
}),
|
||||
|
||||
/**
|
||||
* Given an ObjectActor (identified by its ID), commonly used in the debugger,
|
||||
* webconsole and variablesView, return the corresponding inspector's NodeActor
|
||||
*/
|
||||
getNodeActorFromObjectActor: method(function(objectActorID) {
|
||||
let debuggerObject = this.conn.poolFor(objectActorID).get(objectActorID).obj;
|
||||
let debuggerObject = this.conn.getActor(objectActorID).obj;
|
||||
let rawNode = debuggerObject.unsafeDereference();
|
||||
|
||||
if (!this._isInDOMTree(rawNode)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// This is a special case for the document object whereby it is considered
|
||||
// as document.documentElement (the <html> node)
|
||||
if (rawNode.defaultView && rawNode === rawNode.defaultView.document) {
|
||||
|
@ -2070,7 +2110,7 @@ var WalkerActor = protocol.ActorClass({
|
|||
objectActorID: Arg(0, "string")
|
||||
},
|
||||
response: {
|
||||
nodeFront: RetVal("disconnectedNode")
|
||||
nodeFront: RetVal("nullable:disconnectedNode")
|
||||
}
|
||||
}),
|
||||
});
|
||||
|
@ -2208,7 +2248,7 @@ var WalkerFront = exports.WalkerFront = protocol.FrontClass(WalkerActor, {
|
|||
|
||||
getNodeActorFromObjectActor: protocol.custom(function(objectActorID) {
|
||||
return this._getNodeActorFromObjectActor(objectActorID).then(response => {
|
||||
return response.node;
|
||||
return response ? response.node : null;
|
||||
});
|
||||
}, {
|
||||
impl: "_getNodeActorFromObjectActor"
|
||||
|
|
|
@ -183,7 +183,8 @@ RootActor.prototype = {
|
|||
// Wether the inspector actor implements the getImageDataFromURL
|
||||
// method that returns data-uris for image URLs. This is used for image
|
||||
// tooltips for instance
|
||||
urlToImageDataResolver: true
|
||||
urlToImageDataResolver: true,
|
||||
networkMonitor: true,
|
||||
}
|
||||
};
|
||||
},
|
||||
|
|
|
@ -10,6 +10,14 @@ let Ci = Components.interfaces;
|
|||
let CC = Components.Constructor;
|
||||
|
||||
Cu.import("resource://gre/modules/osfile.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "NetworkMonitorManager", () => {
|
||||
return devtools.require("devtools/toolkit/webconsole/network-monitor")
|
||||
.NetworkMonitorManager;
|
||||
});
|
||||
|
||||
let promise;
|
||||
|
||||
|
@ -822,13 +830,19 @@ WebappsActor.prototype = {
|
|||
.frameLoader
|
||||
.messageManager;
|
||||
let actor = map.get(mm);
|
||||
let netMonitor = null;
|
||||
if (!actor) {
|
||||
let onConnect = actor => {
|
||||
map.set(mm, actor);
|
||||
netMonitor = new NetworkMonitorManager(appFrame);
|
||||
return { actor: actor };
|
||||
};
|
||||
let onDisconnect = mm => {
|
||||
map.delete(mm);
|
||||
if (netMonitor) {
|
||||
netMonitor.destroy();
|
||||
netMonitor = null;
|
||||
}
|
||||
};
|
||||
return DebuggerServer.connectToChild(this.conn, mm, onDisconnect)
|
||||
.then(onConnect);
|
||||
|
|
|
@ -494,12 +494,16 @@ function TabActor(aConnection, aChromeEventHandler)
|
|||
this._extraActors = {};
|
||||
|
||||
this._onWindowCreated = this.onWindowCreated.bind(this);
|
||||
|
||||
this.traits = { reconfigure: true };
|
||||
}
|
||||
|
||||
// XXX (bug 710213): TabActor attach/detach/exit/disconnect is a
|
||||
// *complete* mess, needs to be rethought asap.
|
||||
|
||||
TabActor.prototype = {
|
||||
traits: null,
|
||||
|
||||
get exited() { return !this._chromeEventHandler; },
|
||||
get attached() { return !!this._attached; },
|
||||
|
||||
|
@ -735,7 +739,8 @@ TabActor.prototype = {
|
|||
type: "tabAttached",
|
||||
threadActor: this.threadActor.actorID,
|
||||
cacheEnabled: this._getCacheEnabled(),
|
||||
javascriptEnabled: this._getJavascriptEnabled()
|
||||
javascriptEnabled: this._getJavascriptEnabled(),
|
||||
traits: this.traits,
|
||||
};
|
||||
},
|
||||
|
||||
|
|
|
@ -16,10 +16,21 @@ let devtools = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devto
|
|||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Services",
|
||||
"resource://gre/modules/Services.jsm");
|
||||
XPCOMUtils.defineLazyGetter(this, "NetworkMonitor", () => {
|
||||
return devtools.require("devtools/toolkit/webconsole/network-monitor")
|
||||
.NetworkMonitor;
|
||||
});
|
||||
XPCOMUtils.defineLazyGetter(this, "NetworkMonitorChild", () => {
|
||||
return devtools.require("devtools/toolkit/webconsole/network-monitor")
|
||||
.NetworkMonitorChild;
|
||||
});
|
||||
XPCOMUtils.defineLazyGetter(this, "ConsoleProgressListener", () => {
|
||||
return devtools.require("devtools/toolkit/webconsole/network-monitor")
|
||||
.ConsoleProgressListener;
|
||||
});
|
||||
|
||||
for (let name of ["WebConsoleUtils", "ConsoleServiceListener",
|
||||
"ConsoleAPIListener", "ConsoleProgressListener",
|
||||
"JSTermHelpers", "JSPropertyProvider", "NetworkMonitor",
|
||||
"ConsoleAPIListener", "JSTermHelpers", "JSPropertyProvider",
|
||||
"ConsoleReflowListener"]) {
|
||||
Object.defineProperty(this, name, {
|
||||
get: function(prop) {
|
||||
|
@ -65,6 +76,10 @@ function WebConsoleActor(aConnection, aParentActor)
|
|||
Services.obs.addObserver(this._onObserverNotification,
|
||||
"last-pb-context-exited", false);
|
||||
}
|
||||
|
||||
this.traits = {
|
||||
customNetworkRequest: !this._parentIsContentActor,
|
||||
};
|
||||
}
|
||||
|
||||
WebConsoleActor.l10n = new WebConsoleUtils.l10n("chrome://global/locale/console.properties");
|
||||
|
@ -115,6 +130,23 @@ WebConsoleActor.prototype =
|
|||
*/
|
||||
conn: null,
|
||||
|
||||
/**
|
||||
* List of supported features by the console actor.
|
||||
* @type object
|
||||
*/
|
||||
traits: null,
|
||||
|
||||
/**
|
||||
* Boolean getter that tells if the parent actor is a ContentActor.
|
||||
*
|
||||
* @private
|
||||
* @type boolean
|
||||
*/
|
||||
get _parentIsContentActor() {
|
||||
return "ContentActor" in DebuggerServer &&
|
||||
this.parentActor instanceof DebuggerServer.ContentActor;
|
||||
},
|
||||
|
||||
/**
|
||||
* The window we work with.
|
||||
* @type nsIDOMWindow
|
||||
|
@ -254,13 +286,6 @@ WebConsoleActor.prototype =
|
|||
*/
|
||||
_jstermHelpersCache: null,
|
||||
|
||||
/**
|
||||
* Getter for the NetworkMonitor.saveRequestAndResponseBodies preference.
|
||||
* @type boolean
|
||||
*/
|
||||
get saveRequestAndResponseBodies()
|
||||
this._prefs["NetworkMonitor.saveRequestAndResponseBodies"] || null,
|
||||
|
||||
actorPrefix: "console",
|
||||
|
||||
grip: function WCA_grip()
|
||||
|
@ -477,6 +502,14 @@ WebConsoleActor.prototype =
|
|||
{
|
||||
let startedListeners = [];
|
||||
let window = !this.parentActor.isRootActor ? this.window : null;
|
||||
let appId = null;
|
||||
let messageManager = null;
|
||||
|
||||
if (this._parentIsContentActor) {
|
||||
// Filter network requests by appId on Firefox OS devices.
|
||||
appId = this.parentActor.docShell.appId;
|
||||
messageManager = this.parentActor._chromeGlobal;
|
||||
}
|
||||
|
||||
while (aRequest.listeners.length > 0) {
|
||||
let listener = aRequest.listeners.shift();
|
||||
|
@ -499,8 +532,13 @@ WebConsoleActor.prototype =
|
|||
break;
|
||||
case "NetworkActivity":
|
||||
if (!this.networkMonitor) {
|
||||
if (appId && messageManager) {
|
||||
this.networkMonitor =
|
||||
new NetworkMonitor(window, this);
|
||||
new NetworkMonitorChild(appId, messageManager, this);
|
||||
}
|
||||
else {
|
||||
this.networkMonitor = new NetworkMonitor({ window: window }, this);
|
||||
}
|
||||
this.networkMonitor.init();
|
||||
}
|
||||
startedListeners.push(listener);
|
||||
|
@ -526,6 +564,7 @@ WebConsoleActor.prototype =
|
|||
return {
|
||||
startedListeners: startedListeners,
|
||||
nativeConsoleAPI: this.hasNativeConsoleAPI(this.window),
|
||||
traits: this.traits,
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -821,6 +860,11 @@ WebConsoleActor.prototype =
|
|||
{
|
||||
for (let key in aRequest.preferences) {
|
||||
this._prefs[key] = aRequest.preferences[key];
|
||||
|
||||
if (key == "NetworkMonitor.saveRequestAndResponseBodies" &&
|
||||
this.networkMonitor) {
|
||||
this.networkMonitor.saveRequestAndResponseBodies = this._prefs[key];
|
||||
}
|
||||
}
|
||||
return { updated: Object.keys(aRequest.preferences) };
|
||||
},
|
||||
|
@ -1131,6 +1175,8 @@ WebConsoleActor.prototype =
|
|||
*
|
||||
* @param object aEvent
|
||||
* The initial network request event information.
|
||||
* @param nsIHttpChannel aChannel
|
||||
* The network request nsIHttpChannel object.
|
||||
* @return object
|
||||
* A new NetworkEventActor is returned. This is used for tracking the
|
||||
* network request and response.
|
||||
|
@ -1155,8 +1201,10 @@ WebConsoleActor.prototype =
|
|||
* Get the NetworkEventActor for a nsIChannel, if it exists,
|
||||
* otherwise create a new one.
|
||||
*
|
||||
* @param object aChannel
|
||||
* @param nsIHttpChannel aChannel
|
||||
* The channel for the network event.
|
||||
* @return object
|
||||
* The NetworkEventActor for the given channel.
|
||||
*/
|
||||
getNetworkEventActor: function WCA_getNetworkEventActor(aChannel) {
|
||||
let actor = this._netEvents.get(aChannel);
|
||||
|
|
|
@ -16,19 +16,24 @@ loader.lazyImporter(this, "LongStringClient", "resource://gre/modules/devtools/d
|
|||
*
|
||||
* @param object aDebuggerClient
|
||||
* The DebuggerClient instance we live for.
|
||||
* @param string aActor
|
||||
* The WebConsoleActor ID.
|
||||
* @param object aResponse
|
||||
* The response packet received from the "startListeners" request sent to
|
||||
* the WebConsoleActor.
|
||||
*/
|
||||
function WebConsoleClient(aDebuggerClient, aActor)
|
||||
function WebConsoleClient(aDebuggerClient, aResponse)
|
||||
{
|
||||
this._actor = aActor;
|
||||
this._actor = aResponse.from;
|
||||
this._client = aDebuggerClient;
|
||||
this._longStrings = {};
|
||||
this.traits = aResponse.traits || {};
|
||||
}
|
||||
exports.WebConsoleClient = WebConsoleClient;
|
||||
|
||||
WebConsoleClient.prototype = {
|
||||
_longStrings: null,
|
||||
traits: null,
|
||||
|
||||
get actor() { return this._actor; },
|
||||
|
||||
/**
|
||||
* Retrieve the cached messages from the server.
|
||||
|
|
|
@ -188,6 +188,40 @@ let NetworkHelper = {
|
|||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the web appId that is associated with aRequest.
|
||||
*
|
||||
* @param nsIHttpChannel aRequest
|
||||
* @returns number|null
|
||||
* The appId for the given request, if available.
|
||||
*/
|
||||
getAppIdForRequest: function NH_getAppIdForRequest(aRequest)
|
||||
{
|
||||
try {
|
||||
return this.getRequestLoadContext(aRequest).appId;
|
||||
} catch (ex) {
|
||||
// request loadContent is not always available.
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the topFrameElement that is associated with aRequest.
|
||||
*
|
||||
* @param nsIHttpChannel aRequest
|
||||
* @returns nsIDOMElement|null
|
||||
* The top frame element for the given request, if available.
|
||||
*/
|
||||
getTopFrameForRequest: function NH_getTopFrameForRequest(aRequest)
|
||||
{
|
||||
try {
|
||||
return this.getRequestLoadContext(aRequest).topFrameElement;
|
||||
} catch (ex) {
|
||||
// request loadContent is not always available.
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the nsIDOMWindow that is associated with aRequest.
|
||||
*
|
||||
|
|