This commit is contained in:
Ryan VanderMeulen 2015-07-14 23:38:02 -04:00
Родитель 9897c73236 b8ca505d1b
Коммит 07e1b2ced2
111 изменённых файлов: 1830 добавлений и 536 удалений

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

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="803d04e3829fd4fe9261211aa0ddca6b79d4e328"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="c6ef08964711f461a8e6326eae911789d1ec220c"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c490ae41c67f892232599f4ef049467a922b613e"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
@ -125,7 +125,7 @@
<project name="device-shinano-common" path="device/sony/shinano-common" remote="b2g" revision="e9ef670a15d56ea312e70d4b11c4aaeac404f9d2"/>
<project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="1bb28abbc215f45220620af5cd60a8ac1be93722"/>
<project name="device/qcom/common" path="device/qcom/common" revision="2501e5940ba69ece7654ff85611c76ae5bda299c"/>
<project name="codeaurora_kernel_msm" path="kernel" remote="b2g" revision="5ada05ac150f643ef19e87015df7e106b88effe7"/>
<project name="codeaurora_kernel_msm" path="kernel" remote="b2g" revision="d415406c7d810321a7cdd9af9c6271b2a378794c"/>
<project name="platform/external/bluetooth/bluedroid" path="external/bluetooth/bluedroid" revision="d61fc97258c8b0c362430dd2eb195dcc4d266f14"/>
<project name="init_sh" path="external/init_sh" remote="b2g" revision="3bdd26e092db9c47c5beb04b4809a35f8f767b8a"/>
<project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="0a01977f34d6e86fe23d6c0ec75e96ba988bbebb"/>

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

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="803d04e3829fd4fe9261211aa0ddca6b79d4e328"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="c6ef08964711f461a8e6326eae911789d1ec220c"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c490ae41c67f892232599f4ef049467a922b613e"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

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

@ -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="803d04e3829fd4fe9261211aa0ddca6b79d4e328"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c6ef08964711f461a8e6326eae911789d1ec220c"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c490ae41c67f892232599f4ef049467a922b613e"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="87a2d8ab9248540910e56921654367b78a587095"/>

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

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="803d04e3829fd4fe9261211aa0ddca6b79d4e328"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="c6ef08964711f461a8e6326eae911789d1ec220c"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c490ae41c67f892232599f4ef049467a922b613e"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="cfa80d0c1c13a13a64b049173ea3a1d605f2cffd"/>

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

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="803d04e3829fd4fe9261211aa0ddca6b79d4e328"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="c6ef08964711f461a8e6326eae911789d1ec220c"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c490ae41c67f892232599f4ef049467a922b613e"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

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

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="61e82f99bb8bc78d52b5717e9a2481ec7267fa33">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="803d04e3829fd4fe9261211aa0ddca6b79d4e328"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="c6ef08964711f461a8e6326eae911789d1ec220c"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c490ae41c67f892232599f4ef049467a922b613e"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

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

@ -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="803d04e3829fd4fe9261211aa0ddca6b79d4e328"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c6ef08964711f461a8e6326eae911789d1ec220c"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c490ae41c67f892232599f4ef049467a922b613e"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="87a2d8ab9248540910e56921654367b78a587095"/>

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

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="803d04e3829fd4fe9261211aa0ddca6b79d4e328"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="c6ef08964711f461a8e6326eae911789d1ec220c"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c490ae41c67f892232599f4ef049467a922b613e"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

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

@ -1,9 +1,9 @@
{
"git": {
"git_revision": "803d04e3829fd4fe9261211aa0ddca6b79d4e328",
"git_revision": "c6ef08964711f461a8e6326eae911789d1ec220c",
"remote": "https://git.mozilla.org/releases/gaia.git",
"branch": ""
},
"revision": "ad35f158cbd79823e9449f30df5d693613eef17f",
"revision": "422a64c4639a399fda83fb6ba9af46254a659421",
"repo_path": "integration/gaia-central"
}

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

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="803d04e3829fd4fe9261211aa0ddca6b79d4e328"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="c6ef08964711f461a8e6326eae911789d1ec220c"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c490ae41c67f892232599f4ef049467a922b613e"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="cfa80d0c1c13a13a64b049173ea3a1d605f2cffd"/>

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

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="61e82f99bb8bc78d52b5717e9a2481ec7267fa33">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="803d04e3829fd4fe9261211aa0ddca6b79d4e328"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="c6ef08964711f461a8e6326eae911789d1ec220c"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="c490ae41c67f892232599f4ef049467a922b613e"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

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

@ -1459,6 +1459,7 @@ pref("devtools.debugger.auto-pretty-print", false);
pref("devtools.debugger.auto-black-box", true);
pref("devtools.debugger.tracer", false);
pref("devtools.debugger.workers", false);
pref("devtools.debugger.promise", false);
// The default Debugger UI settings
pref("devtools.debugger.ui.panes-workers-and-sources-width", 200);

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

@ -55,6 +55,7 @@ skip-if = !crashreporter
[browser_CTP_data_urls.js]
[browser_CTP_drag_drop.js]
[browser_CTP_hide_overlay.js]
skip-if = true # Bug 1160788
[browser_CTP_iframe.js]
skip-if = os == 'linux' || os == 'mac' # Bug 984821
[browser_CTP_multi_allow.js]

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

