Merge mozilla-central to mozilla-inbound

This commit is contained in:
Carsten "Tomcat" Book 2014-07-29 15:33:14 +02:00
commit 47d805ce84
73 changed files with 3355 additions and 2770 deletions

View File

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="fadfafa17f5175203b8b9457bfb95e5816f54f58"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="449d632c69b1a4bd5101d07d18f76799d3fd5f38"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="fadfafa17f5175203b8b9457bfb95e5816f54f58"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="449d632c69b1a4bd5101d07d18f76799d3fd5f38"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="71f5a35e3bc1801847413cff1f14fc3b5cd991ca"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="999e945b85c578c503ad445c2285940f16aacdae">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="fadfafa17f5175203b8b9457bfb95e5816f54f58"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="449d632c69b1a4bd5101d07d18f76799d3fd5f38"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="fadfafa17f5175203b8b9457bfb95e5816f54f58"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="449d632c69b1a4bd5101d07d18f76799d3fd5f38"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="fadfafa17f5175203b8b9457bfb95e5816f54f58"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="449d632c69b1a4bd5101d07d18f76799d3fd5f38"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="71f5a35e3bc1801847413cff1f14fc3b5cd991ca"/>

View File

@ -4,6 +4,6 @@
"remote": "",
"branch": ""
},
"revision": "9863517bfb599a39300f5e8c6f94e16189a4c698",
"revision": "d7d92199ea7e8f850ca0c6f0514bf596f178a16f",
"repo_path": "/integration/gaia-central"
}

View File

@ -17,7 +17,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="fadfafa17f5175203b8b9457bfb95e5816f54f58"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="449d632c69b1a4bd5101d07d18f76799d3fd5f38"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -15,7 +15,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="fadfafa17f5175203b8b9457bfb95e5816f54f58"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="449d632c69b1a4bd5101d07d18f76799d3fd5f38"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="fadfafa17f5175203b8b9457bfb95e5816f54f58"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="449d632c69b1a4bd5101d07d18f76799d3fd5f38"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="71f5a35e3bc1801847413cff1f14fc3b5cd991ca"/>

View File

@ -17,7 +17,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="fadfafa17f5175203b8b9457bfb95e5816f54f58"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="449d632c69b1a4bd5101d07d18f76799d3fd5f38"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -1291,6 +1291,7 @@ pref("devtools.toolbox.selectedTool", "webconsole");
pref("devtools.toolbox.toolbarSpec", '["splitconsole", "paintflashing toggle","tilt toggle","scratchpad","resize toggle","eyedropper","screenshot --fullpage"]');
pref("devtools.toolbox.sideEnabled", true);
pref("devtools.toolbox.zoomValue", "1");
pref("devtools.toolbox.splitconsoleEnabled", false);
// Toolbox Button preferences
pref("devtools.command-button-pick.enabled", true);

View File

@ -4,7 +4,6 @@
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
var gStateObject;
var gTreeData;
@ -20,19 +19,7 @@ window.onload = function() {
return;
}
// remove unneeded braces (added for compatibility with Firefox 2.0 and 3.0)
if (sessionData.value.charAt(0) == '(')
sessionData.value = sessionData.value.slice(1, -1);
try {
gStateObject = JSON.parse(sessionData.value);
}
catch (exJSON) {
var s = new Cu.Sandbox("about:blank", {sandboxName: 'aboutSessionRestore'});
gStateObject = Cu.evalInSandbox("(" + sessionData.value + ")", s);
// If we couldn't parse the string with JSON.parse originally, make sure
// that the value in the textbox will be parsable.
sessionData.value = JSON.stringify(gStateObject);
}
gStateObject = JSON.parse(sessionData.value);
// make sure the data is tracked to be restored in case of a subsequent crash
var event = document.createEvent("UIEvents");

View File

@ -134,7 +134,6 @@ skip-if = true
[browser_528776.js]
[browser_579868.js]
[browser_579879.js]
[browser_581593.js]
[browser_581937.js]
[browser_586147.js]
[browser_586068-apptabs.js]

View File

@ -1,46 +0,0 @@
/* 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/. */
let stateBackup = ss.getBrowserState();
function test() {
/** Test for bug 581593 **/
waitForExplicitFinish();
ignoreAllUncaughtExceptions();
let oldState = { windows: [{ tabs: [{ entries: [{ url: "example.com" }] }] }]};
let pageData = {
url: "about:sessionrestore",
formdata: { id: { "sessionData": "(" + JSON.stringify(oldState) + ")" } }
};
let state = { windows: [{ tabs: [{ entries: [pageData] }] }] };
// The form data will be restored before SSTabRestored, so we want to listen
// for that on the currently selected tab (it will be reused)
gBrowser.selectedTab.addEventListener("SSTabRestored", onSSTabRestored, true);
ss.setBrowserState(JSON.stringify(state));
}
function onSSTabRestored(aEvent) {
info("SSTabRestored event");
gBrowser.selectedTab.removeEventListener("SSTabRestored", onSSTabRestored, true);
// This is an ok way to check this because we will make sure that the text
// field is parsable.
let val = gBrowser.selectedBrowser.contentDocument.getElementById("sessionData").value;
try {
JSON.parse(val);
ok(true, "JSON.parse succeeded");
}
catch (e) {
ok(false, "JSON.parse failed");
}
cleanup();
}
function cleanup() {
ss.setBrowserState(stateBackup);
executeSoon(finish);
}

View File

@ -33,6 +33,7 @@ function* runTests() {
let result = toolbox.once("webconsole-ready", () => {
ok(toolbox.splitConsole, "Split console is shown.");
is(dbgWin.gThreadClient.state, "paused", "Execution is still paused.");
Services.prefs.clearUserPref("devtools.toolbox.splitconsoleEnabled");
});
EventUtils.synthesizeKey("VK_ESCAPE", {}, dbgWin);
yield result;

View File

@ -281,22 +281,12 @@ DevTools.prototype = {
this.emit("toolbox-destroyed", target);
});
// If we were asked for a specific tool then we need to wait for the
// tool to be ready and selected, otherwise we can just wait for the
// toolbox open promise.
if (toolId != null) {
toolbox.once(toolId + "-selected", (event, panel) => {
this.emit("toolbox-ready", toolbox);
deferred.resolve(toolbox);
});
toolbox.open();
}
else {
toolbox.open().then(() => {
deferred.resolve(toolbox);
this.emit("toolbox-ready", toolbox);
});
}
// If toolId was passed in, it will already be selected before the
// open promise resolves.
toolbox.open().then(() => {
deferred.resolve(toolbox);
this.emit("toolbox-ready", toolbox);
});
}
return deferred.promise;

View File

