CLOSED TREE
This commit is contained in:
Ryan VanderMeulen 2015-02-13 12:13:22 -05:00
Родитель 9fcecfd6ac 3ea7360ae8
Коммит 3e2d8e9ec4
85 изменённых файлов: 1265 добавлений и 481 удалений

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

@ -24,7 +24,7 @@
<menupopup id="menu_ToolsPopup">
<menuitem id="menu_preferences" label="&preferencesCmdMac.label;" key="key_preferencesCmdMac" oncommand="openPreferences();"/>
<menuitem id="menu_mac_services" label="&servicesMenuMac.label;"/>
<menuitem id="menu_mac_hide_app" label="&hideThisAppCmdMac.label;" key="key_hideThisAppCmdMac"/>
<menuitem id="menu_mac_hide_app" label="&hideThisAppCmdMac2.label;" key="key_hideThisAppCmdMac"/>
<menuitem id="menu_mac_hide_others" label="&hideOtherAppsCmdMac.label;" key="key_hideOtherAppsCmdMac"/>
<menuitem id="menu_mac_show_all" label="&showAllAppsCmdMac.label;"/>
</menupopup>
@ -45,8 +45,8 @@
<menuitem id="menu_openHelp"
oncommand="openHelpLink('firefox-help')"
onclick="checkForMiddleClick(this, event);"
label="&productHelp.label;"
accesskey="&productHelp.accesskey;"
label="&productHelp2.label;"
accesskey="&productHelp2.accesskey;"
#ifdef XP_MACOSX
key="key_openHelpMac"/>
#else
@ -54,8 +54,8 @@
#endif
<menuitem id="menu_openTour"
oncommand="openTourPage();"
label="&helpShowTour.label;"
accesskey="&helpShowTour.accesskey;"/>
label="&helpShowTour2.label;"
accesskey="&helpShowTour2.accesskey;"/>
<menuitem id="menu_keyboardShortcuts"
oncommand="openHelpLink('keyboard-shortcuts')"
onclick="checkForMiddleClick(this, event);"
@ -63,8 +63,8 @@
accesskey="&helpKeyboardShortcuts.accesskey;"/>
#ifdef MOZ_SERVICES_HEALTHREPORT
<menuitem id="healthReport"
label="&healthReport.label;"
accesskey="&healthReport.accesskey;"
label="&healthReport2.label;"
accesskey="&healthReport2.accesskey;"
oncommand="openHealthReport()"
onclick="checkForMiddleClick(this, event);"/>
#endif
@ -84,8 +84,8 @@
oncommand="safeModeRestart();"/>
<menuseparator id="aboutSeparator"/>
<menuitem id="aboutName"
accesskey="&aboutProduct.accesskey;"
label="&aboutProduct.label;"
accesskey="&aboutProduct2.accesskey;"
label="&aboutProduct2.label;"
oncommand="openAboutDialog();"/>
</menupopup>
</menu>
@ -101,7 +101,7 @@
key="&preferencesCmdMac.commandkey;"
modifiers="accel"/>
<key id="key_hideThisAppCmdMac"
key="&hideThisAppCmdMac.commandkey;"
key="&hideThisAppCmdMac2.commandkey;"
modifiers="accel"/>
<key id="key_hideOtherAppsCmdMac"
key="&hideOtherAppsCmdMac.commandkey;"

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

@ -107,11 +107,11 @@
oncommand="BrowserOffline.toggleOfflineStatus();"/>
<menuitem id="menu_FileQuitItem"
#ifdef XP_WIN
label="&quitApplicationCmdWin.label;"
accesskey="&quitApplicationCmdWin.accesskey;"
label="&quitApplicationCmdWin2.label;"
accesskey="&quitApplicationCmdWin2.accesskey;"
#else
#ifdef XP_MACOSX
label="&quitApplicationCmdMac.label;"
label="&quitApplicationCmdMac2.label;"
#else
label="&quitApplicationCmd.label;"
accesskey="&quitApplicationCmd.accesskey;"

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

@ -7128,7 +7128,7 @@ var gIdentityHandler = {
break; }
case this.IDENTITY_MODE_CHROMEUI:
let brandBundle = document.getElementById("bundle_brand");
icon_label = brandBundle.getString("brandShortName");
icon_label = brandBundle.getString("brandShorterName");
break;
default:
tooltip = gNavigatorBundle.getString("identity.unknown.tooltip");

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

@ -816,10 +816,11 @@
hidden="true"
tooltiptext="&pageReportIcon.tooltip;"
onclick="gPopupBlockerObserver.onReportButtonClick(event);"/>
<image id="reader-mode-button"
class="urlbar-icon"
hidden="true"
onclick="ReaderParent.toggleReaderMode(event);"/>
<toolbarbutton id="reader-mode-button"
class="tabbable"
hidden="true"
onclick="ReaderParent.handleReaderButtonEvent(event);"
onkeypress="ReaderParent.handleReaderButtonEvent(event);"/>
</hbox>
<toolbarbutton id="urlbar-go-button"
class="chromeclass-toolbar-additional"

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

@ -2,6 +2,7 @@
- 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/. -->
<!ENTITY brandShorterName "Firefox">
<!ENTITY brandShortName "Firefox Developer Edition">
<!ENTITY brandFullName "Firefox Developer Edition">
<!ENTITY vendorShortName "Mozilla">

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

@ -2,6 +2,7 @@
# 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/.
brandShorterName=Firefox
brandShortName=Firefox Developer Edition
brandFullName=Firefox Developer Edition
vendorShortName=Mozilla

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

@ -2,6 +2,7 @@
- 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/. -->
<!ENTITY brandShorterName "Nightly">
<!ENTITY brandShortName "Nightly">
<!ENTITY brandFullName "Nightly">
<!ENTITY vendorShortName "Mozilla">

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

@ -2,6 +2,7 @@
# 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/.
brandShorterName=Nightly
brandShortName=Nightly
brandFullName=Nightly
vendorShortName=Mozilla

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

@ -2,6 +2,7 @@
- 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/. -->
<!ENTITY brandShorterName "Firefox">
<!ENTITY brandShortName "Firefox">
<!ENTITY brandFullName "Mozilla Firefox">
<!ENTITY vendorShortName "Mozilla">

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

@ -2,6 +2,7 @@
# 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/.
brandShorterName=Firefox
brandShortName=Firefox
brandFullName=Mozilla Firefox
vendorShortName=Mozilla

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

@ -2,6 +2,7 @@
- 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/. -->
<!ENTITY brandShorterName "Mozilla Developer Preview">
<!ENTITY brandShortName "Mozilla Developer Preview">
<!ENTITY brandFullName "Mozilla Developer Preview">
<!ENTITY vendorShortName "mozilla.org">

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

@ -2,6 +2,7 @@
# 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/.
brandShorterName=Mozilla Developer Preview
brandShortName=Mozilla Developer Preview
brandFullName=Mozilla Developer Preview
vendorShortName=mozilla.org

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

@ -42,11 +42,11 @@
<toolbarseparator/>
<toolbarbutton id="PanelUI-quit"
#ifdef XP_WIN
label="&quitApplicationCmdWin.label;"
tooltiptext="&quitApplicationCmdWin.tooltip;"
label="&quitApplicationCmdWin2.label;"
tooltiptext="&quitApplicationCmdWin2.tooltip;"
#else
#ifdef XP_MACOSX
label="&quitApplicationCmdMac.label;"
label="&quitApplicationCmdMac2.label;"
#else
label="&quitApplicationCmd.label;"
#endif

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

@ -114,30 +114,27 @@ skip-if = os == "linux" # Intemittent failures - bug 979207
[browser_973641_button_addon.js]
[browser_973932_addonbar_currentset.js]
[browser_975719_customtoolbars_behaviour.js]
[browser_976792_insertNodeInWindow.js]
skip-if = os == "linux"
[browser_978084_dragEnd_after_move.js]
[browser_980155_add_overflow_toolbar.js]
[browser_981305_separator_insertion.js]
[browser_981418-widget-onbeforecreated-handler.js]
[browser_982656_restore_defaults_builtin_widgets.js]
[browser_984455_bookmarks_items_reparenting.js]
skip-if = os == "linux"
[browser_985815_propagate_setToolbarVisibility.js]
skip-if = e10s # bug 1090635
[browser_981305_separator_insertion.js]
[browser_988072_sidebar_events.js]
[browser_989338_saved_placements_not_resaved.js]
[browser_989751_subviewbutton_class.js]
[browser_987177_destroyWidget_xul.js]
[browser_987177_xul_wrapper_updating.js]
[browser_987185_syncButton.js]
[browser_987492_window_api.js]
[browser_987640_charEncoding.js]
skip-if = e10s # Bug 1088710
[browser_988072_sidebar_events.js]
[browser_989338_saved_placements_not_resaved.js]
[browser_989751_subviewbutton_class.js]
skip-if = os == "linux" && e10s # Bug 1102900, bug 1104745, bug 1104761
[browser_992747_toggle_noncustomizable_toolbar.js]
[browser_993322_widget_notoolbar.js]
[browser_995164_registerArea_during_customize_mode.js]
@ -151,7 +148,7 @@ skip-if = e10s # Bug 1088710
[browser_1087303_button_fullscreen.js]
skip-if = os == "mac"
[browser_1087303_button_preferences.js]
[browser_bootstrapped_custom_toolbar.js]
[browser_panel_toggle.js]
[browser_1089591_still_customizable_after_reset.js]
[browser_1096763_seen_widgets_post_reset.js]
[browser_bootstrapped_custom_toolbar.js]
[browser_panel_toggle.js]

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