@ -17,6 +17,8 @@ loader.lazyRequireGetter(this, "Actors",
"devtools/performance/actors");
loader.lazyRequireGetter(this, "RecordingModel",
"devtools/performance/recording-model", true);
loader.lazyRequireGetter(this, "normalizePerformanceFeatures",
"devtools/performance/recording-utils", true);
loader.lazyRequireGetter(this, "DevToolsUtils",
"devtools/toolkit/DevToolsUtils");
@ -316,16 +318,21 @@ PerformanceFront.prototype = {
* A promise that is resolved once recording has started.
*/
startRecording: Task.async(function*(options = {}) {
let model = new RecordingModel(options);
let model = new RecordingModel(normalizePerformanceFeatures(options, this.getActorSupport()));
this.emit("recording-starting", model);
// All actors are started asynchronously over the remote debugging protocol.
// Get the corresponding start times from each one of them.
// The timeline and memory actors are target-dependent, so start those as well,
// even though these are mocked in older Geckos (FF < 35)
let { startTime, position, generation, totalSize } = yield this._profiler.start(options);
let timelineStartTime = yield this._timeline.start(options);
let memoryStartTime = yield this._memory.start(options);
let profilerStart = this._profiler.start(options);
let timelineStart = this._timeline.start(options);
let memoryStart = this._memory.start(options);
let { startTime, position, generation, totalSize } = yield profilerStart;
let timelineStartTime = yield timelineStart;
let memoryStartTime = yield memoryStart;
let data = {
profilerStartTime: startTime, timelineStartTime, memoryStartTime,

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

@ -27,6 +27,10 @@ const GECKO_SYMBOL = "(Gecko)";
* determines if this marker should be filtered or not.
*/
function isMarkerValid (marker, filter) {
if (!filter || filter.length === 0) {
return true;
}
let isUnknown = !(marker.name in TIMELINE_BLUEPRINT);
if (isUnknown) {
return filter.indexOf("UNKNOWN") === -1;
@ -292,80 +296,6 @@ const DOM = {
}
};
/**
* A series of collapsers used by the blueprint. These functions are
* invoked on a moving window of two markers.
*
* A function determining how markers are collapsed together.
* Invoked with 3 arguments: the current parent marker, the
* current marker and a method for peeking i markers ahead. If
* nothing is returned, the marker is added as a standalone entry
* in the waterfall. Otherwise, an object needs to be returned
* with the following properties:
* - toParent: The marker to be made a new parent. Can use the current
* marker, becoming a parent itself, or make a new marker-esque
* object.
* - collapse: Whether or not this current marker should be nested within
* the current parent.
* - finalize: Whether or not the current parent should be finalized and popped
* off the stack.
*/
const CollapseFunctions = {
/**
* Combines similar markers that are consecutive into a meta marker.
*/
identical: function (parent, curr, peek) {
let next = peek(1);
// If there is a parent marker currently being filled and the current marker
// should go into the parent marker, make it so.
if (parent && parent.name == curr.name) {
let finalize = next && next.name !== curr.name;
return { collapse: true, finalize };
}
// Otherwise if the current marker is the same type as the next marker type,
// create a new parent marker containing the current marker.
if (next && curr.name == next.name) {
return { toParent: { name: curr.name, start: curr.start }, collapse: true };
}
},
/**
* Combines similar markers that are close to each other in time into a meta marker.
*/
adjacent: function (parent, curr, peek) {
let next = peek(1);
if (next && (next.start < curr.end || next.start - curr.end <= 10 /* ms */)) {
return CollapseFunctions.identical(parent, curr, peek);
}
},
/**
* Folds this marker in parent marker if parent marker fully eclipses
* the current markers' time.
*/
child: function (parent, curr, peek) {
let next = peek(1);
// If this marker is consumed by current parent, collapse
if (parent && curr.end <= parent.end) {
let finalize = next && next.end > parent.end;
return { collapse: true, finalize };
}
},
/**
* Turns this marker into a parent marker if the next marker
* is fully eclipsed by the current marker.
*/
parent: function (parent, curr, peek) {
let next = peek(1);
// If the next marker is fully consumed by this marker, make
// it a parent (do not collapse, the marker becomes a parent).
if (next && curr.end >= next.end) {
return { toParent: curr };
}
},
};
/**
* Mapping of JS marker causes to a friendlier form. Only
* markers that are considered "from content" should be labeled here.
@ -480,6 +410,5 @@ exports.getMarkerLabel = getMarkerLabel;
exports.getMarkerClassName = getMarkerClassName;
exports.getMarkerFields = getMarkerFields;
exports.DOM = DOM;
exports.CollapseFunctions = CollapseFunctions;
exports.Formatters = Formatters;
exports.getBlueprintFor = getBlueprintFor;

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

@ -4,12 +4,45 @@
"use strict";
const { Cc, Ci, Cu, Cr } = require("chrome");
loader.lazyRequireGetter(this, "extend",
"sdk/util/object", true);
/**
* Utility functions for managing recording models and their internal data,
* such as filtering profile samples or offsetting timestamps.
*/
/**
* Takes an options object for `startRecording`, and normalizes
* it based off of server support. For example, if the user
* requests to record memory `withMemory = true`, but the server does
* not support that feature, then the `false` will overwrite user preference
* in order to define the recording with what is actually available, not
* what the user initially requested.
*
* @param {object} options
* @param {boolean} support.timeline
* @param {boolean} support.memory
* @param {boolean}
*/
function normalizePerformanceFeatures (options, support) {
let supportOptions = Object.create(null);
// TODO bug 1172180 disable `withAllocations` and `withJITOptimizations` when using the
// pseudo front, as we only want to support it directly from the real actor
// in Fx42+
if (!support.memory) {
supportOptions.withMemory = false;
supportOptions.withAllocations = false;
}
if (!support.timeline) {
supportOptions.withMarkers = false;
supportOptions.withTicks = false;
}
return extend(options, supportOptions);
}
/**
* Filters all the samples in the provided profiler data to be more recent
* than the specified start time.
@ -531,6 +564,7 @@ UniqueStacks.prototype.getOrAddStringIndex = function(s) {
return this._uniqueStrings.getOrAddStringIndex(s);
};
exports.normalizePerformanceFeatures = normalizePerformanceFeatures;
exports.filterSamples = filterSamples;
exports.offsetSampleTimes = offsetSampleTimes;
exports.offsetMarkerTimes = offsetMarkerTimes;

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

@ -7,17 +7,32 @@
* Utility functions for collapsing markers into a waterfall.
*/
loader.lazyRequireGetter(this, "extend",
"sdk/util/object", true);
loader.lazyRequireGetter(this, "MarkerUtils",
"devtools/performance/marker-utils");
/**
* Creates a parent marker, which functions like a regular marker,
* but is able to hold additional child markers.
*
* The marker is seeded with values from `marker`.
* @param object marker
* @return object
*/
function createParentNode (marker) {
return extend(marker, { submarkers: [] });
}
/**
* Collapses markers into a tree-like structure.
* @param object markerNode
* @param object rootNode
* @param array markersList
* @param array filter
*/
function collapseMarkersIntoNode({ markerNode, markersList, filter }) {
let { getCurrentParentNode, collapseMarker, addParentNode, popParentNode } = createParentNodeFactory(markerNode);
function collapseMarkersIntoNode({ rootNode, markersList, filter }) {
let { getCurrentParentNode, pushNode, popParentNode } = createParentNodeFactory(rootNode);
for (let i = 0, len = markersList.length; i < len; i++) {
let curr = markersList[i];
@ -29,48 +44,52 @@ function collapseMarkersIntoNode({ markerNode, markersList, filter }) {
let parentNode = getCurrentParentNode();
let blueprint = MarkerUtils.getBlueprintFor(curr);
let collapse = blueprint.collapseFunc || (() => null);
let peek = distance => markersList[i + distance];
let collapseInfo = collapse(parentNode, curr, peek);
if (collapseInfo) {
let { collapse, toParent, finalize } = collapseInfo;
let nestable = "nestable" in blueprint ? blueprint.nestable : true;
let collapsible = "collapsible" in blueprint ? blueprint.collapsible : true;
// If `toParent` is an object, use it as the next parent marker
if (typeof toParent === "object") {
addParentNode(toParent);
let finalized = null;
// If this marker is collapsible, turn it into a parent marker.
// If there are no children within it later, it will be turned
// back into a normal node.
if (collapsible) {
curr = createParentNode(curr);
}
if (collapse) {
collapseMarker(curr);
// If not nestible, just push it inside the root node,
// like console.time/timeEnd.
if (!nestable) {
pushNode(rootNode, curr);
continue;
}
// If the marker specifies this parent marker is full,
// pop it from the stack.
if (finalize) {
// First off, if any parent nodes exist, finish them off
// recursively upwards if this marker is outside their ranges and nestable.
while (!finalized && parentNode) {
// If this marker is eclipsed by the current parent marker,
// make it a child of the current parent and stop
// going upwards.
if (nestable && curr.end <= parentNode.end) {
pushNode(parentNode, curr);
finalized = true;
break;
}
// If this marker is still nestable, but outside of the range
// of the current parent, iterate upwards on the next parent
// and finalize the current parent.
if (nestable) {
popParentNode();
}
} else {
markerNode.submarkers.push(curr);
}
parentNode = getCurrentParentNode();
continue;
}
}
/**
* Creates a parent marker, which functions like a regular marker,
* but is able to hold additional child markers.
*
* The marker is seeded with values from `marker`.
* @param object marker
* @return object
*/
function makeParentMarkerNode (marker) {
let node = Object.create(null);
for (let prop in marker) {
node[prop] = marker[prop];
if (!finalized) {
pushNode(rootNode, curr);
}
}
node.submarkers = [];
return node;
}
/**
@ -98,6 +117,14 @@ function createParentNodeFactory (root) {
if (lastParent.end == void 0) {
lastParent.end = lastParent.submarkers[lastParent.submarkers.length - 1].end;
}
// If no children were ever pushed into this parent node,
// remove it's submarkers so it behaves like a non collapsible
// node.
if (!lastParent.submarkers.length) {
delete lastParent.submarkers;
}
return lastParent;
},
@ -106,29 +133,22 @@ function createParentNodeFactory (root) {
*/
getCurrentParentNode: () => parentMarkers.length ? parentMarkers[parentMarkers.length - 1] : null,
/**
* Push a new parent node onto the stack and nest it with the
* next most recent parent node, or root if no other parent nodes.
*/
addParentNode: (marker) => {
let parentMarker = makeParentMarkerNode(marker);
(factory.getCurrentParentNode() || root).submarkers.push(parentMarker);
parentMarkers.push(parentMarker);
},
/**
* Push this marker into the most recent parent node.
*/
collapseMarker: (marker) => {
if (parentMarkers.length === 0) {
throw new Error("Cannot collapse marker with no parents.");
pushNode: (parent, marker) => {
parent.submarkers.push(marker);
// If pushing a parent marker, track it as the top of
// the parent stack.
if (marker.submarkers) {
parentMarkers.push(marker);
}
factory.getCurrentParentNode().submarkers.push(marker);
}
};
return factory;
}
exports.makeParentMarkerNode = makeParentMarkerNode;
exports.createParentNode = createParentNode;
exports.collapseMarkersIntoNode = collapseMarkersIntoNode;

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

@ -4,7 +4,7 @@
"use strict";
const { L10N } = require("devtools/performance/global");
const { Formatters, CollapseFunctions: collapse } = require("devtools/performance/marker-utils");
const { Formatters } = require("devtools/performance/marker-utils");
/**
* A simple schema for mapping markers to the timeline UI. The keys correspond
@ -23,19 +23,11 @@ const { Formatters, CollapseFunctions: collapse } = require("devtools/performanc
* for `.marker-details-bullet.{COLORNAME}` for the equivilent
* entry in ./browser/themes/shared/devtools/performance.inc.css
* https://developer.mozilla.org/en-US/docs/Tools/DevToolsColors
* - collapseFunc: A function determining how markers are collapsed together.
* Invoked with 3 arguments: the current parent marker, the
* current marker and a method for peeking i markers ahead. If
* nothing is returned, the marker is added as a standalone entry
* in the waterfall. Otherwise, an object needs to be returned
* with the following properties:
* - toParent: The marker to be made a new parent. Can use the current
* marker, becoming a parent itself, or make a new marker-esque
* object.
* - collapse: Whether or not this current marker should be nested within
* the current parent.
* - finalize: Whether or not the current parent should be finalized and popped
* off the stack.
* - collapsible: Whether or not this marker can contain other markers it
* eclipses, and becomes collapsible to reveal its nestable children.
* Defaults to true.
* - nestable: Whether or not this marker can be nested inside an eclipsing
* collapsible marker. Defaults to true.
* - fields: An optional array of marker properties you wish to display in the
* marker details view. For example, a field in the array such as
* { property: "aCauseName", label: "Cause" } would render a string
@ -61,28 +53,24 @@ const TIMELINE_BLUEPRINT = {
"UNKNOWN": {
group: 2,
colorName: "graphs-grey",
collapseFunc: collapse.child,
label: Formatters.UnknownLabel
label: Formatters.UnknownLabel,
},
/* Group 0 - Reflow and Rendering pipeline */
"Styles": {
group: 0,
colorName: "graphs-purple",
collapseFunc: collapse.child,
label: L10N.getStr("timeline.label.styles2"),
fields: Formatters.StylesFields,
},
"Reflow": {
group: 0,
colorName: "graphs-purple",
collapseFunc: collapse.child,
label: L10N.getStr("timeline.label.reflow2"),
},
"Paint": {
group: 0,
colorName: "graphs-green",
collapseFunc: collapse.child,
label: L10N.getStr("timeline.label.paint"),
},
@ -90,33 +78,28 @@ const TIMELINE_BLUEPRINT = {
"DOMEvent": {
group: 1,
colorName: "graphs-yellow",
collapseFunc: collapse.parent,
label: L10N.getStr("timeline.label.domevent"),
fields: Formatters.DOMEventFields,
},
"Javascript": {
group: 1,
colorName: "graphs-yellow",
collapseFunc: either(collapse.parent, collapse.child),
label: Formatters.JSLabel,
fields: Formatters.JSFields
},
"Parse HTML": {
group: 1,
colorName: "graphs-yellow",
collapseFunc: either(collapse.parent, collapse.child),
label: L10N.getStr("timeline.label.parseHTML"),
},
"Parse XML": {
group: 1,
colorName: "graphs-yellow",
collapseFunc: either(collapse.parent, collapse.child),
label: L10N.getStr("timeline.label.parseXML"),
},
"GarbageCollection": {
group: 1,
colorName: "graphs-red",
collapseFunc: either(collapse.parent, collapse.child),
label: Formatters.GCLabel,
fields: [
{ property: "causeName", label: "Reason:" },
@ -126,14 +109,12 @@ const TIMELINE_BLUEPRINT = {
"nsCycleCollector::Collect": {
group: 1,
colorName: "graphs-red",
collapseFunc: either(collapse.parent, collapse.child),
label: "Cycle Collection",
fields: Formatters.CycleCollectionFields,
},
"nsCycleCollector::ForgetSkippable": {
group: 1,
colorName: "graphs-red",
collapseFunc: either(collapse.parent, collapse.child),
label: "Cycle Collection",
fields: Formatters.CycleCollectionFields,
},
@ -147,34 +128,21 @@ const TIMELINE_BLUEPRINT = {
property: "causeName",
label: L10N.getStr("timeline.markerDetail.consoleTimerName")
}],
nestable: false,
collapsible: false,
},
"TimeStamp": {
group: 2,
colorName: "graphs-blue",
collapseFunc: collapse.child,
label: sublabelForProperty(L10N.getStr("timeline.label.timestamp"), "causeName"),
fields: [{
property: "causeName",
label: "Label:"
}],
collapsible: false,
},
};
/**
* Helper for creating a function that returns the first defined result from
* a list of functions passed in as params, in order.
* @param ...function fun
* @return any
*/
function either(...fun) {
return function() {
for (let f of fun) {
let result = f.apply(null, arguments);
if (result !== undefined) return result;
}
}
}
/**
* Takes a main label (like "Timestamp") and a property,
* and returns a marker that will print out the property

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

@ -485,39 +485,28 @@ let PerformanceController = {
},
/**
* Utility method taking the currently selected recording item's features, or optionally passed
* in recording item, as well as the actor support on the server, returning a boolean
* indicating if the requirements pass or not. Used to toggle features' visibility mostly.
* Utility method taking a string or an array of strings of feature names (like
* "withAllocations" or "withMarkers"), and returns whether or not the current
* recording supports that feature, based off of UI preferences and server support.
*
* @option {Array<string>} features
* An array of strings indicating what configuration is needed on the recording
* @option {Array<string>|string} features
* A string or array of strings indicating what configuration is needed on the recording
* model, like `withTicks`, or `withMemory`.
* @option {Array<string>} actors
* An array of strings indicating what actors must exist.
* @option {boolean} mustBeCompleted
* A boolean indicating whether the recording must be either completed or not.
* Setting to undefined will allow either state.
* @param {RecordingModel} recording
* An optional recording model to use instead of the currently selected.
*
* @return boolean
*/
isFeatureSupported: function ({ features, actors, mustBeCompleted }, recording) {
recording = recording || this.getCurrentRecording();
let recordingConfig = recording ? recording.getConfiguration() : {};
let currentCompletedState = recording ? recording.isCompleted() : void 0;
let actorsSupported = gFront.getActorSupport();
if (mustBeCompleted != null && mustBeCompleted !== currentCompletedState) {
return false;
}
if (actors && !actors.every(a => actorsSupported[a])) {
return false;
}
if (features && !features.every(f => recordingConfig[f])) {
return false;
}
isFeatureSupported: function (features) {
if (!features) {
return true;
}
let recording = this.getCurrentRecording();
if (!recording) {
return false;
}
let config = recording.getConfiguration();
return [].concat(features).every(f => config[f]);
},
/**

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

@ -27,6 +27,7 @@ support-files =
[browser_perf-compatibility-05.js]
[browser_perf-compatibility-06.js]
[browser_perf-compatibility-07.js]
[browser_perf-compatibility-08.js]
[browser_perf-clear-01.js]
[browser_perf-clear-02.js]
[browser_perf-columns-js-calltree.js]

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

@ -66,8 +66,8 @@ let test = Task.async(function*() {
"waterfall view button hidden when timeline mocked");
is($("#select-js-calltree-view").hidden, false,
"jscalltree view button not hidden when timeline/memory mocked");
is($("#select-js-flamegraph-view").hidden, true,
"jsflamegraph view button hidden when timeline mocked");
is($("#select-js-flamegraph-view").hidden, false,
"jsflamegraph view button not hidden when timeline mocked");
is($("#select-memory-calltree-view").hidden, true,
"memorycalltree view button hidden when memory mocked");
is($("#select-memory-flamegraph-view").hidden, true,

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

@ -23,7 +23,6 @@ let test = Task.async(function*() {
yield startRecording(panel);
yield busyWait(100);
yield waitUntil(() => PerformanceController.getCurrentRecording().getTicks().length);
yield waitUntil(() => PerformanceController.getCurrentRecording().getMemory().length);
yield waitUntil(() => PerformanceController.getCurrentRecording().getMarkers().length);
yield stopRecording(panel);

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

@ -0,0 +1,136 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests that when setting recording features in the UI (like enabling framerate or memory),
* if the target does not support these features, then the target's support overrides
* the UI preferences when fetching configuration from a recording.
*/
const WAIT_TIME = 100;
let test = Task.async(function*() {
yield testMockMemory();
yield testMockMemoryAndTimeline();
finish();
});
// Test mock memory
function *testMockMemory () {
let { target, panel, toolbox } = yield initPerformance(SIMPLE_URL, "performance", {
TEST_MOCK_MEMORY_ACTOR: true,
});
Services.prefs.setBoolPref(MEMORY_PREF, true);
Services.prefs.setBoolPref(FRAMERATE_PREF, true);
Services.prefs.setBoolPref(ALLOCATIONS_PREF, true);
let { EVENTS, $, gFront, PerformanceController, PerformanceView, DetailsView, WaterfallView } = panel.panelWin;
let { memory: memorySupport, timeline: timelineSupport } = gFront.getActorSupport();
yield startRecording(panel, { waitForOverview: false });
yield waitUntil(() => PerformanceController.getCurrentRecording().getTicks().length);
yield waitUntil(() => PerformanceController.getCurrentRecording().getMarkers().length);
yield stopRecording(panel, { waitForOverview: false });
let config = PerformanceController.getCurrentRecording().getConfiguration();
let {
markers, allocations, memory, ticks
} = PerformanceController.getCurrentRecording().getAllData();
ok(typeof config.bufferSize === "number", "sanity check, config options contains `bufferSize`.");
is(config.withMemory, false,
"Recording configuration set by target's support, not by UI prefs [No Memory Actor: withMemory]");
is(config.withAllocations, false,
"Recording configuration set by target's support, not by UI prefs [No Memory Actor: withAllocations]");
is(config.withMarkers, true,
"Recording configuration set by target's support, not by UI prefs [No Memory Actor: withMarkers]");
is(config.withTicks, true,
"Recording configuration set by target's support, not by UI prefs [No Memory Actor: withTicks]");
ok(markers.length > 0, "markers exist.");
ok(ticks.length > 0, "ticks exist.");
isEmptyArray(memory, "memory");
isEmptyArray(allocations.sites, "allocations.sites");
isEmptyArray(allocations.timestamps, "allocations.timestamps");
isEmptyArray(allocations.frames, "allocations.frames");
isEmptyArray(allocations.counts, "allocations.counts");
is($("#overview-pane").hidden, false,
"overview pane not hidden when server not supporting memory actors, yet UI prefs request them.");
is($("#select-waterfall-view").hidden, false,
"waterfall view button not hidden when memory mocked, and UI prefs enable them");
is($("#select-js-calltree-view").hidden, false,
"jscalltree view button not hidden when memory mocked, and UI prefs enable them");
is($("#select-js-flamegraph-view").hidden, false,
"jsflamegraph view button not hidden when memory mocked, and UI prefs enable them");
is($("#select-memory-calltree-view").hidden, true,
"memorycalltree view button hidden when memory mocked, and UI prefs enable them");
is($("#select-memory-flamegraph-view").hidden, true,
"memoryflamegraph view button hidden when memory mocked, and UI prefs enable them");
yield gFront.destroy();
yield teardown(panel);
}
// Test mock memory and timeline actor
function *testMockMemoryAndTimeline() {
let { target, panel, toolbox } = yield initPerformance(SIMPLE_URL, "performance", {
TEST_MOCK_MEMORY_ACTOR: true,
TEST_MOCK_TIMELINE_ACTOR: true,
});
Services.prefs.setBoolPref(MEMORY_PREF, true);
Services.prefs.setBoolPref(FRAMERATE_PREF, true);
Services.prefs.setBoolPref(ALLOCATIONS_PREF, true);
let { EVENTS, $, gFront, PerformanceController, PerformanceView, DetailsView, WaterfallView } = panel.panelWin;
let { memory: memorySupport, timeline: timelineSupport } = gFront.getActorSupport();
yield startRecording(panel, { waitForOverview: false });
yield busyWait(WAIT_TIME);
yield stopRecording(panel, { waitForOverview: false });
let config = PerformanceController.getCurrentRecording().getConfiguration();
let {
markers, allocations, memory, ticks
} = PerformanceController.getCurrentRecording().getAllData();
ok(typeof config.bufferSize === "number", "sanity check, config options contains `bufferSize`.");
is(config.withMemory, false,
"Recording configuration set by target's support, not by UI prefs [No Memory/Timeline Actor: withMemory]");
is(config.withAllocations, false,
"Recording configuration set by target's support, not by UI prefs [No Memory/Timeline Actor: withAllocations]");
is(config.withMarkers, false,
"Recording configuration set by target's support, not by UI prefs [No Memory/Timeline Actor: withMarkers]");
is(config.withTicks, false,
"Recording configuration set by target's support, not by UI prefs [No Memory/Timeline Actor: withTicks]");
isEmptyArray(markers, "markers");
isEmptyArray(ticks, "ticks");
isEmptyArray(memory, "memory");
isEmptyArray(allocations.sites, "allocations.sites");
isEmptyArray(allocations.timestamps, "allocations.timestamps");
isEmptyArray(allocations.frames, "allocations.frames");
isEmptyArray(allocations.counts, "allocations.counts");
is($("#overview-pane").hidden, true,
"overview pane hidden when server not supporting memory/timeline actors, yet UI prefs request them.");
is($("#select-waterfall-view").hidden, true,
"waterfall view button hidden when memory/timeline mocked, and UI prefs enable them");
is($("#select-js-calltree-view").hidden, false,
"jscalltree view button not hidden when memory/timeline mocked, and UI prefs enable them");
is($("#select-js-flamegraph-view").hidden, false,
"jsflamegraph view button not hidden when memory/timeline mocked, and UI prefs enable them");
is($("#select-memory-calltree-view").hidden, true,
"memorycalltree view button hidden when memory/timeline mocked, and UI prefs enable them");
is($("#select-memory-flamegraph-view").hidden, true,
"memoryflamegraph view button hidden when memory/timeline mocked, and UI prefs enable them");
yield gFront.destroy();
yield teardown(panel);
}
function isEmptyArray (array, name) {
ok(Array.isArray(array), `${name} is an array`);
is(array.length, 0, `${name} is empty`);
}

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

@ -5,13 +5,17 @@
* Tests if the waterfall collapsing logic works properly.
*/
function test() {
function run_test() {
run_next_test();
}
add_task(function test() {
const WaterfallUtils = devtools.require("devtools/performance/waterfall-utils");
let rootMarkerNode = WaterfallUtils.makeParentMarkerNode({ name: "(root)" });
let rootMarkerNode = WaterfallUtils.createParentNode({ name: "(root)" });
WaterfallUtils.collapseMarkersIntoNode({
markerNode: rootMarkerNode,
rootNode: rootMarkerNode,
markersList: gTestMarkers
});
@ -22,14 +26,13 @@ function test() {
compare(marker.submarkers[i], expected.submarkers[i]);
}
} else if (prop !== "uid") {
is(marker[prop], expected[prop], `${expected.name} matches ${prop}`);
equal(marker[prop], expected[prop], `${expected.name} matches ${prop}`);
}
}
}
compare(rootMarkerNode, gExpectedOutput);
finish();
}
});
const gTestMarkers = [
{ start: 1, end: 18, name: "DOMEvent" },
@ -44,7 +47,7 @@ const gTestMarkers = [
{ start: 12, end: 13, name: "Parse XML" },
{ start: 14, end: 15, name: "GarbageCollection" },
// Test that JS markers can be parents without being a child of DOM events
{ start: 25, end: 30, name: "JavaScript" },
{ start: 25, end: 30, name: "Javascript" },
{ start: 26, end: 27, name: "Paint" },
];
@ -61,7 +64,7 @@ const gExpectedOutput = {
{ start: 14, end: 15, name: "GarbageCollection" },
]}
]},
{ start: 25, end: 30, name: "JavaScript", submarkers: [
{ start: 25, end: 30, name: "Javascript", submarkers: [
{ start: 26, end: 27, name: "Paint" },
]}
]};

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

@ -6,13 +6,17 @@
* markers, as they should ignore any sort of collapsing.
*/
function test() {
function run_test() {
run_next_test();
}
add_task(function test() {
const WaterfallUtils = devtools.require("devtools/performance/waterfall-utils");
let rootMarkerNode = WaterfallUtils.makeParentMarkerNode({ name: "(root)" });
let rootMarkerNode = WaterfallUtils.createParentNode({ name: "(root)" });
WaterfallUtils.collapseMarkersIntoNode({
markerNode: rootMarkerNode,
rootNode: rootMarkerNode,
markersList: gTestMarkers
});
@ -23,14 +27,13 @@ function test() {
compare(marker.submarkers[i], expected.submarkers[i]);
}
} else if (prop !== "uid") {
is(marker[prop], expected[prop], `${expected.name} matches ${prop}`);
equal(marker[prop], expected[prop], `${expected.name} matches ${prop}`);
}
}
}
compare(rootMarkerNode, gExpectedOutput);
finish();
}
});
const gTestMarkers = [
{ start: 2, end: 9, name: "Javascript" },

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

@ -0,0 +1,63 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests that the waterfall collapsing works when atleast two
* collapsible markers downward, and the following marker is outside of both ranges.
*/
function run_test() {
run_next_test();
}
add_task(function test() {
const WaterfallUtils = devtools.require("devtools/performance/waterfall-utils");
let rootMarkerNode = WaterfallUtils.createParentNode({ name: "(root)" });
WaterfallUtils.collapseMarkersIntoNode({
rootNode: rootMarkerNode,
markersList: gTestMarkers
});
function compare (marker, expected) {
for (let prop in expected) {
if (prop === "submarkers") {
for (let i = 0; i < expected.submarkers.length; i++) {
compare(marker.submarkers[i], expected.submarkers[i]);
}
} else if (prop !== "uid") {
equal(marker[prop], expected[prop], `${expected.name} matches ${prop}`);
}
}
}
compare(rootMarkerNode, gExpectedOutput);
});
const gTestMarkers = [
{ start: 2, end: 10, name: "DOMEvent" },
{ start: 3, end: 9, name: "Javascript" },
{ start: 4, end: 8, name: "GarbageCollection" },
{ start: 11, end: 12, name: "Styles" },
{ start: 13, end: 14, name: "Styles" },
{ start: 15, end: 25, name: "DOMEvent" },
{ start: 17, end: 24, name: "Javascript" },
{ start: 18, end: 19, name: "GarbageCollection" },
];
const gExpectedOutput = {
name: "(root)", submarkers: [
{ start: 2, end: 10, name: "DOMEvent", submarkers: [
{ start: 3, end: 9, name: "Javascript", submarkers: [
{ start: 4, end: 8, name: "GarbageCollection" }
]}
]},
{ start: 11, end: 12, name: "Styles" },
{ start: 13, end: 14, name: "Styles" },
{ start: 15, end: 25, name: "DOMEvent", submarkers: [
{ start: 17, end: 24, name: "Javascript", submarkers: [
{ start: 18, end: 19, name: "GarbageCollection" }
]}
]},
]};

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

@ -24,3 +24,4 @@ skip-if = toolkit == 'android' || toolkit == 'gonk'
[test_tree-model-09.js]
[test_waterfall-utils-collapse-01.js]
[test_waterfall-utils-collapse-02.js]
[test_waterfall-utils-collapse-03.js]

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

@ -136,10 +136,10 @@ let WaterfallView = Heritage.extend(DetailsSubview, {
return cached;
}
let rootMarkerNode = WaterfallUtils.makeParentMarkerNode({ name: "(root)" });
let rootMarkerNode = WaterfallUtils.createParentNode({ name: "(root)" });
WaterfallUtils.collapseMarkersIntoNode({
markerNode: rootMarkerNode,
rootNode: rootMarkerNode,
markersList: markers,
filter: this._hiddenMarkers
});

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

@ -16,7 +16,6 @@ let DetailsView = {
"waterfall": {
id: "waterfall-view",
view: WaterfallView,
actors: ["timeline"],
features: ["withMarkers"]
},
"js-calltree": {
@ -26,18 +25,15 @@ let DetailsView = {
"js-flamegraph": {
id: "js-flamegraph-view",
view: JsFlameGraphView,
actors: ["timeline"]
},
"memory-calltree": {
id: "memory-calltree-view",
view: MemoryCallTreeView,
actors: ["memory"],
features: ["withAllocations"]
},
"memory-flamegraph": {
id: "memory-flamegraph-view",
view: MemoryFlameGraphView,
actors: ["memory", "timeline"],
features: ["withAllocations"]
},
"optimizations": {
@ -97,7 +93,7 @@ let DetailsView = {
let invalidCurrentView = false;
for (let [name, { view }] of Iterator(this.components)) {
let isSupported = this._isViewSupported(name, true);
let isSupported = this._isViewSupported(name);
$(`toolbarbutton[data-view=${name}]`).hidden = !isSupported;
@ -123,15 +119,21 @@ let DetailsView = {
}),
/**
* Takes a view name and optionally if there must be a currently recording in progress.
* Takes a view name and determines if the current recording
* can support the view.
*
* @param {string} viewName
* @param {boolean?} mustBeCompleted
* @return {boolean}
*/
_isViewSupported: function (viewName, mustBeCompleted) {
let { features, actors } = this.components[viewName];
return PerformanceController.isFeatureSupported({ features, actors, mustBeCompleted });
_isViewSupported: function (viewName) {
let { features } = this.components[viewName];
let recording = PerformanceController.getCurrentRecording();
if (!recording || !recording.isCompleted()) {
return false;
}
return PerformanceController.isFeatureSupported(features);
},
/**

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

@ -11,15 +11,12 @@ const FRAMERATE_GRAPH_LOW_RES_INTERVAL = 100; // ms
const FRAMERATE_GRAPH_HIGH_RES_INTERVAL = 16; // ms
const GRAPH_REQUIREMENTS = {
timeline: {
actors: ["timeline"],
features: ["withMarkers"]
},
framerate: {
actors: ["timeline"],
features: ["withTicks"]
},
memory: {
actors: ["memory"],
features: ["withMemory"]
},
}
@ -342,7 +339,7 @@ let OverviewView = {
_setGraphVisibilityFromRecordingFeatures: function (recording) {
for (let [graphName, requirements] of Iterator(GRAPH_REQUIREMENTS)) {
this.graphs.enable(graphName, PerformanceController.isFeatureSupported(requirements));
this.graphs.enable(graphName, PerformanceController.isFeatureSupported(requirements.features));
}
},

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

@ -86,6 +86,7 @@ skip-if = e10s # Layouthelpers test should not run in a content page.
skip-if = e10s # Layouthelpers test should not run in a content page.
[browser_mdn-docs-01.js]
[browser_mdn-docs-02.js]
[browser_mdn-docs-03.js]
[browser_num-l10n.js]
[browser_observableobject.js]
[browser_options-view-01.js]

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

@ -38,7 +38,13 @@ const MDN_DOCS_TOOLTIP_FRAME = "chrome://browser/content/devtools/mdn-docs-frame
const BASIC_TESTING_PROPERTY = "html-mdn-css-basic-testing.html";
const BASIC_EXPECTED_SUMMARY = "A summary of the property.";
const BASIC_EXPECTED_SYNTAX = "/* The part we want */\nthis: is-the-part-we-want";
const BASIC_EXPECTED_SYNTAX = [{type: "comment", text: "/* The part we want */"},
{type: "text", text: "\n"},
{type: "property-name", text: "this"},
{type: "text", text: ":"},
{type: "text", text: " "},
{type: "property-value", text: "is-the-part-we-want"},
{type: "text", text: ";"}];
const URI_PARAMS = "?utm_source=mozilla&utm_medium=firefox-inspector&utm_campaign=default";
@ -168,7 +174,5 @@ function checkTooltipContents(doc, expected) {
expected.summary,
"Summary is correct");
is(doc.syntax.textContent,
expected.syntax,
"Syntax is correct");
checkCssSyntaxHighlighterOutput(expected.syntax, doc.syntax);
}

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

@ -28,7 +28,14 @@ const {setBaseCssDocsUrl, MdnDocsWidget} = devtools.require("devtools/shared/wid
const MDN_DOCS_TOOLTIP_FRAME = "chrome://browser/content/devtools/mdn-docs-frame.xhtml";
const BASIC_EXPECTED_SUMMARY = "A summary of the property.";
const BASIC_EXPECTED_SYNTAX = "/* The part we want */\nthis: is-the-part-we-want";
const BASIC_EXPECTED_SYNTAX = [{type: "comment", text: "/* The part we want */"},
{type: "text", text: "\n"},
{type: "property-name", text: "this"},
{type: "text", text: ":"},
{type: "text", text: " "},
{type: "property-value", text: "is-the-part-we-want"},
{type: "text", text: ";"}];
const ERROR_MESSAGE = "Could not load docs page.";
/**
@ -52,7 +59,7 @@ const TEST_DATA = [{
expectedContents: {
propertyName: "i-dont-exist.html",
summary: ERROR_MESSAGE,
syntax: ""
syntax: []
}
}, {
desc: "Test a property whose syntax section is specified using an old-style page",
@ -76,7 +83,7 @@ const TEST_DATA = [{
expectedContents: {
propertyName: NO_SYNTAX,
summary: BASIC_EXPECTED_SUMMARY,
syntax: ""
syntax: []
}
}, {
desc: "Test a property whose page doesn't have a summary or a syntax",
@ -84,7 +91,7 @@ const TEST_DATA = [{
expectedContents: {
propertyName: NO_SUMMARY_OR_SYNTAX,
summary: ERROR_MESSAGE,
syntax: ""
syntax: []
}
}
];
@ -128,7 +135,5 @@ function checkTooltipContents(doc, expected) {
expected.summary,
"Summary is correct");
is(doc.syntax.textContent,
expected.syntax,
"Syntax is correct");
checkCssSyntaxHighlighterOutput(expected.syntax, doc.syntax);
}

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

@ -0,0 +1,277 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* This file tests the CSS syntax highlighter in the MdnDocsWidget object.
*
* The CSS syntax highlighter accepts:
* - a string containing CSS
* - a DOM node
*
* It parses the string and creates a collection of DOM nodes for different
* CSS token types. These DOM nodes have CSS classes applied to them,
* to apply the right style for that particular token type. The DOM nodes
* are returned as children of the node that was passed to the function.
*
* This test code defines a number of different strings containing valid and
* invalid CSS in various forms. For each string it defines the DOM nodes
* that it expects to get from the syntax highlighter.
*
* It then calls the syntax highlighter, and checks that the resulting
* collection of DOM nodes is what we expected.
*/
"use strict";
const {appendSyntaxHighlightedCSS} = devtools.require("devtools/shared/widgets/MdnDocsWidget");
/**
* An array containing the actual test cases.
*
* The test code tests every case in the array. If you want to add more
* test cases, just add more items to the array.
*
* Each test case consists of:
* - description: string describing the salient features of this test case
* - example: the string to test
* - expected: an array of objects, one for each DOM node we expect, that
* captures the information about the node that we expect to test.
*/
const TEST_DATA = [{
description: "Valid syntax, string value.",
example: "name: stringValue;",
expected: [{type: "property-name", text: "name"},
{type: "text", text: ":"},
{type: "text", text: " "},
{type: "property-value", text: "stringValue"},
{type: "text", text: ";"}
]}, {
description: "Valid syntax, numeric value.",
example: "name: 1;",
expected: [{type: "property-name", text: "name"},
{type: "text", text: ":"},
{type: "text", text: " "},
{type: "property-value", text: "1"},
{type: "text", text: ";"}
]}, {
description: "Valid syntax, url value.",
example: "name: url(./name);",
expected: [{type: "property-name", text: "name"},
{type: "text", text: ":"},
{type: "text", text: " "},
{type: "property-value", text: "url(./name)"},
{type: "text", text: ";"}
]}, {
description: "Valid syntax, space before ':'.",
example: "name : stringValue;",
expected: [{type: "property-name", text: "name"},
{type: "text", text: " "},
{type: "text", text: ":"},
{type: "text", text: " "},
{type: "property-value", text: "stringValue"},
{type: "text", text: ";"}
]}, {
description: "Valid syntax, space before ';'.",
example: "name: stringValue ;",
expected: [{type: "property-name", text: "name"},
{type: "text", text: ":"},
{type: "text", text: " "},
{type: "property-value", text: "stringValue"},
{type: "text", text: " "},
{type: "text", text: ";"}
]}, {
description: "Valid syntax, trailing space.",
example: "name: stringValue; ",
expected: [{type: "property-name", text: "name"},
{type: "text", text: ":"},
{type: "text", text: " "},
{type: "property-value", text: "stringValue"},
{type: "text", text: ";"},
{type: "text", text: " "}
]}, {
description: "Valid syntax, leading space.",
example: " name: stringValue;",
expected: [{type: "text", text: " "},
{type: "property-name", text: "name"},
{type: "text", text: ":"},
{type: "text", text: " "},
{type: "property-value", text: "stringValue"},
{type: "text", text: ";"}
]}, {
description: "Valid syntax, two spaces.",
example: "name: stringValue;",
expected: [{type: "property-name", text: "name"},
{type: "text", text: ":"},
{type: "text", text: " "},
{type: "property-value", text: "stringValue"},
{type: "text", text: ";"}
]}, {
description: "Valid syntax, no spaces.",
example: "name:stringValue;",
expected: [{type: "property-name", text: "name"},
{type: "text", text: ":"},
{type: "property-value", text: "stringValue"},
{type: "text", text: ";"}
]}, {
description: "Valid syntax, two-part value.",
example: "name: stringValue 1;",
expected: [{type: "property-name", text: "name"},
{type: "text", text: ":"},
{type: "text", text: " "},
{type: "property-value", text: "stringValue"},
{type: "text", text: " "},
{type: "property-value", text: "1"},
{type: "text", text: ";"}
]}, {
description: "Valid syntax, two declarations.",
example: "name: stringValue;\n" +
"name: 1;",
expected: [{type: "property-name", text: "name"},
{type: "text", text: ":"},
{type: "text", text: " "},
{type: "property-value", text: "stringValue"},
{type: "text", text: ";"},
{type: "text", text: "\n"},
{type: "property-name", text: "name"},
{type: "text", text: ":"},
{type: "text", text: " "},
{type: "property-value", text: "1"},
{type: "text", text: ";"}
]}, {
description: "Valid syntax, commented, numeric value.",
example: "/* comment */\n" +
"name: 1;",
expected: [{type: "comment", text: "/* comment */"},
{type: "text", text: "\n"},
{type: "property-name", text: "name"},
{type: "text", text: ":"},
{type: "text", text: " "},
{type: "property-value", text: "1"},
{type: "text", text: ";"}
]}, {
description: "Valid syntax, multiline commented, string value.",
example: "/* multiline \n" +
"comment */\n" +
"name: stringValue;",
expected: [{type: "comment", text: "/* multiline \ncomment */"},
{type: "text", text: "\n"},
{type: "property-name", text: "name"},
{type: "text", text: ":"},
{type: "text", text: " "},
{type: "property-value", text: "stringValue"},
{type: "text", text: ";"}
]}, {
description: "Valid syntax, commented, two declarations.",
example: "/* comment 1 */\n" +
"name: 1;\n" +
"/* comment 2 */\n" +
"name: stringValue;",
expected: [{type: "comment", text: "/* comment 1 */"},
{type: "text", text: "\n"},
{type: "property-name", text: "name"},
{type: "text", text: ":"},
{type: "text", text: " "},
{type: "property-value", text: "1"},
{type: "text", text: ";"},
{type: "text", text: "\n"},
{type: "comment", text: "/* comment 2 */"},
{type: "text", text: "\n"},
{type: "property-name", text: "name"},
{type: "text", text: ":"},
{type: "text", text: " "},
{type: "property-value", text: "stringValue"},
{type: "text", text: ";"}
]}, {
description: "Valid syntax, multiline.",
example: "name: \n" +
"stringValue;",
expected: [{type: "property-name", text: "name"},
{type: "text", text: ":"},
{type: "text", text: " \n"},
{type: "property-value", text: "stringValue"},
{type: "text", text: ";"}
]}, {
description: "Valid syntax, multiline, two declarations.",
example: "name: \n" +
"stringValue \n" +
"stringValue2;",
expected: [{type: "property-name", text: "name"},
{type: "text", text: ":"},
{type: "text", text: " \n"},
{type: "property-value", text: "stringValue"},
{type: "text", text: " \n"},
{type: "property-value", text: "stringValue2"},
{type: "text", text: ";"}
]}, {
description: "Invalid: not CSS at all.",
example: "not CSS at all",
expected: [{type: "property-name", text: "not"},
{type: "text", text: " "},
{type: "property-name", text: "CSS"},
{type: "text", text: " "},
{type: "property-name", text: "at"},
{type: "text", text: " "},
{type: "property-name", text: "all"}
]}, {
description: "Invalid: switched ':' and ';'.",
example: "name; stringValue:",
expected: [{type: "property-name", text: "name"},
{type: "text", text: ";"},
{type: "text", text: " "},
{type: "property-name", text: "stringValue"},
{type: "text", text: ":"}
]}, {
description: "Invalid: unterminated comment.",
example: "/* unterminated comment\n" +
"name: stringValue;",
expected: [{type: "comment", text: "/* unterminated comment\nname: stringValue;"}
]}, {
description: "Invalid: bad comment syntax.",
example: "// invalid comment\n" +
"name: stringValue;",
expected: [{type: "text", text: "/"},
{type: "text", text: "/"},
{type: "text", text: " "},
{type: "property-name", text: "invalid"},
{type: "text", text: " "},
{type: "property-name", text: "comment"},
{type: "text", text: "\n"},
{type: "property-name", text: "name"},
{type: "text", text: ":"},
{type: "text", text: " "},
{type: "property-value", text: "stringValue"},
{type: "text", text: ";"}
]}, {
description: "Invalid: no trailing ';'.",
example: "name: stringValue\n" +
"name: stringValue2",
expected: [{type: "property-name", text: "name"},
{type: "text", text: ":"},
{type: "text", text: " "},
{type: "property-value", text: "stringValue"},
{type: "text", text: "\n"},
{type: "property-value", text: "name"},
{type: "text", text: ":"},
{type: "text", text: " "},
{type: "property-value", text: "stringValue2"},
]}
];
/**
* Iterate through every test case, calling the syntax highlighter,
* then calling a helper function to check the output.
*/
add_task(function*() {
let doc = gBrowser.selectedTab.ownerDocument;
let parent = doc.createElement("div");
info("Testing all CSS syntax highlighter test cases");
for (let {description, example, expected} of TEST_DATA) {
info("Testing: " + description);
appendSyntaxHighlightedCSS(example, parent);
checkCssSyntaxHighlighterOutput(expected, parent);
while (parent.firstChild) {
parent.firstChild.remove();
}
}
});

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

@ -319,3 +319,104 @@ let showFilterPopupPresetsAndCreatePreset = Task.async(function*(widget, name, v
yield onRender;
});
/**
* Utility function for testing CSS code samples that have been
* syntax-highlighted.
*
* The CSS syntax highlighter emits a collection of DOM nodes that have
* CSS classes applied to them. This function checks that those nodes
* are what we expect.
*
* @param {array} expectedNodes
* A representation of the nodes we expect to see.
* Each node is an object containing two properties:
* - type: a string which can be one of:
* - text, comment, property-name, property-value
* - text: the textContent of the node
*
* For example, given a string like this:
* "<comment> The part we want </comment>\n this: is-the-part-we-want;"
*
* we would represent the expected output like this:
* [{type: "comment", text: "<comment> The part we want </comment>"},
* {type: "text", text: "\n"},
* {type: "property-name", text: "this"},
* {type: "text", text: ":"},
* {type: "text", text: " "},
* {type: "property-value", text: "is-the-part-we-want"},
* {type: "text", text: ";"}];
*
* @param {Node} parent
* The DOM node whose children are the output of the syntax highlighter.
*/
function checkCssSyntaxHighlighterOutput(expectedNodes, parent) {
/**
* The classes applied to the output nodes by the syntax highlighter.
* These must be same as the definitions in MdnDocsWidget.js.
*/
const PROPERTY_NAME_COLOR = "theme-fg-color5";
const PROPERTY_VALUE_COLOR = "theme-fg-color1";
const COMMENT_COLOR = "theme-comment";
/**
* Check the type and content of a single node.
*/
function checkNode(expected, actual) {
ok(actual.textContent == expected.text, "Check that node has the expected textContent");
info("Expected text content: [" + expected.text + "]");
info("Actual text content: [" + actual.textContent + "]");
info("Check that node has the expected type");
if (expected.type == "text") {
ok(actual.nodeType == 3, "Check that node is a text node");
} else {
ok(actual.tagName.toUpperCase() == "SPAN", "Check that node is a SPAN");
}
info("Check that node has the expected className");
let expectedClassName = null;
let actualClassName = null;
switch (expected.type) {
case "property-name":
expectedClassName = PROPERTY_NAME_COLOR;
break;
case "property-value":
expectedClassName = PROPERTY_VALUE_COLOR;
break;
case "comment":
expectedClassName = COMMENT_COLOR;
break;
default:
ok(!actual.classList, "No className expected");
return;
}
ok(actual.classList.length == 1, "One className expected");
actualClassName = actual.classList[0];
ok(expectedClassName == actualClassName, "Check className value");
info("Expected className: " + expectedClassName);
info("Actual className: " + actualClassName);
}
info("Logging the actual nodes we have:");
for (var j = 0; j < parent.childNodes.length; j++) {
var n = parent.childNodes[j];
info(j + " / " +
"nodeType: "+ n.nodeType + " / " +
"textContent: " + n.textContent);
}
ok(parent.childNodes.length == parent.childNodes.length,
"Check we have the expected number of nodes");
info("Expected node count " + expectedNodes.length);
info("Actual node count " + expectedNodes.length);
for (let i = 0; i < expectedNodes.length; i++) {
info("Check node " + i);
checkNode(expectedNodes[i], parent.childNodes[i]);
}
}

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

@ -13,7 +13,7 @@
<h2 id="Syntax">Syntax</h2>
<pre>/* The part we want */
this: is-the-part-we-want</pre>
this: is-the-part-we-want;</pre>

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

@ -13,7 +13,7 @@
<pre>To be ignored.</pre>
<pre>/* The part we want */
this: is-the-part-we-want</pre>
this: is-the-part-we-want;</pre>

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

@ -15,7 +15,7 @@
<pre>The part we should ignore</pre>
<pre>/* The part we want */
this: is-the-part-we-want</pre>
this: is-the-part-we-want;</pre>

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

@ -27,6 +27,8 @@
const {Cc, Cu, Ci} = require("chrome");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Promise.jsm");
const DOMUtils = Cc["@mozilla.org/inspector/dom-utils;1"]
.getService(Ci.inIDOMUtils);
// Parameters for the XHR request
// see https://developer.mozilla.org/en-US/docs/MDN/Kuma/API#Document_parameters
@ -37,12 +39,112 @@ var XHR_CSS_URL = "https://developer.mozilla.org/en-US/docs/Web/CSS/";
// Parameters for the link to MDN in the tooltip, so
// so we know which MDN visits come from this feature
const PAGE_LINK_PARAMS = "?utm_source=mozilla&utm_medium=firefox-inspector&utm_campaign=default"
// URL for the page link
// omits locale, so a locale-specific page will be loaded
// URL for the page link omits locale, so a locale-specific page will be loaded
var PAGE_LINK_URL = "https://developer.mozilla.org/docs/Web/CSS/";
const BROWSER_WINDOW = 'navigator:browser';
const PROPERTY_NAME_COLOR = "theme-fg-color5";
const PROPERTY_VALUE_COLOR = "theme-fg-color1";
const COMMENT_COLOR = "theme-comment";
/**
* Turns a string containing a series of CSS declarations into
* a series of DOM nodes, with classes applied to provide syntax
* highlighting.
*
* It uses the CSS tokenizer to generate a stream of CSS tokens.
* https://mxr.mozilla.org/mozilla-central/source/dom/webidl/CSSLexer.webidl
* lists all the token types.
*
* - "whitespace", "comment", and "symbol" tokens are appended as TEXT nodes,
* and will inherit the default style for text.
*
* - "ident" tokens that we think are property names are considered to be
* a property name, and are appended as SPAN nodes with a distinct color class.
*
* - "ident" nodes which we do not think are property names, and nodes
* of all other types ("number", "url", "percentage", ...) are considered
* to be part of a property value, and are appended as SPAN nodes with
* a different color class.
*
* @param {Document} doc
* Used to create nodes.
*
* @param {String} syntaxText
* The CSS input. This is assumed to consist of a series of
* CSS declarations, with trailing semicolons.
*
* @param {DOM node} syntaxSection
* This is the parent for the output nodes. Generated nodes
* are appended to this as children.
*/
function appendSyntaxHighlightedCSS(cssText, parentElement) {
let doc = parentElement.ownerDocument;
let identClass = PROPERTY_NAME_COLOR;
let lexer = DOMUtils.getCSSLexer(cssText);
/**
* Create a SPAN node with the given text content and class.
*/
function createStyledNode(textContent, className) {
let newNode = doc.createElement("span");
newNode.classList.add(className);
newNode.textContent = textContent;
return newNode;
}
/**
* If the symbol is ":", we will expect the next
* "ident" token to be part of a property value.
*
* If the symbol is ";", we will expect the next
* "ident" token to be a property name.
*/
function updateIdentClass(tokenText) {
if (tokenText === ":") {
identClass = PROPERTY_VALUE_COLOR;
}
else {
if (tokenText === ";") {
identClass = PROPERTY_NAME_COLOR;
}
}
}
/**
* Create the appropriate node for this token type.
*
* If this token is a symbol, also update our expectations
* for what the next "ident" token represents.
*/
function tokenToNode(token, tokenText) {
switch (token.tokenType) {
case "ident":
return createStyledNode(tokenText, identClass);
case "symbol":
updateIdentClass(tokenText);
return doc.createTextNode(tokenText);
case "whitespace":
return doc.createTextNode(tokenText);
case "comment":
return createStyledNode(tokenText, COMMENT_COLOR);
default:
return createStyledNode(tokenText, PROPERTY_VALUE_COLOR);
}
}
let token = lexer.nextToken();
while (token) {
let tokenText = cssText.slice(token.startOffset, token.endOffset);
let newNode = tokenToNode(token, tokenText);
parentElement.appendChild(newNode);
token = lexer.nextToken();
}
}
exports.appendSyntaxHighlightedCSS = appendSyntaxHighlightedCSS;
/**
* Fetch an MDN page.
*
@ -154,6 +256,8 @@ function MdnDocsWidget(tooltipDocument) {
linkToMdn: tooltipDocument.getElementById("visit-mdn-page")
};
this.doc = tooltipDocument;
// get the localized string for the link text
this.elements.linkToMdn.textContent =
l10n.strings.GetStringFromName("docsTooltip.visitMDN");
@ -209,7 +313,9 @@ MdnDocsWidget.prototype = {
// clear docs summary and syntax
elements.summary.textContent = "";
elements.syntax.textContent = "";
while (elements.syntax.firstChild) {
elements.syntax.firstChild.remove();
}
// reset the scroll position
elements.info.scrollTop = 0;
@ -226,7 +332,7 @@ MdnDocsWidget.prototype = {
function finalizeDocument({summary, syntax}) {
// set docs summary and syntax
elements.summary.textContent = summary;
elements.syntax.textContent = syntax;
appendSyntaxHighlightedCSS(syntax, elements.syntax);
// hide the throbber
elements.info.classList.remove("devtools-throbber");
@ -252,11 +358,17 @@ MdnDocsWidget.prototype = {
let deferred = Promise.defer();
let elements = this.elements;
let doc = this.doc;
initializeDocument(propertyName);
getCssDocs(propertyName).then(finalizeDocument, gotError);
return deferred.promise;
},
destroy: function() {
this.elements = null;
this.doc = null;
}
}

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

@ -262,6 +262,62 @@ Section "Uninstall"
Call un.RenameDelete
Push "$INSTDIR\update\updater.exe"
Call un.RenameDelete
Push "$INSTDIR\logs\maintenanceservice.log"
Call un.RenameDelete
Push "$INSTDIR\logs\maintenanceservice-1.log"
Call un.RenameDelete
Push "$INSTDIR\logs\maintenanceservice-2.log"
Call un.RenameDelete
Push "$INSTDIR\logs\maintenanceservice-3.log"
Call un.RenameDelete
Push "$INSTDIR\logs\maintenanceservice-4.log"
Call un.RenameDelete
Push "$INSTDIR\logs\maintenanceservice-5.log"
Call un.RenameDelete
Push "$INSTDIR\logs\maintenanceservice-6.log"
Call un.RenameDelete
Push "$INSTDIR\logs\maintenanceservice-7.log"
Call un.RenameDelete
Push "$INSTDIR\logs\maintenanceservice-8.log"
Call un.RenameDelete
Push "$INSTDIR\logs\maintenanceservice-9.log"
Call un.RenameDelete
Push "$INSTDIR\logs\maintenanceservice-10.log"
Call un.RenameDelete
Push "$INSTDIR\logs\maintenanceservice-install.log"
Call un.RenameDelete
Push "$INSTDIR\logs\maintenanceservice-uninstall.log"
Call un.RenameDelete
SetShellVarContext all
Push "$APPDATA\Mozilla\logs\maintenanceservice.log"
Call un.RenameDelete
Push "$APPDATA\Mozilla\logs\maintenanceservice-1.log"
Call un.RenameDelete
Push "$APPDATA\Mozilla\logs\maintenanceservice-2.log"
Call un.RenameDelete
Push "$APPDATA\Mozilla\logs\maintenanceservice-3.log"
Call un.RenameDelete
Push "$APPDATA\Mozilla\logs\maintenanceservice-4.log"
Call un.RenameDelete
Push "$APPDATA\Mozilla\logs\maintenanceservice-5.log"
Call un.RenameDelete
Push "$APPDATA\Mozilla\logs\maintenanceservice-6.log"
Call un.RenameDelete
Push "$APPDATA\Mozilla\logs\maintenanceservice-7.log"
Call un.RenameDelete
Push "$APPDATA\Mozilla\logs\maintenanceservice-8.log"
Call un.RenameDelete
Push "$APPDATA\Mozilla\logs\maintenanceservice-9.log"
Call un.RenameDelete
Push "$APPDATA\Mozilla\logs\maintenanceservice-10.log"
Call un.RenameDelete
Push "$APPDATA\Mozilla\logs\maintenanceservice-install.log"
Call un.RenameDelete
Push "$APPDATA\Mozilla\logs\maintenanceservice-uninstall.log"
Call un.RenameDelete
RMDir /REBOOTOK "$APPDATA\Mozilla\logs"
RMDir /REBOOTOK "$APPDATA\Mozilla"
RMDir /REBOOTOK "$INSTDIR\logs"
RMDir /REBOOTOK "$INSTDIR\update"
RMDir /REBOOTOK "$INSTDIR"

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

@ -1665,10 +1665,8 @@ toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-ba
-moz-margin-end: 3px;
padding: 3px 4px;
}
#urlbar > #identity-box:hover,
#urlbar > #identity-box[open=true] {
background-color: rgb(240,237,237);
#identity-box {
--identity-box-selected-background-color: rgb(240,237,237);
}
}

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

@ -640,9 +640,9 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
list-style-image: url(chrome://browser/skin/syncProgress-horizontalbar.png);
}
#PanelUI-fxa-icon[fxastatus="migrate-signup"],
#PanelUI-fxa-icon[fxastatus="migrate-verify"] {
list-style-image: url(chrome://browser/skni/warning16.png);
#PanelUI-footer-fxa[fxastatus="migrate-signup"] > #PanelUI-fxa-status > #PanelUI-fxa-label,
#PanelUI-footer-fxa[fxastatus="migrate-verify"] > #PanelUI-fxa-status > #PanelUI-fxa-label {
list-style-image: url(chrome://browser/skin/warning16.png);
-moz-image-region: rect(0, 32px, 16px, 16px);
}
@ -1509,8 +1509,8 @@ menuitem[checked="true"].subviewbutton > .menu-iconic-left {
list-style-image: url(chrome://browser/skin/syncProgress-horizontalbar@2x.png);
}
#PanelUI-fxa-icon[fxastatus="migrate-signup"],
#PanelUI-fxa-icon[fxastatus="migrate-verify"] {
#PanelUI-footer-fxa[fxastatus="migrate-signup"] > #PanelUI-fxa-status > #PanelUI-fxa-label,
#PanelUI-footer-fxa[fxastatus="migrate-verify"] > #PanelUI-fxa-status > #PanelUI-fxa-label {
list-style-image: url(chrome://browser/skin/warning16@2x.png);
-moz-image-region: rect(0, 64px, 32px, 32px);
}

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

@ -50,12 +50,6 @@
--toolbarbutton-combined-boxshadow: none;
--toolbarbutton-combined-backgroundimage: linear-gradient(#5F6670 0, #5F6670 18px);
/* Identity box */
--identity-box-chrome-color: #46afe3;
--identity-box-chrome-background-image: linear-gradient(#5F6670 0, #5F6670 100%);
--identity-box-verified-background-image: linear-gradient(#5F6670 0, #5F6670 100%);
--verified-identity-box-backgroundcolor: transparent;
/* Url and search bars */
--url-and-searchbar-background-color: #171B1F;
--url-and-searchbar-color: #fff;
@ -71,6 +65,14 @@
--panel-ui-button-background-image: linear-gradient(to bottom, transparent, #5F6670 30%, #5F6670 70%, transparent);
}
:root[devtoolstheme="dark"] #identity-box {
--identity-box-chrome-color: #46afe3;
--identity-box-chrome-background-image: linear-gradient(#5F6670 0, #5F6670 100%);
--identity-box-verified-background-image: linear-gradient(#5F6670 0, #5F6670 100%);
--verified-identity-box-background-color: transparent;
--identity-box-selected-background-color: rgba(231,230,230,.2);
}
:root[devtoolstheme="dark"] .searchbar-dropmarker-image {
--searchbar-dropmarker-url: url("chrome://browser/skin/devtools/dropmarker.svg");
--searchbar-dropmarker-2x-url: url("chrome://browser/skin/devtools/dropmarker.svg");

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

@ -9,6 +9,7 @@
hsla(0,0%,16%,.2) 35%,
hsla(0,0%,16%,.2) 65%,
hsla(0,0%,16%,0));
--identity-box-selected-background-color: rgb(231,230,230);
--identity-box-verified-color: hsl(92,100%,30%);
--identity-box-verified-background-image: linear-gradient(hsla(92,81%,16%,0),
hsla(92,81%,16%,.2) 35%,
@ -50,7 +51,7 @@
#identity-box:hover,
#identity-box[open=true] {
background-color: rgb(231,230,230);
background-color: var(--identity-box-selected-background-color);
}
#urlbar[pageproxystate="valid"] > #identity-box.verifiedIdentity {

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

@ -4,6 +4,7 @@
from __future__ import print_function, unicode_literals
import errno
import os
import platform
import sys
@ -22,6 +23,36 @@ use and re-run mach. For this change to take effect forever, you'll likely
want to export this environment variable from your shell's init scripts.
'''.lstrip()
NO_MERCURIAL_SETUP = '''
*** MERCURIAL NOT CONFIGURED ***
mach has detected that you have never run `mach mercurial-setup`.
Running this command will ensure your Mercurial version control tool is up
to date and optimally configured for a better, more productive experience
when working on Mozilla projects.
Please run `mach mercurial-setup` now.
'''.strip()
OLD_MERCURIAL_TOOLS = '''
*** MERCURIAL CONFIGURATION POTENTIALLY OUT OF DATE ***
mach has detected that it has been a while since you have run
`mach mercurial-setup`.
Having the latest Mercurial tools and configuration should lead to a better,
more productive experience when working on Mozilla projects.
Please run `mach mercurial-setup` now.
To avoid this message in the future, run `mach mercurial-setup` once a month.
Or, schedule `mach mercurial-setup --update-only` to run automatically in
the background at least once a month.
'''.strip()
MERCURIAL_SETUP_FATAL_INTERVAL = 31 * 24 * 60 * 60
# TODO Bug 794506 Integrate with the in-tree virtualenv configuration.
SEARCH_PATHS = [
@ -151,6 +182,21 @@ CATEGORIES = {
}
def get_state_dir():
"""Obtain the path to a directory to hold state.
Returns a tuple of the path and a bool indicating whether the value came
from an environment variable.
"""
state_user_dir = os.path.expanduser('~/.mozbuild')
state_env_dir = os.environ.get('MOZBUILD_STATE_PATH', None)
if state_env_dir:
return state_env_dir, True
else:
return state_user_dir, False
def bootstrap(topsrcdir, mozilla_dir=None):
if mozilla_dir is None:
mozilla_dir = topsrcdir
@ -177,23 +223,69 @@ def bootstrap(topsrcdir, mozilla_dir=None):
sys.path[0:0] = [os.path.join(mozilla_dir, path) for path in SEARCH_PATHS]
import mach.main
def pre_dispatch_handler(context, handler, args):
"""Perform global checks before command dispatch.
Currently, our goal is to ensure developers periodically run
`mach mercurial-setup` (when applicable) to ensure their Mercurial
tools are up to date.
"""
# Don't do anything when...
# The user is performing a maintenance command.
if handler.name in ('bootstrap', 'doctor', 'mercurial-setup'):
return
# We are running in automation.
if 'MOZ_AUTOMATION' in os.environ or 'TASK_ID' in os.environ:
return
# We are a curmudgeon who has found this undocumented variable.
if 'I_PREFER_A_SUBOPTIMAL_MERCURIAL_EXPERIENCE' in os.environ:
return
# The environment is likely a machine invocation.
if not sys.stdin.isatty():
return
# Mercurial isn't managing this source checkout.
if not os.path.exists(os.path.join(topsrcdir, '.hg')):
return
state_dir = get_state_dir()[0]
last_check_path = os.path.join(state_dir, 'mercurial',
'setup.lastcheck')
mtime = None
try:
mtime = os.path.getmtime(last_check_path)
except OSError as e:
if e.errno != errno.ENOENT:
raise
# No last run file means mercurial-setup has never completed.
if mtime is None:
print(NO_MERCURIAL_SETUP, file=sys.stderr)
sys.exit(2)
elif time.time() - mtime > MERCURIAL_SETUP_FATAL_INTERVAL:
print(OLD_MERCURIAL_TOOLS, file=sys.stderr)
sys.exit(2)
def populate_context(context, key=None):
if key is None:
return
if key == 'state_dir':
state_user_dir = os.path.expanduser('~/.mozbuild')
state_env_dir = os.environ.get('MOZBUILD_STATE_PATH', None)
if state_env_dir:
if not os.path.exists(state_env_dir):
state_dir, is_environ = get_state_dir()
if is_environ:
if not os.path.exists(state_dir):
print('Creating global state directory from environment variable: %s'
% state_env_dir)
os.makedirs(state_env_dir, mode=0o770)
% state_dir)
os.makedirs(state_dir, mode=0o770)
print('Please re-run mach.')
sys.exit(1)
state_dir = state_env_dir
else:
if not os.path.exists(state_user_dir):
print(STATE_DIR_FIRST_RUN.format(userdir=state_user_dir))
if not os.path.exists(state_dir):
print(STATE_DIR_FIRST_RUN.format(userdir=state_dir))
try:
for i in range(20, -1, -1):
time.sleep(1)
@ -202,15 +294,19 @@ def bootstrap(topsrcdir, mozilla_dir=None):
except KeyboardInterrupt:
sys.exit(1)
print('\nCreating default state directory: %s' % state_user_dir)
os.mkdir(state_user_dir)
print('\nCreating default state directory: %s' % state_dir)
os.mkdir(state_dir)
print('Please re-run mach.')
sys.exit(1)
state_dir = state_user_dir
return state_dir
if key == 'topdir':
return topsrcdir
if key == 'pre_dispatch_handler':
return pre_dispatch_handler
raise AttributeError(key)
mach = mach.main.Mach(os.getcwd())

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

@ -137,13 +137,17 @@ skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
[test_peerConnection_twoAudioTracksInOneStream.html]
skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
[test_peerConnection_twoAudioVideoStreams.html]
skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
# b2g(Bug 960442, video support for WebRTC is disabled on b2g), Bug 1180000 for Linux debug e10s
skip-if = toolkit == 'gonk' || buildapp == 'mulet' || (os == 'linux' && debug && e10s)
[test_peerConnection_twoAudioVideoStreamsCombined.html]
skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
# b2g(Bug 960442, video support for WebRTC is disabled on b2g), Bug 1180000 for Linux debug e10s
skip-if = toolkit == 'gonk' || buildapp == 'mulet' || (os == 'linux' && debug && e10s)
[test_peerConnection_twoVideoStreams.html]
skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
# b2g(Bug 960442, video support for WebRTC is disabled on b2g), Bug 1180000 for Linux debug e10s
skip-if = toolkit == 'gonk' || buildapp == 'mulet' || (os == 'linux' && debug && e10s)
[test_peerConnection_twoVideoTracksInOneStream.html]
skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
# b2g(Bug 960442, video support for WebRTC is disabled on b2g), Bug 1180000 for Linux debug e10s
skip-if = toolkit == 'gonk' || buildapp == 'mulet' || (os == 'linux' && debug && e10s)
[test_peerConnection_addSecondAudioStream.html]
skip-if = toolkit == 'gonk' # B2G emulator is too slow to finish a renegotiation test in under 5 minutes
[test_peerConnection_answererAddSecondAudioStream.html]

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

@ -112,8 +112,12 @@ public class DoorHangerPopup extends AnchoredPopup
final String id = json.getString("value");
final String typeString = json.optString("category");
final boolean isLogin = DoorHanger.Type.LOGIN.toString().equals(typeString);
final DoorHanger.Type doorhangerType = isLogin ? DoorHanger.Type.LOGIN : DoorHanger.Type.DEFAULT;
DoorHanger.Type doorhangerType = DoorHanger.Type.DEFAULT;
if (DoorHanger.Type.LOGIN.toString().equals(typeString)) {
doorhangerType = DoorHanger.Type.LOGIN;
} else if (DoorHanger.Type.GEOLOCATION.toString().equals(typeString)) {
doorhangerType = DoorHanger.Type.GEOLOCATION;
}
final DoorhangerConfig config = new DoorhangerConfig(tabId, id, doorhangerType, this);

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

@ -558,10 +558,11 @@ just addresses the organization to follow, e.g. "This site is run by " -->
<!ENTITY blocked_mixed_content_message_bottom "Most websites will still work properly even when this content is blocked.">
<!-- Tracking content notifications in site identity popup -->
<!ENTITY loaded_tracking_content_message_top "Tracking protection disabled.">
<!ENTITY loaded_tracking_content_message_bottom "Parts of this page may track your online activity.">
<!ENTITY blocked_tracking_content_message_top2 "Tracking protection enabled.">
<!ENTITY blocked_tracking_content_message_bottom2 "Parts of the page that track your online activity have been blocked.">
<!ENTITY doorhanger_tracking_title "Tracking protection">
<!ENTITY doorhanger_tracking_state_enabled "Enabled">
<!ENTITY doorhanger_tracking_state_disabled "Disabled">
<!ENTITY doorhanger_tracking_message_enabled "Blocking tracking elements that may affect your browsing experience.">
<!ENTITY doorhanger_tracking_message_disabled "Attempts to track your online behavior are not being blocked.">
<!-- Common mixed and tracking content strings in site identity popup -->
<!ENTITY learn_more "Learn More">

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

@ -510,6 +510,7 @@ gbjar.sources += [
'widget/ButtonToast.java',
'widget/CheckableLinearLayout.java',
'widget/ClickableWhenDisabledEditText.java',
'widget/ContentSecurityDoorHanger.java',
'widget/DateTimePicker.java',
'widget/DefaultDoorHanger.java',
'widget/Divider.java',

Двоичные данные
mobile/android/base/resources/drawable-hdpi/globe_light.png Normal file

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

После

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

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

До

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

Двоичные данные
mobile/android/base/resources/drawable-hdpi/location.png Normal file

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

После

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

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

До

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

После

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

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

До

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

После

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

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

До

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

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

До

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

После

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

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

До

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

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

До

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

Двоичные данные
mobile/android/base/resources/drawable-xhdpi/globe_light.png Normal file

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

После

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

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

До

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

Двоичные данные
mobile/android/base/resources/drawable-xhdpi/location.png Normal file

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

После

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

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

До

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

После

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

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

До

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

После

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

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

До

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

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

До

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

После

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

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

До

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

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

До

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

Двоичные данные
mobile/android/base/resources/drawable-xxhdpi/globe_light.png Normal file

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

После

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

Двоичные данные
mobile/android/base/resources/drawable-xxhdpi/location.png Normal file

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

После

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

Двоичные данные
mobile/android/base/resources/drawable-xxhdpi/lock_identified.png Normal file

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

После

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

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

До

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

После

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

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

До

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

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

До

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

После

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

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

До

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

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

До

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

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

@ -10,7 +10,7 @@
</shape>
</item>
<item>
<bitmap android:src="@drawable/tab_panel_tab_globe"
<bitmap android:src="@drawable/globe_light"
android:gravity="center"
/>
</item>

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

@ -18,14 +18,14 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginTop="@dimen/doorhanger_section_padding_small"
android:layout_marginTop="@dimen/doorhanger_section_padding_medium"
android:gravity="right"
android:visibility="gone"/>
<CheckBox android:id="@+id/doorhanger_checkbox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/doorhanger_section_padding_small"
android:layout_marginTop="@dimen/doorhanger_section_padding_medium"
android:checked="true"
android:textColor="@color/placeholder_active_grey"
android:visibility="gone"/>

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

@ -8,13 +8,17 @@
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="@dimen/doorhanger_section_padding_small">
android:paddingLeft="@dimen/doorhanger_section_padding_small"
android:paddingRight="@dimen/doorhanger_section_padding_small"
android:paddingBottom="@dimen/doorhanger_section_padding_medium"
android:paddingTop="@dimen/doorhanger_section_padding_medium">
<ImageView android:id="@+id/doorhanger_icon"
android:layout_width="@dimen/doorhanger_icon_size"
android:layout_height="@dimen/doorhanger_icon_size"
android:layout_gravity="center_horizontal"
android:paddingRight="@dimen/doorhanger_section_padding_small"
android:padding="@dimen/doorhanger_section_padding_small"
android:layout_marginRight="@dimen/doorhanger_section_padding_small"
android:visibility="gone"/>
<LinearLayout android:layout_width="match_parent"
@ -24,7 +28,7 @@
<TextView android:id="@+id/doorhanger_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/doorhanger_section_padding_small"
android:layout_marginBottom="@dimen/doorhanger_section_padding_medium"
android:textAppearance="@style/TextAppearance.DoorHanger.Medium.Light"
android:visibility="gone"/>

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

@ -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/. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView android:id="@+id/security_title"
android:focusable="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/doorhanger_subsection_padding"
android:textAppearance="@style/TextAppearance.DoorHanger.Medium"
android:visibility="gone"/>
<TextView android:id="@+id/security_state"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/doorhanger_section_padding_small"
android:textAppearance="@style/TextAppearance.DoorHanger.Medium.Bold"
android:visibility="gone"/>
<TextView android:id="@+id/security_message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.DoorHanger.Medium"/>
</LinearLayout>

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

@ -0,0 +1,26 @@
<?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/. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView android:id="@+id/trackingprotection_title"
android:focusable="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.DoorHanger.Medium"/>
<TextView android:id="@+id/trackingprotection_enabled"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.DoorHanger.Medium.Bold"/>
<TextView android:id="@+id/trackingprotection_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>

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

@ -6,7 +6,7 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/doorhanger_section_padding_small"
android:padding="@dimen/doorhanger_section_padding_medium"
android:orientation="vertical">
<EditText android:id="@+id/username_edit"

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

@ -11,13 +11,17 @@
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="@dimen/doorhanger_section_padding_small">
android:paddingLeft="@dimen/doorhanger_section_padding_small"
android:paddingRight="@dimen/doorhanger_section_padding_small"
android:paddingBottom="@dimen/doorhanger_section_padding_medium"
android:paddingTop="@dimen/doorhanger_section_padding_medium">
<ImageView android:id="@+id/larry"
<ImageView android:id="@+id/site_identity_icon"
android:layout_width="@dimen/doorhanger_icon_size"
android:layout_height="@dimen/doorhanger_icon_size"
android:src="@drawable/larry"
android:paddingRight="@dimen/doorhanger_section_padding_small"/>
android:gravity="center_horizontal"
android:padding="@dimen/doorhanger_section_padding_small"
android:layout_marginRight="@dimen/doorhanger_section_padding_small"/>
<LinearLayout android:layout_width="0dp"
android:layout_height="wrap_content"
@ -41,7 +45,7 @@
<TextView android:id="@+id/site_identity_encrypted"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/doorhanger_section_padding_small"
android:layout_marginBottom="@dimen/doorhanger_section_padding_medium"
android:textAppearance="@style/TextAppearance.DoorHanger.Medium.Bold"
android:textColor="@color/affirmative_green"
android:text="@string/identity_encrypted"/>
@ -62,7 +66,7 @@
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.DoorHanger.Medium.Light"
android:text="@string/identity_run_by"
android:layout_marginTop="@dimen/doorhanger_section_padding_small"/>
android:layout_marginTop="@dimen/doorhanger_section_padding_medium"/>
<TextView android:id="@+id/owner"
android:layout_width="wrap_content"
@ -74,7 +78,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.DoorHanger.Medium.Light"
android:layout_marginTop="@dimen/doorhanger_section_padding_small"/>
android:layout_marginTop="@dimen/doorhanger_section_padding_medium"/>
</LinearLayout>
<TextView android:id="@+id/site_settings_link"
@ -83,7 +87,7 @@
android:textAppearance="@style/TextAppearance.DoorHanger.Medium"
android:textColor="@color/link_blue"
android:layout_marginTop="@dimen/doorhanger_section_padding_large"
android:layout_marginBottom="@dimen/doorhanger_section_padding_small"
android:layout_marginBottom="@dimen/doorhanger_section_padding_medium"
android:text="@string/contextmenu_site_settings"
android:visibility="gone"/>
</LinearLayout>

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

@ -19,6 +19,6 @@
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.DoorHanger.Medium"
android:text="@string/identity_not_encrypted"
android:paddingTop="@dimen/doorhanger_section_padding_small"/>
android:paddingTop="@dimen/doorhanger_section_padding_medium"/>
</LinearLayout>

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

@ -7,6 +7,7 @@
<!-- Fennec color palette (bug 1127517) -->
<color name="fennec_ui_orange">#FF9500</color>
<color name="affirmative_green">#6FBE4A</color>
<color name="rejection_red">#D23228</color>
<color name="action_orange">#E66000</color>
<color name="action_orange_pressed">#DC5600</color>
<color name="link_blue">#0096DD</color>

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

@ -99,7 +99,8 @@
<dimen name="doorhanger_GB_offsetY">7dp</dimen>
<dimen name="doorhanger_drawable_padding">5dp</dimen>
<dimen name="doorhanger_subsection_padding">8dp</dimen>
<dimen name="doorhanger_section_padding_small">20dp</dimen>
<dimen name="doorhanger_section_padding_small">10dp</dimen>
<dimen name="doorhanger_section_padding_medium">20dp</dimen>
<dimen name="doorhanger_section_padding_large">30dp</dimen>
<dimen name="doorhanger_icon_size">60dp</dimen>

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

@ -463,10 +463,13 @@
<string name="loaded_mixed_content_message">&loaded_mixed_content_message;</string>
<string name="blocked_mixed_content_message_top">&blocked_mixed_content_message_top;</string>
<string name="blocked_mixed_content_message_bottom">&blocked_mixed_content_message_bottom;</string>
<string name="loaded_tracking_content_message_top">&loaded_tracking_content_message_top;</string>
<string name="loaded_tracking_content_message_bottom">&loaded_tracking_content_message_bottom;</string>
<string name="blocked_tracking_content_message_top">&blocked_tracking_content_message_top2;</string>
<string name="blocked_tracking_content_message_bottom">&blocked_tracking_content_message_bottom2;</string>
<string name="doorhanger_tracking_title">&doorhanger_tracking_title;</string>
<string name="doorhanger_tracking_state_enabled">&doorhanger_tracking_state_enabled;</string>
<string name="doorhanger_tracking_state_disabled">&doorhanger_tracking_state_disabled;</string>
<string name="doorhanger_tracking_message_enabled">&doorhanger_tracking_message_enabled;</string>
<string name="doorhanger_tracking_message_disabled">&doorhanger_tracking_message_disabled;</string>
<string name="learn_more">&learn_more;</string>
<string name="enable_protection">&enable_protection;</string>
<string name="disable_protection">&disable_protection;</string>

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

@ -68,6 +68,7 @@ public class SiteIdentityPopup extends AnchoredPopup implements GeckoEventListen
private LinearLayout mIdentityKnownContainer;
private LinearLayout mIdentityUnknownContainer;
private ImageView mIcon;
private TextView mTitle;
private TextView mEncrypted;
private TextView mHost;
@ -110,6 +111,7 @@ public class SiteIdentityPopup extends AnchoredPopup implements GeckoEventListen
(LinearLayout) mIdentity.findViewById(R.id.site_identity_unknown_container);
mIcon = (ImageView) mIdentity.findViewById(R.id.site_identity_icon);
mTitle = (TextView) mIdentity.findViewById(R.id.site_identity_title);
mEncrypted = (TextView) mIdentityKnownContainer.findViewById(R.id.site_identity_encrypted);
mHost = (TextView) mIdentityKnownContainer.findViewById(R.id.host);
@ -284,13 +286,19 @@ public class SiteIdentityPopup extends AnchoredPopup implements GeckoEventListen
mIdentityKnownContainer.setVisibility(View.VISIBLE);
mIdentityUnknownContainer.setVisibility(View.GONE);
} else {
mIcon.setImageResource(R.drawable.globe_light);
mIdentityKnownContainer.setVisibility(View.GONE);
mIdentityUnknownContainer.setVisibility(View.VISIBLE);
}
}
private void updateIdentityInformation(final SiteIdentity siteIdentity) {
mEncrypted.setVisibility(siteIdentity.getEncrypted() ? View.VISIBLE : View.GONE);
if (siteIdentity.getEncrypted()) {
mEncrypted.setVisibility(View.VISIBLE);
mIcon.setImageResource(R.drawable.lock_identified);
} else {
mEncrypted.setVisibility(View.GONE);
}
mHost.setText(siteIdentity.getHost());
@ -321,11 +329,11 @@ public class SiteIdentityPopup extends AnchoredPopup implements GeckoEventListen
final DoorhangerConfig config = new DoorhangerConfig(DoorHanger.Type.MIXED_CONTENT, mContentButtonClickListener);
int icon;
if (blocked) {
icon = R.drawable.shield_enabled_doorhanger;
icon = R.drawable.shield_enabled;
config.setMessage(mContext.getString(R.string.blocked_mixed_content_message_top) + "\n\n" +
mContext.getString(R.string.blocked_mixed_content_message_bottom));
} else {
icon = R.drawable.shield_disabled_doorhanger;
icon = R.drawable.shield_disabled;
config.setMessage(mContext.getString(R.string.loaded_mixed_content_message));
}
@ -352,25 +360,26 @@ public class SiteIdentityPopup extends AnchoredPopup implements GeckoEventListen
final DoorhangerConfig config = new DoorhangerConfig(DoorHanger.Type.TRACKING, mContentButtonClickListener);
int icon;
if (blocked) {
icon = R.drawable.shield_enabled_doorhanger;
config.setMessage(mContext.getString(R.string.blocked_tracking_content_message_top) + "\n\n" +
mContext.getString(R.string.blocked_tracking_content_message_bottom));
} else {
icon = R.drawable.shield_disabled_doorhanger;
config.setMessage(mContext.getString(R.string.loaded_tracking_content_message_top) + "\n\n" +
mContext.getString(R.string.loaded_tracking_content_message_bottom));
final int icon = blocked ? R.drawable.shield_enabled: R.drawable.shield_disabled;
final JSONObject options = new JSONObject();
final JSONObject tracking = new JSONObject();
try {
tracking.put("enabled", blocked);
options.put("tracking_protection", tracking);
} catch (JSONException e) {
Log.e(LOGTAG, "Error adding tracking protection options", e);
}
config.setOptions(options);
config.setLink(mContext.getString(R.string.learn_more), TRACKING_CONTENT_SUPPORT_URL);
addNotificationButtons(config, blocked);
mTrackingContentNotification = DoorHanger.Get(mContext, config);
mTrackingContentNotification.setIcon(icon);
mContent.addView(mTrackingContentNotification);
mDivider.setVisibility(View.VISIBLE);
}
@ -385,7 +394,6 @@ public class SiteIdentityPopup extends AnchoredPopup implements GeckoEventListen
private void addNotificationButtons(DoorhangerConfig config, boolean blocked) {
if (blocked) {
config.setButton(mContext.getString(R.string.disable_protection), ButtonType.DISABLE.ordinal(), false);
config.setButton(mContext.getString(R.string.keep_blocking), ButtonType.KEEP_BLOCKING.ordinal(), true);
} else {
config.setButton(mContext.getString(R.string.enable_protection), ButtonType.ENABLE.ordinal(), true);
}

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

@ -0,0 +1,122 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* 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.gecko.widget;
import android.util.Log;
import android.widget.Button;
import android.widget.TextView;
import org.mozilla.gecko.R;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.Context;
import android.view.View;
import org.mozilla.gecko.toolbar.SiteIdentityPopup;
public class ContentSecurityDoorHanger extends DoorHanger {
private static final String LOGTAG = "GeckoSecurityDoorHanger";
private final TextView mTitle;
private final TextView mSecurityState;
private final TextView mMessage;
public ContentSecurityDoorHanger(Context context, DoorhangerConfig config, Type type) {
super(context, config, type);
mTitle = (TextView) findViewById(R.id.security_title);
mSecurityState = (TextView) findViewById(R.id.security_state);
mMessage = (TextView) findViewById(R.id.security_message);
loadConfig(config);
}
@Override
protected void loadConfig(DoorhangerConfig config) {
final String message = config.getMessage();
if (message != null) {
mMessage.setText(message);
}
final JSONObject options = config.getOptions();
if (options != null) {
setOptions(options);
}
final DoorhangerConfig.Link link = config.getLink();
if (link != null) {
addLink(link.label, link.url);
}
addButtonsToLayout(config);
}
@Override
protected int getContentResource() {
return R.layout.doorhanger_security;
}
@Override
public void setOptions(final JSONObject options) {
super.setOptions(options);
final JSONObject link = options.optJSONObject("link");
if (link != null) {
try {
final String linkLabel = link.getString("label");
final String linkUrl = link.getString("url");
addLink(linkLabel, linkUrl);
} catch (JSONException e) { }
}
final JSONObject trackingProtection = options.optJSONObject("tracking_protection");
if (trackingProtection != null) {
mTitle.setVisibility(VISIBLE);
mTitle.setText(R.string.doorhanger_tracking_title);
try {
final boolean enabled = trackingProtection.getBoolean("enabled");
if (enabled) {
mMessage.setText(R.string.doorhanger_tracking_message_enabled);
mSecurityState.setText(R.string.doorhanger_tracking_state_enabled);
mSecurityState.setTextColor(getResources().getColor(R.color.affirmative_green));
} else {
mMessage.setText(R.string.doorhanger_tracking_message_disabled);
mSecurityState.setText(R.string.doorhanger_tracking_state_disabled);
mSecurityState.setTextColor(getResources().getColor(R.color.rejection_red));
}
mMessage.setVisibility(VISIBLE);
mSecurityState.setVisibility(VISIBLE);
} catch (JSONException e) {}
}
}
@Override
protected OnClickListener makeOnButtonClickListener(final int id) {
return new Button.OnClickListener() {
@Override
public void onClick(View v) {
final JSONObject response = new JSONObject();
try {
switch (mType) {
case MIXED_CONTENT:
response.put("allowContent", (id == SiteIdentityPopup.ButtonType.DISABLE.ordinal()));
response.put("contentType", ("mixed"));
break;
case TRACKING:
response.put("allowContent", (id == SiteIdentityPopup.ButtonType.DISABLE.ordinal()));
response.put("contentType", ("tracking"));
break;
default:
Log.w(LOGTAG, "Unknown doorhanger type " + mType.toString());
}
} catch (JSONException e) {
Log.e(LOGTAG, "Error creating onClick response", e);
}
mOnButtonClickListener.onButtonClick(response, ContentSecurityDoorHanger.this);
}
};
}
}

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

@ -11,7 +11,6 @@ import android.util.Log;
import android.widget.Button;
import android.widget.TextView;
import org.mozilla.gecko.R;
import org.mozilla.gecko.Tabs;
import org.mozilla.gecko.prompts.PromptInput;
import org.json.JSONArray;
@ -23,7 +22,6 @@ import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import org.mozilla.gecko.toolbar.SiteIdentityPopup;
import java.util.ArrayList;
import java.util.List;
@ -45,6 +43,13 @@ public class DefaultDoorHanger extends DoorHanger {
if (sSpinnerTextColor == -1) {
sSpinnerTextColor = mResources.getColor(R.color.text_color_primary_disable_only);
}
switch (mType) {
case GEOLOCATION:
mIcon.setImageResource(R.drawable.location);
mIcon.setVisibility(VISIBLE);
}
loadConfig(config);
}
@ -84,14 +89,6 @@ public class DefaultDoorHanger extends DoorHanger {
@Override
public void setOptions(final JSONObject options) {
super.setOptions(options);
final JSONObject link = options.optJSONObject("link");
if (link != null) {
try {
final String linkLabel = link.getString("label");
final String linkUrl = link.getString("url");
addLink(linkLabel, linkUrl);
} catch (JSONException e) { }
}
final JSONArray inputs = options.optJSONArray("inputs");
if (inputs != null) {
@ -105,7 +102,7 @@ public class DefaultDoorHanger extends DoorHanger {
PromptInput input = PromptInput.getInput(inputs.getJSONObject(i));
mInputs.add(input);
final int padding = mResources.getDimensionPixelSize(R.dimen.doorhanger_section_padding_small);
final int padding = mResources.getDimensionPixelSize(R.dimen.doorhanger_section_padding_medium);
View v = input.getView(getContext());
styleInput(input, v);
v.setPadding(0, 0, 0, padding);
@ -129,17 +126,6 @@ public class DefaultDoorHanger extends DoorHanger {
public void onClick(View v) {
final JSONObject response = new JSONObject();
try {
// TODO: Bug 1149359 - Split this into each Doorhanger Type class.
switch (mType) {
case MIXED_CONTENT:
response.put("allowContent", (id == SiteIdentityPopup.ButtonType.DISABLE.ordinal()));
response.put("contentType", ("mixed"));
break;
case TRACKING:
response.put("allowContent", (id == SiteIdentityPopup.ButtonType.DISABLE.ordinal()));
response.put("contentType", ("tracking"));
break;
default:
response.put("callback", id);
CheckBox checkBox = getCheckBox();
@ -156,7 +142,6 @@ public class DefaultDoorHanger extends DoorHanger {
}
response.put("inputs", inputs);
}
}
} catch (JSONException e) {
Log.e(LOGTAG, "Error creating onClick response", e);
}
@ -171,17 +156,6 @@ public class DefaultDoorHanger extends DoorHanger {
mMessage.setText(markupMessage);
}
private void addLink(String label, final String url) {
mLink.setText(label);
mLink.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
Tabs.getInstance().loadUrlInTab(url);
}
});
mLink.setVisibility(VISIBLE);
}
private void styleInput(PromptInput input, View view) {
if (input instanceof PromptInput.MenulistInput) {
styleDropdownInputs(input, view);

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

@ -9,7 +9,6 @@ import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewStub;
@ -30,12 +29,12 @@ public abstract class DoorHanger extends LinearLayout {
return new LoginDoorHanger(context, config);
case TRACKING:
case MIXED_CONTENT:
return new DefaultDoorHanger(context, config, type);
return new ContentSecurityDoorHanger(context, config, type);
}
return new DefaultDoorHanger(context, config, type);
}
public static enum Type { DEFAULT, LOGIN, TRACKING, MIXED_CONTENT}
public static enum Type { DEFAULT, LOGIN, TRACKING, MIXED_CONTENT, GEOLOCATION }
public interface OnButtonClickListener {
public void onButtonClick(JSONObject response, DoorHanger doorhanger);
@ -155,6 +154,17 @@ public abstract class DoorHanger extends LinearLayout {
mIcon.setVisibility(View.VISIBLE);
}
protected void addLink(String label, final String url) {
mLink.setText(label);
mLink.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
Tabs.getInstance().loadUrlInTab(url);
}
});
mLink.setVisibility(VISIBLE);
}
protected abstract OnClickListener makeOnButtonClickListener(final int id);
/*

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

@ -128,7 +128,7 @@ ContentPermissionPrompt.prototype = {
let message = browserBundle.formatStringFromName(entityName + ".ask", [requestor], 1);
let options = { checkbox: browserBundle.GetStringFromName(entityName + ".dontAskAgain") };
chromeWin.NativeWindow.doorhanger.show(message, entityName + request.principal.URI.host, buttons, tab.id, options);
chromeWin.NativeWindow.doorhanger.show(message, entityName + request.principal.URI.host, buttons, tab.id, options, entityName.toUpperCase());
}
};

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

@ -3962,6 +3962,7 @@ pref("signon.autologin.proxy", false);
pref("signon.storeWhenAutocompleteOff", true);
pref("signon.ui.experimental", false);
pref("signon.debug", false);
pref("signon.recipes.path", "chrome://passwordmgr/content/recipes.json");
// Satchel (Form Manager) prefs
pref("browser.formfill.debug", false);

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

@ -61,6 +61,11 @@ class MachRegistrar(object):
if handler.pass_context and not context:
raise Exception('mach command class requires context.')
if context:
prerun = getattr(context, 'pre_dispatch_handler', None)
if prerun:
prerun(context, handler, args=kwargs)
if handler.pass_context:
instance = cls(context)
else:

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

@ -73,12 +73,18 @@ function hasChanged(oldData, newData) {
this.FxAccountsProfile = function (options = {}) {
this._cachedProfile = null;
this._cachedAt = 0; // when we saved the cached version.
this._currentFetchPromise = null;
this._isNotifying = false; // are we sending a notification?
this.fxa = options.fxa || fxAccounts;
this.client = options.profileClient || new FxAccountsProfileClient({
fxa: this.fxa,
serverURL: options.profileServerUrl,
});
// An observer to invalidate our _cachedAt optimization. We use a weak-ref
// just incase this.tearDown isn't called in some cases.
Services.obs.addObserver(this, ON_PROFILE_CHANGE_NOTIFICATION, true);
// for testing
if (options.channel) {
this.channel = options.channel;
@ -86,11 +92,25 @@ this.FxAccountsProfile = function (options = {}) {
}
this.FxAccountsProfile.prototype = {
// If we get subsequent requests for a profile within this period, don't bother
// making another request to determine if it is fresh or not.
PROFILE_FRESHNESS_THRESHOLD: 120000, // 2 minutes
observe(subject, topic, data) {
// If we get a profile change notification from our webchannel it means
// the user has just changed their profile via the web, so we want to
// ignore our "freshness threshold"
if (topic == ON_PROFILE_CHANGE_NOTIFICATION && !this._isNotifying) {
log.debug("FxAccountsProfile observed profile change");
this._cachedAt = 0;
}
},
tearDown: function () {
this.fxa = null;
this.client = null;
this._cachedProfile = null;
Services.obs.removeObserver(this, ON_PROFILE_CHANGE_NOTIFICATION);
},
_getCachedProfile: function () {
@ -100,7 +120,9 @@ this.FxAccountsProfile.prototype = {
},
_notifyProfileChange: function (uid) {
this._isNotifying = true;
Services.obs.notifyObservers(null, ON_PROFILE_CHANGE_NOTIFICATION, uid);
this._isNotifying = false;
},
// Cache fetched data if it is different from what's in the cache.
@ -111,6 +133,7 @@ this.FxAccountsProfile.prototype = {
return Promise.resolve(null); // indicates no change (but only tests care)
}
this._cachedProfile = profileData;
this._cachedAt = Date.now();
return this.fxa.getSignedInUser()
.then(userData => {
log.debug("notifying profile changed for user ${uid}", userData);
@ -120,10 +143,20 @@ this.FxAccountsProfile.prototype = {
},
_fetchAndCacheProfile: function () {
return this.client.fetchProfile()
.then(profile => {
return this._cacheProfile(profile).then(() => profile);
if (!this._currentFetchPromise) {
this._currentFetchPromise = this.client.fetchProfile().then(profile => {
return this._cacheProfile(profile).then(() => {
return profile;
});
}).then(profile => {
this._currentFetchPromise = null;
return profile;
}, err => {
this._currentFetchPromise = null;
throw err;
});
}
return this._currentFetchPromise
},
// Returns cached data right away if available, then fetches the latest profile
@ -133,11 +166,15 @@ this.FxAccountsProfile.prototype = {
return this._getCachedProfile()
.then(cachedProfile => {
if (cachedProfile) {
if (Date.now() > this._cachedAt + this.PROFILE_FRESHNESS_THRESHOLD) {
// Note that _fetchAndCacheProfile isn't returned, so continues
// in the background.
this._fetchAndCacheProfile().catch(err => {
log.error("Background refresh of profile failed", err);
});
} else {
log.trace("not checking freshness of profile as it remains recent");
}
return cachedProfile;
}
return this._fetchAndCacheProfile();
@ -146,4 +183,9 @@ this.FxAccountsProfile.prototype = {
return profile;
});
},
QueryInterface: XPCOMUtils.generateQI([
Ci.nsIObserver,
Ci.nsISupportsWeakReference,
]),
};

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

@ -164,6 +164,137 @@ add_test(function fetchAndCacheProfile_ok() {
});
});
// Check that a second profile request when one is already in-flight reuses
// the in-flight one.
add_task(function fetchAndCacheProfileOnce() {
// A promise that remains unresolved while we fire off 2 requests for
// a profile.
let resolveProfile;
let promiseProfile = new Promise(resolve => {
resolveProfile = resolve;
});
let numFetches = 0;
let client = mockClient(mockFxa());
client.fetchProfile = function () {
numFetches += 1;
return promiseProfile;
};
let profile = CreateFxAccountsProfile(null, client);
let request1 = profile._fetchAndCacheProfile();
let request2 = profile._fetchAndCacheProfile();
// should be one request made to fetch the profile (but the promise returned
// by it remains unresolved)
do_check_eq(numFetches, 1);
// resolve the promise.
resolveProfile({ avatar: "myimg"});
// both requests should complete with the same data.
let got1 = yield request1;
do_check_eq(got1.avatar, "myimg");
let got2 = yield request1;
do_check_eq(got2.avatar, "myimg");
// and still only 1 request was made.
do_check_eq(numFetches, 1);
});
// Check that sharing a single fetch promise works correctly when the promise
// is rejected.
add_task(function fetchAndCacheProfileOnce() {
// A promise that remains unresolved while we fire off 2 requests for
// a profile.
let rejectProfile;
let promiseProfile = new Promise((resolve,reject) => {
rejectProfile = reject;
});
let numFetches = 0;
let client = mockClient(mockFxa());
client.fetchProfile = function () {
numFetches += 1;
return promiseProfile;
};
let profile = CreateFxAccountsProfile(null, client);
let request1 = profile._fetchAndCacheProfile();
let request2 = profile._fetchAndCacheProfile();
// should be one request made to fetch the profile (but the promise returned
// by it remains unresolved)
do_check_eq(numFetches, 1);
// reject the promise.
rejectProfile("oh noes");
// both requests should reject.
try {
yield request1;
throw new Error("should have rejected");
} catch (ex if ex == "oh noes") {}
try {
yield request2;
throw new Error("should have rejected");
} catch (ex if ex == "oh noes") {}
// but a new request should work.
client.fetchProfile = function () {
return Promise.resolve({ avatar: "myimg"});
};
let got = yield profile._fetchAndCacheProfile();
do_check_eq(got.avatar, "myimg");
});
// Check that a new profile request within PROFILE_FRESHNESS_THRESHOLD of the
// last one doesn't kick off a new request to check the cached copy is fresh.
add_task(function fetchAndCacheProfileAfterThreshold() {
let numFetches = 0;
let client = mockClient(mockFxa());
client.fetchProfile = function () {
numFetches += 1;
return Promise.resolve({ avatar: "myimg"});
};
let profile = CreateFxAccountsProfile(null, client);
profile.PROFILE_FRESHNESS_THRESHOLD = 1000;
yield profile.getProfile();
do_check_eq(numFetches, 1);
yield profile.getProfile();
do_check_eq(numFetches, 1);
yield new Promise(resolve => {
do_timeout(1000, resolve);
});
yield profile.getProfile();
do_check_eq(numFetches, 2);
});
// Check that a new profile request within PROFILE_FRESHNESS_THRESHOLD of the
// last one *does* kick off a new request if ON_PROFILE_CHANGE_NOTIFICATION
// is sent.
add_task(function fetchAndCacheProfileBeforeThresholdOnNotification() {
let numFetches = 0;
let client = mockClient(mockFxa());
client.fetchProfile = function () {
numFetches += 1;
return Promise.resolve({ avatar: "myimg"});
};
let profile = CreateFxAccountsProfile(null, client);
profile.PROFILE_FRESHNESS_THRESHOLD = 1000;
yield profile.getProfile();
do_check_eq(numFetches, 1);
Services.obs.notifyObservers(null, ON_PROFILE_CHANGE_NOTIFICATION, null);
yield profile.getProfile();
do_check_eq(numFetches, 2);
});
add_test(function tearDown_ok() {
let profile = CreateFxAccountsProfile();

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

@ -130,18 +130,13 @@ wmain(int argc, WCHAR **argv)
BOOL
GetLogDirectoryPath(WCHAR *path)
{
HRESULT hr = SHGetFolderPathW(nullptr, CSIDL_COMMON_APPDATA, nullptr,
SHGFP_TYPE_CURRENT, path);
if (FAILED(hr)) {
if (!GetModuleFileNameW(nullptr, path, MAX_PATH)) {
return FALSE;
}
if (!PathAppendSafe(path, L"Mozilla")) {
if (!PathRemoveFileSpecW(path)) {
return FALSE;
}
// The directory should already be created from the installer, but
// just to be safe in case someone deletes.
CreateDirectoryW(path, nullptr);
if (!PathAppendSafe(path, L"logs")) {
return FALSE;

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

@ -152,7 +152,9 @@ var LoginManagerParent = {
XPCOMUtils.defineLazyGetter(this, "recipeParentPromise", () => {
const { LoginRecipesParent } = Cu.import("resource://gre/modules/LoginRecipes.jsm", {});
this._recipeManager = new LoginRecipesParent();
this._recipeManager = new LoginRecipesParent({
defaults: Services.prefs.getComplexValue("signon.recipes.path", Ci.nsISupportsString).data,
});
return this._recipeManager.initializationPromise;
});
@ -274,22 +276,30 @@ var LoginManagerParent = {
}
if (!showMasterPassword && !Services.logins.isLoggedIn) {
try {
target.sendAsyncMessage("RemoteLogins:loginsFound", {
requestId: requestId,
logins: [],
recipes,
});
} catch (e) {
log("error sending message to target", e);
}
return;
}
let allLoginsCount = Services.logins.countLogins(formOrigin, "", null);
// If there are no logins for this site, bail out now.
if (!allLoginsCount) {
try {
target.sendAsyncMessage("RemoteLogins:loginsFound", {
requestId: requestId,
logins: [],
recipes,
});
} catch (e) {
log("error sending message to target", e);
}
return;
}

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

@ -13,6 +13,7 @@ const SUPPORTED_KEYS = REQUIRED_KEYS.concat(OPTIONAL_KEYS);
Cu.importGlobalProperties(["URL"]);
Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
@ -27,9 +28,11 @@ XPCOMUtils.defineLazyGetter(this, "log", () => LoginHelper.createLogger("LoginRe
* calling methods on the object.
*
* @constructor
* @param {boolean} [aOptions.defaults=true] whether to load default application recipes.
* @param {String} [aOptions.defaults=null] the URI to load the recipes from.
* If it's null, nothing is loaded.
*
*/
function LoginRecipesParent(aOptions = { defaults: true }) {
function LoginRecipesParent(aOptions = { defaults: null }) {
if (Services.appinfo.processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT) {
throw new Error("LoginRecipesParent should only be used from the main process");
}
@ -88,10 +91,29 @@ LoginRecipesParent.prototype = {
this._recipesByHost = new Map();
if (this._defaults) {
// XXX: Bug 1134850 will handle reading recipes from a file.
this.initializationPromise = this.load(DEFAULT_RECIPES).then(resolve => {
let channel = NetUtil.newChannel({uri: NetUtil.newURI(this._defaults, "UTF-8"),
loadUsingSystemPrincipal: true});
channel.contentType = "application/json";
try {
this.initializationPromise = new Promise(function(resolve) {
NetUtil.asyncFetch(channel, function (stream, result) {
if (!Components.isSuccessCode(result)) {
throw new Error("Error fetching recipe file:" + result);
return;
}
let count = stream.available();
let data = NetUtil.readInputStreamToString(stream, count, { charset: "UTF-8" });
resolve(JSON.parse(data));
});
}).then(recipes => {
return this.load(recipes);
}).then(resolve => {
return this;
});
} catch (e) {
throw new Error("Error reading recipe file:" + e);
}
} else {
this.initializationPromise = Promise.resolve(this);
}
@ -237,23 +259,3 @@ let LoginRecipesContent = {
return field;
},
};
const DEFAULT_RECIPES = {
"siteRecipes": [
{
"description": "okta uses a hidden password field to disable filling",
"hosts": ["mozilla.okta.com"],
"passwordSelector": "#pass-signin"
},
{
"description": "anthem uses a hidden password and username field to disable filling",
"hosts": ["www.anthem.com"],
"passwordSelector": "#LoginContent_txtLoginPass"
},
{
"description": "An ephemeral password-shim field is incorrectly selected as the username field.",
"hosts": ["www.discover.com"],
"usernameSelector": "#login-account"
}
]
};

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

@ -0,0 +1,19 @@
{
"siteRecipes": [
{
"description": "okta uses a hidden password field to disable filling",
"hosts": ["mozilla.okta.com"],
"passwordSelector": "#pass-signin"
},
{
"description": "anthem uses a hidden password and username field to disable filling",
"hosts": ["www.anthem.com"],
"passwordSelector": "#LoginContent_txtLoginPass"
},
{
"description": "An ephemeral password-shim field is incorrectly selected as the username field.",
"hosts": ["www.discover.com"],
"usernameSelector": "#login-account"
}
]
}

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

@ -10,3 +10,4 @@ toolkit.jar:
* content/passwordmgr/passwordManagerExceptions.js (content/passwordManagerExceptions.js)
content/passwordmgr/passwordManagerExceptions.xul (content/passwordManagerExceptions.xul)
content/passwordmgr/passwordManagerCommon.js (content/passwordManagerCommon.js)
content/passwordmgr/recipes.json (content/recipes.json)

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