This commit is contained in:
Ryan VanderMeulen 2014-03-08 01:12:53 -08:00
Родитель 4240714652 117bce68a7
Коммит 83ea4e27c8
101 изменённых файлов: 3845 добавлений и 1605 удалений

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

До

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

После

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

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

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

До

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

После

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

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

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

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

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

До

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

После

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

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

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

До

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

После

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

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

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

До

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

После

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

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

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

До

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

После

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

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

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

До

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

После

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

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

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

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

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

До

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

После

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

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

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

До

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

После

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

До

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

После

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

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

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

До

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

После

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

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

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

До

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

После

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

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

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

До

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

После

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

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

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

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

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

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

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

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

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

До

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

После

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

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

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

До

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

После

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -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,32 +170,38 @@ HelperAppLauncherDialog.prototype = {
}
}
if (apps.length > 1) {
HelperApps.prompt(apps, {
title: bundle.GetStringFromName("helperapps.pick"),
buttons: [
bundle.GetStringFromName("helperapps.alwaysUse"),
bundle.GetStringFromName("helperapps.useJustOnce")
]
}, (data) => {
if (data.button < 0)
return;
callback(apps[data.icongrid0]);
if (data.button == 0)
this._setPreferredApp(aLauncher, apps[data.icongrid0]);
});
} else {
// 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: [
bundle.GetStringFromName("helperapps.alwaysUse"),
bundle.GetStringFromName("helperapps.useJustOnce")
]
}, (data) => {
if (data.button < 0) {
return;
}
callback(apps[data.icongrid0]);
if (data.button === 0) {
this._setPreferredApp(aLauncher, apps[data.icongrid0]);
}
});
},
_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.
this.deferCanceled.resolve();
// 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) {
this.networkMonitor =
new NetworkMonitor(window, this);
if (appId && messageManager) {
this.networkMonitor =
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.
*

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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