@ -74,19 +74,14 @@ function createMockAllocations () {
* @param {TabTarget} target
* @return {Boolean}
*/
function* memoryActorSupported (target) {
function memoryActorSupported (target) {
// This `target` property is used only in tests to test
// instances where the memory actor is not available.
if (target.TEST_MOCK_MEMORY_ACTOR) {
return false;
}
for (let method of REQUIRED_MEMORY_ACTOR_METHODS) {
if (!(yield target.actorHasMethod("memory", method))) {
return false;
}
}
return true;
return !!target.getTrait("memoryActorAllocations");
}
exports.memoryActorSupported = Task.async(memoryActorSupported);
@ -97,13 +92,13 @@ exports.memoryActorSupported = Task.async(memoryActorSupported);
* @param {TabTarget} target
* @return {Boolean}
*/
function* timelineActorSupported(target) {
function timelineActorSupported(target) {
// This `target` property is used only in tests to test
// instances where the timeline actor is not available.
if (target.TEST_MOCK_TIMELINE_ACTOR) {
return false;
}
return yield target.hasActor("timeline");
return target.hasActor("timeline");
}
exports.timelineActorSupported = Task.async(timelineActorSupported);

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

@ -12,6 +12,8 @@ support-files =
[browser_perf-allocations-to-samples.js]
[browser_perf-compatibility-01.js]
[browser_perf-compatibility-02.js]
[browser_perf-compatibility-03.js]
[browser_perf-compatibility-04.js]
[browser_perf-clear-01.js]
[browser_perf-clear-02.js]
[browser_perf-data-massaging-01.js]

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

@ -12,6 +12,7 @@ function spawnTest () {
TEST_MOCK_MEMORY_ACTOR: true,
TEST_MOCK_TIMELINE_ACTOR: true
});
Services.prefs.setBoolPref(MEMORY_PREF, true);
let { memory, timeline } = front.getMocksInUse();
ok(memory, "memory should be mocked.");

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

@ -13,6 +13,7 @@ let test = Task.async(function*() {
TEST_MOCK_MEMORY_ACTOR: true,
TEST_MOCK_TIMELINE_ACTOR: true
});
Services.prefs.setBoolPref(MEMORY_PREF, true);
let { EVENTS, gFront, PerformanceController, PerformanceView } = panel.panelWin;
let { memory: memoryMock, timeline: timelineMock } = gFront.getMocksInUse();

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

@ -0,0 +1,63 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test basic functionality of PerformanceFront with only mock memory.
*/
let WAIT_TIME = 100;
function spawnTest () {
let { target, front } = yield initBackend(SIMPLE_URL, {
TEST_MOCK_MEMORY_ACTOR: true
});
Services.prefs.setBoolPref(MEMORY_PREF, true);
let { memory, timeline } = front.getMocksInUse();
ok(memory, "memory should be mocked.");
ok(!timeline, "timeline should not be mocked.");
let {
profilerStartTime,
timelineStartTime,
memoryStartTime
} = yield front.startRecording({
withTicks: true,
withMemory: true,
withAllocations: true
});
ok(typeof profilerStartTime === "number",
"The front.startRecording() emits a profiler start time.");
ok(typeof timelineStartTime === "number",
"The front.startRecording() emits a timeline start time.");
ok(typeof memoryStartTime === "number",
"The front.startRecording() emits a memory start time.");
yield busyWait(WAIT_TIME);
let {
profilerEndTime,
timelineEndTime,
memoryEndTime
} = yield front.stopRecording({
withAllocations: true
});
ok(typeof profilerEndTime === "number",
"The front.stopRecording() emits a profiler end time.");
ok(typeof timelineEndTime === "number",
"The front.stopRecording() emits a timeline end time.");
ok(typeof memoryEndTime === "number",
"The front.stopRecording() emits a memory end time.");
ok(profilerEndTime > profilerStartTime,
"The profilerEndTime is after profilerStartTime.");
ok(timelineEndTime > timelineStartTime,
"The timelineEndTime is after timelineStartTime.");
is(memoryEndTime, memoryStartTime,
"The memoryEndTime is the same as memoryStartTime.");
yield removeTab(target.tab);
finish();
}

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

@ -0,0 +1,67 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests that the recording model is populated correctly when using timeline
* and memory actor mocks.
*/
const WAIT_TIME = 1000;
let test = Task.async(function*() {
let { target, panel, toolbox } = yield initPerformance(SIMPLE_URL, "performance", {
TEST_MOCK_MEMORY_ACTOR: true
});
Services.prefs.setBoolPref(MEMORY_PREF, true);
let { EVENTS, gFront, PerformanceController, PerformanceView } = panel.panelWin;
let { memory: memoryMock, timeline: timelineMock } = gFront.getMocksInUse();
ok(memoryMock, "memory should be mocked.");
ok(!timelineMock, "timeline should not be mocked.");
yield startRecording(panel);
yield busyWait(100);
yield waitUntil(() => PerformanceController.getCurrentRecording().getTicks().length);
yield waitUntil(() => PerformanceController.getCurrentRecording().getMemory().length);
yield waitUntil(() => PerformanceController.getCurrentRecording().getMarkers().length);
yield stopRecording(panel);
let {
label, duration, allocations, profile
} = PerformanceController.getCurrentRecording().getAllData();
is(label, "", "Empty label for mock.");
is(typeof duration, "number", "duration is a number");
ok(duration > 0, "duration is not 0");
isEmptyArray(allocations.sites, "allocations.sites");
isEmptyArray(allocations.timestamps, "allocations.timestamps");
isEmptyArray(allocations.frames, "allocations.frames");
isEmptyArray(allocations.counts, "allocations.counts");
let sampleCount = 0;
for (let thread of profile.threads) {
info("Checking thread: " + thread.name);
for (let sample of thread.samples) {
sampleCount++;
if (sample.frames[0].location != "(root)") {
ok(false, "The sample " + sample.toSource() + " doesn't have a root node.");
}
}
}
ok(sampleCount > 0,
"At least some samples have been iterated over, checking for root nodes.");
yield teardown(panel);
finish();
});
function isEmptyArray (array, name) {
ok(Array.isArray(array), `${name} is an array`);
is(array.length, 0, `${name} is empty`);
}

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

@ -7,6 +7,8 @@
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console.html?" + Date.now();
const TEST_XHR_ERROR_URI = `http://example.com/404.html?${Date.now()}`;
"use strict";
let test = asyncTest(function*() {
@ -49,6 +51,12 @@ function consoleOpened(hud)
xhr.open("get", TEST_URI, true);
xhr.send();
// Check for xhr error.
let xhrErr = new XMLHttpRequest();
xhrErr.onload = () => console.log("xhr error loaded, status is: " + xhrErr.status);
xhrErr.open("get", TEST_XHR_ERROR_URI, true);
xhrErr.send();
return waitForMessages({
webconsole: hud,
messages: [
@ -81,6 +89,14 @@ function consoleOpened(hud)
text: "test-console.html",
category: CATEGORY_NETWORK,
severity: SEVERITY_INFO,
isXhr: true,
},
{
name: "xhr error message",
text: "404.html",
category: CATEGORY_NETWORK,
severity: SEVERITY_ERROR,
isXhr: true,
},
],
});

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

@ -4,7 +4,7 @@
// Tests that network log messages bring up the network panel.
const TEST_NETWORK_REQUEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-network-request.html";
const TEST_NETWORK_REQUEST_URI = "https://example.com/browser/browser/devtools/webconsole/test/test-network-request.html";
const TEST_IMG = "http://example.com/browser/browser/devtools/webconsole/test/test-image.png";
@ -20,14 +20,20 @@ let hud, browser;
function test()
{
const PREF = "devtools.webconsole.persistlog";
let original = Services.prefs.getBoolPref("devtools.webconsole.filter.networkinfo");
let originalXhr = Services.prefs.getBoolPref("devtools.webconsole.filter.netxhr");
Services.prefs.setBoolPref("devtools.webconsole.filter.networkinfo", true);
Services.prefs.setBoolPref("devtools.webconsole.filter.netxhr", true);
const NET_PREF = "devtools.webconsole.filter.networkinfo";
const NETXHR_PREF = "devtools.webconsole.filter.netxhr"
const MIXED_AC_PREF = "security.mixed_content.block_active_content"
let original = Services.prefs.getBoolPref(NET_PREF);
let originalXhr = Services.prefs.getBoolPref(NETXHR_PREF);
let originalMixedActive = Services.prefs.getBoolPref(MIXED_AC_PREF);
Services.prefs.setBoolPref(NET_PREF, true);
Services.prefs.setBoolPref(NETXHR_PREF, true);
Services.prefs.setBoolPref(MIXED_AC_PREF, false);
Services.prefs.setBoolPref(PREF, true);
registerCleanupFunction(() => {
Services.prefs.setBoolPref("devtools.webconsole.filter.networkinfo", original);
Services.prefs.setBoolPref("devtools.webconsole.filter.netxhr", originalXhr);
Services.prefs.setBoolPref(NET_PREF, original);
Services.prefs.setBoolPref(NETXHR_PREF, originalXhr);
Services.prefs.setBoolPref(MIXED_AC_PREF, originalMixedActive);
Services.prefs.clearUserPref(PREF);
});
@ -100,13 +106,27 @@ function testXhrGet()
is(lastRequest.request.method, "GET", "Method is correct");
lastRequest = null;
requestCallback = null;
executeSoon(testXhrPost);
executeSoon(testXhrWarn);
};
// Start the XMLHttpRequest() GET test.
content.wrappedJSObject.testXhrGet();
}
function testXhrWarn()
{
requestCallback = function() {
ok(lastRequest, "testXhrWarn() was logged");
is(lastRequest.request.method, "GET", "Method is correct");
lastRequest = null;
requestCallback = null;
executeSoon(testXhrPost);
};
// Start the XMLHttpRequest() warn test.
content.wrappedJSObject.testXhrWarn();
}
function testXhrPost()
{
requestCallback = function() {
@ -143,8 +163,16 @@ function testFormSubmission()
text: "test-data.json",
category: CATEGORY_NETWORK,
severity: SEVERITY_INFO,
isXhr: true,
count: 2,
},
{
text: "http://example.com/",
category: CATEGORY_NETWORK,
severity: SEVERITY_WARNING,
isXhr: true,
count: 1,
},
],
}).then(testLiveFilteringOnSearchStrings);
};

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

@ -1117,6 +1117,14 @@ function waitForMessages(aOptions)
return true;
}
function hasXhrLabel(aElement) {
let xhr = aElement.querySelector('.xhr');
if (!xhr) {
return false;
}
return true;
}
function checkMessage(aRule, aElement)
{
let elemText = aElement.textContent;
@ -1161,6 +1169,14 @@ function waitForMessages(aOptions)
return false;
}
if (aRule.isXhr && !hasXhrLabel(aElement)) {
return false;
}
if (!aRule.isXhr && hasXhrLabel(aElement)) {
return false;
}
let partialMatch = !!(aRule.consoleTrace || aRule.consoleTime ||
aRule.consoleTimeEnd);

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

@ -7,7 +7,7 @@
var str = "Dolske Digs Bacon, Now and Forevermore."
for (var i=0; i < 5; i++) {
console.log(str);
}
}
}
console.info("INLINE SCRIPT:");
test();

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

@ -19,6 +19,10 @@
makeXhr('get', 'test-data.json', null, aCallback);
}
function testXhrWarn(aCallback) {
makeXhr('get', 'http://example.com', null, aCallback);
}
function testXhrPost(aCallback) {
makeXhr('post', 'test-data.json', "Hello world!", aCallback);
}
@ -28,7 +32,7 @@
<h1>Heads Up Display HTTP Logging Testpage</h1>
<h2>This page is used to test the HTTP logging.</h2>
<form action="http://example.com/browser/browser/devtools/webconsole/test/test-network-request.html" method="post">
<form action="https://example.com/browser/browser/devtools/webconsole/test/test-network-request.html" method="post">
<input name="name" type="text" value="foo bar"><br>
<input name="age" type="text" value="144"><br>
</form>

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