@ -6,6 +6,7 @@
const MAX_ORDINAL = 99;
const ZOOM_PREF = "devtools.toolbox.zoomValue";
const SPLITCONSOLE_ENABLED_PREF = "devtools.toolbox.splitconsoleEnabled";
const MIN_ZOOM = 0.5;
const MAX_ZOOM = 2;
@ -247,7 +248,6 @@ Toolbox.prototype = {
this._buildDockButtons();
this._buildOptions();
this._buildTabs();
let buttonsPromise = this._buildButtons();
this._applyCacheSettings();
this._addKeysToWindow();
this._addReloadKeys();
@ -255,10 +255,20 @@ Toolbox.prototype = {
this._addZoomKeys();
this._loadInitialZoom();
let splitConsolePromise = promise.resolve();
if (Services.prefs.getBoolPref(SPLITCONSOLE_ENABLED_PREF)) {
// Force the split console on if pref is true.
splitConsolePromise = this.toggleSplitConsole(true);
}
let buttonsPromise = this._buildButtons();
this._telemetry.toolOpened("toolbox");
this.selectTool(this._defaultToolId).then(panel => {
buttonsPromise.then(() => {
promise.all([
splitConsolePromise,
buttonsPromise
]).then(() => {
this.emit("ready");
deferred.resolve();
}, deferred.reject);
@ -730,7 +740,7 @@ Toolbox.prototype = {
radio.appendChild(image);
}
if (toolDefinition.label) {
if (toolDefinition.label && !toolDefinition.iconOnly) {
let label = this.doc.createElement("label");
label.setAttribute("value", toolDefinition.label)
label.setAttribute("crop", "end");
@ -962,23 +972,35 @@ Toolbox.prototype = {
/**
* Toggles the split state of the webconsole. If the webconsole panel
* is already selected, then this command is ignored.
* is already selected and no forceToggle is not set, then this command
* is ignored.
*
* @param {bool} forceToggle
* Should the console be toggled regardless of the selected panel.
*
* @returns {Promise} a promise that resolves once the tool has been
* loaded and focused.
*/
toggleSplitConsole: function() {
toggleSplitConsole: function(forceToggle = false) {
let openedConsolePanel = this.currentToolId === "webconsole";
let ret = promise.resolve();
// Don't allow changes when console is open, since it could be confusing
if (!openedConsolePanel) {
if (!openedConsolePanel || forceToggle) {
this._splitConsole = !this._splitConsole;
Services.prefs.setBoolPref(SPLITCONSOLE_ENABLED_PREF, this._splitConsole);
this._refreshConsoleDisplay();
this.emit("split-console");
if (this._splitConsole) {
this.loadTool("webconsole").then(() => {
ret = this.loadTool("webconsole").then(() => {
this.focusConsoleInput();
});
}
}
return ret;
},
/**

View File

@ -72,7 +72,7 @@ browser.jar:
content/browser/devtools/canvasdebugger.xul (canvasdebugger/canvasdebugger.xul)
content/browser/devtools/canvasdebugger.js (canvasdebugger/canvasdebugger.js)
content/browser/devtools/webaudioeditor.xul (webaudioeditor/webaudioeditor.xul)
content/browser/devtools/d3.js (webaudioeditor/lib/d3.js)
content/browser/devtools/d3.js (shared/d3.js)
content/browser/devtools/dagre-d3.js (webaudioeditor/lib/dagre-d3.js)
content/browser/devtools/webaudioeditor-controller.js (webaudioeditor/webaudioeditor-controller.js)
content/browser/devtools/webaudioeditor-view.js (webaudioeditor/webaudioeditor-view.js)

View File

@ -70,6 +70,8 @@ Tools.options = {
icon: "chrome://browser/skin/devtools/tool-options.svg",
invertIconForLightTheme: true,
bgTheme: "theme-body",
label: l10n("options.label", toolboxStrings),
iconOnly: true,
panelLabel: l10n("options.panelLabel", toolboxStrings),
tooltip: l10n("optionsButton.tooltip", toolboxStrings),
inMenu: false,

View File

@ -102,6 +102,7 @@ function test() {
// Menus are correctly updated?
is(document.getElementById("Tools:ResponsiveUI").getAttribute("checked"), "false", "menu unchecked");
Services.prefs.clearUserPref("devtools.toolbox.splitconsoleEnabled");
gBrowser.removeCurrentTab();
finish();
}

View File

@ -40,7 +40,7 @@ function spawnTest() {
yield Promise.all([destroyed, waitForGraphRendered(panelWin, 3, 2)]);
// Test internal storage
ok(panelWin.AudioNodes.length, 3, "All nodes should be GC'd except one gain, osc and dest node.");
is(panelWin.AudioNodes.length, 3, "All nodes should be GC'd except one gain, osc and dest node.");
// Test graph rendering
ok(findGraphNode(panelWin, actorIDs[0]), "dest should be in graph");

View File

@ -17,7 +17,7 @@
<script type="application/javascript;version=1.8"
src="chrome://browser/content/devtools/theme-switching.js"/>
<script type="application/javascript" src="d3.js"/>
<script type="application/javascript" src="chrome://browser/content/devtools/d3.js"/>
<script type="application/javascript" src="dagre-d3.js"/>
<script type="application/javascript" src="webaudioeditor-controller.js"/>
<script type="application/javascript" src="webaudioeditor-view.js"/>

View File

@ -281,6 +281,7 @@ skip-if = buildapp == 'mulet'
[browser_webconsole_scratchpad_panel_link.js]
[browser_webconsole_split.js]
[browser_webconsole_split_escape_key.js]
[browser_webconsole_split_persist.js]
[browser_webconsole_view_source.js]
[browser_webconsole_reflow.js]
[browser_webconsole_log_file_filter.js]

View File

@ -38,6 +38,8 @@ function test()
function onConsoleMessage()
{
let currentPosition = hud.outputNode.parentNode.scrollTop;
let bottom = currentPosition;
EventUtils.synthesizeKey("VK_PAGE_UP", {});
isnot(hud.outputNode.parentNode.scrollTop, currentPosition, "scroll position changed after page up");
@ -45,6 +47,12 @@ function test()
EventUtils.synthesizeKey("VK_PAGE_DOWN", {});
ok(hud.outputNode.parentNode.scrollTop > currentPosition, "scroll position now at bottom");
EventUtils.synthesizeKey("VK_HOME", {});
is(hud.outputNode.parentNode.scrollTop, 0, "scroll position now at top");
EventUtils.synthesizeKey("VK_END", {});
is(hud.outputNode.parentNode.scrollTop, bottom, "scroll position now at bottom");
hud.jsterm.once("messages-cleared", onClear);
info("try ctrl-l to clear output");
EventUtils.synthesizeKey("l", { ctrlKey: true });

View File

@ -103,6 +103,12 @@ function consoleOpened(aHud) {
ok(popup.selectedIndex < currentSelectionIndex, "Index is less after Page UP");
EventUtils.synthesizeKey("VK_END", {});
is(popup.selectedIndex, 17, "index is last after End");
EventUtils.synthesizeKey("VK_HOME", {});
is(popup.selectedIndex, 0, "index is first after Home");
info("press Tab and wait for popup to hide");
popup._panel.addEventListener("popuphidden", popupHideAfterTab, false);
EventUtils.synthesizeKey("VK_TAB", {});

View File

@ -0,0 +1,67 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
function test() {
info("Test that the split console state is persisted");
let toolbox;
let TEST_URI = "data:text/html;charset=utf-8,<p>Web Console test for splitting</p>";
Task.spawn(runner).then(finish);
function* runner() {
info("Opening a tab while there is no user setting on split console pref");
let {tab} = yield loadTab(TEST_URI);
let target = TargetFactory.forTab(tab);
toolbox = yield gDevTools.showToolbox(target, "inspector");
ok(!toolbox.splitConsole, "Split console is hidden by default.");
yield toggleSplitConsoleWithEscape();
ok(toolbox.splitConsole, "Split console is now visible.");
ok(getPrefValue(), "Pref is true");
yield toolbox.destroy();
info("Opening a tab while there is a true user setting on split console pref");
let {tab} = yield loadTab(TEST_URI);
let target = TargetFactory.forTab(tab);
toolbox = yield gDevTools.showToolbox(target, "inspector");
ok(toolbox.splitConsole, "Split console is visible by default.");
yield toggleSplitConsoleWithEscape();
ok(!toolbox.splitConsole, "Split console is now hidden.");
ok(!getPrefValue(), "Pref is false");
yield toolbox.destroy();
info("Opening a tab while there is a false user setting on split console pref");
let {tab} = yield loadTab(TEST_URI);
let target = TargetFactory.forTab(tab);
toolbox = yield gDevTools.showToolbox(target, "inspector");
ok(!toolbox.splitConsole, "Split console is hidden by default.");
ok(!getPrefValue(), "Pref is false");
yield toolbox.destroy();
}
function getPrefValue() {
return Services.prefs.getBoolPref("devtools.toolbox.splitconsoleEnabled");
}
function toggleSplitConsoleWithEscape() {
let onceSplitConsole = toolbox.once("split-console");
let contentWindow = toolbox.frame.contentWindow;
contentWindow.focus();
EventUtils.sendKey("ESCAPE", contentWindow);
return onceSplitConsole;
}
function finish() {
toolbox = TEST_URI = null;
Services.prefs.clearUserPref("devtools.toolbox.splitconsoleEnabled");
finishTest();
}
}

View File

@ -4021,7 +4021,25 @@ JSTerm.prototype = {
break;
case Ci.nsIDOMKeyEvent.DOM_VK_HOME:
if (this.autocompletePopup.isOpen) {
this.autocompletePopup.selectedIndex = 0;
aEvent.preventDefault();
} else if (this.inputNode.value.length <= 0) {
this.hud.outputNode.parentNode.scrollTop = 0;
aEvent.preventDefault();
}
break;
case Ci.nsIDOMKeyEvent.DOM_VK_END:
if (this.autocompletePopup.isOpen) {
this.autocompletePopup.selectedIndex = this.autocompletePopup.itemCount - 1;
aEvent.preventDefault();
} else if (this.inputNode.value.length <= 0) {
this.hud.outputNode.parentNode.scrollTop = this.hud.outputNode.parentNode.scrollHeight;
aEvent.preventDefault();
}
break;
case Ci.nsIDOMKeyEvent.DOM_VK_LEFT:
if (this.autocompletePopup.isOpen || this.lastCompletion.value) {
this.clearCompletion();

View File

@ -18,6 +18,8 @@ webide.jar:
content/runtimedetails.xhtml (runtimedetails.xhtml)
content/prefs.js (prefs.js)
content/prefs.xhtml (prefs.xhtml)
content/monitor.xhtml (monitor.xhtml)
content/monitor.js (monitor.js)
# Temporarily include locales in content, until we're ready
# to localize webide

View File

@ -0,0 +1,676 @@
/* 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/. */
const Cu = Components.utils;
Cu.import('resource:///modules/devtools/gDevTools.jsm');
const {require} = Cu.import('resource://gre/modules/devtools/Loader.jsm', {}).devtools;
const {Services} = Cu.import('resource://gre/modules/Services.jsm');
const {AppManager} = require('devtools/webide/app-manager');
const {AppActorFront} = require('devtools/app-actor-front');
const {Connection} = require('devtools/client/connection-manager');
const EventEmitter = require('devtools/toolkit/event-emitter');
window.addEventListener('load', function onLoad() {
window.removeEventListener('load', onLoad);
window.addEventListener('resize', Monitor.resize);
window.addEventListener('unload', Monitor.unload);
document.querySelector('#close').onclick = () => {
window.parent.UI.openProject();
};
Monitor.load();
});
/**
* The Monitor is a WebIDE tool used to display any kind of time-based data in
* the form of graphs.
*
* The data can come from a Firefox OS device, simulator, or from a WebSockets
* server running locally.
*
* The format of a data update is typically an object like:
*
* { graph: 'mygraph', curve: 'mycurve', value: 42, time: 1234 }
*
* or an array of such objects. For more details on the data format, see the
* `Graph.update(data)` method.
*/
let Monitor = {
apps: new Map(),
graphs: new Map(),
front: null,
socket: null,
wstimeout: null,
loaded: false,
/**
* Add new data to the graphs, create a new graph if necessary.
*/
update: function(data, fallback) {
if (Array.isArray(data)) {
data.forEach(d => Monitor.update(d, fallback));
return;
}
if (fallback) {
for (let key in fallback) {
if (!data[key]) {
data[key] = fallback[key];
}
}
}
let graph = Monitor.graphs.get(data.graph);
if (!graph) {
let element = document.createElement('div');
element.classList.add('graph');
document.body.appendChild(element);
graph = new Graph(data.graph, element);
Monitor.resize(); // a scrollbar might have dis/reappeared
Monitor.graphs.set(data.graph, graph);
}
graph.update(data);
},
/**
* Initialize the Monitor.
*/
load: function() {
AppManager.on('app-manager-update', Monitor.onAppManagerUpdate);
Monitor.connectToRuntime();
Monitor.connectToWebSocket();
Monitor.loaded = true;
},
/**
* Clean up the Monitor.
*/
unload: function() {
AppManager.off('app-manager-update', Monitor.onAppManagerUpdate);
Monitor.disconnectFromRuntime();
Monitor.disconnectFromWebSocket();
},
/**
* Resize all the graphs.
*/
resize: function() {
for (let graph of Monitor.graphs.values()) {
graph.resize();
}
},
/**
* When WebIDE connects to a new runtime, start its data forwarders.
*/
onAppManagerUpdate: function(event, what, details) {
switch (what) {
case 'list-tabs-response':
Monitor.connectToRuntime();
break;
case 'connection':
if (AppManager.connection.status == Connection.Status.DISCONNECTED) {
Monitor.disconnectFromRuntime();
}
break;
}
},
/**
* Use an AppActorFront on a runtime to watch track its apps.
*/
connectToRuntime: function() {
let client = AppManager.connection && AppManager.connection.client;
let resp = AppManager._listTabsResponse;
if (client && resp && !Monitor.front) {
Monitor.front = new AppActorFront(client, resp);
Monitor.front.watchApps(Monitor.onRuntimeAppEvent);
}
},
/**
* Destroy our AppActorFront.
*/
disconnectFromRuntime: function() {
if (Monitor.front) {
Monitor.front.unwatchApps(Monitor.onRuntimeAppEvent);
Monitor.front = null;
}
},
/**
* Try connecting to a local websockets server and accept updates from it.
*/
connectToWebSocket: function() {
let webSocketURL = Services.prefs.getCharPref('devtools.webide.monitorWebSocketURL');
try {
Monitor.socket = new WebSocket(webSocketURL);
Monitor.socket.onmessage = function(event) {
Monitor.update(JSON.parse(event.data));
};
Monitor.socket.onclose = function() {
Monitor.wstimeout = setTimeout(Monitor.connectToWebsocket, 1000);
};
} catch(e) {
Monitor.wstimeout = setTimeout(Monitor.connectToWebsocket, 1000);
}
},
/**
* Used when cleaning up.
*/
disconnectFromWebSocket: function() {
clearTimeout(Monitor.wstimeout);
if (Monitor.socket) {
Monitor.socket.onclose = () => {};
Monitor.socket.close();
}
},
/**
* When an app starts on the runtime, start a monitor actor for its process.
*/
onRuntimeAppEvent: function(type, app) {
if (type !== 'appOpen' && type !== 'appClose') {
return;
}
let client = AppManager.connection.client;
app.getForm().then(form => {
if (type === 'appOpen') {
app.monitorClient = new MonitorClient(client, form);
app.monitorClient.start();
app.monitorClient.on('update', Monitor.onRuntimeUpdate);
Monitor.apps.set(form.monitorActor, app);
} else {
let app = Monitor.apps.get(form.monitorActor)
if (app) {
app.monitorClient.stop(() => app.monitorClient.destroy());
Monitor.apps.delete(form.monitorActor);
}
}
});
},
/**
* Accept data updates from the monitor actors of a runtime.
*/
onRuntimeUpdate: function(type, packet) {
let fallback = {}, app = Monitor.apps.get(packet.from);
if (app) {
fallback.curve = app.manifest.name
}
Monitor.update(packet.data, fallback);
}
};
/**
* A MonitorClient is used as an actor client of a runtime's monitor actors,
* receiving its updates.
*/
function MonitorClient(client, form) {
this.client = client;
this.actor = form.monitorActor;
this.events = ['update'];
EventEmitter.decorate(this);
this.client.registerClient(this);
}
MonitorClient.prototype.destroy = function () {
this.client.unregisterClient(this);
}
MonitorClient.prototype.detach = function () {}
MonitorClient.prototype.start = function () {
this.client.request({
to: this.actor,
type: 'start'
});
}
MonitorClient.prototype.stop = function (callback) {
this.client.request({
to: this.actor,
type: 'stop'
}, callback);
}
/**
* A Graph populates a container DOM element with an SVG graph and a legend.
*/
function Graph(name, element) {
this.name = name;
this.element = element;
this.curves = new Map();
this.events = new Map();
this.ignored = new Set();
this.enabled = true;
this.request = null;
this.x = d3.time.scale();
this.y = d3.scale.linear();
this.xaxis = d3.svg.axis().scale(this.x).orient('bottom');
this.yaxis = d3.svg.axis().scale(this.y).orient('left');
this.xformat = d3.time.format('%I:%M:%S');
this.yformat = this.formatter(1);
this.yaxis.tickFormat(this.formatter(0));
this.line = d3.svg.line().interpolate('linear')
.x(function(d) { return this.x(d.time); })
.y(function(d) { return this.y(d.value); });
this.color = d3.scale.category10();
this.svg = d3.select(element).append('svg').append('g')
.attr('transform', 'translate(' + this.margin.left + ',' + this.margin.top + ')');
this.xelement = this.svg.append('g').attr('class', 'x axis').call(this.xaxis);
this.yelement = this.svg.append('g').attr('class', 'y axis').call(this.yaxis);
// RULERS on axes
let xruler = this.xruler = this.svg.select('.x.axis').append('g').attr('class', 'x ruler');
xruler.append('line').attr('y2', 6);
xruler.append('line').attr('stroke-dasharray', '1,1');
xruler.append('text').attr('y', 9).attr('dy', '.71em');
let yruler = this.yruler = this.svg.select('.y.axis').append('g').attr('class', 'y ruler');
yruler.append('line').attr('x2', -6);
yruler.append('line').attr('stroke-dasharray', '1,1');
yruler.append('text').attr('x', -9).attr('dy', '.32em');
let self = this;
d3.select(element).select('svg')
.on('mousemove', function() {
let mouse = d3.mouse(this);
self.mousex = mouse[0] - self.margin.left,
self.mousey = mouse[1] - self.margin.top;
xruler.attr('transform', 'translate(' + self.mousex + ',0)');
yruler.attr('transform', 'translate(0,' + self.mousey + ')');
});
/*.on('mouseout', function() {
self.xruler.attr('transform', 'translate(-500,0)');
self.yruler.attr('transform', 'translate(0,-500)');
});*/
this.mousex = this.mousey = -500;
let sidebar = d3.select(this.element).append('div').attr('class', 'sidebar');
let title = sidebar.append('label').attr('class', 'graph-title');
title.append('input')
.attr('type', 'checkbox')
.attr('checked', 'true')
.on('click', function() { self.toggle(); });
title.append('span').text(this.name);
this.legend = sidebar.append('div').attr('class', 'legend');
this.resize = this.resize.bind(this);
this.render = this.render.bind(this);
this.averages = this.averages.bind(this);
setInterval(this.averages, 1000);
this.resize();
}
Graph.prototype = {
/**
* These margin are used to properly position the SVG graph items inside the
* container element.
*/
margin: {
top: 10,
right: 150,
bottom: 20,
left: 50
},
/**
* A Graph can be collapsed by the user.
*/
toggle: function() {
if (this.enabled) {
this.element.classList.add('disabled');
this.enabled = false;
} else {
this.element.classList.remove('disabled');
this.enabled = true;
}
Monitor.resize();
},
/**
* If the container element is resized (e.g. because the window was resized or
* a scrollbar dis/appeared), the graph needs to be resized as well.
*/
resize: function() {
let style = getComputedStyle(this.element),
height = parseFloat(style.height) - this.margin.top - this.margin.bottom,
width = parseFloat(style.width) - this.margin.left - this.margin.right;
d3.select(this.element).select('svg')
.attr('width', width + this.margin.left)
.attr('height', height + this.margin.top + this.margin.bottom);
this.x.range([0, width]);
this.y.range([height, 0]);
this.xelement.attr('transform', 'translate(0,' + height + ')')
this.xruler.select('line[stroke-dasharray]').attr('y2', -height);
this.yruler.select('line[stroke-dasharray]').attr('x2', width);
},
/**
* If the domain of the Graph's data changes (on the time axis and/or on the
* value axis), the axes' domains need to be updated and the graph items need
* to be rescaled in order to represent all the data.
*/
rescale: function() {
let gettime = v => { return v.time; },
getvalue = v => { return v.value; },
ignored = c => { return this.ignored.has(c.id); };
let xmin = null, xmax = null, ymin = null, ymax = null;
for (let curve of this.curves.values()) {
if (ignored(curve)) {
continue;
}
if (xmax == null || curve.xmax > xmax) {
xmax = curve.xmax;
}
if (xmin == null || curve.xmin < xmin) {
xmin = curve.xmin;
}
if (ymax == null || curve.ymax > ymax) {
ymax = curve.ymax;
}
if (ymin == null || curve.ymin < ymin) {
ymin = curve.ymin;
}
}
for (let event of this.events.values()) {
if (ignored(event)) {
continue;
}
if (xmax == null || event.xmax > xmax) {
xmax = event.xmax;
}
if (xmin == null || event.xmin < xmin) {
xmin = event.xmin;
}
}
let oldxdomain = this.x.domain();
if (xmin != null && xmax != null) {
this.x.domain([xmin, xmax]);
let newxdomain = this.x.domain();
if (newxdomain[0] !== oldxdomain[0] || newxdomain[1] !== oldxdomain[1]) {
this.xelement.call(this.xaxis);
}
}
let oldydomain = this.y.domain();
if (ymin != null && ymax != null) {
this.y.domain([ymin, ymax]).nice();
let newydomain = this.y.domain();
if (newydomain[0] !== oldydomain[0] || newydomain[1] !== oldydomain[1]) {
this.yelement.call(this.yaxis);
}
}
},
/**
* Add new values to the graph.
*/
update: function(data) {
delete data.graph;
let time = data.time || Date.now();
delete data.time;
let curve = data.curve;
delete data.curve;
// Single curve value, e.g. { curve: 'memory', value: 42, time: 1234 }.
if ('value' in data) {
this.push(this.curves, curve, [{time: time, value: data.value}]);
delete data.value;
}
// Several curve values, e.g. { curve: 'memory', values: [{value: 42, time: 1234}] }.
if ('values' in data) {
this.push(this.curves, curve, data.values);
delete data.values;
}
// Punctual event, e.g. { event: 'gc', time: 1234 },
// event with duration, e.g. { event: 'jank', duration: 425, time: 1234 }.
if ('event' in data) {
this.push(this.events, data.event, [{time: time, value: data.duration}]);
delete data.event;
delete data.duration;
}
// Remaining keys are curves, e.g. { time: 1234, memory: 42, battery: 13, temperature: 45 }.
for (let key in data) {
this.push(this.curves, key, [{time: time, value: data[key]}]);
}
// If no render is currently pending, request one.
if (this.enabled && !this.request) {
this.request = requestAnimationFrame(this.render);
}
},
/**
* Insert new data into the graph's data structures.
*/
push: function(collection, id, values) {
// Note: collection is either `this.curves` or `this.events`.
let item = collection.get(id);
if (!item) {
item = { id: id, values: [], xmin: null, xmax: null, ymin: 0, ymax: null, average: 0 };
collection.set(id, item);
}
for (let v of values) {
let time = new Date(v.time), value = +v.value;
// Update the curve/event's domain values.
if (item.xmax == null || time > item.xmax) {
item.xmax = time;
}
if (item.xmin == null || time < item.xmin) {
item.xmin = time;
}
if (item.ymax == null || value > item.ymax) {
item.ymax = value;
}
if (item.ymin == null || value < item.ymin) {
item.ymin = value;
}
// Note: A curve's average is not computed here. Call `graph.averages()`.
item.values.push({ time: time, value: value });
}
},
/**
* Render the SVG graph with curves, events, crosshair and legend.
*/
render: function() {
this.request = null;
this.rescale();
// DATA
let self = this,
getid = d => { return d.id; },
gettime = d => { return d.time.getTime(); },
getline = d => { return self.line(d.values); },
getcolor = d => { return self.color(d.id); },
getvalues = d => { return d.values; },
ignored = d => { return self.ignored.has(d.id); };
// Convert our maps to arrays for d3.
let curvedata = [...this.curves.values()],
eventdata = [...this.events.values()],
data = curvedata.concat(eventdata);
// CURVES
// Map curve data to curve elements.
let curves = this.svg.selectAll('.curve').data(curvedata, getid);
// Create new curves (no element corresponding to the data).
curves.enter().append('g').attr('class', 'curve').append('path')
.style('stroke', getcolor);
// Delete old curves (elements corresponding to data not present anymore).
curves.exit().remove();
// Update all curves from data.
this.svg.selectAll('.curve').select('path')
.attr('d', d => { return ignored(d) ? '' : getline(d); });
let height = parseFloat(getComputedStyle(this.element).height) - this.margin.top - this.margin.bottom;
// EVENTS
// Map event data to event elements.
let events = this.svg.selectAll('.event-slot').data(eventdata, getid);
// Create new events.
events.enter().append('g').attr('class', 'event-slot');
// Remove old events.
events.exit().remove();
// Get all occurences of an event, and map its data to them.
let lines = this.svg.selectAll('.event-slot')
.style('stroke', d => { return ignored(d) ? 'none' : getcolor(d); })
.selectAll('.event')
.data(getvalues, gettime);
// Create new event occurrence.
lines.enter().append('line').attr('class', 'event').attr('y2', height);
// Delete old event occurrence.
lines.exit().remove();
// Update all event occurrences from data.
this.svg.selectAll('.event')
.attr('transform', d => { return 'translate(' + self.x(d.time) + ',0)'; });
// CROSSHAIR
// TODO select curves and events, intersect with curves and show values/hovers
// e.g. look like http://code.shutterstock.com/rickshaw/examples/lines.html
// Update crosshair labels on each axis.
this.xruler.select('text').text(self.xformat(self.x.invert(self.mousex)));
this.yruler.select('text').text(self.yformat(self.y.invert(self.mousey)));
// LEGEND
// Map data to legend elements.
let legends = this.legend.selectAll('label').data(data, getid);
// Update averages.
legends.attr('title', c => { return 'Average: ' + self.yformat(c.average); });
// Create new legends.
let newlegend = legends.enter().append('label');
newlegend.append('input').attr('type', 'checkbox').attr('checked', 'true').on('click', function(c) {
if (ignored(c)) {
this.parentElement.classList.remove('disabled');
self.ignored.delete(c.id);
} else {
this.parentElement.classList.add('disabled');
self.ignored.add(c.id);
}
self.update({}); // if no re-render is pending, request one.
});
newlegend.append('span').attr('class', 'legend-color').style('background-color', getcolor);
newlegend.append('span').attr('class', 'legend-id').text(getid);
// Delete old legends.
legends.exit().remove();
},
/**
* Returns a SI value formatter with a given precision.
*/
formatter: function(decimals) {
return value => {
// Don't use sub-unit SI prefixes (milli, micro, etc.).
if (Math.abs(value) < 1) return value.toFixed(decimals);
// SI prefix, e.g. 1234567 will give '1.2M' at precision 1.
let prefix = d3.formatPrefix(value);
return prefix.scale(value).toFixed(decimals) + prefix.symbol;
};
},
/**
* Compute the average of each time series.
*/
averages: function() {
for (let c of this.curves.values()) {
let length = c.values.length;
if (length > 0) {
let total = 0;
c.values.forEach(v => total += v.value);
c.average = (total / length);
}
}
},
/**
* Bisect a time serie to find the data point immediately left of `time`.
*/
bisectTime: d3.bisector(d => d.time).left,
/**
* Get all curve values at a given time.
*/
valuesAt: function(time) {
let values = { time: time };
for (let id of this.curves.keys()) {
let curve = this.curves.get(id);
// Find the closest value just before `time`.
let i = this.bisectTime(curve.values, time);
if (i < 0) {
// Curve starts after `time`, use first value.
values[id] = curve.values[0].value;
} else if (i > curve.values.length-2) {
// Curve ends before `time`, use last value.
values[id] = curve.values[curve.values.length-1].value;
} else {
// Curve has two values around `time`, interpolate.
let v1 = curve.values[i],
v2 = curve.values[i+1],
delta = (time - v1.time) / (v2.time - v1.time);
values[id] = v1.value + (v2.value - v1.time) * delta;
}
}
return values;
}
};

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 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/. -->
<!DOCTYPE html [
<!ENTITY % webideDTD SYSTEM "chrome://webide/content/webide.dtd" >
%webideDTD;
]>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf8"/>
<link rel="stylesheet" href="chrome://webide/skin/deck.css" type="text/css"/>
<link rel="stylesheet" href="chrome://webide/skin/monitor.css" type="text/css"/>
<script src="chrome://browser/content/devtools/d3.js"></script>
<script type="application/javascript;version=1.8" src="monitor.js"></script>
</head>
<body>
<div id="controls">
<a href="https://developer.mozilla.org/docs/Tools/WebIDE/Monitor" target="_blank">&monitor_help;</a>
<a id="close">&deck_close;</a>
</div>
<h1>&monitor_title;</h1>
</body>
</html>

View File

@ -488,6 +488,11 @@ let UI = {
this.resetFocus();
let deck = document.querySelector("#deck");
let panel = deck.querySelector("#deck-panel-" + id);
let lazysrc = panel.getAttribute("lazysrc");
if (lazysrc) {
panel.removeAttribute("lazysrc");
panel.setAttribute("src", lazysrc);
}
deck.selectedPanel = panel;
this.updateProjectEditorMenusVisibility();
},
@ -881,6 +886,10 @@ let Cmds = {
UI.selectDeckPanel("runtimedetails");
},
showMonitor: function() {
UI.selectDeckPanel("monitor");
},
play: function() {
switch(AppManager.selectedProject.type) {
case "packaged":

View File

@ -39,6 +39,7 @@
<command id="cmd_showProjectPanel" oncommand="Cmds.showProjectPanel()"/>
<command id="cmd_showRuntimePanel" oncommand="Cmds.showRuntimePanel()"/>
<command id="cmd_disconnectRuntime" oncommand="Cmds.disconnectRuntime()" label="&runtimeMenu_disconnect_label;"/>
<command id="cmd_showMonitor" oncommand="Cmds.showMonitor()" label="&runtimeMenu_showMonitor_label;"/>
<command id="cmd_showPermissionsTable" oncommand="Cmds.showPermissionsTable()" label="&runtimeMenu_showPermissionTable_label;"/>
<command id="cmd_showRuntimeDetails" oncommand="Cmds.showRuntimeDetails()" label="&runtimeMenu_showDetails_label;"/>
<command id="cmd_takeScreenshot" oncommand="Cmds.takeScreenshot()" label="&runtimeMenu_takeScreenshot_label;"/>
@ -72,6 +73,7 @@
<menu id="menu-runtime" label="&runtimeMenu_label;" accesskey="&runtimeMenu_accesskey;">
<menupopup id="menu-runtime-popup">
<menuitem command="cmd_showMonitor" accesskey="&runtimeMenu_showMonitor_accesskey;"/>
<menuitem command="cmd_takeScreenshot" accesskey="&runtimeMenu_takeScreenshot_accesskey;"/>
<menuitem command="cmd_showPermissionsTable" accesskey="&runtimeMenu_showPermissionTable_accesskey;"/>
<menuitem command="cmd_showRuntimeDetails" accesskey="&runtimeMenu_showDetails_accesskey;"/>
@ -177,6 +179,7 @@
<iframe id="deck-panel-prefs" flex="1" src="prefs.xhtml"/>
<iframe id="deck-panel-permissionstable" flex="1" src="permissionstable.xhtml"/>
<iframe id="deck-panel-runtimedetails" flex="1" src="runtimedetails.xhtml"/>
<iframe id="deck-panel-monitor" flex="1" lazysrc="monitor.xhtml"/>
</deck>
</notificationbox>

View File

@ -35,6 +35,8 @@
<!ENTITY runtimeMenu_takeScreenshot_accesskey "S">
<!ENTITY runtimeMenu_showDetails_label "Runtime Info">
<!ENTITY runtimeMenu_showDetails_accesskey "E">
<!ENTITY runtimeMenu_showMonitor_label "Monitor">
<!ENTITY runtimeMenu_showMonitor_accesskey "M">
<!ENTITY viewMenu_label "View">
<!ENTITY viewMenu_accesskey "V">
@ -128,3 +130,7 @@
<!ENTITY runtimedetails_restrictedPrivileges "DevTools restricted privileges: ">
<!ENTITY runtimedetails_requestPrivileges "request higher privileges">
<!ENTITY runtimedetails_privilegesWarning "(Will reboot device. Requires root access.)">
<!-- Monitor -->
<!ENTITY monitor_title "Monitor">
<!ENTITY monitor_help "Help">

View File

@ -14,3 +14,4 @@ webide.jar:
skin/prefs.css (prefs.css)
skin/runtimedetails.css (runtimedetails.css)
skin/permissionstable.css (permissionstable.css)
skin/monitor.css (monitor.css)

View File

@ -0,0 +1,86 @@
/* 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/. */
/* Graph */
.graph {
height: 500px;
width: 100%;
padding-top: 20px;
padding-bottom: 20px;
margin-bottom: 30px;
background-color: white;
}
.graph > svg, .sidebar {
display: inline-block;
vertical-align: top;
}
.disabled {
opacity: 0.5;
}
.graph.disabled {
height: 30px;
}
.graph.disabled > svg {
visibility: hidden;
}
.curve path, .event-slot line {
fill: none;
stroke-width: 1.5px;
}
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.axis path {
fill: none;
stroke: black;
stroke-width: 1px;
shape-rendering: crispEdges;
}
.tick text, .x.ruler text, .y.ruler text {
font-size: 0.9em;
}
.x.ruler text {
text-anchor: middle;
}
.y.ruler text {
text-anchor: end;
}
/* Sidebar */
.sidebar {
width: 150px;
overflow-x: hidden;
}
.sidebar label {
cursor: pointer;
display: block;
}
.sidebar span:not(.color) {
vertical-align: 13%;
}
.sidebar input {
visibility: hidden;
}
.sidebar input:hover {
visibility: visible;
}
.graph-title {
margin-top: 5px;
font-size: 1.2em;
}
.legend-color {
display: inline-block;
height: 10px;
width: 10px;
margin-left: 1px;
margin-right: 3px;
}
.legend-id {
font-size: .9em;
}
.graph.disabled > .sidebar > .legend {
display: none;
}

View File

@ -14,3 +14,4 @@ pref("devtools.webide.simulatorAddonsURL", "https://ftp.mozilla.org/pub/mozilla.
pref("devtools.webide.simulatorAddonID", "fxos_#SLASHED_VERSION#_simulator@mozilla.org");
pref("devtools.webide.adbAddonURL", "https://ftp.mozilla.org/pub/mozilla.org/labs/fxos-simulator/adb-helper/#OS#/adbhelper-#OS#-latest.xpi");
pref("devtools.webide.adbAddonID", "adbhelper@mozilla.org");
pref("devtools.webide.monitorWebSocketURL", "ws://localhost:9000");

View File

@ -1,4 +1,4 @@
This is the pdf.js project output, https://github.com/mozilla/pdf.js
Current extension version is: 1.0.370
Current extension version is: 1.0.473

View File

@ -44,10 +44,22 @@ this.PdfJsTelemetry = {
let histogram = Services.telemetry.getHistogramById("PDF_VIEWER_DOCUMENT_GENERATOR");
histogram.add(generatorId);
},
onEmbed: function (isObject) {
let histogram = Services.telemetry.getHistogramById("PDF_VIEWER_EMBED");
histogram.add(isObject);
},
onFontType: function (fontTypeId) {
let histogram = Services.telemetry.getHistogramById("PDF_VIEWER_FONT_TYPES");
histogram.add(fontTypeId);
},
onForm: function (isAcroform) {
let histogram = Services.telemetry.getHistogramById("PDF_VIEWER_FORM");
histogram.add(isAcroform);
},
onPrint: function () {
let histogram = Services.telemetry.getHistogramById("PDF_VIEWER_PRINT");
histogram.add(true);
},
onStreamType: function (streamTypeId) {
let histogram = Services.telemetry.getHistogramById("PDF_VIEWER_STREAM_TYPES");
histogram.add(streamTypeId);

View File

@ -232,6 +232,7 @@ function ChromeActions(domWindow, contentDispositionFilename) {
documentInfo: false,
firstPageInfo: false,
streamTypesUsed: [],
fontTypesUsed: [],
startAt: Date.now()
};
}
@ -373,18 +374,39 @@ ChromeActions.prototype = {
this.telemetryState.firstPageInfo = true;
}
break;
case 'streamInfo':
if (!Array.isArray(probeInfo.streamTypes)) {
case 'documentStats':
// documentStats can be called several times for one documents.
// if stream/font types are reported, trying not to submit the same
// enumeration value multiple times.
var documentStats = probeInfo.stats;
if (!documentStats || typeof documentStats !== 'object') {
break;
}
for (var i = 0; i < probeInfo.streamTypes.length; i++) {
var streamTypeId = probeInfo.streamTypes[i] | 0;
if (streamTypeId >= 0 && streamTypeId < 10 &&
!this.telemetryState.streamTypesUsed[streamTypeId]) {
PdfJsTelemetry.onStreamType(streamTypeId);
this.telemetryState.streamTypesUsed[streamTypeId] = true;
var streamTypes = documentStats.streamTypes;
if (Array.isArray(streamTypes)) {
var STREAM_TYPE_ID_LIMIT = 20;
for (var i = 0; i < STREAM_TYPE_ID_LIMIT; i++) {
if (streamTypes[i] &&
!this.telemetryState.streamTypesUsed[i]) {
PdfJsTelemetry.onStreamType(i);
this.telemetryState.streamTypesUsed[i] = true;
}
}
}
var fontTypes = documentStats.fontTypes;
if (Array.isArray(fontTypes)) {
var FONT_TYPE_ID_LIMIT = 20;
for (var i = 0; i < FONT_TYPE_ID_LIMIT; i++) {
if (fontTypes[i] &&
!this.telemetryState.fontTypesUsed[i]) {
PdfJsTelemetry.onFontType(i);
this.telemetryState.fontTypesUsed[i] = true;
}
}
}
break;
case 'print':
PdfJsTelemetry.onPrint();
break;
}
},
@ -942,6 +964,12 @@ PdfStreamConverter.prototype = {
findEventManager.bind();
}
listener.onStopRequest(aRequest, context, statusCode);
if (domWindow.frameElement) {
var isObjectEmbed = domWindow.frameElement.tagName !== 'IFRAME' ||
domWindow.frameElement.className === 'previewPluginContentFrame';
PdfJsTelemetry.onEmbed(isObjectEmbed);
}
}
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -71,72 +71,72 @@ http://sourceforge.net/adobe/cmap/wiki/License/
<div id="mainContainer">
<div class="findbar hidden doorHanger hiddenSmallView" id="findbar">
<label for="findInput" class="toolbarLabel" data-l10n-id="find_label">Find:</label>
<input id="findInput" class="toolbarField" tabindex="41">
<input id="findInput" class="toolbarField" tabindex="91">
<div class="splitToolbarButton">
<button class="toolbarButton findPrevious" title="" id="findPrevious" tabindex="42" data-l10n-id="find_previous">
<button class="toolbarButton findPrevious" title="" id="findPrevious" tabindex="92" data-l10n-id="find_previous">
<span data-l10n-id="find_previous_label">Previous</span>
</button>
<div class="splitToolbarButtonSeparator"></div>
<button class="toolbarButton findNext" title="" id="findNext" tabindex="43" data-l10n-id="find_next">
<button class="toolbarButton findNext" title="" id="findNext" tabindex="93" data-l10n-id="find_next">
<span data-l10n-id="find_next_label">Next</span>
</button>
</div>
<input type="checkbox" id="findHighlightAll" class="toolbarField">
<label for="findHighlightAll" class="toolbarLabel" tabindex="44" data-l10n-id="find_highlight">Highlight all</label>
<label for="findHighlightAll" class="toolbarLabel" tabindex="94" data-l10n-id="find_highlight">Highlight all</label>
<input type="checkbox" id="findMatchCase" class="toolbarField">
<label for="findMatchCase" class="toolbarLabel" tabindex="45" data-l10n-id="find_match_case_label">Match case</label>
<label for="findMatchCase" class="toolbarLabel" tabindex="95" data-l10n-id="find_match_case_label">Match case</label>
<span id="findMsg" class="toolbarLabel"></span>
</div> <!-- findbar -->
<div id="secondaryToolbar" class="secondaryToolbar hidden doorHangerRight">
<div id="secondaryToolbarButtonContainer">
<button id="secondaryPresentationMode" class="secondaryToolbarButton presentationMode visibleLargeView" title="Switch to Presentation Mode" tabindex="19" data-l10n-id="presentation_mode">
<button id="secondaryPresentationMode" class="secondaryToolbarButton presentationMode visibleLargeView" title="Switch to Presentation Mode" tabindex="51" data-l10n-id="presentation_mode">
<span data-l10n-id="presentation_mode_label">Presentation Mode</span>
</button>
<button id="secondaryOpenFile" class="secondaryToolbarButton openFile visibleLargeView" title="Open File" tabindex="20" data-l10n-id="open_file">
<button id="secondaryOpenFile" class="secondaryToolbarButton openFile visibleLargeView" title="Open File" tabindex="52" data-l10n-id="open_file">
<span data-l10n-id="open_file_label">Open</span>
</button>
<button id="secondaryPrint" class="secondaryToolbarButton print visibleMediumView" title="Print" tabindex="21" data-l10n-id="print">
<button id="secondaryPrint" class="secondaryToolbarButton print visibleMediumView" title="Print" tabindex="53" data-l10n-id="print">
<span data-l10n-id="print_label">Print</span>
</button>
<button id="secondaryDownload" class="secondaryToolbarButton download visibleMediumView" title="Download" tabindex="22" data-l10n-id="download">
<button id="secondaryDownload" class="secondaryToolbarButton download visibleMediumView" title="Download" tabindex="54" data-l10n-id="download">
<span data-l10n-id="download_label">Download</span>
</button>
<a href="#" id="secondaryViewBookmark" class="secondaryToolbarButton bookmark visibleSmallView" title="Current view (copy or open in new window)" tabindex="23" data-l10n-id="bookmark">
<a href="#" id="secondaryViewBookmark" class="secondaryToolbarButton bookmark visibleSmallView" title="Current view (copy or open in new window)" tabindex="55" data-l10n-id="bookmark">
<span data-l10n-id="bookmark_label">Current View</span>
</a>
<div class="horizontalToolbarSeparator visibleLargeView"></div>
<button id="firstPage" class="secondaryToolbarButton firstPage" title="Go to First Page" tabindex="24" data-l10n-id="first_page">
<button id="firstPage" class="secondaryToolbarButton firstPage" title="Go to First Page" tabindex="56" data-l10n-id="first_page">
<span data-l10n-id="first_page_label">Go to First Page</span>
</button>
<button id="lastPage" class="secondaryToolbarButton lastPage" title="Go to Last Page" tabindex="25" data-l10n-id="last_page">
<button id="lastPage" class="secondaryToolbarButton lastPage" title="Go to Last Page" tabindex="57" data-l10n-id="last_page">
<span data-l10n-id="last_page_label">Go to Last Page</span>
</button>
<div class="horizontalToolbarSeparator"></div>
<button id="pageRotateCw" class="secondaryToolbarButton rotateCw" title="Rotate Clockwise" tabindex="26" data-l10n-id="page_rotate_cw">
<button id="pageRotateCw" class="secondaryToolbarButton rotateCw" title="Rotate Clockwise" tabindex="58" data-l10n-id="page_rotate_cw">
<span data-l10n-id="page_rotate_cw_label">Rotate Clockwise</span>
</button>
<button id="pageRotateCcw" class="secondaryToolbarButton rotateCcw" title="Rotate Counterclockwise" tabindex="27" data-l10n-id="page_rotate_ccw">
<button id="pageRotateCcw" class="secondaryToolbarButton rotateCcw" title="Rotate Counterclockwise" tabindex="59" data-l10n-id="page_rotate_ccw">
<span data-l10n-id="page_rotate_ccw_label">Rotate Counterclockwise</span>
</button>
<div class="horizontalToolbarSeparator"></div>
<button id="toggleHandTool" class="secondaryToolbarButton handTool" title="Enable hand tool" tabindex="28" data-l10n-id="hand_tool_enable">
<button id="toggleHandTool" class="secondaryToolbarButton handTool" title="Enable hand tool" tabindex="60" data-l10n-id="hand_tool_enable">
<span data-l10n-id="hand_tool_enable_label">Enable hand tool</span>
</button>
<div class="horizontalToolbarSeparator"></div>
<button id="documentProperties" class="secondaryToolbarButton documentProperties" title="Document Properties…" tabindex="29" data-l10n-id="document_properties">
<button id="documentProperties" class="secondaryToolbarButton documentProperties" title="Document Properties…" tabindex="61" data-l10n-id="document_properties">
<span data-l10n-id="document_properties_label">Document Properties…</span>
</button>
</div>
@ -146,66 +146,66 @@ http://sourceforge.net/adobe/cmap/wiki/License/
<div id="toolbarContainer">
<div id="toolbarViewer">
<div id="toolbarViewerLeft">
<button id="sidebarToggle" class="toolbarButton" title="Toggle Sidebar" tabindex="5" data-l10n-id="toggle_sidebar">
<button id="sidebarToggle" class="toolbarButton" title="Toggle Sidebar" tabindex="11" data-l10n-id="toggle_sidebar">
<span data-l10n-id="toggle_sidebar_label">Toggle Sidebar</span>
</button>
<div class="toolbarButtonSpacer"></div>
<button id="viewFind" class="toolbarButton group hiddenSmallView" title="Find in Document" tabindex="6" data-l10n-id="findbar">
<button id="viewFind" class="toolbarButton group hiddenSmallView" title="Find in Document" tabindex="12" data-l10n-id="findbar">
<span data-l10n-id="findbar_label">Find</span>
</button>
<div class="splitToolbarButton">
<button class="toolbarButton pageUp" title="Previous Page" id="previous" tabindex="7" data-l10n-id="previous">
<button class="toolbarButton pageUp" title="Previous Page" id="previous" tabindex="13" data-l10n-id="previous">
<span data-l10n-id="previous_label">Previous</span>
</button>
<div class="splitToolbarButtonSeparator"></div>
<button class="toolbarButton pageDown" title="Next Page" id="next" tabindex="8" data-l10n-id="next">
<button class="toolbarButton pageDown" title="Next Page" id="next" tabindex="14" data-l10n-id="next">
<span data-l10n-id="next_label">Next</span>
</button>
</div>
<label id="pageNumberLabel" class="toolbarLabel" for="pageNumber" data-l10n-id="page_label">Page: </label>
<input type="number" id="pageNumber" class="toolbarField pageNumber" value="1" size="4" min="1" tabindex="9">
<input type="number" id="pageNumber" class="toolbarField pageNumber" value="1" size="4" min="1" tabindex="15">
<span id="numPages" class="toolbarLabel"></span>
</div>
<div id="toolbarViewerRight">
<button id="presentationMode" class="toolbarButton presentationMode hiddenLargeView" title="Switch to Presentation Mode" tabindex="13" data-l10n-id="presentation_mode">
<button id="presentationMode" class="toolbarButton presentationMode hiddenLargeView" title="Switch to Presentation Mode" tabindex="31" data-l10n-id="presentation_mode">
<span data-l10n-id="presentation_mode_label">Presentation Mode</span>
</button>
<button id="openFile" class="toolbarButton openFile hiddenLargeView" title="Open File" tabindex="14" data-l10n-id="open_file">
<button id="openFile" class="toolbarButton openFile hiddenLargeView" title="Open File" tabindex="32" data-l10n-id="open_file">
<span data-l10n-id="open_file_label">Open</span>
</button>
<button id="print" class="toolbarButton print hiddenMediumView" title="Print" tabindex="15" data-l10n-id="print">
<button id="print" class="toolbarButton print hiddenMediumView" title="Print" tabindex="33" data-l10n-id="print">
<span data-l10n-id="print_label">Print</span>
</button>
<button id="download" class="toolbarButton download hiddenMediumView" title="Download" tabindex="16" data-l10n-id="download">
<button id="download" class="toolbarButton download hiddenMediumView" title="Download" tabindex="34" data-l10n-id="download">
<span data-l10n-id="download_label">Download</span>
</button>
<!-- <div class="toolbarButtonSpacer"></div> -->
<a href="#" id="viewBookmark" class="toolbarButton bookmark hiddenSmallView" title="Current view (copy or open in new window)" tabindex="17" data-l10n-id="bookmark">
<a href="#" id="viewBookmark" class="toolbarButton bookmark hiddenSmallView" title="Current view (copy or open in new window)" tabindex="35" data-l10n-id="bookmark">
<span data-l10n-id="bookmark_label">Current View</span>
</a>
<div class="verticalToolbarSeparator hiddenSmallView"></div>
<button id="secondaryToolbarToggle" class="toolbarButton" title="Tools" tabindex="18" data-l10n-id="tools">
<button id="secondaryToolbarToggle" class="toolbarButton" title="Tools" tabindex="36" data-l10n-id="tools">
<span data-l10n-id="tools_label">Tools</span>
</button>
</div>
<div class="outerCenter">
<div class="innerCenter" id="toolbarViewerMiddle">
<div class="splitToolbarButton">
<button id="zoomOut" class="toolbarButton zoomOut" title="Zoom Out" tabindex="10" data-l10n-id="zoom_out">
<button id="zoomOut" class="toolbarButton zoomOut" title="Zoom Out" tabindex="21" data-l10n-id="zoom_out">
<span data-l10n-id="zoom_out_label">Zoom Out</span>
</button>
<div class="splitToolbarButtonSeparator"></div>
<button id="zoomIn" class="toolbarButton zoomIn" title="Zoom In" tabindex="11" data-l10n-id="zoom_in">
<button id="zoomIn" class="toolbarButton zoomIn" title="Zoom In" tabindex="22" data-l10n-id="zoom_in">
<span data-l10n-id="zoom_in_label">Zoom In</span>
</button>
</div>
<span id="scaleSelectContainer" class="dropdownToolbarButton">
<select id="scaleSelect" title="Zoom" tabindex="12" data-l10n-id="zoom">
<select id="scaleSelect" title="Zoom" tabindex="23" data-l10n-id="zoom">
<option id="pageAutoOption" title="" value="auto" selected="selected" data-l10n-id="page_scale_auto">Automatic Zoom</option>
<option id="pageActualOption" title="" value="page-actual" data-l10n-id="page_scale_actual">Actual Size</option>
<option id="pageFitOption" title="" value="page-fit" data-l10n-id="page_scale_fit">Fit Page</option>

View File

@ -28,7 +28,7 @@ var DEFAULT_URL = 'compressed.tracemonkey-pldi-09.pdf';
var DEFAULT_SCALE = 'auto';
var DEFAULT_SCALE_DELTA = 1.1;
var UNKNOWN_SCALE = 0;
var CACHE_SIZE = 10;
var DEFAULT_CACHE_SIZE = 10;
var CSS_UNITS = 96.0 / 72.0;
var SCROLLBAR_PADDING = 40;
var VERTICAL_PADDING = 5;
@ -301,6 +301,12 @@ var Cache = function cacheCache(size) {
data.shift().destroy();
}
};
this.resize = function (newSize) {
size = newSize;
while (data.length > size) {
data.shift().destroy();
}
};
};
@ -585,7 +591,7 @@ Preferences._readFromStorage = function (prefObj) {
var cache = new Cache(CACHE_SIZE);
var cache = new Cache(DEFAULT_CACHE_SIZE);
var currentPageNumber = 1;
@ -844,45 +850,41 @@ var PDFFindBar = {
var PDFFindController = {
startedTextExtraction: false,
extractTextPromises: [],
pendingFindMatches: {},
// If active, find results will be highlighted.
active: false,
// Stores the text for each page.
pageContents: [],
active: false, // If active, find results will be highlighted.
pageContents: [], // Stores the text for each page.
pageMatches: [],
// Currently selected match.
selected: {
selected: { // Currently selected match.
pageIdx: -1,
matchIdx: -1
},
// Where find algorithm currently is in the document.
offset: {
offset: { // Where the find algorithm currently is in the document.
pageIdx: null,
matchIdx: null
},
resumePageIdx: null,
state: null,
dirtyMatch: false,
findTimeout: null,
pdfPageSource: null,
integratedFind: false,
charactersToNormalize: {
'\u2018': '\'', // Left single quotation mark
'\u2019': '\'', // Right single quotation mark
'\u201A': '\'', // Single low-9 quotation mark
'\u201B': '\'', // Single high-reversed-9 quotation mark
'\u201C': '"', // Left double quotation mark
'\u201D': '"', // Right double quotation mark
'\u201E': '"', // Double low-9 quotation mark
'\u201F': '"', // Double high-reversed-9 quotation mark
'\u00BC': '1/4', // Vulgar fraction one quarter
'\u00BD': '1/2', // Vulgar fraction one half
'\u00BE': '3/4' // Vulgar fraction three quarters
},
initialize: function(options) {
if(typeof PDFFindBar === 'undefined' || PDFFindBar === null) {
if (typeof PDFFindBar === 'undefined' || PDFFindBar === null) {
throw 'PDFFindController cannot be initialized ' +
'without a PDFFindBar instance';
}
@ -890,6 +892,10 @@ var PDFFindController = {
this.pdfPageSource = options.pdfPageSource;
this.integratedFind = options.integratedFind;
// Compile the regular expression for text normalization once
var replace = Object.keys(this.charactersToNormalize).join('');
this.normalizationRegex = new RegExp('[' + replace + ']', 'g');
var events = [
'find',
'findagain',
@ -902,7 +908,7 @@ var PDFFindController = {
}.bind(this));
this.handleEvent = this.handleEvent.bind(this);
for (var i = 0; i < events.length; i++) {
for (var i = 0, len = events.length; i < len; i++) {
window.addEventListener(events[i], this.handleEvent);
}
},
@ -913,14 +919,20 @@ var PDFFindController = {
this.active = false;
},
normalize: function pdfFindControllerNormalize(text) {
return text.replace(this.normalizationRegex, function (ch) {
return PDFFindController.charactersToNormalize[ch];
});
},
calcFindMatch: function(pageIndex) {
var pageContent = this.pageContents[pageIndex];
var query = this.state.query;
var pageContent = this.normalize(this.pageContents[pageIndex]);
var query = this.normalize(this.state.query);
var caseSensitive = this.state.caseSensitive;
var queryLen = query.length;
if (queryLen === 0) {
// Do nothing the matches should be wiped out already.
// Do nothing: the matches should be wiped out already.
return;
}
@ -930,14 +942,12 @@ var PDFFindController = {
}
var matches = [];
var matchIdx = -queryLen;
while (true) {
matchIdx = pageContent.indexOf(query, matchIdx + queryLen);
if (matchIdx === -1) {
break;
}
matches.push(matchIdx);
}
this.pageMatches[pageIndex] = matches;
@ -956,7 +966,8 @@ var PDFFindController = {
this.pageContents = [];
var extractTextPromisesResolves = [];
for (var i = 0, ii = this.pdfPageSource.pdfDocument.numPages; i < ii; i++) {
var numPages = this.pdfPageSource.pdfDocument.numPages;
for (var i = 0; i < numPages; i++) {
this.extractTextPromises.push(new Promise(function (resolve) {
extractTextPromisesResolves.push(resolve);
}));
@ -967,14 +978,14 @@ var PDFFindController = {
self.pdfPageSource.pages[pageIndex].getTextContent().then(
function textContentResolved(textContent) {
var textItems = textContent.items;
var str = '';
var str = [];
for (var i = 0; i < textItems.length; i++) {
str += textItems[i].str;
for (var i = 0, len = textItems.length; i < len; i++) {
str.push(textItems[i].str);
}
// Store the pageContent as a string.
self.pageContents.push(str);
self.pageContents.push(str.join(''));
extractTextPromisesResolves[pageIndex](pageIndex);
if ((pageIndex + 1) < self.pdfPageSource.pages.length) {
@ -1072,15 +1083,16 @@ var PDFFindController = {
var numPageMatches = this.pageMatches[offset.pageIdx].length;
if ((!previous && offset.matchIdx + 1 < numPageMatches) ||
(previous && offset.matchIdx > 0)) {
// The simple case, we just have advance the matchIdx to select the next
// match on the page.
// The simple case; we just have advance the matchIdx to select
// the next match on the page.
this.hadMatch = true;
offset.matchIdx = previous ? offset.matchIdx - 1 : offset.matchIdx + 1;
offset.matchIdx = (previous ? offset.matchIdx - 1 :
offset.matchIdx + 1);
this.updateMatch(true);
return;
}
// We went beyond the current page's matches, so we advance to the next
// page.
// We went beyond the current page's matches, so we advance to
// the next page.
this.advanceOffsetPage(previous);
}
// Start searching through the page.
@ -1094,24 +1106,23 @@ var PDFFindController = {
if (numMatches) {
// There were matches for the page, so initialize the matchIdx.
this.hadMatch = true;
offset.matchIdx = previous ? numMatches - 1 : 0;
offset.matchIdx = (previous ? numMatches - 1 : 0);
this.updateMatch(true);
// matches were found
return true;
} else {
// No matches attempt to search the next page.
// No matches, so attempt to search the next page.
this.advanceOffsetPage(previous);
if (offset.wrapped) {
offset.matchIdx = null;
if (!this.hadMatch) {
// No point in wrapping there were no matches.
// No point in wrapping, there were no matches.
this.updateMatch(false);
// while matches were not found, searching for a page
// with matches should nevertheless halt.
return true;
}
}
// matches were not found (and searching is not done)
// Matches were not found (and searching is not done).
return false;
}
},
@ -1135,10 +1146,10 @@ var PDFFindController = {
advanceOffsetPage: function(previous) {
var offset = this.offset;
var numPages = this.extractTextPromises.length;
offset.pageIdx = previous ? offset.pageIdx - 1 : offset.pageIdx + 1;
offset.pageIdx = (previous ? offset.pageIdx - 1 : offset.pageIdx + 1);
offset.matchIdx = null;
if (offset.pageIdx >= numPages || offset.pageIdx < 0) {
offset.pageIdx = previous ? numPages - 1 : 0;
offset.pageIdx = (previous ? numPages - 1 : 0);
offset.wrapped = true;
return;
}
@ -1152,7 +1163,7 @@ var PDFFindController = {
var previousPage = this.selected.pageIdx;
this.selected.pageIdx = this.offset.pageIdx;
this.selected.matchIdx = this.offset.matchIdx;
state = wrapped ? FindStates.FIND_WRAPPED : FindStates.FIND_FOUND;
state = (wrapped ? FindStates.FIND_WRAPPED : FindStates.FIND_FOUND);
// Update the currently selected page to wipe out any selected matches.
if (previousPage !== -1 && previousPage !== this.selected.pageIdx) {
this.updatePage(previousPage);
@ -1167,7 +1178,7 @@ var PDFFindController = {
updateUIState: function(state, previous) {
if (this.integratedFind) {
FirefoxCom.request('updateFindControlState',
{result: state, findPrevious: previous});
{ result: state, findPrevious: previous });
return;
}
PDFFindBar.updateUIState(state, previous);
@ -2532,6 +2543,7 @@ var PDFView = {
fellback: false,
pdfDocument: null,
sidebarOpen: false,
printing: false,
pageViewScroll: null,
thumbnailViewScroll: null,
pageRotation: 0,
@ -3599,6 +3611,11 @@ var PDFView = {
}
}
if (this.printing) {
// If printing is currently ongoing do not reschedule cleanup.
return;
}
PDFView.idleTimeout = setTimeout(function () {
PDFView.cleanup();
}, CLEANUP_TIMEOUT);
@ -3908,11 +3925,18 @@ var PDFView = {
return;
}
this.printing = true;
this.renderHighestPriority();
var body = document.querySelector('body');
body.setAttribute('data-mozPrintCallback', true);
for (i = 0, ii = this.pages.length; i < ii; ++i) {
this.pages[i].beforePrint();
}
FirefoxCom.request('reportTelemetry', JSON.stringify({
type: 'print'
}));
},
afterPrint: function pdfViewSetupAfterPrint() {
@ -3920,6 +3944,9 @@ var PDFView = {
while (div.hasChildNodes()) {
div.removeChild(div.lastChild);
}
this.printing = false;
this.renderHighestPriority();
},
rotatePages: function pdfViewRotatePages(delta) {
@ -4317,16 +4344,15 @@ var PageView = function pageView(container, id, scale,
} else {
for (i = 0, ii = annotationsData.length; i < ii; i++) {
data = annotationsData[i];
var annotation = PDFJS.Annotation.fromData(data);
if (!annotation || !annotation.hasHtml()) {
if (!data || !data.hasHtml) {
continue;
}
element = annotation.getHtmlElement(pdfPage.commonObjs);
element = PDFJS.AnnotationUtils.getHtmlElement(data,
pdfPage.commonObjs);
element.setAttribute('data-annotation-id', data.id);
mozL10n.translate(element);
data = annotation.getData();
var rect = data.rect;
var view = pdfPage.view;
rect = PDFJS.Util.normalizeRect([
@ -4622,7 +4648,13 @@ var PageView = function pageView(container, id, scale,
FirefoxCom.request('reportTelemetry', JSON.stringify({
type: 'pageInfo'
}));
// TODO add stream types report here
// It is a good time to report stream and font types
PDFView.pdfDocument.getStats().then(function (stats) {
FirefoxCom.request('reportTelemetry', JSON.stringify({
type: 'documentStats',
stats: stats
}));
});
callback();
}
@ -4955,335 +4987,311 @@ ThumbnailView.tempImageCache = null;
var FIND_SCROLL_OFFSET_TOP = -50;
var FIND_SCROLL_OFFSET_LEFT = -400;
var MAX_TEXT_DIVS_TO_RENDER = 100000;
var RENDER_DELAY = 200; // ms
/**
* TextLayerBuilder provides text-selection
* functionality for the PDF. It does this
* by creating overlay divs over the PDF
* text. This divs contain text that matches
* the PDF text they are overlaying. This
* object also provides for a way to highlight
* text that is being searched for.
* TextLayerBuilder provides text-selection functionality for the PDF.
* It does this by creating overlay divs over the PDF text. These divs
* contain text that matches the PDF text they are overlaying. This object
* also provides a way to highlight text that is being searched for.
*/
var TextLayerBuilder = function textLayerBuilder(options) {
var textLayerFrag = document.createDocumentFragment();
this.textLayerDiv = options.textLayerDiv;
this.layoutDone = false;
this.divContentDone = false;
this.pageIdx = options.pageIndex;
this.matches = [];
this.lastScrollSource = options.lastScrollSource;
this.viewport = options.viewport;
this.isViewerInPresentationMode = options.isViewerInPresentationMode;
this.textDivs = [];
if (typeof PDFFindController === 'undefined') {
window.PDFFindController = null;
var TextLayerBuilder = (function TextLayerBuilderClosure() {
function TextLayerBuilder(options) {
this.textLayerDiv = options.textLayerDiv;
this.layoutDone = false;
this.divContentDone = false;
this.pageIdx = options.pageIndex;
this.matches = [];
this.lastScrollSource = options.lastScrollSource || null;
this.viewport = options.viewport;
this.isViewerInPresentationMode = options.isViewerInPresentationMode;
this.textDivs = [];
this.findController = window.PDFFindController || null;
}
if (typeof this.lastScrollSource === 'undefined') {
this.lastScrollSource = null;
}
TextLayerBuilder.prototype = {
renderLayer: function TextLayerBuilder_renderLayer() {
var textLayerFrag = document.createDocumentFragment();
var textDivs = this.textDivs;
var textDivsLength = textDivs.length;
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
this.renderLayer = function textLayerBuilderRenderLayer() {
var textDivs = this.textDivs;
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
// No point in rendering so many divs as it'd make the browser unusable
// even after the divs are rendered
var MAX_TEXT_DIVS_TO_RENDER = 100000;
if (textDivs.length > MAX_TEXT_DIVS_TO_RENDER) {
return;
}
for (var i = 0, ii = textDivs.length; i < ii; i++) {
var textDiv = textDivs[i];
if ('isWhitespace' in textDiv.dataset) {
continue;
}
ctx.font = textDiv.style.fontSize + ' ' + textDiv.style.fontFamily;
var width = ctx.measureText(textDiv.textContent).width;
if (width > 0) {
textLayerFrag.appendChild(textDiv);
var textScale = textDiv.dataset.canvasWidth / width;
var rotation = textDiv.dataset.angle;
var transform = 'scale(' + textScale + ', 1)';
transform = 'rotate(' + rotation + 'deg) ' + transform;
CustomStyle.setProp('transform' , textDiv, transform);
CustomStyle.setProp('transformOrigin' , textDiv, '0% 0%');
}
}
this.textLayerDiv.appendChild(textLayerFrag);
this.renderingDone = true;
this.updateMatches();
};
this.setupRenderLayoutTimer = function textLayerSetupRenderLayoutTimer() {
// Schedule renderLayout() if user has been scrolling, otherwise
// run it right away
var RENDER_DELAY = 200; // in ms
var self = this;
var lastScroll = (this.lastScrollSource === null ?
0 : this.lastScrollSource.lastScroll);
if (Date.now() - lastScroll > RENDER_DELAY) {
// Render right away
this.renderLayer();
} else {
// Schedule
if (this.renderTimer) {
clearTimeout(this.renderTimer);
}
this.renderTimer = setTimeout(function() {
self.setupRenderLayoutTimer();
}, RENDER_DELAY);
}
};
this.appendText = function textLayerBuilderAppendText(geom, styles) {
var style = styles[geom.fontName];
var textDiv = document.createElement('div');
this.textDivs.push(textDiv);
if (!/\S/.test(geom.str)) {
textDiv.dataset.isWhitespace = true;
return;
}
var tx = PDFJS.Util.transform(this.viewport.transform, geom.transform);
var angle = Math.atan2(tx[1], tx[0]);
if (style.vertical) {
angle += Math.PI / 2;
}
var fontHeight = Math.sqrt((tx[2] * tx[2]) + (tx[3] * tx[3]));
var fontAscent = (style.ascent ? style.ascent * fontHeight :
(style.descent ? (1 + style.descent) * fontHeight : fontHeight));
textDiv.style.position = 'absolute';
textDiv.style.left = (tx[4] + (fontAscent * Math.sin(angle))) + 'px';
textDiv.style.top = (tx[5] - (fontAscent * Math.cos(angle))) + 'px';
textDiv.style.fontSize = fontHeight + 'px';
textDiv.style.fontFamily = style.fontFamily;
textDiv.textContent = geom.str;
textDiv.dataset.fontName = geom.fontName;
textDiv.dataset.angle = angle * (180 / Math.PI);
if (style.vertical) {
textDiv.dataset.canvasWidth = geom.height * this.viewport.scale;
} else {
textDiv.dataset.canvasWidth = geom.width * this.viewport.scale;
}
};
this.setTextContent = function textLayerBuilderSetTextContent(textContent) {
this.textContent = textContent;
var textItems = textContent.items;
for (var i = 0; i < textItems.length; i++) {
this.appendText(textItems[i], textContent.styles);
}
this.divContentDone = true;
this.setupRenderLayoutTimer();
};
this.convertMatches = function textLayerBuilderConvertMatches(matches) {
var i = 0;
var iIndex = 0;
var bidiTexts = this.textContent.items;
var end = bidiTexts.length - 1;
var queryLen = (PDFFindController === null ?
0 : PDFFindController.state.query.length);
var ret = [];
// Loop over all the matches.
for (var m = 0; m < matches.length; m++) {
var matchIdx = matches[m];
// # Calculate the begin position.
// Loop over the divIdxs.
while (i !== end && matchIdx >= (iIndex + bidiTexts[i].str.length)) {
iIndex += bidiTexts[i].str.length;
i++;
}
// TODO: Do proper handling here if something goes wrong.
if (i == bidiTexts.length) {
console.error('Could not find matching mapping');
}
var match = {
begin: {
divIdx: i,
offset: matchIdx - iIndex
}
};
// # Calculate the end position.
matchIdx += queryLen;
// Somewhat same array as above, but use a > instead of >= to get the end
// position right.
while (i !== end && matchIdx > (iIndex + bidiTexts[i].str.length)) {
iIndex += bidiTexts[i].str.length;
i++;
}
match.end = {
divIdx: i,
offset: matchIdx - iIndex
};
ret.push(match);
}
return ret;
};
this.renderMatches = function textLayerBuilder_renderMatches(matches) {
// Early exit if there is nothing to render.
if (matches.length === 0) {
return;
}
var bidiTexts = this.textContent.items;
var textDivs = this.textDivs;
var prevEnd = null;
var isSelectedPage = (PDFFindController === null ?
false : (this.pageIdx === PDFFindController.selected.pageIdx));
var selectedMatchIdx = (PDFFindController === null ?
-1 : PDFFindController.selected.matchIdx);
var highlightAll = (PDFFindController === null ?
false : PDFFindController.state.highlightAll);
var infty = {
divIdx: -1,
offset: undefined
};
function beginText(begin, className) {
var divIdx = begin.divIdx;
var div = textDivs[divIdx];
div.textContent = '';
appendTextToDiv(divIdx, 0, begin.offset, className);
}
function appendText(from, to, className) {
appendTextToDiv(from.divIdx, from.offset, to.offset, className);
}
function appendTextToDiv(divIdx, fromOffset, toOffset, className) {
var div = textDivs[divIdx];
var content = bidiTexts[divIdx].str.substring(fromOffset, toOffset);
var node = document.createTextNode(content);
if (className) {
var span = document.createElement('span');
span.className = className;
span.appendChild(node);
div.appendChild(span);
// No point in rendering many divs as it would make the browser
// unusable even after the divs are rendered.
if (textDivsLength > MAX_TEXT_DIVS_TO_RENDER) {
return;
}
div.appendChild(node);
}
function highlightDiv(divIdx, className) {
textDivs[divIdx].className = className;
}
var i0 = selectedMatchIdx, i1 = i0 + 1, i;
if (highlightAll) {
i0 = 0;
i1 = matches.length;
} else if (!isSelectedPage) {
// Not highlighting all and this isn't the selected page, so do nothing.
return;
}
for (i = i0; i < i1; i++) {
var match = matches[i];
var begin = match.begin;
var end = match.end;
var isSelected = isSelectedPage && i === selectedMatchIdx;
var highlightSuffix = (isSelected ? ' selected' : '');
if (isSelected && !this.isViewerInPresentationMode) {
scrollIntoView(textDivs[begin.divIdx], { top: FIND_SCROLL_OFFSET_TOP,
left: FIND_SCROLL_OFFSET_LEFT });
}
// Match inside new div.
if (!prevEnd || begin.divIdx !== prevEnd.divIdx) {
// If there was a previous div, then add the text at the end
if (prevEnd !== null) {
appendText(prevEnd, infty);
for (var i = 0; i < textDivsLength; i++) {
var textDiv = textDivs[i];
if (textDiv.dataset.isWhitespace !== undefined) {
continue;
}
// clears the divs and set the content until the begin point.
beginText(begin);
} else {
appendText(prevEnd, begin);
}
if (begin.divIdx === end.divIdx) {
appendText(begin, end, 'highlight' + highlightSuffix);
} else {
appendText(begin, infty, 'highlight begin' + highlightSuffix);
for (var n = begin.divIdx + 1; n < end.divIdx; n++) {
highlightDiv(n, 'highlight middle' + highlightSuffix);
ctx.font = textDiv.style.fontSize + ' ' + textDiv.style.fontFamily;
var width = ctx.measureText(textDiv.textContent).width;
if (width > 0) {
textLayerFrag.appendChild(textDiv);
var textScale = textDiv.dataset.canvasWidth / width;
var rotation = textDiv.dataset.angle;
var transform = 'scale(' + textScale + ', 1)';
transform = 'rotate(' + rotation + 'deg) ' + transform;
CustomStyle.setProp('transform' , textDiv, transform);
CustomStyle.setProp('transformOrigin' , textDiv, '0% 0%');
}
beginText(end, 'highlight end' + highlightSuffix);
}
prevEnd = end;
}
if (prevEnd) {
appendText(prevEnd, infty);
this.textLayerDiv.appendChild(textLayerFrag);
this.renderingDone = true;
this.updateMatches();
},
setupRenderLayoutTimer:
function TextLayerBuilder_setupRenderLayoutTimer() {
// Schedule renderLayout() if the user has been scrolling,
// otherwise run it right away.
var self = this;
var lastScroll = (this.lastScrollSource === null ?
0 : this.lastScrollSource.lastScroll);
if (Date.now() - lastScroll > RENDER_DELAY) { // Render right away
this.renderLayer();
} else { // Schedule
if (this.renderTimer) {
clearTimeout(this.renderTimer);
}
this.renderTimer = setTimeout(function() {
self.setupRenderLayoutTimer();
}, RENDER_DELAY);
}
},
appendText: function TextLayerBuilder_appendText(geom, styles) {
var style = styles[geom.fontName];
var textDiv = document.createElement('div');
this.textDivs.push(textDiv);
if (!/\S/.test(geom.str)) {
textDiv.dataset.isWhitespace = true;
return;
}
var tx = PDFJS.Util.transform(this.viewport.transform, geom.transform);
var angle = Math.atan2(tx[1], tx[0]);
if (style.vertical) {
angle += Math.PI / 2;
}
var fontHeight = Math.sqrt((tx[2] * tx[2]) + (tx[3] * tx[3]));
var fontAscent = (style.ascent ? style.ascent * fontHeight :
(style.descent ? (1 + style.descent) * fontHeight : fontHeight));
textDiv.style.position = 'absolute';
textDiv.style.left = (tx[4] + (fontAscent * Math.sin(angle))) + 'px';
textDiv.style.top = (tx[5] - (fontAscent * Math.cos(angle))) + 'px';
textDiv.style.fontSize = fontHeight + 'px';
textDiv.style.fontFamily = style.fontFamily;
textDiv.textContent = geom.str;
textDiv.dataset.fontName = geom.fontName;
textDiv.dataset.angle = angle * (180 / Math.PI);
if (style.vertical) {
textDiv.dataset.canvasWidth = geom.height * this.viewport.scale;
} else {
textDiv.dataset.canvasWidth = geom.width * this.viewport.scale;
}
},
setTextContent: function TextLayerBuilder_setTextContent(textContent) {
this.textContent = textContent;
var textItems = textContent.items;
for (var i = 0, len = textItems.length; i < len; i++) {
this.appendText(textItems[i], textContent.styles);
}
this.divContentDone = true;
this.setupRenderLayoutTimer();
},
convertMatches: function TextLayerBuilder_convertMatches(matches) {
var i = 0;
var iIndex = 0;
var bidiTexts = this.textContent.items;
var end = bidiTexts.length - 1;
var queryLen = (this.findController === null ?
0 : this.findController.state.query.length);
var ret = [];
for (var m = 0, len = matches.length; m < len; m++) {
// Calculate the start position.
var matchIdx = matches[m];
// Loop over the divIdxs.
while (i !== end && matchIdx >= (iIndex + bidiTexts[i].str.length)) {
iIndex += bidiTexts[i].str.length;
i++;
}
if (i === bidiTexts.length) {
console.error('Could not find a matching mapping');
}
var match = {
begin: {
divIdx: i,
offset: matchIdx - iIndex
}
};
// Calculate the end position.
matchIdx += queryLen;
// Somewhat the same array as above, but use > instead of >= to get
// the end position right.
while (i !== end && matchIdx > (iIndex + bidiTexts[i].str.length)) {
iIndex += bidiTexts[i].str.length;
i++;
}
match.end = {
divIdx: i,
offset: matchIdx - iIndex
};
ret.push(match);
}
return ret;
},
renderMatches: function TextLayerBuilder_renderMatches(matches) {
// Early exit if there is nothing to render.
if (matches.length === 0) {
return;
}
var bidiTexts = this.textContent.items;
var textDivs = this.textDivs;
var prevEnd = null;
var isSelectedPage = (this.findController === null ?
false : (this.pageIdx === this.findController.selected.pageIdx));
var selectedMatchIdx = (this.findController === null ?
-1 : this.findController.selected.matchIdx);
var highlightAll = (this.findController === null ?
false : this.findController.state.highlightAll);
var infinity = {
divIdx: -1,
offset: undefined
};
function beginText(begin, className) {
var divIdx = begin.divIdx;
textDivs[divIdx].textContent = '';
appendTextToDiv(divIdx, 0, begin.offset, className);
}
function appendTextToDiv(divIdx, fromOffset, toOffset, className) {
var div = textDivs[divIdx];
var content = bidiTexts[divIdx].str.substring(fromOffset, toOffset);
var node = document.createTextNode(content);
if (className) {
var span = document.createElement('span');
span.className = className;
span.appendChild(node);
div.appendChild(span);
return;
}
div.appendChild(node);
}
var i0 = selectedMatchIdx, i1 = i0 + 1;
if (highlightAll) {
i0 = 0;
i1 = matches.length;
} else if (!isSelectedPage) {
// Not highlighting all and this isn't the selected page, so do nothing.
return;
}
for (var i = i0; i < i1; i++) {
var match = matches[i];
var begin = match.begin;
var end = match.end;
var isSelected = (isSelectedPage && i === selectedMatchIdx);
var highlightSuffix = (isSelected ? ' selected' : '');
if (isSelected && !this.isViewerInPresentationMode) {
scrollIntoView(textDivs[begin.divIdx],
{ top: FIND_SCROLL_OFFSET_TOP,
left: FIND_SCROLL_OFFSET_LEFT });
}
// Match inside new div.
if (!prevEnd || begin.divIdx !== prevEnd.divIdx) {
// If there was a previous div, then add the text at the end.
if (prevEnd !== null) {
appendTextToDiv(prevEnd.divIdx, prevEnd.offset, infinity.offset);
}
// Clear the divs and set the content until the starting point.
beginText(begin);
} else {
appendTextToDiv(prevEnd.divIdx, prevEnd.offset, begin.offset);
}
if (begin.divIdx === end.divIdx) {
appendTextToDiv(begin.divIdx, begin.offset, end.offset,
'highlight' + highlightSuffix);
} else {
appendTextToDiv(begin.divIdx, begin.offset, infinity.offset,
'highlight begin' + highlightSuffix);
for (var n0 = begin.divIdx + 1, n1 = end.divIdx; n0 < n1; n0++) {
textDivs[n0].className = 'highlight middle' + highlightSuffix;
}
beginText(end, 'highlight end' + highlightSuffix);
}
prevEnd = end;
}
if (prevEnd) {
appendTextToDiv(prevEnd.divIdx, prevEnd.offset, infinity.offset);
}
},
updateMatches: function TextLayerBuilder_updateMatches() {
// Only show matches when all rendering is done.
if (!this.renderingDone) {
return;
}
// Clear all matches.
var matches = this.matches;
var textDivs = this.textDivs;
var bidiTexts = this.textContent.items;
var clearedUntilDivIdx = -1;
// Clear all current matches.
for (var i = 0, len = matches.length; i < len; i++) {
var match = matches[i];
var begin = Math.max(clearedUntilDivIdx, match.begin.divIdx);
for (var n = begin, end = match.end.divIdx; n <= end; n++) {
var div = textDivs[n];
div.textContent = bidiTexts[n].str;
div.className = '';
}
clearedUntilDivIdx = match.end.divIdx + 1;
}
if (this.findController === null || !this.findController.active) {
return;
}
// Convert the matches on the page controller into the match format
// used for the textLayer.
this.matches = this.convertMatches(this.findController === null ?
[] : (this.findController.pageMatches[this.pageIdx] || []));
this.renderMatches(this.matches);
}
};
this.updateMatches = function textLayerUpdateMatches() {
// Only show matches, once all rendering is done.
if (!this.renderingDone) {
return;
}
// Clear out all matches.
var matches = this.matches;
var textDivs = this.textDivs;
var bidiTexts = this.textContent.items;
var clearedUntilDivIdx = -1;
// Clear out all current matches.
for (var i = 0; i < matches.length; i++) {
var match = matches[i];
var begin = Math.max(clearedUntilDivIdx, match.begin.divIdx);
for (var n = begin; n <= match.end.divIdx; n++) {
var div = textDivs[n];
div.textContent = bidiTexts[n].str;
div.className = '';
}
clearedUntilDivIdx = match.end.divIdx + 1;
}
if (PDFFindController === null || !PDFFindController.active) {
return;
}
// Convert the matches on the page controller into the match format used
// for the textLayer.
this.matches = matches = (this.convertMatches(PDFFindController === null ?
[] : (PDFFindController.pageMatches[this.pageIdx] || [])));
this.renderMatches(this.matches);
};
};
return TextLayerBuilder;
})();
@ -5584,6 +5592,10 @@ function updateViewarea() {
return;
}
var suggestedCacheSize = Math.max(DEFAULT_CACHE_SIZE,
2 * visiblePages.length + 1);
cache.resize(suggestedCacheSize);
PDFView.renderHighestPriority(visible);
var currentId = PDFView.page;

View File

@ -43,6 +43,10 @@ toolbox.label=Developer Tools
# for the options panel tab.
optionsButton.tooltip=Toolbox Options
# LOCALIZATION NOTE (options.label): This is used as the label of the tab in
# the devtools window.
options.label=Options
# LOCALIZATION NOTE (options.panelLabel): This is used as the label for the
# toolbox panel.
options.panelLabel=Toolbox Options Panel

View File

@ -3,95 +3,71 @@
- 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/. -->
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
x="0px" y="0px"
viewBox="0 0 16 16"
enable-background="new 0 0 16 16"
xml:space="preserve">
<style>
g:not(:target) {
use:not(:target) {
display: none;
}
path {
use {
fill: menutext;
}
g.active > path {
use[id$="-active"] {
fill: -moz-menuhovertext;
}
use[id$="-disabled"] {
fill: graytext;
}
</style>
<g id="back">
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.192,8.893L2.21,9.964c0.064,0.065,0.136,0.117,0.214,0.159
<defs style="display:none">
<path id="back-shape" fill-rule="evenodd" clip-rule="evenodd" d="M1.192,8.893L2.21,9.964c0.064,0.065,0.136,0.117,0.214,0.159
l5.199,5.301c0.607,0.63,1.465,0.764,1.915,0.297l1.02-1.082c0.449-0.467,0.32-1.357-0.288-1.99l-2.116-2.158h5.705
c0.671,0,1.215-0.544,1.215-1.215v-2.43c0-0.671-0.544-1.215-1.215-1.215H8.094l2.271-2.309c0.609-0.626,0.737-1.512,0.288-1.974
L9.635,0.278C9.184-0.188,8.327-0.055,7.718,0.575L2.479,5.901C2.38,5.946,2.289,6.008,2.21,6.089L1.192,7.171
c-0.21,0.219-0.293,0.53-0.26,0.864C0.899,8.367,0.981,8.676,1.192,8.893z"/>
</g>
<g id="forward">
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.808,7.107L13.79,6.036c-0.064-0.065-0.136-0.117-0.214-0.159
<path id="forward-shape" fill-rule="evenodd" clip-rule="evenodd" d="M14.808,7.107L13.79,6.036c-0.064-0.065-0.136-0.117-0.214-0.159
L8.377,0.576C7.77-0.054,6.912-0.189,6.461,0.278L5.441,1.36c-0.449,0.467-0.32,1.357,0.288,1.99l2.116,2.158H2.14
c-0.671,0-1.215,0.544-1.215,1.215v2.43c0,0.671,0.544,1.215,1.215,1.215h5.765l-2.271,2.309c-0.609,0.626-0.737,1.512-0.288,1.974
l1.019,1.072c0.451,0.465,1.308,0.332,1.917-0.297l5.238-5.326c0.1-0.045,0.191-0.107,0.269-0.188l1.019-1.082
c0.21-0.219,0.293-0.53,0.26-0.864C15.101,7.633,15.019,7.324,14.808,7.107z"/>
</g>
<g id="reload">
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.429,8h-8l3.207-3.207C9.889,4.265,8.986,3.947,8,3.947
<path id="reload-shape" fill-rule="evenodd" clip-rule="evenodd" d="M15.429,8h-8l3.207-3.207C9.889,4.265,8.986,3.947,8,3.947
c-2.554,0-4.625,2.071-4.625,4.625S5.446,13.196,8,13.196c1.638,0,3.069-0.857,3.891-2.141l2.576,1.104
C13.199,14.439,10.794,16,8,16c-4.103,0-7.429-3.326-7.429-7.429S3.897,1.143,8,1.143c1.762,0,3.366,0.624,4.631,1.654L15.429,0V8z"/>
</g>
<g id="stop">
<polygon fill-rule="evenodd" clip-rule="evenodd" points="16,2.748 13.338,0.079 8.038,5.391 2.661,0 0,2.669
<polygon id="stop-shape" fill-rule="evenodd" clip-rule="evenodd" points="16,2.748 13.338,0.079 8.038,5.391 2.661,0 0,2.669
5.377,8.059 0.157,13.292 2.819,15.961 8.039,10.728 13.298,16 15.959,13.331 10.701,8.06"/>
</g>
<g id="bookmark">
<path d="M8.008,3.632l0.986,2.012l0.452,0.922l1.014,0.169l2.326,0.389l-1.719,1.799l-0.676,0.708l0.145,0.967
<path id="bookmark-shape" d="M8.008,3.632l0.986,2.012l0.452,0.922l1.014,0.169l2.326,0.389l-1.719,1.799l-0.676,0.708l0.145,0.967
L10.896,13l-1.959-1.039l-0.937-0.497l-0.937,0.497l-1.957,1.038L5.468,10.6l0.146-0.968L4.937,8.924L3.219,7.126l2.351-0.39
l1.023-0.17l0.45-0.934L8.008,3.632 M8,0C7.72,0,7.44,0.217,7.228,0.65L5.242,4.766L0.907,5.485c-0.958,0.159-1.195,0.861-0.53,1.56
l3.113,3.258l-0.69,4.583c-0.105,0.689,0.172,1.092,0.658,1.092c0.185,0,0.399-0.058,0.635-0.181l3.906-2.072l3.906,2.072
c0.236,0.123,0.45,0.181,0.635,0.181c0.486,0,0.762-0.403,0.659-1.092l-0.687-4.583l3.109-3.255c0.666-0.702,0.428-1.404-0.53-1.564
l-4.303-0.719L8.772,0.65C8.56,0.217,8.28,0,8,0L8,0z"/>
</g>
<g id="bookmark-starred">
<path d="M8,0C7.719,0,7.438,0.217,7.225,0.651L5.233,4.773l-4.35,0.72c-0.961,0.159-1.199,0.862-0.531,1.562
<path id="bookmarked-shape" d="M8,0C7.719,0,7.438,0.217,7.225,0.651L5.233,4.773l-4.35,0.72c-0.961,0.159-1.199,0.862-0.531,1.562
l3.124,3.262l-0.692,4.589C2.679,15.596,2.957,16,3.444,16c0.185,0,0.401-0.058,0.637-0.181L8,13.744l3.919,2.075
C12.156,15.942,12.372,16,12.557,16c0.487,0,0.764-0.404,0.661-1.094l-0.69-4.589l3.12-3.259c0.668-0.703,0.43-1.406-0.532-1.566
l-4.317-0.72L8.775,0.651C8.562,0.217,8.281,0,8,0L8,0z"/>
</g>
<g id="back-active" class="active">
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.192,8.893L2.21,9.964c0.064,0.065,0.136,0.117,0.214,0.159
l5.199,5.301c0.607,0.63,1.465,0.764,1.915,0.297l1.02-1.082c0.449-0.467,0.32-1.357-0.288-1.99l-2.116-2.158h5.705
c0.671,0,1.215-0.544,1.215-1.215v-2.43c0-0.671-0.544-1.215-1.215-1.215H8.094l2.271-2.309c0.609-0.626,0.737-1.512,0.288-1.974
L9.635,0.278C9.184-0.188,8.327-0.055,7.718,0.575L2.479,5.901C2.38,5.946,2.289,6.008,2.21,6.089L1.192,7.171
c-0.21,0.219-0.293,0.53-0.26,0.864C0.899,8.367,0.981,8.676,1.192,8.893z"/>
</g>
<g id="forward-active" class="active">
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.808,7.107L13.79,6.036c-0.064-0.065-0.136-0.117-0.214-0.159
L8.377,0.576C7.77-0.054,6.912-0.189,6.461,0.278L5.441,1.36c-0.449,0.467-0.32,1.357,0.288,1.99l2.116,2.158H2.14
c-0.671,0-1.215,0.544-1.215,1.215v2.43c0,0.671,0.544,1.215,1.215,1.215h5.765l-2.271,2.309c-0.609,0.626-0.737,1.512-0.288,1.974
l1.019,1.072c0.451,0.465,1.308,0.332,1.917-0.297l5.238-5.326c0.1-0.045,0.191-0.107,0.269-0.188l1.019-1.082
c0.21-0.219,0.293-0.53,0.26-0.864C15.101,7.633,15.019,7.324,14.808,7.107z"/>
</g>
<g id="reload-active" class="active">
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.429,8h-8l3.207-3.207C9.889,4.265,8.986,3.947,8,3.947
c-2.554,0-4.625,2.071-4.625,4.625S5.446,13.196,8,13.196c1.638,0,3.069-0.857,3.891-2.141l2.576,1.104
C13.199,14.439,10.794,16,8,16c-4.103,0-7.429-3.326-7.429-7.429S3.897,1.143,8,1.143c1.762,0,3.366,0.624,4.631,1.654L15.429,0V8z"/>
</g>
<g id="stop-active" class="active">
<polygon fill-rule="evenodd" clip-rule="evenodd" points="16,2.748 13.338,0.079 8.038,5.391 2.661,0 0,2.669
5.377,8.059 0.157,13.292 2.819,15.961 8.039,10.728 13.298,16 15.959,13.331 10.701,8.06"/>
</g>
<g id="bookmark-active" class="active">
<path d="M8.008,3.632l0.986,2.012l0.452,0.922l1.014,0.169l2.326,0.389l-1.719,1.799l-0.676,0.708l0.145,0.967
L10.896,13l-1.959-1.039l-0.937-0.497l-0.937,0.497l-1.957,1.038L5.468,10.6l0.146-0.968L4.937,8.924L3.219,7.126l2.351-0.39
l1.023-0.17l0.45-0.934L8.008,3.632 M8,0C7.72,0,7.44,0.217,7.228,0.65L5.242,4.766L0.907,5.485c-0.958,0.159-1.195,0.861-0.53,1.56
l3.113,3.258l-0.69,4.583c-0.105,0.689,0.172,1.092,0.658,1.092c0.185,0,0.399-0.058,0.635-0.181l3.906-2.072l3.906,2.072
c0.236,0.123,0.45,0.181,0.635,0.181c0.486,0,0.762-0.403,0.659-1.092l-0.687-4.583l3.109-3.255c0.666-0.702,0.428-1.404-0.53-1.564
l-4.303-0.719L8.772,0.65C8.56,0.217,8.28,0,8,0L8,0z"/>
</g>
<g id="bookmark-starred-active" class="active">
<path d="M8,0C7.719,0,7.438,0.217,7.225,0.651L5.233,4.773l-4.35,0.72c-0.961,0.159-1.199,0.862-0.531,1.562
l3.124,3.262l-0.692,4.589C2.679,15.596,2.957,16,3.444,16c0.185,0,0.401-0.058,0.637-0.181L8,13.744l3.919,2.075
C12.156,15.942,12.372,16,12.557,16c0.487,0,0.764-0.404,0.661-1.094l-0.69-4.589l3.12-3.259c0.668-0.703,0.43-1.406-0.532-1.566
l-4.317-0.72L8.775,0.651C8.562,0.217,8.281,0,8,0L8,0z"/>
</g>
</defs>
<use id="back" xlink:href="#back-shape"/>
<use id="back-active" xlink:href="#back-shape"/>
<use id="back-disabled" xlink:href="#back-shape"/>
<use id="forward" xlink:href="#forward-shape"/>
<use id="forward-active" xlink:href="#forward-shape"/>
<use id="forward-disabled" xlink:href="#forward-shape"/>
<use id="reload" xlink:href="#reload-shape"/>
<use id="reload-active" xlink:href="#reload-shape"/>
<use id="reload-disabled" xlink:href="#reload-shape"/>
<use id="stop" xlink:href="#stop-shape"/>
<use id="stop-active" xlink:href="#stop-shape"/>
<use id="stop-disabled" xlink:href="#stop-shape"/>
<use id="bookmark" xlink:href="#bookmark-shape"/>
<use id="bookmark-active" xlink:href="#bookmark-shape"/>
<use id="bookmark-disabled" xlink:href="#bookmark-shape"/>
<use id="bookmarked" xlink:href="#bookmarked-shape"/>
<use id="bookmarked-active" xlink:href="#bookmarked-shape"/>
<use id="bookmarked-disabled" xlink:href="#bookmarked-shape"/>
</svg>

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -3,95 +3,71 @@
- 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/. -->
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
x="0px" y="0px"
viewBox="0 0 16 16"
enable-background="new 0 0 16 16"
xml:space="preserve">
<style>
g:not(:target) {
use:not(:target) {
display: none;
}
path {
use {
fill: menutext;
}
g.active > path {
use[id$="-active"] {
fill: -moz-mac-menutextselect;
}
use[id$="-disabled"] {
fill: -moz-mac-menutextdisable;
}
</style>
<g id="back">
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.192,8.893L2.21,9.964c0.064,0.065,0.136,0.117,0.214,0.159
<defs style="display:none">
<path id="back-shape" fill-rule="evenodd" clip-rule="evenodd" d="M1.192,8.893L2.21,9.964c0.064,0.065,0.136,0.117,0.214,0.159
l5.199,5.301c0.607,0.63,1.465,0.764,1.915,0.297l1.02-1.082c0.449-0.467,0.32-1.357-0.288-1.99l-2.116-2.158h5.705
c0.671,0,1.215-0.544,1.215-1.215v-2.43c0-0.671-0.544-1.215-1.215-1.215H8.094l2.271-2.309c0.609-0.626,0.737-1.512,0.288-1.974
L9.635,0.278C9.184-0.188,8.327-0.055,7.718,0.575L2.479,5.901C2.38,5.946,2.289,6.008,2.21,6.089L1.192,7.171
c-0.21,0.219-0.293,0.53-0.26,0.864C0.899,8.367,0.981,8.676,1.192,8.893z"/>
</g>
<g id="forward">
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.808,7.107L13.79,6.036c-0.064-0.065-0.136-0.117-0.214-0.159
<path id="forward-shape" fill-rule="evenodd" clip-rule="evenodd" d="M14.808,7.107L13.79,6.036c-0.064-0.065-0.136-0.117-0.214-0.159
L8.377,0.576C7.77-0.054,6.912-0.189,6.461,0.278L5.441,1.36c-0.449,0.467-0.32,1.357,0.288,1.99l2.116,2.158H2.14
c-0.671,0-1.215,0.544-1.215,1.215v2.43c0,0.671,0.544,1.215,1.215,1.215h5.765l-2.271,2.309c-0.609,0.626-0.737,1.512-0.288,1.974
l1.019,1.072c0.451,0.465,1.308,0.332,1.917-0.297l5.238-5.326c0.1-0.045,0.191-0.107,0.269-0.188l1.019-1.082
c0.21-0.219,0.293-0.53,0.26-0.864C15.101,7.633,15.019,7.324,14.808,7.107z"/>
</g>
<g id="reload">
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.429,8h-8l3.207-3.207C9.889,4.265,8.986,3.947,8,3.947
<path id="reload-shape" fill-rule="evenodd" clip-rule="evenodd" d="M15.429,8h-8l3.207-3.207C9.889,4.265,8.986,3.947,8,3.947
c-2.554,0-4.625,2.071-4.625,4.625S5.446,13.196,8,13.196c1.638,0,3.069-0.857,3.891-2.141l2.576,1.104
C13.199,14.439,10.794,16,8,16c-4.103,0-7.429-3.326-7.429-7.429S3.897,1.143,8,1.143c1.762,0,3.366,0.624,4.631,1.654L15.429,0V8z"/>
</g>
<g id="stop">
<polygon fill-rule="evenodd" clip-rule="evenodd" points="16,2.748 13.338,0.079 8.038,5.391 2.661,0 0,2.669
<polygon id="stop-shape" fill-rule="evenodd" clip-rule="evenodd" points="16,2.748 13.338,0.079 8.038,5.391 2.661,0 0,2.669
5.377,8.059 0.157,13.292 2.819,15.961 8.039,10.728 13.298,16 15.959,13.331 10.701,8.06"/>
</g>
<g id="bookmark">
<path d="M8.008,3.632l0.986,2.012l0.452,0.922l1.014,0.169l2.326,0.389l-1.719,1.799l-0.676,0.708l0.145,0.967
<path id="bookmark-shape" d="M8.008,3.632l0.986,2.012l0.452,0.922l1.014,0.169l2.326,0.389l-1.719,1.799l-0.676,0.708l0.145,0.967
L10.896,13l-1.959-1.039l-0.937-0.497l-0.937,0.497l-1.957,1.038L5.468,10.6l0.146-0.968L4.937,8.924L3.219,7.126l2.351-0.39
l1.023-0.17l0.45-0.934L8.008,3.632 M8,0C7.72,0,7.44,0.217,7.228,0.65L5.242,4.766L0.907,5.485c-0.958,0.159-1.195,0.861-0.53,1.56
l3.113,3.258l-0.69,4.583c-0.105,0.689,0.172,1.092,0.658,1.092c0.185,0,0.399-0.058,0.635-0.181l3.906-2.072l3.906,2.072
c0.236,0.123,0.45,0.181,0.635,0.181c0.486,0,0.762-0.403,0.659-1.092l-0.687-4.583l3.109-3.255c0.666-0.702,0.428-1.404-0.53-1.564
l-4.303-0.719L8.772,0.65C8.56,0.217,8.28,0,8,0L8,0z"/>
</g>
<g id="bookmark-starred">
<path d="M8,0C7.719,0,7.438,0.217,7.225,0.651L5.233,4.773l-4.35,0.72c-0.961,0.159-1.199,0.862-0.531,1.562
<path id="bookmarked-shape" d="M8,0C7.719,0,7.438,0.217,7.225,0.651L5.233,4.773l-4.35,0.72c-0.961,0.159-1.199,0.862-0.531,1.562
l3.124,3.262l-0.692,4.589C2.679,15.596,2.957,16,3.444,16c0.185,0,0.401-0.058,0.637-0.181L8,13.744l3.919,2.075
C12.156,15.942,12.372,16,12.557,16c0.487,0,0.764-0.404,0.661-1.094l-0.69-4.589l3.12-3.259c0.668-0.703,0.43-1.406-0.532-1.566
l-4.317-0.72L8.775,0.651C8.562,0.217,8.281,0,8,0L8,0z"/>
</g>
<g id="back-active" class="active">
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.192,8.893L2.21,9.964c0.064,0.065,0.136,0.117,0.214,0.159
l5.199,5.301c0.607,0.63,1.465,0.764,1.915,0.297l1.02-1.082c0.449-0.467,0.32-1.357-0.288-1.99l-2.116-2.158h5.705
c0.671,0,1.215-0.544,1.215-1.215v-2.43c0-0.671-0.544-1.215-1.215-1.215H8.094l2.271-2.309c0.609-0.626,0.737-1.512,0.288-1.974
L9.635,0.278C9.184-0.188,8.327-0.055,7.718,0.575L2.479,5.901C2.38,5.946,2.289,6.008,2.21,6.089L1.192,7.171
c-0.21,0.219-0.293,0.53-0.26,0.864C0.899,8.367,0.981,8.676,1.192,8.893z"/>
</g>
<g id="forward-active" class="active">
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.808,7.107L13.79,6.036c-0.064-0.065-0.136-0.117-0.214-0.159
L8.377,0.576C7.77-0.054,6.912-0.189,6.461,0.278L5.441,1.36c-0.449,0.467-0.32,1.357,0.288,1.99l2.116,2.158H2.14
c-0.671,0-1.215,0.544-1.215,1.215v2.43c0,0.671,0.544,1.215,1.215,1.215h5.765l-2.271,2.309c-0.609,0.626-0.737,1.512-0.288,1.974
l1.019,1.072c0.451,0.465,1.308,0.332,1.917-0.297l5.238-5.326c0.1-0.045,0.191-0.107,0.269-0.188l1.019-1.082
c0.21-0.219,0.293-0.53,0.26-0.864C15.101,7.633,15.019,7.324,14.808,7.107z"/>
</g>
<g id="reload-active" class="active">
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.429,8h-8l3.207-3.207C9.889,4.265,8.986,3.947,8,3.947
c-2.554,0-4.625,2.071-4.625,4.625S5.446,13.196,8,13.196c1.638,0,3.069-0.857,3.891-2.141l2.576,1.104
C13.199,14.439,10.794,16,8,16c-4.103,0-7.429-3.326-7.429-7.429S3.897,1.143,8,1.143c1.762,0,3.366,0.624,4.631,1.654L15.429,0V8z"/>
</g>
<g id="stop-active" class="active">
<polygon fill-rule="evenodd" clip-rule="evenodd" points="16,2.748 13.338,0.079 8.038,5.391 2.661,0 0,2.669
5.377,8.059 0.157,13.292 2.819,15.961 8.039,10.728 13.298,16 15.959,13.331 10.701,8.06"/>
</g>
<g id="bookmark-active" class="active">
<path d="M8.008,3.632l0.986,2.012l0.452,0.922l1.014,0.169l2.326,0.389l-1.719,1.799l-0.676,0.708l0.145,0.967
L10.896,13l-1.959-1.039l-0.937-0.497l-0.937,0.497l-1.957,1.038L5.468,10.6l0.146-0.968L4.937,8.924L3.219,7.126l2.351-0.39
l1.023-0.17l0.45-0.934L8.008,3.632 M8,0C7.72,0,7.44,0.217,7.228,0.65L5.242,4.766L0.907,5.485c-0.958,0.159-1.195,0.861-0.53,1.56
l3.113,3.258l-0.69,4.583c-0.105,0.689,0.172,1.092,0.658,1.092c0.185,0,0.399-0.058,0.635-0.181l3.906-2.072l3.906,2.072
c0.236,0.123,0.45,0.181,0.635,0.181c0.486,0,0.762-0.403,0.659-1.092l-0.687-4.583l3.109-3.255c0.666-0.702,0.428-1.404-0.53-1.564
l-4.303-0.719L8.772,0.65C8.56,0.217,8.28,0,8,0L8,0z"/>
</g>
<g id="bookmark-starred-active" class="active">
<path d="M8,0C7.719,0,7.438,0.217,7.225,0.651L5.233,4.773l-4.35,0.72c-0.961,0.159-1.199,0.862-0.531,1.562
l3.124,3.262l-0.692,4.589C2.679,15.596,2.957,16,3.444,16c0.185,0,0.401-0.058,0.637-0.181L8,13.744l3.919,2.075
C12.156,15.942,12.372,16,12.557,16c0.487,0,0.764-0.404,0.661-1.094l-0.69-4.589l3.12-3.259c0.668-0.703,0.43-1.406-0.532-1.566
l-4.317-0.72L8.775,0.651C8.562,0.217,8.281,0,8,0L8,0z"/>
</g>
</defs>
<use id="back" xlink:href="#back-shape"/>
<use id="back-active" xlink:href="#back-shape"/>
<use id="back-disabled" xlink:href="#back-shape"/>
<use id="forward" xlink:href="#forward-shape"/>
<use id="forward-active" xlink:href="#forward-shape"/>
<use id="forward-disabled" xlink:href="#forward-shape"/>
<use id="reload" xlink:href="#reload-shape"/>
<use id="reload-active" xlink:href="#reload-shape"/>
<use id="reload-disabled" xlink:href="#reload-shape"/>
<use id="stop" xlink:href="#stop-shape"/>
<use id="stop-active" xlink:href="#stop-shape"/>
<use id="stop-disabled" xlink:href="#stop-shape"/>
<use id="bookmark" xlink:href="#bookmark-shape"/>
<use id="bookmark-active" xlink:href="#bookmark-shape"/>
<use id="bookmark-disabled" xlink:href="#bookmark-shape"/>
<use id="bookmarked" xlink:href="#bookmarked-shape"/>
<use id="bookmarked-active" xlink:href="#bookmarked-shape"/>
<use id="bookmarked-disabled" xlink:href="#bookmarked-shape"/>
</svg>

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -1,8 +1,3 @@
menugroup > .menuitem-iconic[disabled="true"] > .menu-iconic-left {
opacity: .3;
}
#context-navigation > .menuitem-iconic {
-moz-box-flex: 1;
-moz-box-pack: center;
@ -13,46 +8,66 @@ menugroup > .menuitem-iconic[disabled="true"] > .menu-iconic-left {
-moz-appearance: none;
}
#context-back > .menu-iconic-left {
#context-back {
list-style-image: url("chrome://browser/skin/content-contextmenu.svg#back");
}
#context-back[_moz-menuactive=true]:not([disabled]) > .menu-iconic-left {
#context-back[_moz-menuactive=true] {
list-style-image: url("chrome://browser/skin/content-contextmenu.svg#back-active");
}
#context-forward > .menu-iconic-left {
#context-back[disabled=true] {
list-style-image: url("chrome://browser/skin/content-contextmenu.svg#back-disabled");
}
#context-forward {
list-style-image: url("chrome://browser/skin/content-contextmenu.svg#forward");
}
#context-forward[_moz-menuactive=true]:not([disabled]) > .menu-iconic-left {
#context-forward[_moz-menuactive=true] {
list-style-image: url("chrome://browser/skin/content-contextmenu.svg#forward-active");
}
#context-reload > .menu-iconic-left {
#context-forward[disabled=true] {
list-style-image: url("chrome://browser/skin/content-contextmenu.svg#forward-disabled");
}
#context-reload {
list-style-image: url("chrome://browser/skin/content-contextmenu.svg#reload");
}
#context-reload[_moz-menuactive=true]:not([disabled]) > .menu-iconic-left {
#context-reload[_moz-menuactive=true] {
list-style-image: url("chrome://browser/skin/content-contextmenu.svg#reload-active");
}
#context-stop > .menu-iconic-left {
#context-reload[disabled=true] {
list-style-image: url("chrome://browser/skin/content-contextmenu.svg#reload-disabled");
}
#context-stop {
list-style-image: url("chrome://browser/skin/content-contextmenu.svg#stop");
}
#context-stop[_moz-menuactive=true]:not([disabled]) > .menu-iconic-left {
#context-stop[_moz-menuactive=true] {
list-style-image: url("chrome://browser/skin/content-contextmenu.svg#stop-active");
}
#context-bookmarkpage > .menu-iconic-left {
#context-stop[disabled=true] {
list-style-image: url("chrome://browser/skin/content-contextmenu.svg#stop-disabled");
}
#context-bookmarkpage {
list-style-image: url("chrome://browser/skin/content-contextmenu.svg#bookmark");
}
#context-bookmarkpage[_moz-menuactive=true]:not([disabled]) > .menu-iconic-left {
#context-bookmarkpage[_moz-menuactive=true] {
list-style-image: url("chrome://browser/skin/content-contextmenu.svg#bookmark-active");
}
#context-bookmarkpage[disabled=true] {
list-style-image: url("chrome://browser/skin/content-contextmenu.svg#bookmark-disabled");
}
#context-back:-moz-locale-dir(rtl),
#context-forward:-moz-locale-dir(rtl),
#context-reload:-moz-locale-dir(rtl) {

View File

@ -13,6 +13,7 @@
#banners-and-logs {
display: flex;
flex-grow: 1;
max-height: 100%;
}
#logs {

View File

@ -3,95 +3,71 @@
- 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/. -->
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
x="0px" y="0px"
viewBox="0 0 16 16"
enable-background="new 0 0 16 16"
xml:space="preserve">
<style>
g:not(:target) {
use:not(:target) {
display: none;
}
path {
use {
fill: menutext;
}
g.active > path {
use[id$="-active"] {
fill: -moz-menuhovertext;
}
use[id$="-disabled"] {
fill: graytext;
}
</style>
<g id="back">
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.192,8.893L2.21,9.964c0.064,0.065,0.136,0.117,0.214,0.159
<defs style="display:none">
<path id="back-shape" fill-rule="evenodd" clip-rule="evenodd" d="M1.192,8.893L2.21,9.964c0.064,0.065,0.136,0.117,0.214,0.159
l5.199,5.301c0.607,0.63,1.465,0.764,1.915,0.297l1.02-1.082c0.449-0.467,0.32-1.357-0.288-1.99l-2.116-2.158h5.705
c0.671,0,1.215-0.544,1.215-1.215v-2.43c0-0.671-0.544-1.215-1.215-1.215H8.094l2.271-2.309c0.609-0.626,0.737-1.512,0.288-1.974
L9.635,0.278C9.184-0.188,8.327-0.055,7.718,0.575L2.479,5.901C2.38,5.946,2.289,6.008,2.21,6.089L1.192,7.171
c-0.21,0.219-0.293,0.53-0.26,0.864C0.899,8.367,0.981,8.676,1.192,8.893z"/>
</g>
<g id="forward">
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.808,7.107L13.79,6.036c-0.064-0.065-0.136-0.117-0.214-0.159
<path id="forward-shape" fill-rule="evenodd" clip-rule="evenodd" d="M14.808,7.107L13.79,6.036c-0.064-0.065-0.136-0.117-0.214-0.159
L8.377,0.576C7.77-0.054,6.912-0.189,6.461,0.278L5.441,1.36c-0.449,0.467-0.32,1.357,0.288,1.99l2.116,2.158H2.14
c-0.671,0-1.215,0.544-1.215,1.215v2.43c0,0.671,0.544,1.215,1.215,1.215h5.765l-2.271,2.309c-0.609,0.626-0.737,1.512-0.288,1.974
l1.019,1.072c0.451,0.465,1.308,0.332,1.917-0.297l5.238-5.326c0.1-0.045,0.191-0.107,0.269-0.188l1.019-1.082
c0.21-0.219,0.293-0.53,0.26-0.864C15.101,7.633,15.019,7.324,14.808,7.107z"/>
</g>
<g id="reload">
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.429,8h-8l3.207-3.207C9.889,4.265,8.986,3.947,8,3.947
<path id="reload-shape" fill-rule="evenodd" clip-rule="evenodd" d="M15.429,8h-8l3.207-3.207C9.889,4.265,8.986,3.947,8,3.947
c-2.554,0-4.625,2.071-4.625,4.625S5.446,13.196,8,13.196c1.638,0,3.069-0.857,3.891-2.141l2.576,1.104
C13.199,14.439,10.794,16,8,16c-4.103,0-7.429-3.326-7.429-7.429S3.897,1.143,8,1.143c1.762,0,3.366,0.624,4.631,1.654L15.429,0V8z"/>
</g>
<g id="stop">
<polygon fill-rule="evenodd" clip-rule="evenodd" points="16,2.748 13.338,0.079 8.038,5.391 2.661,0 0,2.669
<polygon id="stop-shape" fill-rule="evenodd" clip-rule="evenodd" points="16,2.748 13.338,0.079 8.038,5.391 2.661,0 0,2.669
5.377,8.059 0.157,13.292 2.819,15.961 8.039,10.728 13.298,16 15.959,13.331 10.701,8.06"/>
</g>
<g id="bookmark">
<path d="M8.008,3.632l0.986,2.012l0.452,0.922l1.014,0.169l2.326,0.389l-1.719,1.799l-0.676,0.708l0.145,0.967
<path id="bookmark-shape" d="M8.008,3.632l0.986,2.012l0.452,0.922l1.014,0.169l2.326,0.389l-1.719,1.799l-0.676,0.708l0.145,0.967
L10.896,13l-1.959-1.039l-0.937-0.497l-0.937,0.497l-1.957,1.038L5.468,10.6l0.146-0.968L4.937,8.924L3.219,7.126l2.351-0.39
l1.023-0.17l0.45-0.934L8.008,3.632 M8,0C7.72,0,7.44,0.217,7.228,0.65L5.242,4.766L0.907,5.485c-0.958,0.159-1.195,0.861-0.53,1.56
l3.113,3.258l-0.69,4.583c-0.105,0.689,0.172,1.092,0.658,1.092c0.185,0,0.399-0.058,0.635-0.181l3.906-2.072l3.906,2.072
c0.236,0.123,0.45,0.181,0.635,0.181c0.486,0,0.762-0.403,0.659-1.092l-0.687-4.583l3.109-3.255c0.666-0.702,0.428-1.404-0.53-1.564
l-4.303-0.719L8.772,0.65C8.56,0.217,8.28,0,8,0L8,0z"/>
</g>
<g id="bookmark-starred">
<path d="M8,0C7.719,0,7.438,0.217,7.225,0.651L5.233,4.773l-4.35,0.72c-0.961,0.159-1.199,0.862-0.531,1.562
<path id="bookmarked-shape" d="M8,0C7.719,0,7.438,0.217,7.225,0.651L5.233,4.773l-4.35,0.72c-0.961,0.159-1.199,0.862-0.531,1.562
l3.124,3.262l-0.692,4.589C2.679,15.596,2.957,16,3.444,16c0.185,0,0.401-0.058,0.637-0.181L8,13.744l3.919,2.075
C12.156,15.942,12.372,16,12.557,16c0.487,0,0.764-0.404,0.661-1.094l-0.69-4.589l3.12-3.259c0.668-0.703,0.43-1.406-0.532-1.566
l-4.317-0.72L8.775,0.651C8.562,0.217,8.281,0,8,0L8,0z"/>
</g>
<g id="back-active" class="active">
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.192,8.893L2.21,9.964c0.064,0.065,0.136,0.117,0.214,0.159
l5.199,5.301c0.607,0.63,1.465,0.764,1.915,0.297l1.02-1.082c0.449-0.467,0.32-1.357-0.288-1.99l-2.116-2.158h5.705
c0.671,0,1.215-0.544,1.215-1.215v-2.43c0-0.671-0.544-1.215-1.215-1.215H8.094l2.271-2.309c0.609-0.626,0.737-1.512,0.288-1.974
L9.635,0.278C9.184-0.188,8.327-0.055,7.718,0.575L2.479,5.901C2.38,5.946,2.289,6.008,2.21,6.089L1.192,7.171
c-0.21,0.219-0.293,0.53-0.26,0.864C0.899,8.367,0.981,8.676,1.192,8.893z"/>
</g>
<g id="forward-active" class="active">
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.808,7.107L13.79,6.036c-0.064-0.065-0.136-0.117-0.214-0.159
L8.377,0.576C7.77-0.054,6.912-0.189,6.461,0.278L5.441,1.36c-0.449,0.467-0.32,1.357,0.288,1.99l2.116,2.158H2.14
c-0.671,0-1.215,0.544-1.215,1.215v2.43c0,0.671,0.544,1.215,1.215,1.215h5.765l-2.271,2.309c-0.609,0.626-0.737,1.512-0.288,1.974
l1.019,1.072c0.451,0.465,1.308,0.332,1.917-0.297l5.238-5.326c0.1-0.045,0.191-0.107,0.269-0.188l1.019-1.082
c0.21-0.219,0.293-0.53,0.26-0.864C15.101,7.633,15.019,7.324,14.808,7.107z"/>
</g>
<g id="reload-active" class="active">
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.429,8h-8l3.207-3.207C9.889,4.265,8.986,3.947,8,3.947
c-2.554,0-4.625,2.071-4.625,4.625S5.446,13.196,8,13.196c1.638,0,3.069-0.857,3.891-2.141l2.576,1.104
C13.199,14.439,10.794,16,8,16c-4.103,0-7.429-3.326-7.429-7.429S3.897,1.143,8,1.143c1.762,0,3.366,0.624,4.631,1.654L15.429,0V8z"/>
</g>
<g id="stop-active" class="active">
<polygon fill-rule="evenodd" clip-rule="evenodd" points="16,2.748 13.338,0.079 8.038,5.391 2.661,0 0,2.669
5.377,8.059 0.157,13.292 2.819,15.961 8.039,10.728 13.298,16 15.959,13.331 10.701,8.06"/>
</g>
<g id="bookmark-active" class="active">
<path d="M8.008,3.632l0.986,2.012l0.452,0.922l1.014,0.169l2.326,0.389l-1.719,1.799l-0.676,0.708l0.145,0.967
L10.896,13l-1.959-1.039l-0.937-0.497l-0.937,0.497l-1.957,1.038L5.468,10.6l0.146-0.968L4.937,8.924L3.219,7.126l2.351-0.39
l1.023-0.17l0.45-0.934L8.008,3.632 M8,0C7.72,0,7.44,0.217,7.228,0.65L5.242,4.766L0.907,5.485c-0.958,0.159-1.195,0.861-0.53,1.56
l3.113,3.258l-0.69,4.583c-0.105,0.689,0.172,1.092,0.658,1.092c0.185,0,0.399-0.058,0.635-0.181l3.906-2.072l3.906,2.072
c0.236,0.123,0.45,0.181,0.635,0.181c0.486,0,0.762-0.403,0.659-1.092l-0.687-4.583l3.109-3.255c0.666-0.702,0.428-1.404-0.53-1.564
l-4.303-0.719L8.772,0.65C8.56,0.217,8.28,0,8,0L8,0z"/>
</g>
<g id="bookmark-starred-active" class="active">
<path d="M8,0C7.719,0,7.438,0.217,7.225,0.651L5.233,4.773l-4.35,0.72c-0.961,0.159-1.199,0.862-0.531,1.562
l3.124,3.262l-0.692,4.589C2.679,15.596,2.957,16,3.444,16c0.185,0,0.401-0.058,0.637-0.181L8,13.744l3.919,2.075
C12.156,15.942,12.372,16,12.557,16c0.487,0,0.764-0.404,0.661-1.094l-0.69-4.589l3.12-3.259c0.668-0.703,0.43-1.406-0.532-1.566
l-4.317-0.72L8.775,0.651C8.562,0.217,8.281,0,8,0L8,0z"/>
</g>
</defs>
<use id="back" xlink:href="#back-shape"/>
<use id="back-active" xlink:href="#back-shape"/>
<use id="back-disabled" xlink:href="#back-shape"/>
<use id="forward" xlink:href="#forward-shape"/>
<use id="forward-active" xlink:href="#forward-shape"/>
<use id="forward-disabled" xlink:href="#forward-shape"/>
<use id="reload" xlink:href="#reload-shape"/>
<use id="reload-active" xlink:href="#reload-shape"/>
<use id="reload-disabled" xlink:href="#reload-shape"/>
<use id="stop" xlink:href="#stop-shape"/>
<use id="stop-active" xlink:href="#stop-shape"/>
<use id="stop-disabled" xlink:href="#stop-shape"/>
<use id="bookmark" xlink:href="#bookmark-shape"/>
<use id="bookmark-active" xlink:href="#bookmark-shape"/>
<use id="bookmark-disabled" xlink:href="#bookmark-shape"/>
<use id="bookmarked" xlink:href="#bookmarked-shape"/>
<use id="bookmarked-active" xlink:href="#bookmarked-shape"/>
<use id="bookmarked-disabled" xlink:href="#bookmarked-shape"/>
</svg>

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -116,10 +116,6 @@ public:
using namespace mozilla::gfx;
#ifdef MOZ_WIDGET_GONK
extern nsIntRect gScreenBounds;
#endif
namespace mozilla {
namespace gl {
@ -205,12 +201,6 @@ CreateSurfaceForWindow(nsIWidget* widget, const EGLConfig& config) {
#else
MOZ_ASSERT(widget != nullptr);
newSurface = sEGLLibrary.fCreateWindowSurface(EGL_DISPLAY(), config, GET_NATIVE_WINDOW(widget), 0);
#ifdef MOZ_WIDGET_GONK
gScreenBounds.x = 0;
gScreenBounds.y = 0;
sEGLLibrary.fQuerySurface(EGL_DISPLAY(), newSurface, LOCAL_EGL_WIDTH, &gScreenBounds.width);
sEGLLibrary.fQuerySurface(EGL_DISPLAY(), newSurface, LOCAL_EGL_HEIGHT, &gScreenBounds.height);
#endif
#endif
return newSurface;
}

View File

@ -2545,10 +2545,12 @@ public class BrowserApp extends GeckoApp
tab = Tabs.getInstance().getSelectedTab();
if (tab != null) {
if (item.isChecked()) {
Telemetry.sendUIEvent(TelemetryContract.Event.UNSAVE, TelemetryContract.Method.MENU, "bookmark");
tab.removeBookmark();
Toast.makeText(this, R.string.bookmark_removed, Toast.LENGTH_SHORT).show();
item.setIcon(R.drawable.ic_menu_bookmark_add);
} else {
Telemetry.sendUIEvent(TelemetryContract.Event.SAVE, TelemetryContract.Method.MENU, "bookmark");
tab.addBookmark();
getButtonToast().show(false,
getResources().getString(R.string.bookmark_added),

View File

@ -62,7 +62,6 @@ public interface TelemetryContract {
SANITIZE("sanitize.1"),
// Saving a resource (reader, bookmark, etc) for viewing later.
// Note: Only used in JavaScript for now, but here for completeness.
SAVE("save.1"),
// Sharing content.
@ -76,7 +75,6 @@ public interface TelemetryContract {
UNPIN("unpin.1"),
// Stop holding a resource (reader, bookmark, etc) for viewing later.
// Note: Only used in JavaScript for now, but here for completeness.
UNSAVE("unsave.1"),
// VALUES BELOW THIS LINE ARE EXCLUSIVE TO TESTING.

View File

@ -61,6 +61,8 @@ let AboutReader = function(doc, win) {
win.addEventListener("popstate", this, false);
win.addEventListener("resize", this, false);
doc.addEventListener("visibilitychange", this, false);
this._setupAllDropdowns();
this._setupButton("toggle-button", this._onReaderToggle.bind(this));
this._setupButton("share-button", this._onShare.bind(this));
@ -271,6 +273,10 @@ AboutReader.prototype = {
this._handleDeviceLight(aEvent.value);
break;
case "visibilitychange":
this._handleVisibilityChange();
break;
case "unload":
Services.obs.removeObserver(this, "Reader:Add");
Services.obs.removeObserver(this, "Reader:Remove");
@ -399,6 +405,29 @@ AboutReader.prototype = {
this._totalLux -= oldLux;
},
_handleVisibilityChange: function Reader_handleVisibilityChange() {
let colorScheme = Services.prefs.getCharPref("reader.color_scheme");
if (colorScheme != "auto") {
return;
}
// Turn off the ambient light sensor if the page is hidden
this._enableAmbientLighting(!this._doc.hidden);
},
// Setup or teardown the ambient light tracking system.
_enableAmbientLighting: function Reader_enableAmbientLighting(enable) {
if (enable) {
this._win.addEventListener("devicelight", this, false);
this._luxValues = [];
this._totalLux = 0;
} else {
this._win.removeEventListener("devicelight", this, false);
delete this._luxValues;
delete this._totalLux;
}
},
_updateColorScheme: function Reader_updateColorScheme(luxValue) {
// Upper bound value for "dark" color scheme beyond which it changes to "light".
let upperBoundDark = 50;
@ -419,7 +448,8 @@ AboutReader.prototype = {
},
_setColorScheme: function Reader_setColorScheme(newColorScheme) {
if (this._colorScheme === newColorScheme)
// "auto" is not a real color scheme
if (this._colorScheme === newColorScheme || newColorScheme === "auto")
return;
let bodyClasses = this._doc.body.classList;
@ -434,16 +464,8 @@ AboutReader.prototype = {
// Pref values include "dark", "light", and "auto", which automatically switches
// between light and dark color schemes based on the ambient light level.
_setColorSchemePref: function Reader_setColorSchemePref(colorSchemePref) {
if (colorSchemePref === "auto") {
this._win.addEventListener("devicelight", this, false);
this._luxValues = [];
this._totalLux = 0;
} else {
this._win.removeEventListener("devicelight", this, false);
this._setColorScheme(colorSchemePref);
delete this._luxValues;
delete this._totalLux;
}
this._enableAmbientLighting(colorSchemePref === "auto");
this._setColorScheme(colorSchemePref);
Services.prefs.setCharPref("reader.color_scheme", colorSchemePref);
},

View File

@ -1653,7 +1653,7 @@ var BrowserApp = {
case "Passwords:Init": {
let storage = Cc["@mozilla.org/login-manager/storage/mozStorage;1"].
getService(Ci.nsILoginManagerStorage);
storage.init();
storage.initialize();
Services.obs.removeObserver(this, "Passwords:Init");
break;
}

View File

@ -5,6 +5,7 @@
package org.mozilla.search;
import android.app.Activity;
import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle;
import android.support.v4.app.Fragment;
@ -76,7 +77,10 @@ public class PreSearchFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
listView = (ListView) inflater.inflate(R.layout.search_fragment_pre_search, container, false);
final View mainView = inflater.inflate(R.layout.search_fragment_pre_search, container, false);
// Initialize listview.
listView = (ListView) mainView.findViewById(R.id.list_view);
listView.setAdapter(cursorAdapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
@ -91,7 +95,15 @@ public class PreSearchFragment extends Fragment {
}
}
});
return listView;
// Apply click handler to settings button.
mainView.findViewById(R.id.settings_button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(getActivity(), SearchPreferenceActivity.class));
}
});
return mainView;
}
@Override

View File

@ -0,0 +1,96 @@
/* 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/. */
package org.mozilla.search;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.util.Log;
import android.widget.Toast;
import org.mozilla.gecko.db.BrowserContract;
/**
* This activity allows users to modify the settings for the search activity.
*
* A note on implementation: At the moment, we don't have tablet-specific designs.
* Therefore, this implementation uses the old-style PreferenceActivity. When
* we start optimizing for tablets, we can migrate to Fennec's PreferenceFragment
* implementation.
*
* TODO: Change this to PreferenceFragment when we stop supporting devices older than SDK 11.
*/
public class SearchPreferenceActivity extends PreferenceActivity {
private static final String LOGTAG = "SearchPreferenceActivity";
private static final String CLEAR_SEARCH_HISTORY_BUTTON_KEY = "clear_search_history_button";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
if (getActionBar() != null) {
getActionBar().setDisplayHomeAsUpEnabled(true);
}
}
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
setupPrefsScreen();
}
@SuppressWarnings("deprecation")
private void setupPrefsScreen() {
addPreferencesFromResource(R.xml.search_preferences);
final Preference clearHistoryButton = findPreference(CLEAR_SEARCH_HISTORY_BUTTON_KEY);
clearHistoryButton.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
final AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(SearchPreferenceActivity.this);
dialogBuilder.setNegativeButton(android.R.string.cancel, null);
dialogBuilder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
clearHistory();
}
});
dialogBuilder.setMessage(R.string.search_pref_clear_history_dialog_message);
dialogBuilder.show();
return false;
}
});
}
private void clearHistory() {
final AsyncTask<Void, Void, Boolean> clearHistoryTask = new AsyncTask<Void, Void, Boolean>() {
@Override
protected Boolean doInBackground(Void... params) {
final int numDeleted = getContentResolver().delete(
BrowserContract.SearchHistory.CONTENT_URI, null, null);
return numDeleted >= 0;
}
@Override
protected void onPostExecute(Boolean success) {
if (success) {
getContentResolver().notifyChange(BrowserContract.SearchHistory.CONTENT_URI, null);
Toast.makeText(SearchPreferenceActivity.this, SearchPreferenceActivity.this.getResources()
.getString(R.string.search_pref_clear_history_confirmation), Toast.LENGTH_SHORT).show();
} else {
Log.e(LOGTAG, "Error clearing search history.");
}
}
};
clearHistoryTask.execute();
}
}

View File

@ -3,10 +3,6 @@
android:label="@string/search_app_name"
android:theme="@style/AppTheme"
android:screenOrientation="portrait">
<!-- Add this to activity declaration to hide keyboard on launch -->
<!-- android:windowSoftInputMode="stateHidden" -->
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
@ -18,3 +14,13 @@
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<activity
android:name="org.mozilla.search.SearchPreferenceActivity"
android:label="@string/search_pref_title"
android:parentActivityName="org.mozilla.search.MainActivity"
android:theme="@style/SettingsTheme" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.mozilla.search.MainActivity"/>
</activity>

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 B

View File

@ -2,9 +2,26 @@
- 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/. -->
<ListView
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="@null"
android:dividerHeight="0dp"/>
android:layout_height="match_parent">
<ListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="@null"
android:dividerHeight="0dp"/>
<ImageButton
android:id="@+id/settings_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:padding="15dp"
android:src="@drawable/ic_action_overflow"
android:text="@string/search_settings_icon"
android:layout_gravity="bottom|right"/>
</FrameLayout>

View File

@ -10,4 +10,6 @@
<item name="android:colorBackground">@color/global_background_color</item>
</style>
<style name="SettingsTheme" parent="@android:style/Theme.Holo.Light"/>
</resources>

View File

@ -10,4 +10,6 @@
<item name="android:colorBackground">@color/global_background_color</item>
</style>
<style name="SettingsTheme" parent="@android:style/Theme.Light"/>
</resources>

View File

@ -0,0 +1,9 @@
<!-- 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/. -->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<Preference
android:key="clear_search_history_button"
android:title="@string/search_pref_clear_history_title"/>
</PreferenceScreen>

View File

@ -15,4 +15,5 @@ search_activity_sources = [
'java/org/mozilla/search/MainActivity.java',
'java/org/mozilla/search/PostSearchFragment.java',
'java/org/mozilla/search/PreSearchFragment.java',
'java/org/mozilla/search/SearchPreferenceActivity.java',
]

View File

@ -3,6 +3,12 @@
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!ENTITY search_jump_arrow '&#8598;'>
<!ENTITY search_settings_icon '&#8942;'>
<!ENTITY search_app_name 'Firefox Search'>
<!ENTITY search_header_image_content_description 'Firefox Search Header Image'>
<!ENTITY search_for_something 'Search for something'>
<!ENTITY search_pref_title 'Settings'>
<!ENTITY search_pref_clear_history_confirmation 'History cleared'>
<!ENTITY search_pref_clear_history_dialog_message 'Delete all search history from this device?'>
<!ENTITY search_pref_clear_history_title 'Clear search history'>

View File

@ -1,4 +1,10 @@
<string name="search_app_name">&search_app_name;</string>
<string name="search_jump_arrow">&search_jump_arrow;</string>
<string name="search_header_image_content_description">&search_header_image_content_description;</string>
<string name="search_settings_icon">&search_settings_icon;</string>
<string name="search_app_name">&search_app_name;</string>
<string name="search_for_something">&search_for_something;</string>
<string name="search_pref_title">&search_pref_title;</string>
<string name="search_pref_clear_history_confirmation">&search_pref_clear_history_confirmation;</string>
<string name="search_pref_clear_history_dialog_message">&search_pref_clear_history_dialog_message;</string>
<string name="search_pref_clear_history_title">&search_pref_clear_history_title;</string>

View File

@ -36,7 +36,9 @@ toolkit.jar:
* content/global/customizeToolbar.js (customizeToolbar.js)
content/global/customizeToolbar.xul (customizeToolbar.xul)
content/global/devicestorage.properties (devicestorage.properties)
#ifndef ANDROID
content/global/directoryLinks.json (directoryLinks.json)
#endif
content/global/editMenuOverlay.js (editMenuOverlay.js)
*+ content/global/editMenuOverlay.xul (editMenuOverlay.xul)
content/global/finddialog.js (finddialog.js)

View File

@ -29,7 +29,6 @@ this.EXPORTED_SYMBOLS = ["DebuggerTransport",
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Timer.jsm");
let promise = Cu.import("resource://gre/modules/devtools/deprecated-sync-thenables.js").Promise;
const { defer, resolve, reject } = promise;
@ -85,6 +84,8 @@ let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
loader.loadSubScript("resource://gre/modules/devtools/transport/transport.js", this);
/**
* TODO: Get rid of this API in favor of EventTarget (bug 1042642)
*
* Add simple event notification to a prototype object. Any object that has
* some use for event notifications or the observer pattern in general can be
* augmented with the necessary facilities by passing its prototype to this
@ -175,7 +176,7 @@ function eventSource(aProto) {
* All arguments will be passed along to the listeners,
* including the name argument.
*/
aProto.notify = function () {
aProto.emit = function () {
if (!this._listeners) {
return;
}
@ -277,7 +278,7 @@ this.DebuggerClient = function (aTransport)
this.mainRoot = null;
this.expectReply("root", (aPacket) => {
this.mainRoot = new RootClient(this, aPacket);
this.notify("connected", aPacket.applicationType, aPacket.traits);
this.emit("connected", aPacket.applicationType, aPacket.traits);
});
}
@ -453,7 +454,7 @@ DebuggerClient.prototype = {
javascriptEnabled: cachedTab.javascriptEnabled,
traits: cachedTab.traits,
};
setTimeout(() => aOnResponse(cachedResponse, cachedTab), 0);
DevToolsUtils.executeSoon(() => aOnResponse(cachedResponse, cachedTab));
return;
}
@ -543,7 +544,7 @@ DebuggerClient.prototype = {
*/
attachThread: function (aThreadActor, aOnResponse = noop, aOptions={}) {
if (this._clients.has(aThreadActor)) {
setTimeout(() => aOnResponse({}, this._clients.get(aThreadActor)), 0);
DevToolsUtils.executeSoon(() => aOnResponse({}, this._clients.get(aThreadActor)));
return;
}
@ -572,7 +573,7 @@ DebuggerClient.prototype = {
*/
attachTracer: function (aTraceActor, aOnResponse = noop) {
if (this._clients.has(aTraceActor)) {
setTimeout(() => aOnResponse({}, this._clients.get(aTraceActor)), 0);
DevToolsUtils.executeSoon(() => aOnResponse({}, this._clients.get(aTraceActor)));
return;
}
@ -918,7 +919,7 @@ DebuggerClient.prototype = {
// Only try to notify listeners on events, not responses to requests
// that lack a packet type.
if (aPacket.type) {
this.notify(aPacket.type, aPacket);
this.emit(aPacket.type, aPacket);
}
if (activeRequest) {
@ -991,7 +992,7 @@ DebuggerClient.prototype = {
* the stream.
*/
onClosed: function (aStatus) {
this.notify("closed");
this.emit("closed");
},
registerClient: function (client) {
@ -1282,7 +1283,7 @@ TabClient.prototype = {
*/
attachThread: function(aOptions={}, aOnResponse = noop) {
if (this.thread) {
setTimeout(() => aOnResponse({}, this.thread), 0);
DevToolsUtils.executeSoon(() => aOnResponse({}, this.thread));
return;
}
@ -1670,7 +1671,7 @@ ThreadClient.prototype = {
// the next resumption. Otherwise we have to force a pause in order to send
// the array.
if (this.paused) {
setTimeout(() => onResponse({}), 0);
DevToolsUtils.executeSoon(() => onResponse({}));
return;
}
this.interrupt(response => {
@ -1853,7 +1854,7 @@ ThreadClient.prototype = {
_clearScripts: function () {
if (Object.keys(this._scriptCache).length > 0) {
this._scriptCache = {}
this.notify("scriptscleared");
this.emit("scriptscleared");
}
},
@ -1923,7 +1924,7 @@ ThreadClient.prototype = {
// If we got as many frames as we asked for, there might be more
// frames available.
this.notify("framesadded");
this.emit("framesadded");
aCallback(aResponse);
});
@ -1938,7 +1939,7 @@ ThreadClient.prototype = {
_clearFrames: function () {
if (this._frameCache.length > 0) {
this._frameCache = [];
this.notify("framescleared");
this.emit("framescleared");
}
},
@ -2038,7 +2039,7 @@ ThreadClient.prototype = {
this._clearFrames();
this._clearPauseGrips();
aPacket.type === ThreadStateTypes.detached && this._clearThreadGrips();
this.client._eventsEnabled && this.notify(aPacket.type, aPacket);
this.client._eventsEnabled && this.emit(aPacket.type, aPacket);
},
/**
@ -2389,7 +2390,7 @@ SourceClient.prototype = {
if (!aResponse.error) {
this._isBlackBoxed = true;
if (this._activeThread) {
this._activeThread.notify("blackboxchange", this);
this._activeThread.emit("blackboxchange", this);
}
}
return aResponse;
@ -2410,7 +2411,7 @@ SourceClient.prototype = {
if (!aResponse.error) {
this._isBlackBoxed = false;
if (this._activeThread) {
this._activeThread.notify("blackboxchange", this);
this._activeThread.emit("blackboxchange", this);
}
}
return aResponse;
@ -2443,7 +2444,7 @@ SourceClient.prototype = {
if (!aResponse.error) {
this._isPrettyPrinted = true;
this._activeThread._clearFrames();
this._activeThread.notify("prettyprintchange", this);
this._activeThread.emit("prettyprintchange", this);
}
this._onSourceResponse(aResponse, aCallback);
});
@ -2461,7 +2462,7 @@ SourceClient.prototype = {
if (!aResponse.error) {
this._isPrettyPrinted = false;
this._activeThread._clearFrames();
this._activeThread.notify("prettyprintchange", this);
this._activeThread.emit("prettyprintchange", this);
}
this._onSourceResponse(aResponse, aCallback);
});

View File

@ -2,57 +2,58 @@
* 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/. */
const {Ci,Cu} = require("chrome");
const {Ci,Cu,Cc} = require("chrome");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
const Services = require("Services");
let {setTimeout} = require("sdk/timers");
let {setTimeout,clearTimeout} = require("sdk/timers");
function MonitorActor(aConnection) {
this.conn = aConnection;
this._updates = [];
this._started = false;
}
MonitorActor.prototype = {
actorPrefix: "monitor",
// Updates
// Updates.
_toSend: [],
_timeout: null,
_started: false,
_scheduleUpdate: function() {
if (this._started && !this._timeout) {
this._timeout = setTimeout(() => {
if (this._toSend.length > 0) {
this.conn.sendActorEvent(this.actorID, "update", this._toSend);
this._toSend = [];
}
this._timeout = null;
}, 200);
_sendUpdate: function() {
if (this._started) {
this.conn.sendActorEvent(this.actorID, "update", { data: this._updates });
this._updates = [];
}
},
// Methods available from the front
// Methods available from the front.
start: function() {
if (!this._started) {
this._started = true;
Services.obs.addObserver(this, "devtools-monitor-update", false);
Services.obs.notifyObservers(null, "devtools-monitor-start", "");
this._agents.forEach(agent => this._startAgent(agent));
}
return {};
},
stop: function() {
if (this._started) {
this._agents.forEach(agent => agent.stop());
Services.obs.notifyObservers(null, "devtools-monitor-stop", "");
Services.obs.removeObserver(this, "devtools-monitor-update");
this._started = false;
}
return {};
},
// nsIObserver
disconnect: function() {
this.stop();
},
// nsIObserver.
observe: function (subject, topic, data) {
if (topic == "devtools-monitor-update") {
@ -64,16 +65,42 @@ MonitorActor.prototype = {
return;
}
if (!Array.isArray(data)) {
this._toSend.push(data);
this._updates.push(data);
} else {
this._toSend = this._toSend.concat(data);
this._updates = this._updates.concat(data);
}
this._scheduleUpdate();
this._sendUpdate();
}
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
}
// Update agents (see USSAgent for an example).
_agents: [],
_startAgent: function(agent) {
try {
agent.start();
} catch (e) {
this._removeAgent(agent);
}
},
_addAgent: function(agent) {
this._agents.push(agent);
if (this._started) {
this._startAgent(agent);
}
},
_removeAgent: function(agent) {
let index = this._agents.indexOf(agent);
if (index > -1) {
this._agents.splice(index, 1);
}
},
};
MonitorActor.prototype.requestTypes = {
"start": MonitorActor.prototype.start,
@ -91,3 +118,40 @@ exports.unregister = function(handle) {
handle.removeGlobalActor(MonitorActor, "monitorActor");
handle.removeTabActor(MonitorActor, "monitorActor");
};
let USSAgent = {
_mgr: null,
_timeout: null,
_packet: {
graph: "USS",
time: null,
value: null
},
start: function() {
USSAgent._mgr = Cc["@mozilla.org/memory-reporter-manager;1"].getService(Ci.nsIMemoryReporterManager);
if (!USSAgent._mgr.residentUnique) {
throw "Couldn't get USS.";
}
USSAgent.update();
},
update: function() {
if (!USSAgent._mgr) {
USSAgent.stop();
return;
}
USSAgent._packet.time = Date.now();
USSAgent._packet.value = USSAgent._mgr.residentUnique;
Services.obs.notifyObservers(null, "devtools-monitor-update", JSON.stringify(USSAgent._packet));
USSAgent._timeout = setTimeout(USSAgent.update, 300);
},
stop: function() {
clearTimeout(USSAgent._timeout);
USSAgent._mgr = null;
}
};
MonitorActor.prototype._addAgent(USSAgent);

View File

@ -1174,7 +1174,7 @@ DebuggerServerConnection.prototype = {
this._forwardingPrefixes.delete(aPrefix);
},
sendActorEvent: function (actorID, eventName, event) {
sendActorEvent: function (actorID, eventName, event = {}) {
event.from = actorID;
event.type = eventName;
this.send(event);

View File

@ -28,11 +28,11 @@ function run_test()
client.unregisterClient(this);
}
MonitorClient.prototype.detach = function () {}
MonitorClient.prototype.start = function () {
MonitorClient.prototype.start = function (callback) {
this.client.request({
to: this.actor,
type: "start"
});
}, callback);
}
MonitorClient.prototype.stop = function (callback) {
this.client.request({
@ -43,34 +43,39 @@ function run_test()
let monitor;
// Start tracking event loop lags.
// Start the monitor actor.
client.connect(function () {
client.listTabs(function(resp) {
monitor = new MonitorClient(client, resp);
monitor.start();
monitor.on("update", gotUpdate);
do_execute_soon(update);
monitor.start(update);
});
});
let time = new Date().getTime();
let time = Date.now();
function update() {
let event = {
graph: "Test",
curve: "test",
value: 42,
time: time,
value: 42
};
Services.obs.notifyObservers(null, "devtools-monitor-update", JSON.stringify(event));
}
function gotUpdate(type, data) {
do_check_eq(data.length, 1);
let evt = data[0];
do_check_eq(evt.value, 42);
do_check_eq(evt.time, time);
monitor.stop(function (aResponse) {
monitor.destroy();
finishClient(client);
function gotUpdate(type, packet) {
packet.data.forEach(function(event) {
// Ignore updates that were not sent by this test.
if (event.graph === "Test") {
do_check_eq(event.curve, "test");
do_check_eq(event.value, 42);
do_check_eq(event.time, time);
monitor.stop(function (aResponse) {
monitor.destroy();
finishClient(client);
});
}
});
}