@ -1499,6 +1499,7 @@ WebConsoleFrame.prototype = {
let clipboardText = request.method + " " + request.url;
let severity = SEVERITY_LOG;
if (networkInfo.isXHR) {
clipboardText = request.method + " XHR " + request.url;
severity = SEVERITY_INFO;
}
let mixedRequest =
@ -1523,6 +1524,14 @@ WebConsoleFrame.prototype = {
let body = methodNode.parentNode;
body.setAttribute("aria-haspopup", true);
if (networkInfo.isXHR) {
let xhrNode = this.document.createElementNS(XHTML_NS, "span");
xhrNode.className = "xhr";
xhrNode.textContent = l10n.getStr("webConsoleXhrIndicator");
body.appendChild(xhrNode);
body.appendChild(this.document.createTextNode(" "));
}
let displayUrl = request.url;
let pos = displayUrl.indexOf("?");
if (pos > -1) {
@ -1855,6 +1864,7 @@ WebConsoleFrame.prototype = {
let hasEventTimings = updates.indexOf("eventTimings") > -1;
let hasResponseStart = updates.indexOf("responseStart") > -1;
let request = networkInfo.request;
let methodText = (networkInfo.isXHR)? request.method + ' XHR' : request.method;
let response = networkInfo.response;
let updated = false;
@ -1872,7 +1882,7 @@ WebConsoleFrame.prototype = {
let statusNode = messageNode.getElementsByClassName("status")[0];
statusNode.textContent = statusText;
messageNode.clipboardText = [request.method, request.url, statusText]
messageNode.clipboardText = [methodText, request.url, statusText]
.join(" ");
if (hasResponseStart && response.status >= MIN_HTTP_ERROR_CODE &&

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

@ -14,10 +14,10 @@
for the help button in the menubar but Gnome does not. -->
<!ENTITY helpMenuWin.label "Help">
<!ENTITY helpMenuWin.accesskey "H">
<!ENTITY aboutProduct.label "About &brandShortName;">
<!ENTITY aboutProduct.accesskey "A">
<!ENTITY productHelp.label "&brandShortName; Help">
<!ENTITY productHelp.accesskey "H">
<!ENTITY aboutProduct2.label "About &brandShorterName;">
<!ENTITY aboutProduct2.accesskey "A">
<!ENTITY productHelp2.label "&brandShorterName; Help">
<!ENTITY productHelp2.accesskey "H">
<!ENTITY helpMac.commandkey "?">
<!ENTITY helpKeyboardShortcuts.label "Keyboard Shortcuts">
@ -26,8 +26,8 @@
<!ENTITY helpSafeMode.label "Restart with Add-ons Disabled…">
<!ENTITY helpSafeMode.accesskey "R">
<!ENTITY healthReport.label "&brandShortName; Health Report">
<!ENTITY healthReport.accesskey "e">
<!ENTITY healthReport2.label "&brandShorterName; Health Report">
<!ENTITY healthReport2.accesskey "e">
<!ENTITY helpTroubleshootingInfo.label "Troubleshooting Information">
<!ENTITY helpTroubleshootingInfo.accesskey "T">
@ -35,16 +35,16 @@
<!ENTITY helpFeedbackPage.label "Submit Feedback…">
<!ENTITY helpFeedbackPage.accesskey "S">
<!ENTITY helpShowTour.label "&brandShortName; Tour">
<!ENTITY helpShowTour.accesskey "o">
<!ENTITY helpShowTour2.label "&brandShorterName; Tour">
<!ENTITY helpShowTour2.accesskey "o">
<!ENTITY preferencesCmdMac.label "Preferences…">
<!ENTITY preferencesCmdMac.commandkey ",">
<!ENTITY servicesMenuMac.label "Services">
<!ENTITY hideThisAppCmdMac.label "Hide &brandShortName;">
<!ENTITY hideThisAppCmdMac.commandkey "H">
<!ENTITY hideThisAppCmdMac2.label "Hide &brandShorterName;">
<!ENTITY hideThisAppCmdMac2.commandkey "H">
<!ENTITY hideOtherAppsCmdMac.label "Hide Others">
<!ENTITY hideOtherAppsCmdMac.commandkey "H">

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

@ -605,14 +605,14 @@ you can use these alternative items. Otherwise, their values should be empty. -
<!ENTITY sidebarCloseButton.tooltip "Close sidebar">
<!ENTITY quitApplicationCmdWin.label "Exit">
<!ENTITY quitApplicationCmdWin.accesskey "x">
<!ENTITY quitApplicationCmdWin.tooltip "Exit &brandShortName;">
<!ENTITY quitApplicationCmdWin2.label "Exit">
<!ENTITY quitApplicationCmdWin2.accesskey "x">
<!ENTITY quitApplicationCmdWin2.tooltip "Exit &brandShorterName;">
<!ENTITY goBackCmd.commandKey "[">
<!ENTITY goForwardCmd.commandKey "]">
<!ENTITY quitApplicationCmd.label "Quit">
<!ENTITY quitApplicationCmd.accesskey "Q">
<!ENTITY quitApplicationCmdMac.label "Quit &brandShortName;">
<!ENTITY quitApplicationCmdMac2.label "Quit &brandShorterName;">
<!-- LOCALIZATION NOTE(quitApplicationCmdUnix.key): This keyboard shortcut is used by both Linux and OSX builds. -->
<!ENTITY quitApplicationCmdUnix.key "Q">

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

@ -70,6 +70,11 @@ ConsoleAPIDisabled=The Web Console logging API (console.log, console.info, conso
# the URL the correct direction. Parameters: %S is the web page URL.
webConsoleWindowTitleAndURL=Web Console - %S
# LOCALIZATION NOTE (webConsoleXhrIndicator): the indicator displayed before
# a URL in the Web Console that was requested using an XMLHttpRequest.
# Should probably be the same as &btnConsoleXhr; in webConsole.dtd
webConsoleXhrIndicator=XHR
# LOCALIZATION NOTE (webConsoleMixedContentWarning): the message displayed
# after a URL in the Web Console that has been flagged for Mixed Content (i.e.
# http content in an https page).

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

@ -0,0 +1,6 @@
# 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/.
readerMode.enter=Enter Reader Mode
readerMode.exit=Exit Reader Mode

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

@ -138,6 +138,7 @@
locale/browser/preferences/tabs.dtd (%chrome/browser/preferences/tabs.dtd)
locale/browser/preferences/search.dtd (%chrome/browser/preferences/search.dtd)
locale/browser/preferences/translation.dtd (%chrome/browser/preferences/translation.dtd)
locale/browser/readerMode.properties (%chrome/browser/readerMode.properties)
#ifdef MOZ_SERVICES_SYNC
locale/browser/syncBrand.dtd (%chrome/browser/syncBrand.dtd)
locale/browser/syncSetup.dtd (%chrome/browser/syncSetup.dtd)

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

@ -15,6 +15,8 @@ Cu.import("resource://gre/modules/Task.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ReaderMode", "resource://gre/modules/ReaderMode.jsm");
const gStringBundle = Services.strings.createBundle("chrome://browser/locale/readerMode.properties");
let ReaderParent = {
MESSAGES: [
@ -101,13 +103,23 @@ let ReaderParent = {
if (browser.currentURI.spec.startsWith("about:reader")) {
button.setAttribute("readeractive", true);
button.hidden = false;
button.setAttribute("tooltiptext", gStringBundle.GetStringFromName("readerMode.exit"));
} else {
button.removeAttribute("readeractive");
button.setAttribute("tooltiptext", gStringBundle.GetStringFromName("readerMode.enter"));
button.hidden = !browser.isArticle;
}
},
toggleReaderMode: function(event) {
handleReaderButtonEvent: function(event) {
event.stopPropagation();
if ((event.type == "click" && event.button != 0) ||
(event.type == "keypress" && event.charCode != Ci.nsIDOMKeyEvent.DOM_VK_SPACE &&
event.keyCode != Ci.nsIDOMKeyEvent.DOM_VK_RETURN)) {
return; // Left click, space or enter only
}
let win = event.target.ownerDocument.defaultView;
let url = win.gBrowser.selectedBrowser.currentURI.spec;
if (url.startsWith("about:reader")) {

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

@ -30,6 +30,7 @@ EXTRA_JS_MODULES += [
'offlineAppCache.jsm',
'PanelFrame.jsm',
'ProcessHangMonitor.jsm',
'ReaderParent.jsm',
'RemotePrompt.jsm',
'SitePermissions.jsm',
'Social.jsm',
@ -52,7 +53,6 @@ if CONFIG['NIGHTLY_BUILD']:
EXTRA_PP_JS_MODULES += [
'AboutHome.jsm',
'PluginContent.jsm',
'ReaderParent.jsm',
'RecentWindow.jsm',
'webrtcUI.jsm',
]

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

@ -1617,10 +1617,20 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-
/* Reader mode button */
#reader-mode-button {
-moz-appearance: none;
padding: 0;
list-style-image: url("chrome://browser/skin/reader-mode-16.png");
-moz-image-region: rect(0, 16px, 16px, 0);
}
#reader-mode-button > .toolbarbutton-icon {
width: 16px;
}
#reader-mode-button:focus {
outline: 1px dotted;
}
#reader-mode-button:hover:active,
#reader-mode-button[readeractive] {
-moz-image-region: rect(0, 32px, 16px, 16px);

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

@ -2521,20 +2521,29 @@ richlistitem[type~="action"][actiontype="switchtab"][selected="true"] > .ac-url-
/* Reader mode button */
#reader-mode-button {
-moz-appearance: none;
padding: 0;
list-style-image: url("chrome://browser/skin/reader-mode-16.png");
-moz-image-region: rect(0, 16px, 16px, 0);
}
#reader-mode-button > .toolbarbutton-icon {
width: 16px;
}
#reader-mode-button:focus {
@hudButtonFocused@
}
#reader-mode-button:hover:active,
#reader-mode-button[readeractive] {
-moz-image-region: rect(0, 32px, 16px, 16px);
}
@media (min-resolution: 2dppx) {
#reader-mode-button{
#reader-mode-button {
list-style-image: url("chrome://browser/skin/reader-mode-16@2x.png");
-moz-image-region: rect(0, 32px, 32px, 0);
width: 22px;
}
#reader-mode-button:hover:active,

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

@ -123,7 +123,7 @@
.call-tree-header[type="samples"],
.call-tree-cell[type="samples"] {
width: 4vw;
width: 4.5vw;
}
.call-tree-header[type="allocations"],

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

@ -262,6 +262,17 @@ a {
margin: 0 6px;
}
.message[category=network] .xhr {
background-color: var(--theme-body-color-alt);
color: var(--theme-body-background);
border-radius: 3px;
font-weight: bold;
font-size: 10px;
padding: 2px;
line-height: 10px;
-moz-margin-end: 1ex;
}
/* CSS styles */
.webconsole-filter-button[category="css"] > .toolbarbutton-menubutton-button:before {
background-image: linear-gradient(#2DC3F3, #00B6F0);

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

@ -1579,10 +1579,20 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-
/* Reader mode button */
#reader-mode-button {
-moz-appearance: none;
padding: 0;
list-style-image: url("chrome://browser/skin/reader-mode-16.png");
-moz-image-region: rect(0, 16px, 16px, 0);
}
#reader-mode-button > .toolbarbutton-icon {
width: 16px;
}
#reader-mode-button:focus {
outline: 1px dotted;
}
#reader-mode-button:hover:active,
#reader-mode-button[readeractive] {
-moz-image-region: rect(0, 32px, 16px, 16px);

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

@ -850,7 +850,19 @@ pref("dom.meta-viewport.enabled", true);
// Enable the OpenH264 plugin support in the addon manager.
pref("media.gmp-gmpopenh264.provider.enabled", true);
// The default color scheme in reader mode (light, dark, print, auto)
// The default color scheme in reader mode (light, dark, auto)
// auto = color automatically adjusts according to ambient light level
// (auto only works on platforms where the 'devicelight' event is enabled)
pref("reader.color_scheme", "auto");
// Color scheme values available in reader mode UI.
pref("reader.color_scheme.values", "[\"light\",\"dark\",\"auto\"]");
// The font type in reader (charis-sil, clear-sans)
pref("reader.font_type", "clear-sans");
// Font type values available in reader mode UI.
pref("reader.font_type.values", "[\"charis-sil\",\"clear-sans\"]");
// Whether to use a vertical or horizontal toolbar.
pref("reader.toolbar.vertical", false);

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

@ -839,9 +839,7 @@ public abstract class GeckoApp
@Override
public void onToastHidden(ButtonToast.ReasonHidden reason) {
if (reason == ButtonToast.ReasonHidden.TIMEOUT) {
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Toast:Hidden", buttonId));
}
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Toast:Hidden", buttonId));
}
});
}

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

@ -329,18 +329,17 @@ public class AccountPickler {
NonObjectJSONException, NoSuchAlgorithmException {
// TODO: Should copy-pasta BUNDLE_KEY_STATE & LABEL to this file to ensure we maintain
// old versions?
final StateLabel stateLabel = StateLabel.valueOf(
final StateLabel stateLabelString = StateLabel.valueOf(
bundle.getString(AndroidFxAccount.BUNDLE_KEY_STATE_LABEL));
final String stateString = bundle.getString(AndroidFxAccount.BUNDLE_KEY_STATE);
if (stateLabel == null) {
throw new IllegalStateException("stateLabel must not be null");
}
if (stateString == null) {
throw new IllegalStateException("stateString must not be null");
if (stateLabelString == null || stateString == null) {
throw new IllegalStateException("stateLabel and stateString must not be null, but: " +
"(stateLabel == null) = " + (stateLabelString == null) +
" and (stateString == null) = " + (stateString == null));
}
try {
return StateFactory.fromJSONObject(stateLabel, new ExtendedJSONObject(stateString));
return StateFactory.fromJSONObject(stateLabelString, new ExtendedJSONObject(stateString));
} catch (Exception e) {
throw new IllegalStateException("could not get state", e);
}

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

@ -12,6 +12,7 @@ import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.mozilla.gecko.AppConstants;
import org.mozilla.gecko.background.common.GlobalConstants;
@ -83,6 +84,21 @@ public class AndroidFxAccount {
protected final AccountManager accountManager;
protected final Account account;
/**
* A cache associating Account name (email address) to a representation of the
* account's internal bundle.
* <p>
* The cache is invalidated entirely when <it>any</it> new Account is added,
* because there is no reliable way to know that an Account has been removed
* and then re-added.
*/
protected static final ConcurrentHashMap<String, ExtendedJSONObject> perAccountBundleCache =
new ConcurrentHashMap<>();
public static void invalidateCaches() {
perAccountBundleCache.clear();
}
/**
* Create an Android Firefox Account instance backed by an Android Account
* instance.
@ -138,15 +154,28 @@ public class AndroidFxAccount {
* Saves the given data as the internal bundle associated with this account.
* @param bundle to write to account.
*/
protected void persistBundle(ExtendedJSONObject bundle) {
protected synchronized void persistBundle(ExtendedJSONObject bundle) {
perAccountBundleCache.put(account.name, bundle);
accountManager.setUserData(account, ACCOUNT_KEY_DESCRIPTOR, bundle.toJSONString());
}
protected ExtendedJSONObject unbundle() {
return unbundle(true);
}
/**
* Retrieve the internal bundle associated with this account.
* @return bundle associated with account.
*/
protected ExtendedJSONObject unbundle() {
protected synchronized ExtendedJSONObject unbundle(boolean allowCachedBundle) {
if (allowCachedBundle) {
final ExtendedJSONObject cachedBundle = perAccountBundleCache.get(account.name);
if (cachedBundle != null) {
Logger.debug(LOG_TAG, "Returning cached account bundle.");
return cachedBundle;
}
}
final int version = getAccountVersion();
if (version < CURRENT_ACCOUNT_VERSION) {
// Needs upgrade. For now, do nothing. We'd like to just put your account
@ -159,11 +188,14 @@ public class AndroidFxAccount {
return null;
}
String bundle = accountManager.getUserData(account, ACCOUNT_KEY_DESCRIPTOR);
if (bundle == null) {
String bundleString = accountManager.getUserData(account, ACCOUNT_KEY_DESCRIPTOR);
if (bundleString == null) {
return null;
}
return unbundleAccountV2(bundle);
final ExtendedJSONObject bundle = unbundleAccountV2(bundleString);
perAccountBundleCache.put(account.name, bundle);
Logger.info(LOG_TAG, "Account bundle persisted to cache.");
return bundle;
}
protected String getBundleData(String key) {
@ -194,25 +226,18 @@ public class AndroidFxAccount {
return o.getByteArrayHex(key);
}
protected void updateBundleDataBytes(String key, byte[] value) {
updateBundleValue(key, value == null ? null : Utils.byte2Hex(value));
}
protected void updateBundleValue(String key, boolean value) {
protected void updateBundleValues(String key, String value, String... more) {
if (more.length % 2 != 0) {
throw new IllegalArgumentException("more must be a list of key, value pairs");
}
ExtendedJSONObject descriptor = unbundle();
if (descriptor == null) {
return;
}
descriptor.put(key, value);
persistBundle(descriptor);
}
protected void updateBundleValue(String key, String value) {
ExtendedJSONObject descriptor = unbundle();
if (descriptor == null) {
return;
for (int i = 0; i + 1 < more.length; i += 2) {
descriptor.put(more[i], more[i+1]);
}
descriptor.put(key, value);
persistBundle(descriptor);
}
@ -497,8 +522,9 @@ public class AndroidFxAccount {
}
Logger.info(LOG_TAG, "Moving account named like " + getObfuscatedEmail() +
" to state " + state.getStateLabel().toString());
updateBundleValue(BUNDLE_KEY_STATE_LABEL, state.getStateLabel().name());
updateBundleValue(BUNDLE_KEY_STATE, state.toJSONObject().toJSONString());
updateBundleValues(
BUNDLE_KEY_STATE_LABEL, state.getStateLabel().name(),
BUNDLE_KEY_STATE, state.toJSONObject().toJSONString());
broadcastAccountStateChangedIntent();
}
@ -511,11 +537,10 @@ public class AndroidFxAccount {
public synchronized State getState() {
String stateLabelString = getBundleData(BUNDLE_KEY_STATE_LABEL);
String stateString = getBundleData(BUNDLE_KEY_STATE);
if (stateLabelString == null) {
throw new IllegalStateException("stateLabelString must not be null");
}
if (stateString == null) {
throw new IllegalStateException("stateString must not be null");
if (stateLabelString == null || stateString == null) {
throw new IllegalStateException("stateLabelString and stateString must not be null, but: " +
"(stateLabelString == null) = " + (stateLabelString == null) +
" and (stateString == null) = " + (stateString == null));
}
try {

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

@ -36,6 +36,10 @@ public class FxAccountAuthenticator extends AbstractAccountAuthenticator {
throws NetworkErrorException {
Logger.debug(LOG_TAG, "addAccount");
// The data associated to each Account should be invalidated when we change
// the set of Firefox Accounts on the system.
AndroidFxAccount.invalidateCaches();
final Bundle res = new Bundle();
if (!FxAccountConstants.ACCOUNT_TYPE.equals(accountType)) {

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

@ -175,6 +175,13 @@ public class RemoteTabsExpandableListFragment extends RemoteTabsBaseFragment {
@Override
protected void updateUiFromClients(List<RemoteClient> clients, List<RemoteClient> hiddenClients) {
if (getView() == null) {
// Early abort. It is possible to get UI updates after the view is
// destroyed; this can happen due to asynchronous loaders or
// animations complete.
return;
}
// We have three states: no clients (including hidden clients) at all;
// all clients hidden; some clients hidden. We want to show the empty
// list view only when we have no clients at all. This flag

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

@ -204,6 +204,13 @@ public class RemoteTabsSplitPlaneFragment extends RemoteTabsBaseFragment {
@Override
protected void updateUiFromClients(List<RemoteClient> clients, List<RemoteClient> hiddenClients) {
if (getView() == null) {
// Early abort. It is possible to get UI updates after the view is
// destroyed; this can happen due to asynchronous loaders or
// animations complete.
return;
}
// We have three states: no clients (including hidden clients) at all;
// all clients hidden; some clients hidden. We want to show the empty
// list view only when we have no clients at all. This flag

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

До

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

После

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

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

До

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

После

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

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

До

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

После

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

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

До

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

После

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

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

@ -13,32 +13,31 @@
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<LinearLayout android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:weightSum="1.0">
<org.mozilla.gecko.widget.GeckoSwipeRefreshLayout
android:id="@id/remote_tabs_refresh_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<org.mozilla.gecko.home.HomeListView
android:id="@+id/clients_list"
style="@style/Widget.RemoteTabsListView"
android:layout_weight="0.5"
android:layout_width="wrap_content"
android:layout_height="match_parent"/>
<org.mozilla.gecko.widget.GeckoSwipeRefreshLayout
android:id="@id/remote_tabs_refresh_layout"
android:layout_weight="0.5"
android:layout_width="wrap_content"
android:layout_height="match_parent">
<LinearLayout android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<org.mozilla.gecko.home.HomeListView
android:id="@+id/tabs_list"
style="@style/Widget.RemoteTabsListView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
android:id="@+id/clients_list"
style="@style/Widget.RemoteTabsListView"
android:layout_weight="0.32"
android:layout_width="0dp"
android:layout_height="match_parent"/>
</org.mozilla.gecko.widget.GeckoSwipeRefreshLayout>
<org.mozilla.gecko.home.HomeListView
android:id="@+id/tabs_list"
style="@style/Widget.RemoteTabsListView"
android:layout_weight="0.68"
android:layout_width="0dp"
android:layout_height="match_parent"/>
</LinearLayout>
</LinearLayout>
</org.mozilla.gecko.widget.GeckoSwipeRefreshLayout>
</LinearLayout>

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

@ -6,6 +6,11 @@
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:gecko="http://schemas.android.com/apk/res-auto">
<!-- Overall, we want 12dp of padding from mic to the right edge of the toolbar.
However, setting a value of 12dp (using the padding from the parent container)
does not match drawablePadding=12dp. Part of this is the url_bar_entry drawable
overlaps the EditText, but I can't figure out the rest. Thus eyeballing for
paddingRight. -->
<org.mozilla.gecko.toolbar.ToolbarEditText
android:id="@+id/url_edit_text"
style="@style/UrlBar.Title"
@ -17,6 +22,8 @@
android:selectAllOnFocus="true"
android:contentDescription="@string/url_bar_default_text"
android:drawableRight="@drawable/ab_mic"
android:drawablePadding="12dp"
android:paddingRight="8dp"
gecko:autoUpdateTheme="false"/>
</merge>

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

@ -11,11 +11,12 @@ import org.mozilla.gecko.Element;
import org.mozilla.gecko.R;
import org.mozilla.gecko.EventDispatcher;
import org.mozilla.gecko.util.GeckoEventListener;
import org.json.JSONObject;
import org.mozilla.gecko.util.EventCallback;
import org.mozilla.gecko.util.NativeEventListener;
import org.mozilla.gecko.util.NativeJSObject;
public class testFindInPage extends JavascriptTest implements GeckoEventListener {
public class testFindInPage extends JavascriptTest implements NativeEventListener {
private static final int WAIT_FOR_TEST = 3000;
protected Element next, close;
@ -24,14 +25,15 @@ public class testFindInPage extends JavascriptTest implements GeckoEventListener
}
@Override
public void handleMessage(String event, final JSONObject message) {
public void handleMessage(final String event, final NativeJSObject message,
final EventCallback callback) {
if (event.equals("Test:FindInPage")) {
try {
final String text = message.getString("text");
final int nrOfMatches = Integer.parseInt(message.getString("nrOfMatches"));
final int nrOfMatches = message.getInt("nrOfMatches");
findText(text, nrOfMatches);
} catch (Exception e) {
fFail("Can't extract find query from JSON");
callback.sendError("Can't extract find query from JSON :" + e.toString());
}
}
@ -39,9 +41,11 @@ public class testFindInPage extends JavascriptTest implements GeckoEventListener
try {
close.click();
} catch (Exception e) {
fFail("FindInPage prompt not opened");
callback.sendError("FindInPage prompt not opened");
}
}
callback.sendSuccess("done");
}
@Override

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

@ -35,17 +35,21 @@ function openTabWithUrl(url) {
}
function findInPage(browser, text, nrOfMatches) {
let repaintPromise = promiseBrowserEvent(browser, "MozAfterPaint");
do_print("Send findInPageMessage: " + text + " nth: " + nrOfMatches);
Messaging.sendRequest({ type: "Test:FindInPage", text: text, nrOfMatches: nrOfMatches });
return repaintPromise;
let messagePromise = Messaging.sendRequestForResult({
type: "Test:FindInPage",
text: text,
nrOfMatches: nrOfMatches
});
let repaintPromise = promiseBrowserEvent(browser, "MozAfterPaint");
return Promise.all([messagePromise, repaintPromise]);
}
function closeFindInPage(browser) {
let repaintPromise = promiseBrowserEvent(browser, "MozAfterPaint");
do_print("Send closeFindInPageMessage");
Messaging.sendRequest({ type: "Test:CloseFindInPage" });
return repaintPromise;
let messagePromise = Messaging.sendRequestForResult({ type: "Test:CloseFindInPage" });
let repaintPromise = promiseBrowserEvent(browser, "MozAfterPaint");
return Promise.all([messagePromise, repaintPromise]);
}
function assertSelection(document, expectedSelection = false, expectedAnchorText = false) {

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

@ -150,8 +150,21 @@ var FindHelper = {
this._targetTab.sendViewportUpdate();
}
} else {
// Disabled until bug 1014113 is fixed
// ZoomHelper.zoomToRect(aData.rect);
// Defines the space around the highlighted element as a factor of the element's size.
const spacingFactor = 6;
// We replace the start of the zoom rect to keep the highlighted word in the middle.
// We divide this offset by two to consider a spacing on each side of the rect.
let x = aData.rect.x + (aData.rect.width * (1 - spacingFactor)) / 2;
let y = aData.rect.y + (aData.rect.height * (1 - spacingFactor)) / 2;
let rect = new Rect(Math.max(x, 0),
Math.max(y, 0),
// we use a bigger viewport than just the highlighted word
aData.rect.width * spacingFactor,
aData.rect.height * spacingFactor);
ZoomHelper.zoomToRect(rect);
this._viewportChanged = true;
}
}

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

@ -123,6 +123,11 @@ var ZoomHelper = {
* if it is really tall.
*/
zoomToRect: function(aRect, aClickY = -1) {
if(aRect.isEmpty()) {
// Protect from empty or negative-sized rects & potentials NaN in following calculations
return;
}
let viewport = BrowserApp.selectedTab.getViewport();
let rect = {

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

@ -7656,8 +7656,9 @@ var Distribution = {
}
// Apply a lightweight theme if necessary
if (prefs["lightweightThemes.isThemeSelected"])
if (prefs && prefs["lightweightThemes.isThemeSelected"]) {
Services.obs.notifyObservers(null, "lightweight-theme-apply", "");
}
let localizedString = Cc["@mozilla.org/pref-localizedstring;1"].createInstance(Ci.nsIPrefLocalizedString);
let localizeablePrefs = aData["LocalizablePreferences"];

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

@ -25,11 +25,11 @@ body {
color: #eeeeee;
}
.sans-serif {
.clear-sans {
font-family: sans-serif;
}
.serif {
.charis-sil {
font-family: serif;
}
@ -107,61 +107,61 @@ body {
color: #aaaaaa;
}
.font-size1 > .header > h1 {
.font-size1 > body > .header > h1 {
font-size: 27px;
}
.font-size2 > .header > h1 {
.font-size2 > body > .header > h1 {
font-size: 29px;
}
.font-size3 > .header > h1 {
.font-size3 > body > .header > h1 {
font-size: 31px;
}
.font-size4 > .header > h1 {
.font-size4 > body > .header > h1 {
font-size: 33px;
}
.font-size5 > .header > h1 {
.font-size5 > body > .header > h1 {
font-size: 35px;
}
/* This covers caption, domain, and credits
texts in the reader UI */
.font-size1 > .content .wp-caption-text,
.font-size1 > .content figcaption,
.font-size1 > .header > .domain,
.font-size1 > .header > .credits {
.font-size1 > body > .content .wp-caption-text,
.font-size1 > body > .content figcaption,
.font-size1 > body > .header > .domain,
.font-size1 > body > .header > .credits {
font-size: 10px;
}
.font-size2 > .content .wp-caption-text,
.font-size2 > .content figcaption,
.font-size2 > .header > .domain,
.font-size2 > .header > .credits {
.font-size2 > body > .content .wp-caption-text,
.font-size2 > body > .content figcaption,
.font-size2 > body > .header > .domain,
.font-size2 > body > .header > .credits {
font-size: 13px;
}
.font-size3 > .content .wp-caption-text,
.font-size3 > .content figcaption,
.font-size3 > .header > .domain,
.font-size3 > .header > .credits {
.font-size3 > body > .content .wp-caption-text,
.font-size3 > body > .content figcaption,
.font-size3 > body > .header > .domain,
.font-size3 > body > .header > .credits {
font-size: 15px;
}
.font-size4 > .content .wp-caption-text,
.font-size4 > .content figcaption,
.font-size4 > .header > .domain,
.font-size4 > .header > .credits {
.font-size4 > body > .content .wp-caption-text,
.font-size4 > body > .content figcaption,
.font-size4 > body > .header > .domain,
.font-size4 > body > .header > .credits {
font-size: 17px;
}
.font-size5 > .content .wp-caption-text,
.font-size5 > .content figcaption,
.font-size5 > .header > .domain,
.font-size5 > .header > .credits {
.font-size5 > body > .content .wp-caption-text,
.font-size5 > body > .content figcaption,
.font-size5 > body > .header > .domain,
.font-size5 > body > .header > .credits {
font-size: 19px;
}
@ -296,27 +296,27 @@ body {
}
.font-size1-sample,
.font-size1 > .content {
.font-size1 > body > .content {
font-size: 14px !important;
}
.font-size2-sample,
.font-size2 > .content {
.font-size2 > body > .content {
font-size: 16px !important;
}
.font-size3-sample,
.font-size3 > .content {
.font-size3 > body > .content {
font-size: 18px !important;
}
.font-size4-sample,
.font-size4 > .content {
.font-size4 > body > .content {
font-size: 20px !important;
}
.font-size5-sample,
.font-size5 > .content {
.font-size5 > body > .content {
font-size: 22px !important;
}
@ -488,6 +488,12 @@ body {
font-size: 12px;
}
/* desktop-only controls */
.close-button,
.list-button {
display: none;
}
.toggle-button.on {
background-image: url('chrome://browser/skin/images/reader-toggle-on-icon-mdpi.png');
}

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

@ -4538,18 +4538,27 @@ pref("reader.parse-on-load.force-enabled", false);
// The default relative font size in reader mode (1-5)
pref("reader.font_size", 3);
// The default color scheme in reader mode (light, dark, print, auto)
// The default color scheme in reader mode (light, dark, sepia, auto)
// auto = color automatically adjusts according to ambient light level
// (auto only works on platforms where the 'devicelight' event is enabled)
pref("reader.color_scheme", "light");
// Color scheme values available in reader mode UI.
pref("reader.color_scheme.values", "[\"light\",\"dark\",\"sepia\"]");
// The font type in reader (sans-serif, serif)
pref("reader.font_type", "sans-serif");
// Font type values available in reader mode UI.
pref("reader.font_type.values", "[\"serif\",\"sans-serif\"]");
// Whether or not the user has interacted with the reader mode toolbar.
// This is used to show a first-launch tip in reader mode.
pref("reader.has_used_toolbar", false);
// Whether to use a vertical or horizontal toolbar.
pref("reader.toolbar.vertical", true);
#if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
// Whether to allow, on a Linux system that doesn't support the necessary sandboxing
// features, loading Gecko Media Plugins unsandboxed. However, EME CDMs will not be

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

@ -150,6 +150,16 @@ nsChannelClassifier::ShouldEnableTrackingProtection(nsIChannel *aChannel,
// the security state. If any channels are subsequently cancelled
// (page elements blocked) the state will be then updated.
if (*result) {
#ifdef DEBUG
nsCString topspec;
nsCString spec;
uri->GetSpec(topspec);
aChannel->GetURI(getter_AddRefs(uri));
uri->GetSpec(spec);
LOG(("nsChannelClassifier[%p]: Enabling tracking protection checks on channel[%p] "
"with uri %s for toplevel window %s", this, aChannel, spec.get(),
topspec.get()));
#endif
return NS_OK;
}
@ -289,14 +299,25 @@ nsChannelClassifier::StartInternal()
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIPrincipal> principal;
rv = securityManager->GetChannelResultPrincipal(mChannel,
getter_AddRefs(principal));
rv = securityManager->GetChannelURIPrincipal(mChannel, getter_AddRefs(principal));
NS_ENSURE_SUCCESS(rv, rv);
bool expectCallback;
bool trackingProtectionEnabled = false;
(void)ShouldEnableTrackingProtection(mChannel, &trackingProtectionEnabled);
#ifdef DEBUG
{
nsCString uriSpec;
uri->GetSpec(uriSpec);
nsCOMPtr<nsIURI> principalURI;
principal->GetURI(getter_AddRefs(principalURI));
nsCString principalSpec;
principalURI->GetSpec(principalSpec);
LOG(("nsChannelClassifier: Classifying principal %s on channel with uri %s "
"[this=%p]", principalSpec.get(), uriSpec.get(), this));
}
#endif
rv = uriClassifier->Classify(principal, trackingProtectionEnabled, this,
&expectCallback);
if (NS_FAILED(rv)) {

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

@ -4853,14 +4853,20 @@ nsHttpChannel::BeginConnect()
nsCOMPtr<nsIPrincipal> principal = GetPrincipal(false);
bool tp = false;
channelClassifier->ShouldEnableTrackingProtection(this, &tp);
// See bug 1122691
// We skip speculative connections by setting mLocalBlocklist only
// when tracking protection is enabled. Though we could do this for
// both phishing and malware, it is not necessary for correctness,
// since no network events will be received while the
// nsChannelClassifier is in progress. See bug 1122691.
if (tp) {
nsresult response = NS_OK;
classifier->ClassifyLocal(principal, tp, &response);
if (NS_FAILED(response)) {
LOG(("nsHttpChannel::Found principal on local blocklist "
"[this=%p]", this));
LOG(("nsHttpChannel::ClassifyLocal found principal on local "
"blocklist [this=%p]", this));
mLocalBlocklist = true;
} else {
LOG(("nsHttpChannel::ClassifyLocal no result found [this=%p]", this));
}
}
}
@ -4928,25 +4934,29 @@ nsHttpChannel::BeginConnect()
}
mCaps &= ~NS_HTTP_ALLOW_PIPELINING;
}
// mLocalBlocklist is true only if the URI is not a tracking domain, it
// makes not guarantees about phishing or malware, so we must call
if (!(mLoadFlags & LOAD_CLASSIFY_URI)) {
return ContinueBeginConnect();
}
// mLocalBlocklist is true only if tracking protection is enabled and the
// URI is a tracking domain, it makes no guarantees about phishing or
// malware, so if LOAD_CLASSIFY_URI is true we must call
// nsChannelClassifier to catch phishing and malware URIs.
bool callContinueBeginConnect = true;
if (mCanceled || !mLocalBlocklist) {
rv = ContinueBeginConnect();
if (NS_FAILED(rv)) {
return rv;
}
callContinueBeginConnect = false;
rv = ContinueBeginConnect();
if (NS_FAILED(rv)) {
return rv;
}
callContinueBeginConnect = false;
}
// nsChannelClassifier calls ContinueBeginConnect if it has not already
// been called, after optionally cancelling the channel once we have a
// remote verdict. We call a concrete class instead of an nsI* that might
// be overridden.
if (!mCanceled) {
LOG(("nsHttpChannel::Starting nsChannelClassifier %p [this=%p]",
LOG(("nsHttpChannel::Starting nsChannelClassifier %p [this=%p]",
channelClassifier.get(), this));
channelClassifier->Start(this, callContinueBeginConnect);
channelClassifier->Start(this, callContinueBeginConnect);
}
return NS_OK;
}

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

@ -56,34 +56,29 @@ let AboutReader = function(mm, win) {
doc.addEventListener("visibilitychange", this, false);
this._setupStyleDropdown();
this._setupButton("close-button", this._onReaderClose.bind(this));
this._setupButton("toggle-button", this._onReaderToggle.bind(this));
this._setupButton("share-button", this._onShare.bind(this));
let colorSchemeOptions = [
{ name: gStrings.GetStringFromName("aboutReader.colorSchemeDark"),
value: "dark"},
{ name: gStrings.GetStringFromName("aboutReader.colorSchemeLight"),
value: "light"},
{ name: gStrings.GetStringFromName("aboutReader.colorSchemeAuto"),
value: "auto"}
];
let colorSchemeValues = JSON.parse(Services.prefs.getCharPref("reader.color_scheme.values"));
let colorSchemeOptions = colorSchemeValues.map((value) => {
return { name: gStrings.GetStringFromName("aboutReader.colorScheme." + value),
value: value,
itemClass: value + "-button" };
});
let colorScheme = Services.prefs.getCharPref("reader.color_scheme");
this._setupSegmentedButton("color-scheme-buttons", colorSchemeOptions, colorScheme, this._setColorSchemePref.bind(this));
this._setColorSchemePref(colorScheme);
let fontTypeSample = gStrings.GetStringFromName("aboutReader.fontTypeSample");
let fontTypeOptions = [
{ name: fontTypeSample,
description: gStrings.GetStringFromName("aboutReader.fontTypeSerif"),
value: "serif",
linkClass: "serif" },
{ name: fontTypeSample,
description: gStrings.GetStringFromName("aboutReader.fontTypeSansSerif"),
value: "sans-serif",
linkClass: "sans-serif"
},
];
let fontTypeValues = JSON.parse(Services.prefs.getCharPref("reader.font_type.values"));
let fontTypeOptions = fontTypeValues.map((value) => {
return { name: fontTypeSample,
description: gStrings.GetStringFromName("aboutReader.fontType." + value),
value: value,
linkClass: value };
});
let fontType = Services.prefs.getCharPref("reader.font_type");
this._setupSegmentedButton("font-type-buttons", fontTypeOptions, fontType, this._setFontType.bind(this));
@ -112,13 +107,11 @@ let AboutReader = function(mm, win) {
this._setupSegmentedButton("font-size-buttons", fontSizeOptions, fontSize, this._setFontSize.bind(this));
this._setFontSize(fontSize);
let queryArgs = this._decodeQueryString(win.location.href);
// Track status of reader toolbar add/remove toggle button
this._isReadingListItem = -1;
this._updateToggleButton();
this._loadArticle(queryArgs.url);
this._loadArticle();
}
AboutReader.prototype = {
@ -163,6 +156,13 @@ AboutReader.prototype = {
return this._messageElementRef.get();
},
get _isToolbarVertical() {
if (this._toolbarVertical !== undefined) {
return this._toolbarVertical;
}
return this._toolbarVertical = Services.prefs.getBoolPref("reader.toolbar.vertical");
},
receiveMessage: function (message) {
switch (message.name) {
case "Reader:Added": {
@ -253,6 +253,10 @@ AboutReader.prototype = {
this._mm.sendAsyncMessage("Reader:ListStatusRequest", { url: this._article.url });
},
_onReaderClose: function Reader_onToggle() {
this._win.location.href = this._getOriginalUrl();
},
_onReaderToggle: function Reader_onToggle() {
if (!this._article)
return;
@ -278,13 +282,13 @@ AboutReader.prototype = {
},
_setFontSize: function Reader_setFontSize(newFontSize) {
let bodyClasses = this._doc.body.classList;
let htmlClasses = this._doc.documentElement.classList;
if (this._fontSize > 0)
bodyClasses.remove("font-size" + this._fontSize);
htmlClasses.remove("font-size" + this._fontSize);
this._fontSize = newFontSize;
bodyClasses.add("font-size" + this._fontSize);
htmlClasses.add("font-size" + this._fontSize);
Services.prefs.setIntPref("reader.font_size", this._fontSize);
},
@ -438,7 +442,8 @@ AboutReader.prototype = {
this._mm.sendAsyncMessage("Reader:SystemUIVisibility", { visible: visible });
},
_loadArticle: Task.async(function* (url) {
_loadArticle: Task.async(function* () {
let url = this._getOriginalUrl();
this._showProgressDelayed();
let article = yield this._getArticle(url);
@ -617,18 +622,17 @@ AboutReader.prototype = {
}.bind(this), 300);
},
_decodeQueryString: function Reader_decodeQueryString(url) {
let result = {};
let query = url.split("?")[1];
if (query) {
let pairs = query.split("&");
for (let i = 0; i < pairs.length; i++) {
let [name, value] = pairs[i].split("=");
result[name] = decodeURIComponent(value);
}
/**
* Returns the original article URL for this about:reader view.
*/
_getOriginalUrl: function() {
let url = this._win.location.href;
let searchParams = new URLSearchParams(url.split("?")[1]);
if (!searchParams.has("url")) {
Cu.reportError("Error finding original URL for about:reader URL: " + url);
return url;
}
return result;
return decodeURIComponent(searchParams.get("url"));
},
_setupSegmentedButton: function Reader_setupSegmentedButton(id, options, initialValue, callback) {
@ -643,6 +647,9 @@ AboutReader.prototype = {
link.textContent = option.name;
item.appendChild(link);
if (option.itemClass !== undefined)
item.classList.add(option.itemClass);
if (option.linkClass !== undefined)
link.classList.add(option.linkClass);
@ -655,7 +662,7 @@ AboutReader.prototype = {
link.style.MozUserSelect = 'none';
segmentedButton.appendChild(item);
link.addEventListener("click", function(aEvent) {
item.addEventListener("click", function(aEvent) {
if (!aEvent.isTrusted)
return;
@ -696,24 +703,30 @@ AboutReader.prototype = {
let win = this._win;
let dropdown = doc.getElementById("style-dropdown");
let dropdownToggle = dropdown.querySelector(".dropdown-toggle");
let dropdownPopup = dropdown.querySelector(".dropdown-popup");
let dropdownArrow = dropdown.querySelector(".dropdown-arrow");
let updatePopupPosition = function() {
let popupWidth = dropdownPopup.offsetWidth + 30;
let arrowWidth = dropdownArrow.offsetWidth;
let toggleWidth = dropdownToggle.offsetWidth;
let toggleLeft = dropdownToggle.offsetLeft;
let updatePopupPosition = () => {
if (this._isToolbarVertical) {
let toggleHeight = dropdownToggle.offsetHeight;
let toggleTop = dropdownToggle.offsetTop;
let popupTop = toggleTop - toggleHeight / 2;
dropdownPopup.style.top = popupTop + "px";
} else {
let popupWidth = dropdownPopup.offsetWidth + 30;
let arrowWidth = dropdownArrow.offsetWidth;
let toggleWidth = dropdownToggle.offsetWidth;
let toggleLeft = dropdownToggle.offsetLeft;
let popupShift = (toggleWidth - popupWidth) / 2;
let popupLeft = Math.max(0, Math.min(win.innerWidth - popupWidth, toggleLeft + popupShift));
dropdownPopup.style.left = popupLeft + "px";
let popupShift = (toggleWidth - popupWidth) / 2;
let popupLeft = Math.max(0, Math.min(win.innerWidth - popupWidth, toggleLeft + popupShift));
dropdownPopup.style.left = popupLeft + "px";
let arrowShift = (toggleWidth - arrowWidth) / 2;
let arrowLeft = toggleLeft - popupLeft + arrowShift;
dropdownArrow.style.left = arrowLeft + "px";
let arrowShift = (toggleWidth - arrowWidth) / 2;
let arrowLeft = toggleLeft - popupLeft + arrowShift;
dropdownArrow.style.left = arrowLeft + "px";
}
};
win.addEventListener("resize", event => {
@ -730,14 +743,12 @@ AboutReader.prototype = {
event.stopPropagation();
if (!this._getToolbarVisibility())
return;
if (dropdown.classList.contains("open")) {
dropdown.classList.remove("open");
} else {
dropdown.classList.add("open");
updatePopupPosition();
}
}, true);
}
},
};

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

@ -25,6 +25,7 @@
</div>
<ul id="reader-toolbar" class="toolbar toolbar-hidden">
<li><a id="close-button" class="button close-button" href="#"></a></li>
<li><a id="share-button" class="button share-button" href="#"></a></li>
<ul id="style-dropdown" class="dropdown">
<li><a class="dropdown-toggle button style-button" href="#"></a></li>
@ -38,6 +39,7 @@
</li>
</ul>
<li><a id="toggle-button" class="button toggle-button" href="#"></a></li>
<li><a id="list-button" class="button list-button" href="#"></a></li>
</ul>
</body>

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

@ -3574,7 +3574,7 @@ SearchService.prototype = {
this._defaultEngine = null;
// Clear the metadata service.
engineMetadataService._initState = engineMetadataService._InitStates.NOT_STARTED;
engineMetadataService._initialized = false;
engineMetadataService._initializer = null;
Task.spawn(function* () {
@ -4867,29 +4867,8 @@ SearchService.prototype = {
var engineMetadataService = {
_jsonFile: OS.Path.join(OS.Constants.Path.profileDir, "search-metadata.json"),
/**
* Possible values for |_initState|.
*
* We have two paths to perform initialization: a default asynchronous
* path and a fallback synchronous path that can interrupt the async
* path. For this reason, initialization is actually something of a
* finite state machine, represented with the following states:
*
* @enum
*/
_InitStates: {
NOT_STARTED: "NOT_STARTED"
/**Initialization has not started*/,
FINISHED_SUCCESS: "FINISHED_SUCCESS"
/**Setup complete, with a success*/
},
/**
* The latest step completed by initialization. One of |InitStates|
*
* @type {engineMetadataService._InitStates}
*/
_initState: null,
// Boolean flag that is true if initialization was successful.
_initialized: false,
// A promise fulfilled once initialization is complete
_initializer: null,
@ -4905,34 +4884,32 @@ var engineMetadataService = {
let initializer = this._initializer = Promise.defer();
Task.spawn((function task_init() {
LOG("metadata init: starting");
switch (this._initState) {
case engineMetadataService._InitStates.NOT_STARTED:
// 1. Load json file if it exists
try {
let contents = yield OS.File.read(this._jsonFile);
if (this._initState == engineMetadataService._InitStates.FINISHED_SUCCESS) {
// No need to pursue asynchronous initialization,
// synchronous fallback was called and has finished.
return;
}
this._store = JSON.parse(new TextDecoder().decode(contents));
} catch (ex) {
if (this._initState == engineMetadataService._InitStates.FINISHED_SUCCESS) {
// No need to pursue asynchronous initialization,
// synchronous fallback was called and has finished.
return;
}
// Couldn't load json, use an empty store
LOG("metadata init: could not load JSON file " + ex);
this._store = {};
}
break;
default:
throw new Error("metadata init: invalid state " + this._initState);
if (this._initialized) {
throw new Error("metadata init: invalid state, _initialized is " +
"true but initialization promise has not been " +
"resolved");
}
// 1. Load json file if it exists
try {
let contents = yield OS.File.read(this._jsonFile);
if (this._initialized) {
// No need to pursue asynchronous initialization,
// synchronous fallback was called and has finished.
return;
}
this._store = JSON.parse(new TextDecoder().decode(contents));
} catch (ex) {
if (this._initialized) {
// No need to pursue asynchronous initialization,
// synchronous fallback was called and has finished.
return;
}
// Couldn't load json, use an empty store
LOG("metadata init: could not load JSON file " + ex);
this._store = {};
}
this._initState = this._InitStates.FINISHED_SUCCESS;
this._initialized = true;
LOG("metadata init: complete");
}).bind(this)).then(
// 3. Inform any observers
@ -4957,39 +4934,32 @@ var engineMetadataService = {
*/
syncInit: function epsSyncInit() {
LOG("metadata syncInit start");
if (this._initState == engineMetadataService._InitStates.FINISHED_SUCCESS) {
if (this._initialized) {
return;
}
switch (this._initState) {
case engineMetadataService._InitStates.NOT_STARTED:
let jsonFile = new FileUtils.File(this._jsonFile);
// 1. Load json file if it exists
if (jsonFile.exists()) {
try {
let uri = Services.io.newFileURI(jsonFile);
let stream = Services.io.newChannelFromURI2(uri,
null, // aLoadingNode
Services.scriptSecurityManager.getSystemPrincipal(),
null, // aTriggeringPrincipal
Ci.nsILoadInfo.SEC_NORMAL,
Ci.nsIContentPolicy.TYPE_OTHER).open();
this._store = parseJsonFromStream(stream);
} catch (x) {
LOG("metadata syncInit: could not load JSON file " + x);
this._store = {};
}
} else {
LOG("metadata syncInit: using an empty store");
this._store = {};
}
this._initState = this._InitStates.FINISHED_SUCCESS;
break;
default:
throw new Error("metadata syncInit: invalid state " + this._initState);
let jsonFile = new FileUtils.File(this._jsonFile);
// 1. Load json file if it exists
if (jsonFile.exists()) {
try {
let uri = Services.io.newFileURI(jsonFile);
let stream = Services.io.newChannelFromURI2(uri,
null, // aLoadingNode
Services.scriptSecurityManager.getSystemPrincipal(),
null, // aTriggeringPrincipal
Ci.nsILoadInfo.SEC_NORMAL,
Ci.nsIContentPolicy.TYPE_OTHER).open();
this._store = parseJsonFromStream(stream);
} catch (x) {
LOG("metadata syncInit: could not load JSON file " + x);
this._store = {};
}
} else {
LOG("metadata syncInit: using an empty store");
this._store = {};
}
this._initialized = true;
// 3. Inform any observers
if (this._initializer) {
this._initializer.resolve();
@ -5123,7 +5093,7 @@ var engineMetadataService = {
_lazyWriter: null
};
engineMetadataService._initState = engineMetadataService._InitStates.NOT_STARTED;
engineMetadataService._initialized = false;
const SEARCH_UPDATE_LOG_PREFIX = "*** Search update: ";

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

@ -88,6 +88,41 @@ let AnimationPlayerActor = ActorClass({
return data;
},
/**
* Some of the player's properties are retrieved from the node's
* computed-styles because the Web Animations API does not provide them yet.
* But the computed-styles may contain multiple animations for a node and so
* we need to know which is the index of the current animation in the style.
* @return {Number}
*/
getPlayerIndex: function() {
let names = this.styles.animationName;
// If no names are found, then it's probably a transition, in which case we
// can't find the actual index, so just trust the playerIndex passed by
// the AnimationsActor at initialization time.
// Note that this may be incorrect if by the time the AnimationPlayerActor
// is initialized, one of the transitions has ended, but it's the best we
// can do for now.
if (!names) {
return this.playerIndex;
}
// If there's only one name.
if (names.contains(",") === -1) {
return 0;
}
// If there are several names, retrieve the index of the animation name in
// the list.
names = names.split(",").map(n => n.trim());
for (let i = 0; i < names.length; i ++) {
if (names[i] === this.player.source.effect.name) {
return i;
}
}
},
/**
* Get the animation duration from this player, in milliseconds.
* Note that the Web Animations API doesn't yet offer a way to retrieve this
@ -105,8 +140,10 @@ let AnimationPlayerActor = ActorClass({
return null;
}
// If the computed duration has multiple entries, we need to find the right
// one.
if (durationText.indexOf(",") !== -1) {
durationText = durationText.split(",")[this.playerIndex];
durationText = durationText.split(",")[this.getPlayerIndex()];
}
return parseFloat(durationText) * 1000;
@ -130,7 +167,7 @@ let AnimationPlayerActor = ActorClass({
}
if (delayText.indexOf(",") !== -1) {
delayText = delayText.split(",")[this.playerIndex];
delayText = delayText.split(",")[this.getPlayerIndex()];
}
return parseFloat(delayText) * 1000;
@ -148,7 +185,7 @@ let AnimationPlayerActor = ActorClass({
getIterationCount: function() {
let iterationText = this.styles.animationIterationCount;
if (iterationText.indexOf(",") !== -1) {
iterationText = iterationText.split(",")[this.playerIndex];
iterationText = iterationText.split(",")[this.getPlayerIndex()];
}
return iterationText === "infinite"

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

@ -172,7 +172,8 @@ let MemoryActor = protocol.ActorClass({
options: Arg(0, "nullable:AllocationsRecordingOptions")
},
response: {
value: RetVal(0, "number")
// Accept `nullable` in the case of server Gecko <= 37, handled on the front
value: RetVal(0, "nullable:number")
}
}),
@ -187,7 +188,8 @@ let MemoryActor = protocol.ActorClass({
}), {
request: {},
response: {
value: RetVal(0, "number")
// Accept `nullable` in the case of server Gecko <= 37, handled on the front
value: RetVal(0, "nullable:number")
}
}),

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

@ -157,7 +157,10 @@ RootActor.prototype = {
noPrettyPrinting: false,
// Whether the page style actor implements the getUsedFontFaces method
// that returns the font faces used on a node
getUsedFontFaces: true
getUsedFontFaces: true,
// Trait added in Gecko 38, indicating that all features necessary for
// grabbing allocations from the MemoryActor are available for the performance tool
memoryActorAllocations: true
},
/**

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

@ -44,7 +44,8 @@ const DEFAULT_TIMELINE_DATA_PULL_TIMEOUT = 200; // ms
*/
protocol.types.addType("array-of-numbers-as-strings", {
write: (v) => v.join(","),
read: (v) => v.split(",")
// In Gecko <= 37, `v` is an array; do not transform in this case.
read: (v) => typeof v === "string" ? v.split(",") : v
});
/**

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

@ -1,5 +1,14 @@
<!DOCTYPE html>
<style>
.not-animated {
display: inline-block;
width: 50px;
height: 50px;
border-radius: 50%;
background: #eee;
}
.simple-animation {
display: inline-block;
@ -83,6 +92,17 @@
animation: move .5s 1s 10, glow 1s .75s 30;
}
.multiple-animations-2 {
display: inline-block;
width: 50px;
height: 50px;
border-radius: 50%;
background: blue;
animation: move 2s, glow 1s 2s infinite, grow 3s 1s 100;
}
@keyframes move {
100% {
transform: translateY(100px);
@ -94,6 +114,12 @@
background: yellow;
}
}
@keyframes grow {
100% {
width: 100px;
}
}
</style>
<div class="not-animated"></div>
<div class="simple-animation"></div>
@ -103,6 +129,7 @@
<div class="delayed-animation"></div>
<div class="delayed-transition"></div>
<div class="delayed-multiple-animations"></div>
<div class="multiple-animations-2"></div>
<script type="text/javascript">
// Get the transitions started when the page loads
var players;

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

@ -24,6 +24,7 @@ support-files =
[browser_animation_actors_07.js]
[browser_animation_actors_08.js]
[browser_animation_actors_09.js]
[browser_animation_actors_10.js]
[browser_navigateEvents.js]
[browser_storage_dynamic_windows.js]
[browser_storage_listings.js]

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

@ -47,7 +47,7 @@ function* theRightNumberOfPlayersIsReturned(walker, front) {
function* playersCanBePausedAndResumed(walker, front) {
let node = yield walker.querySelector(walker.rootNode, ".simple-animation");
let [player] = yield front.getAnimationPlayersForNode(node);
yield player.ready;
yield player.ready();
ok(player.initialState, "The player has an initialState");
ok(player.getCurrentState, "The player has the getCurrentState method");

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

@ -0,0 +1,69 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Check that the right duration/iterationCount/delay are retrieved even when
// the node has multiple animations and one of them already ended before getting
// the player objects.
// See toolkit/devtools/server/actors/animation.js |getPlayerIndex| for more
// information.
const {AnimationsFront} = require("devtools/server/actors/animation");
const {InspectorFront} = require("devtools/server/actors/inspector");
add_task(function*() {
let doc = yield addTab(MAIN_DOMAIN + "animation.html");
initDebuggerServer();
let client = new DebuggerClient(DebuggerServer.connectPipe());
let form = yield connectDebuggerClient(client);
let inspector = InspectorFront(client, form);
let walker = yield inspector.getWalker();
let front = AnimationsFront(client, form);
info("Retrieve a non animated node");
let node = yield walker.querySelector(walker.rootNode, ".not-animated");
info("Apply the multiple-animations-2 class to start the animations");
yield node.modifyAttributes([
{attributeName: "class", newValue: "multiple-animations-2"}
]);
info("Retrieve the list of animation players for the node");
let players = yield front.getAnimationPlayersForNode(node);
is(players.length, 3, "3 animations are currently applied to the node");
info("Waiting for the first animation to end");
let player = players[0];
player.startAutoRefresh();
let onFinished = new Promise(resolve => {
let onNewState = (e, state) => {
if (state.playState === "finished") {
info("Received the 'finished' playState event");
player.off(player.AUTO_REFRESH_EVENT, onNewState);
resolve();
}
};
info("Listening for auto-refresh events");
player.on(player.AUTO_REFRESH_EVENT, onNewState);
});
yield onFinished;
info("Get the list of players again");
players = yield front.getAnimationPlayersForNode(node);
is(players.length, 2, "2 animations remain on the node");
is(players[0].state.duration, 1000, "The duration of the first animation is correct");
is(players[0].state.delay, 2000, "The delay of the first animation is correct");
is(players[0].state.iterationCount, null, "The iterationCount of the first animation is correct");
is(players[1].state.duration, 3000, "The duration of the second animation is correct");
is(players[1].state.delay, 1000, "The delay of the second animation is correct");
is(players[1].state.iterationCount, 100, "The iterationCount of the second animation is correct");
yield closeDebuggerClient(client);
gBrowser.removeCurrentTab();
});

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

@ -5,15 +5,20 @@
aboutReader.loading=Loading...
aboutReader.loadError=Failed to load article from page
aboutReader.colorSchemeLight=Light
aboutReader.colorSchemeDark=Dark
aboutReader.colorSchemeSepia=Sepia
aboutReader.colorSchemeAuto=Auto
aboutReader.colorScheme.light=Light
aboutReader.colorScheme.dark=Dark
aboutReader.colorScheme.sepia=Sepia
aboutReader.colorScheme.auto=Auto
# LOCALIZATION NOTE (aboutReader.fontTypeSerif, aboutReader.fontTypeSansSerif):
# These are the names of the fonts that are used.
aboutReader.fontTypeSerif=Charis SIL Compact
aboutReader.fontTypeSansSerif=Clear Sans
# LOCALIZATION NOTE (aboutReader.fontType.serif, aboutReader.fontType.sans-serif):
# These are the styles of typeface that are used on desktop.
aboutReader.fontType.serif=Serif
aboutReader.fontType.sans-serif=Sans-serif
# LOCALIZATION NOTE (aboutReader.fontType.charis-sil, aboutReader.fontType.clear-sans):
# These are the names of the fonts that are used on Android
aboutReader.fontType.charis-sil=Charis SIL Compact
aboutReader.fontType.clear-sans=Clear Sans
# LOCALIZATION NOTE (aboutReader.fontTypeSample): String used to sample font types.
aboutReader.fontTypeSample=Aa

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

@ -9,50 +9,6 @@ this.EXPORTED_SYMBOLS = ["PromiseUtils"];
Components.utils.import("resource://gre/modules/Timer.jsm");
this.PromiseUtils = {
/*
* A simple timeout mechanism.
*
* Example:
* resolveOrTimeout(myModule.shutdown(), 1000,
* () => new Error("The module took too long to shutdown"));
*
* @param {Promise} promise The Promise that should resolve/reject quickly.
* @param {number} delay A delay after which to stop waiting for `promise`, in milliseconds.
* @param {function} rejection If `promise` hasn't resolved/rejected after `delay`,
* a value used to construct the rejection.
*
* @return {Promise} A promise that behaves as `promise`, if `promise` is
* resolved/rejected within `delay` ms, or rejects with `rejection()` otherwise.
*/
resolveOrTimeout : function(promise, delay, rejection) {
// throw a TypeError if <promise> is not a Promise object
if (!(promise instanceof Promise)) {
throw new TypeError("first argument <promise> must be a Promise object");
}
// throw a TypeError if <delay> is not a number
if (typeof delay != "number" || delay < 0) {
throw new TypeError("second argument <delay> must be a positive number");
}
// throws a TypeError if <rejection> is not a function
if (rejection && typeof rejection != "function") {
throw new TypeError("third optional argument <rejection> must be a function");
}
return new Promise((resolve, reject) => {
promise.then(resolve, reject);
let id = setTimeout(() => {
try {
rejection ? reject(rejection()) : reject(new Error("Promise Timeout"));
} catch(ex) {
reject(ex);
}
clearTimeout(id);
}, delay);
});
},
/*
* Creates a new pending Promise and provide methods to resolve and reject this Promise.
*

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

@ -11,76 +11,6 @@ function run_test() {
run_next_test();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Tests for PromiseUtils.resolveOrTimeout()
///////////////////////////////////////////////////////////////////////////////////////////
/* Tests for the case when arguments to resolveOrTimeout
* are not of correct type */
add_task(function* test_wrong_arguments() {
let p = new Promise((resolve, reject) => {});
// for the first argument
Assert.throws(() => PromiseUtils.resolveOrTimeout("string", 200), /first argument <promise> must be a Promise object/,
"TypeError thrown because first argument is not a Promsie object");
// for second argument
Assert.throws(() => PromiseUtils.resolveOrTimeout(p, "string"), /second argument <delay> must be a positive number/,
"TypeError thrown because second argument is not a positive number");
// for the third argument
Assert.throws(() => PromiseUtils.resolveOrTimeout(p, 200, "string"), /third optional argument <rejection> must be a function/,
"TypeError thrown because thrird argument is not a function");
});
/* Tests for the case when the optional third argument is not provided
* In that case the returned promise rejects with a default Error */
add_task(function* test_optional_third_argument() {
let p = new Promise((resolve, reject) => {});
yield Assert.rejects(PromiseUtils.resolveOrTimeout(p, 200), /Promise Timeout/, "Promise rejects with a default Error");
});
/* Test for the case when the passed promise resolves quickly
* In that case the returned promise also resolves with the same value */
add_task(function* test_resolve_quickly() {
let p = new Promise((resolve, reject) => setTimeout(() => resolve("Promise is resolved"), 20));
let result = yield PromiseUtils.resolveOrTimeout(p, 200);
Assert.equal(result, "Promise is resolved", "Promise resolves quickly");
});
/* Test for the case when the passed promise rejects quickly
* In that case the returned promise also rejects with the same value */
add_task(function* test_reject_quickly() {
let p = new Promise((resolve, reject) => setTimeout(() => reject("Promise is rejected"), 20));
yield Assert.rejects(PromiseUtils.resolveOrTimeout(p, 200), /Promise is rejected/, "Promise rejects quickly");
});
/* Tests for the case when the passed promise doesn't settle
* and rejection returns string/object/undefined */
add_task(function* test_rejection_function() {
let p = new Promise((resolve, reject) => {});
// for rejection returning string
yield Assert.rejects(PromiseUtils.resolveOrTimeout(p, 200, () => {
return "Rejection returned a string";
}), /Rejection returned a string/, "Rejection returned a string");
// for rejection returning object
yield Assert.rejects(PromiseUtils.resolveOrTimeout(p, 200, () => {
return {Name:"Promise"};
}), Object, "Rejection returned an object");
// for rejection returning undefined
yield Assert.rejects(PromiseUtils.resolveOrTimeout(p, 200, () => {
return;
}), undefined, "Rejection returned undefined");
});
/* Tests for the case when the passed promise doesn't settles
* and rejection throws an error */
add_task(function* test_rejection_throw_error() {
let p = new Promise((resolve, reject) => {});
yield Assert.rejects(PromiseUtils.resolveOrTimeout(p, 200, () => {
throw new Error("Rejection threw an Error");
}), /Rejection threw an Error/, "Rejection threw an error");
});
///////////////////////////////////////////////////////////////////////////////////////
// Tests for PromiseUtils.defer()
///////////////////////////////////////////////////////////////////////////////////////

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

@ -5,8 +5,5 @@
[DEFAULT]
head =
tail =
skip-if = toolkit == 'android'
[consumerNotifications.js]
# Bug 676992: test consistently fails on Android
fail-if = os == "android"

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

@ -196,6 +196,13 @@ toolkit.jar:
skin/classic/global/in-content/radio.svg (../../shared/in-content/radio.svg)
skin/classic/global/in-content/sorter.png (../../shared/in-content/sorter.png)
skin/classic/global/in-content/sorter@2x.png (../../shared/in-content/sorter@2x.png)
skin/classic/global/reader/RM-Add-24x24.svg (../../shared/reader/RM-Add-24x24.svg)
skin/classic/global/reader/RM-Close-24x24.svg (../../shared/reader/RM-Close-24x24.svg)
skin/classic/global/reader/RM-Close-hover-24x24.svg (../../shared/reader/RM-Close-hover-24x24.svg)
skin/classic/global/reader/RM-Delete-24x24.svg (../../shared/reader/RM-Delete-24x24.svg)
skin/classic/global/reader/RM-Reading-List-24x24.svg (../../shared/reader/RM-Reading-List-24x24.svg)
skin/classic/global/reader/RM-Type-Controls-24x24.svg (../../shared/reader/RM-Type-Controls-24x24.svg)
skin/classic/global/reader/RM-Type-Controls-Arrow.svg (../../shared/reader/RM-Type-Controls-Arrow.svg)
skin/classic/global/scale/scale-tray-horiz.gif (scale/scale-tray-horiz.gif)
skin/classic/global/scale/scale-tray-vert.gif (scale/scale-tray-vert.gif)
skin/classic/global/splitter/dimple.png (splitter/dimple.png)

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

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" id="Icons" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<path fill-rule="evenodd" clip-rule="evenodd" fill="#808080" d="M12,2C6.477,2,2,6.477,2,12c0,5.523,4.477,10,10,10s10-4.477,10-10
C22,6.477,17.523,2,12,2z M17.714,12.714h-5v5h-1.429v-5h-5v-1.429h5v-5h1.429v5h5V12.714z"/>
</svg>

После

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

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

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" id="Icons" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<g>
<polygon fill-rule="evenodd" clip-rule="evenodd" fill="#808080" points="20,6.748 17.338,4.079 12.038,9.391 6.661,4 4,6.669
9.377,12.059 4.157,17.292 6.819,19.961 12.039,14.728 17.298,20 19.959,17.331 14.701,12.06 "/>
</g>
</svg>

После

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

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

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" id="Icons" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<g>
<polygon fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" points="20,6.748 17.338,4.079 12.038,9.391 6.661,4 4,6.669
9.377,12.059 4.157,17.292 6.819,19.961 12.039,14.728 17.298,20 19.959,17.331 14.701,12.06 "/>
</g>
</svg>

После

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

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

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" id="Icons" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<path fill-rule="evenodd" clip-rule="evenodd" fill="#808080" d="M12,2C6.477,2,2,6.477,2,12c0,5.523,4.477,10,10,10s10-4.477,10-10
C22,6.477,17.523,2,12,2z M11.286,6.286 M6.286,12.714v-1.429h11.429v1.429H6.286z"/>
</svg>

После

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

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

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" id="Icons" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<g>
<rect x="8" y="10" fill-rule="evenodd" clip-rule="evenodd" fill="#808080" width="14" height="4"/>
<rect x="8" y="16" fill-rule="evenodd" clip-rule="evenodd" fill="#808080" width="14" height="4"/>
<rect x="8" y="4" fill-rule="evenodd" clip-rule="evenodd" fill="#808080" width="14" height="4"/>
<circle fill-rule="evenodd" clip-rule="evenodd" fill="#808080" cx="4" cy="6" r="2"/>
<circle fill-rule="evenodd" clip-rule="evenodd" fill="#808080" cx="4" cy="12" r="2"/>
<circle fill-rule="evenodd" clip-rule="evenodd" fill="#808080" cx="4" cy="18" r="2"/>
</g>
<g enable-background="new ">
<g>
<defs>
<rect id="SVGID_2_" x="-197" y="-1054" width="22" height="34"/>
</defs>
<clipPath id="SVGID_1_">
<use xlink:href="#SVGID_2_" overflow="visible"/>
</clipPath>
<g clip-path="url(#SVGID_1_)">
<defs>
<rect id="SVGID_6_" x="-293" y="-1459" width="1080" height="2896"/>
</defs>
<clipPath id="SVGID_3_">
<use xlink:href="#SVGID_6_" overflow="visible"/>
</clipPath>
</g>
<g clip-path="url(#SVGID_1_)">
<defs>
<rect id="SVGID_10_" x="-293" y="-1459" width="1080" height="2896"/>
</defs>
<clipPath id="SVGID_5_">
<use xlink:href="#SVGID_10_" overflow="visible"/>
</clipPath>
</g>
<g clip-path="url(#SVGID_1_)">
<defs>
<rect id="SVGID_16_" x="-293" y="-1459" width="1080" height="2896"/>
</defs>
<clipPath id="SVGID_7_">
<use xlink:href="#SVGID_16_" overflow="visible"/>
</clipPath>
</g>
</g>
</g>
</svg>

После

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

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

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" id="Icons" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<g>
<path fill="#808080" d="M8.23,18.748v-1.016l1.182-0.146c0.065-0.013,0.12-0.055,0.166-0.127s0.052-0.143,0.02-0.215L8.513,14.07
H4.236l-1.104,3.096c-0.039,0.104-0.02,0.199,0.059,0.283s0.153,0.13,0.225,0.137l1.123,0.146v1.016H0.232v-1.016l1.123-0.166
L5.837,5.008h2.275l4.443,12.197c0.052,0.124,0.103,0.21,0.151,0.259s0.145,0.089,0.288,0.122l0.762,0.146v1.016H8.23z
M6.296,8.084l-1.68,4.805h3.398L6.296,8.084z"/>
<path fill="#808080" d="M20.05,18.748l-0.264-0.996c-0.345,0.234-0.706,0.443-1.083,0.625c-0.331,0.156-0.702,0.298-1.112,0.425
c-0.409,0.127-0.835,0.19-1.277,0.19c-0.364,0-0.704-0.06-1.02-0.181s-0.592-0.293-0.829-0.518s-0.424-0.497-0.561-0.815
S13.7,16.801,13.7,16.404c0-0.299,0.024-0.576,0.073-0.83s0.146-0.49,0.293-0.708s0.356-0.426,0.63-0.625s0.637-0.392,1.089-0.581
s1.008-0.377,1.665-0.566s1.445-0.384,2.363-0.586v-0.244c0-0.098,0.003-0.186,0.01-0.264c0-0.091,0.003-0.182,0.01-0.273
c0.007-0.189-0.013-0.392-0.059-0.61s-0.137-0.42-0.273-0.605s-0.329-0.338-0.576-0.459s-0.569-0.181-0.967-0.181
c-0.137,0-0.259,0.003-0.366,0.01s-0.197,0.016-0.269,0.029c-0.085,0.013-0.159,0.026-0.225,0.039v1.895h-2.061
c-0.169,0.013-0.319-0.003-0.449-0.049c-0.11-0.039-0.213-0.107-0.308-0.205s-0.142-0.25-0.142-0.459
c0-0.371,0.132-0.703,0.396-0.996c0.263-0.293,0.607-0.542,1.033-0.747s0.904-0.363,1.435-0.474
c0.529-0.111,1.058-0.166,1.585-0.166c0.572,0,1.096,0.042,1.57,0.127s0.883,0.249,1.224,0.493c0.342,0.244,0.604,0.587,0.79,1.03
s0.278,1.025,0.278,1.748v5.137c0,0.124,0.042,0.229,0.127,0.317s0.188,0.138,0.312,0.151l0.879,0.059v0.938H20.05z M19.793,13.592
c-0.645,0.13-1.177,0.264-1.597,0.4s-0.754,0.293-1.001,0.469s-0.42,0.376-0.518,0.601s-0.146,0.487-0.146,0.786
c0,0.208,0.034,0.402,0.103,0.581s0.166,0.335,0.293,0.469s0.278,0.239,0.454,0.317s0.368,0.117,0.576,0.117
c0.215,0,0.432-0.023,0.649-0.068s0.415-0.094,0.591-0.146c0.208-0.065,0.407-0.14,0.596-0.225V13.592z"/>
</g>
</svg>

После

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

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

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" id="Icons" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<polygon opacity="0.15" fill-rule="evenodd" clip-rule="evenodd" points="16.583,0.015 16.569,0 4.583,12 16.569,24 16.583,23.985
"/>
<g enable-background="new ">
<g>
<defs>
<rect id="SVGID_2_" x="-125" y="-1086.667" width="22" height="34"/>
</defs>
<clipPath id="SVGID_1_">
<use xlink:href="#SVGID_2_" overflow="visible"/>
</clipPath>
<g clip-path="url(#SVGID_1_)">
<defs>
<rect id="SVGID_6_" x="-221" y="-1491.667" width="1080" height="2896"/>
</defs>
<clipPath id="SVGID_3_">
<use xlink:href="#SVGID_6_" overflow="visible"/>
</clipPath>
</g>
<g clip-path="url(#SVGID_1_)">
<defs>
<rect id="SVGID_10_" x="-221" y="-1491.667" width="1080" height="2896"/>
</defs>
<clipPath id="SVGID_5_">
<use xlink:href="#SVGID_10_" overflow="visible"/>
</clipPath>
</g>
<g clip-path="url(#SVGID_1_)">
<defs>
<rect id="SVGID_16_" x="-221" y="-1491.667" width="1080" height="2896"/>
</defs>
<clipPath id="SVGID_7_">
<use xlink:href="#SVGID_16_" overflow="visible"/>
</clipPath>
</g>
</g>
</g>
<polygon fill-rule="evenodd" clip-rule="evenodd" fill="#FBFBFB" points="16.575,1.021 16.561,1.008 5.583,12 16.577,23.008
16.591,22.994 "/>
</svg>

После

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

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

@ -9,19 +9,22 @@ body {
margin-right: auto;
}
.light {
.light,
.light-button {
color: #333333;
background-color: #ffffff;
}
.dark {
.dark,
.dark-button {
color: #eeeeee;
background-color: #333333;
}
.print {
.sepia,
.sepia-button {
color: #333333;
background-color: #fff1df;
background-color: #f0ece7;
}
.sans-serif {
@ -32,6 +35,32 @@ body {
font-family: serif;
}
.font-size1,
.font-size1-sample {
font-size: 14px;
}
.font-size2,
.font-size2-sample {
font-size: 16px;
}
.font-size3,
.font-size3-sample {
font-size: 18px;
}
.font-size4,
.font-size4-sample {
font-size: 20px;
}
.font-size5,
.font-size5-sample {
font-size: 22px;
}
/* Loading/error message */
.message {
@ -39,7 +68,7 @@ body {
display: none;
text-align: center;
width: 100%;
font-size: 16px;
font-size: 0.9rem;
}
/* Header */
@ -50,8 +79,8 @@ body {
}
.domain {
font-size: 16px;
line-height: 24px;
font-size: 0.9rem;
line-height: 1.33rem;
padding-bottom: 4px;
font-family: sans-serif;
text-decoration: none;
@ -60,7 +89,7 @@ body {
}
.light > .header > .domain,
.print > .header > .domain {
.sepia > .header > .domain {
border-bottom-color: #333333;
}
@ -69,16 +98,16 @@ body {
}
.header > h1 {
font-size: 24px;
line-height: 30px;
font-size: 1.33rem;
line-height: 1.66rem;
width: 100%;
margin: 30px 0;
padding: 0;
}
.header > .credits {
font-size: 16px;
line-height: 24px;
font-size: 0.9rem;
line-height: 1.33rem;
margin: 0 0 30px 0;
padding: 0;
font-style: italic;
@ -88,8 +117,8 @@ body {
.content {
display: none;
font-size: 18px;
line-height: 26px;
font-size: 1rem;
line-height: 1.44rem;
}
.content h1,
@ -99,18 +128,18 @@ body {
}
.content h1 {
font-size: 24px;
line-height: 30px;
font-size: 1.33rem;
line-height: 1.66rem;
}
.content h2 {
font-size: 20px;
line-height: 26px;
font-size: 1.1rem;
line-height: 1.66rem;
}
.content h3 {
font-size: 18px;
line-height: 26px;
font-size: 1rem;
line-height: 1.66rem;
}
.content a {
@ -149,8 +178,8 @@ body {
.content .caption,
.content .wp-caption-text,
.content figcaption {
font-size: 16px;
line-height: 24px;
font-size: 0.9rem;
line-height: 1.33rem;
font-style: italic;
}
@ -165,7 +194,7 @@ body {
}
.light > .content blockquote,
.print > .content blockquote {
.sepia > .content blockquote {
-moz-border-start: 2px solid #333333;
}
@ -191,5 +220,184 @@ body {
/* Toolbar */
.toolbar {
font-family: sans-serif;
position: fixed;
height: 100%;
top: 0px;
left: 0px;
margin: 0;
padding: 0;
list-style: none;
background-color: #FBFBFB;
-moz-user-select: none;
border-right: 1px solid #B5B5B5;
}
.button {
color: white;
display: block;
background-position: center;
background-size: 24px 24px;
background-repeat: no-repeat;
padding: 4px;
height: 32px;
width: 32px;
border-bottom: 1px solid #c1c1c1;
}
.dropdown {
text-align: center;
list-style: none;
margin: 0px;
padding: 0px;
}
.dropdown li {
margin: 0px;
padding: 0px;
}
.dropdown-popup {
text-align: start;
position: absolute;
left: 48px; /* offset to account for toolbar width */
z-index: 1000;
background-color: #FBFBFB;
font-size: 14px;
visibility: hidden;
cursor: pointer;
border-radius: 4px;
border: 1px solid #B5B5B5;
box-shadow: 0px 1px 12px #666;
color: black;
}
.dropdown-popup > hr {
width: 100%;
height: 0px;
border: 0px;
border-top: 1px solid #B5B5B5;
margin: 0;
}
.open > .dropdown-popup {
visibility: visible;
}
.dropdown-arrow {
position: absolute;
top: 30px; /* offset arrow from top of popup */
left: -16px;
width: 24px;
height: 24px;
background-image: url("chrome://global/skin/reader/RM-Type-Controls-Arrow.svg");
display: block;
}
#font-type-buttons,
.segmented-button {
display: flex;
flex-direction: row;
list-style: none;
white-space: nowrap;
padding: 0;
}
#font-type-buttons > li,
.segmented-button > li {
flex: 1 0 auto;
text-align: center;
border-left: 1px solid #B5B5B5;
padding: 10px;
}
#font-type-buttons > li {
width: 50px; /* combined with flex, this acts as a minimum width */
}
.segmented-button > li {
width: 30px; /* combined with flex, this acts as a minimum width */
line-height: 20px;
}
#font-type-buttons > li:first-child,
.segmented-button > li:first-child {
border-left: 0px;
}
#font-type-buttons > li > a,
.segmented-button > li > a {
vertical-align: middle;
text-decoration: none;
}
#font-type-buttons > li > a {
display: inline-block;
font-size: 48px;
line-height: 50px;
}
.segmented-button > li > a {
display: block;
font-family: sans-serif;
font-weight: lighter;
}
#font-type-buttons > li:active:hover,
#font-type-buttons > li.selected {
border-bottom: 3px solid #FC6420;
}
.button:hover,
#font-size-buttons > li:hover {
background-color: #EBEBEB;
}
.dropdown.open,
.button:active,
#font-size-buttons > li:active,
#font-size-buttons > li.selected {
background-color: #DADADA;
}
#font-type-buttons > li > .sans-serif {
font-weight: lighter;
}
#font-type-buttons > li > div {
color: #666;
font-size: 12px;
}
/* Android-only controls */
.share-button {
display: none;
}
.close-button {
background-image: url("chrome://global/skin/reader/RM-Close-24x24.svg");
height: 60px;
background-position: center 8px;
}
.close-button:active,
.close-button:hover {
background-image: url("chrome://global/skin/reader/RM-Close-hover-24x24.svg");
background-color: #d94141;
}
.style-button {
background-image: url("chrome://global/skin/reader/RM-Type-Controls-24x24.svg");
}
.toggle-button.on {
background-image: url("chrome://global/skin/reader/RM-Delete-24x24.svg");
}
.toggle-button {
background-image: url("chrome://global/skin/reader/RM-Add-24x24.svg");
}
.list-button {
background-image: url("chrome://global/skin/reader/RM-Reading-List-24x24.svg");
}

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

@ -187,6 +187,14 @@ toolkit.jar:
skin/classic/global/printpreview/arrow-right-end.png (printpreview/arrow-right-end.png)
skin/classic/global/radio/radio-check.gif (radio/radio-check.gif)
skin/classic/global/radio/radio-check-dis.gif (radio/radio-check-dis.gif)
skin/classic/global/reader/RM-Add-24x24.svg (../../shared/reader/RM-Add-24x24.svg)
skin/classic/global/reader/RM-Close-24x24.svg (../../shared/reader/RM-Close-24x24.svg)
skin/classic/global/reader/RM-Close-hover-24x24.svg (../../shared/reader/RM-Close-hover-24x24.svg)
skin/classic/global/reader/RM-Delete-24x24.svg (../../shared/reader/RM-Delete-24x24.svg)
skin/classic/global/reader/RM-Reading-List-24x24.svg (../../shared/reader/RM-Reading-List-24x24.svg)
skin/classic/global/reader/RM-Type-Controls-24x24.svg (../../shared/reader/RM-Type-Controls-24x24.svg)
skin/classic/global/reader/RM-Type-Controls-Arrow.svg (../../shared/reader/RM-Type-Controls-Arrow.svg)
skin/classic/global/scrollbar/slider.gif (scrollbar/slider.gif)
skin/classic/global/splitter/grip-bottom.gif (splitter/grip-bottom.gif)
skin/classic/global/splitter/grip-top.gif (splitter/grip-top.gif)
@ -383,6 +391,13 @@ toolkit.jar:
skin/classic/aero/global/printpreview/arrow-right-end.png (printpreview/arrow-right-end-aero.png)
skin/classic/aero/global/radio/radio-check.gif (radio/radio-check.gif)
skin/classic/aero/global/radio/radio-check-dis.gif (radio/radio-check-dis.gif)
skin/classic/aero/global/reader/RM-Add-24x24.svg (../../shared/reader/RM-Add-24x24.svg)
skin/classic/aero/global/reader/RM-Close-24x24.svg (../../shared/reader/RM-Close-24x24.svg)
skin/classic/aero/global/reader/RM-Close-hover-24x24.svg (../../shared/reader/RM-Close-hover-24x24.svg)
skin/classic/aero/global/reader/RM-Delete-24x24.svg (../../shared/reader/RM-Delete-24x24.svg)
skin/classic/aero/global/reader/RM-Reading-List-24x24.svg (../../shared/reader/RM-Reading-List-24x24.svg)
skin/classic/aero/global/reader/RM-Type-Controls-24x24.svg (../../shared/reader/RM-Type-Controls-24x24.svg)
skin/classic/aero/global/reader/RM-Type-Controls-Arrow.svg (../../shared/reader/RM-Type-Controls-Arrow.svg)
skin/classic/aero/global/scrollbar/slider.gif (scrollbar/slider.gif)
skin/classic/aero/global/splitter/grip-bottom.gif (splitter/grip-bottom.gif)
skin/classic/aero/global/splitter/grip-top.gif (splitter/grip-top.gif)