зеркало из https://github.com/mozilla/gecko-dev.git
merge autoland to mozilla-central. r=merge a=merge
MozReview-Commit-ID: H1xftEhaJOS
This commit is contained in:
Коммит
98ce3de2ed
|
@ -85,6 +85,9 @@ pref("extensions.geckoProfiler.getSymbolRules", "localBreakpad,remoteBreakpad");
|
|||
pref("extensions.webextensions.base-content-security-policy", "script-src 'self' https://* moz-extension: blob: filesystem: 'unsafe-eval' 'unsafe-inline'; object-src 'self' https://* moz-extension: blob: filesystem:;");
|
||||
pref("extensions.webextensions.default-content-security-policy", "script-src 'self'; object-src 'self';");
|
||||
|
||||
// Extensions that should not be flagged as legacy in about:addons
|
||||
pref("extensions.legacy.exceptions", "{972ce4c6-7e08-4474-a285-3208198ce6fd},testpilot@cliqz.com,@testpilot-containers,jid1-NeEaf3sAHdKHPA@jetpack,@activity-streams,pulse@mozilla.com,@testpilot-addon,@min-vid,tabcentertest1@mozilla.com,snoozetabs@mozilla.com,speaktome@mozilla.com,hoverpad@mozilla.com");
|
||||
|
||||
// Require signed add-ons by default
|
||||
pref("xpinstall.signatures.required", true);
|
||||
pref("xpinstall.signatures.devInfoURL", "https://wiki.mozilla.org/Addons/Extension_Signing");
|
||||
|
|
|
@ -8,44 +8,30 @@
|
|||
|
||||
// React & Redux
|
||||
const {
|
||||
createClass,
|
||||
DOM: dom,
|
||||
PropTypes,
|
||||
} = require("devtools/client/shared/vendor/react");
|
||||
|
||||
const { l10n } = require("devtools/client/webconsole/new-console-output/utils/messages");
|
||||
const messageToggleDetails = l10n.getStr("messageToggleDetails");
|
||||
|
||||
const CollapseButton = createClass({
|
||||
function CollapseButton(props) {
|
||||
const {
|
||||
open,
|
||||
onClick,
|
||||
title = messageToggleDetails,
|
||||
} = props;
|
||||
|
||||
displayName: "CollapseButton",
|
||||
let classes = ["theme-twisty"];
|
||||
|
||||
propTypes: {
|
||||
onClick: PropTypes.func.isRequired,
|
||||
open: PropTypes.bool.isRequired,
|
||||
title: PropTypes.string,
|
||||
},
|
||||
|
||||
getDefaultProps: function () {
|
||||
return {
|
||||
title: l10n.getStr("messageToggleDetails")
|
||||
};
|
||||
},
|
||||
|
||||
render: function () {
|
||||
const { open, onClick, title } = this.props;
|
||||
|
||||
let classes = ["theme-twisty"];
|
||||
|
||||
if (open) {
|
||||
classes.push("open");
|
||||
}
|
||||
|
||||
return dom.a({
|
||||
className: classes.join(" "),
|
||||
onClick,
|
||||
title: title,
|
||||
});
|
||||
if (open) {
|
||||
classes.push("open");
|
||||
}
|
||||
});
|
||||
|
||||
return dom.a({
|
||||
className: classes.join(" "),
|
||||
onClick,
|
||||
title: title,
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = CollapseButton;
|
||||
|
|
|
@ -101,19 +101,14 @@ const ConsoleOutput = createClass({
|
|||
tableData: messagesTableData.get(message.id),
|
||||
autoscroll,
|
||||
indent: parentGroups.length,
|
||||
timestampsVisible,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
let classList = ["webconsole-output"];
|
||||
|
||||
if (!timestampsVisible) {
|
||||
classList.push("hideTimestamps");
|
||||
}
|
||||
|
||||
return (
|
||||
dom.div({
|
||||
className: classList.join(" "),
|
||||
className: "webconsole-output",
|
||||
onContextMenu: this.onContextMenu,
|
||||
ref: node => {
|
||||
this.outputNode = node;
|
||||
|
|
|
@ -14,11 +14,10 @@ if (typeof define === "undefined") {
|
|||
|
||||
// React
|
||||
const {
|
||||
createFactory,
|
||||
PropTypes
|
||||
} = require("devtools/client/shared/vendor/react");
|
||||
|
||||
const VariablesViewLink = createFactory(require("devtools/client/webconsole/new-console-output/components/variables-view-link"));
|
||||
const VariablesViewLink = require("devtools/client/webconsole/new-console-output/components/variables-view-link");
|
||||
|
||||
const { REPS, MODE } = require("devtools/client/shared/components/reps/reps");
|
||||
const Rep = REPS.Rep;
|
||||
|
@ -94,23 +93,23 @@ function GripMessageBody(props) {
|
|||
);
|
||||
}
|
||||
|
||||
// Regular expression that matches the allowed CSS property names.
|
||||
const allowedStylesRegex = new RegExp(
|
||||
"^(?:-moz-)?(?:background|border|box|clear|color|cursor|display|float|font|line|" +
|
||||
"margin|padding|text|transition|outline|white-space|word|writing|" +
|
||||
"(?:min-|max-)?width|(?:min-|max-)?height)"
|
||||
);
|
||||
|
||||
// Regular expression that matches the forbidden CSS property values.
|
||||
const forbiddenValuesRegexs = [
|
||||
// url(), -moz-element()
|
||||
/\b(?:url|(?:-moz-)?element)[\s('"]+/gi,
|
||||
|
||||
// various URL protocols
|
||||
/['"(]*(?:chrome|resource|about|app|data|https?|ftp|file):+\/*/gi,
|
||||
];
|
||||
|
||||
function cleanupStyle(userProvidedStyle, createElement) {
|
||||
// Regular expression that matches the allowed CSS property names.
|
||||
const allowedStylesRegex = new RegExp(
|
||||
"^(?:-moz-)?(?:background|border|box|clear|color|cursor|display|float|font|line|" +
|
||||
"margin|padding|text|transition|outline|white-space|word|writing|" +
|
||||
"(?:min-|max-)?width|(?:min-|max-)?height)"
|
||||
);
|
||||
|
||||
// Regular expression that matches the forbidden CSS property values.
|
||||
const forbiddenValuesRegexs = [
|
||||
// url(), -moz-element()
|
||||
/\b(?:url|(?:-moz-)?element)[\s('"]+/gi,
|
||||
|
||||
// various URL protocols
|
||||
/['"(]*(?:chrome|resource|about|app|data|https?|ftp|file):+\/*/gi,
|
||||
];
|
||||
|
||||
// Use a dummy element to parse the style string.
|
||||
let dummy = createElement("div");
|
||||
dummy.style = userProvidedStyle;
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
// React & Redux
|
||||
const {
|
||||
createClass,
|
||||
createFactory,
|
||||
|
||||
PropTypes
|
||||
} = require("devtools/client/shared/vendor/react");
|
||||
|
||||
|
@ -37,6 +37,7 @@ const MessageContainer = createClass({
|
|||
autoscroll: PropTypes.bool.isRequired,
|
||||
indent: PropTypes.number.isRequired,
|
||||
tableData: PropTypes.object,
|
||||
timestampsVisible: PropTypes.bool.isRequired,
|
||||
},
|
||||
|
||||
getDefaultProps: function () {
|
||||
|
@ -52,14 +53,21 @@ const MessageContainer = createClass({
|
|||
const tableDataChanged = this.props.tableData !== nextProps.tableData;
|
||||
const responseChanged = this.props.message.response !== nextProps.message.response;
|
||||
const totalTimeChanged = this.props.message.totalTime !== nextProps.message.totalTime;
|
||||
return repeatChanged || openChanged || tableDataChanged || responseChanged ||
|
||||
totalTimeChanged;
|
||||
const timestampVisibleChanged =
|
||||
this.props.timestampsVisible !== nextProps.timestampsVisible;
|
||||
|
||||
return repeatChanged
|
||||
|| openChanged
|
||||
|| tableDataChanged
|
||||
|| responseChanged
|
||||
|| totalTimeChanged
|
||||
|| timestampVisibleChanged;
|
||||
},
|
||||
|
||||
render() {
|
||||
const { message } = this.props;
|
||||
|
||||
let MessageComponent = createFactory(getMessageComponent(message));
|
||||
let MessageComponent = getMessageComponent(message);
|
||||
return MessageComponent(this.props);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -23,7 +23,7 @@ function MessageIcon(props) {
|
|||
const { level } = props;
|
||||
|
||||
const title = l10n.getStr("level." + level);
|
||||
return dom.div({
|
||||
return dom.span({
|
||||
className: "icon",
|
||||
title
|
||||
});
|
||||
|
|
|
@ -8,28 +8,18 @@
|
|||
|
||||
// React & Redux
|
||||
const {
|
||||
createClass,
|
||||
DOM: dom,
|
||||
PropTypes,
|
||||
} = require("devtools/client/shared/vendor/react");
|
||||
|
||||
const INDENT_WIDTH = 12;
|
||||
const MessageIndent = createClass({
|
||||
|
||||
displayName: "MessageIndent",
|
||||
|
||||
propTypes: {
|
||||
indent: PropTypes.number.isRequired,
|
||||
},
|
||||
|
||||
render: function () {
|
||||
const { indent } = this.props;
|
||||
return dom.span({
|
||||
className: "indent",
|
||||
style: {"width": indent * INDENT_WIDTH}
|
||||
});
|
||||
}
|
||||
});
|
||||
function MessageIndent(props) {
|
||||
const { indent } = props;
|
||||
return dom.span({
|
||||
className: "indent",
|
||||
style: {"width": indent * INDENT_WIDTH}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports.MessageIndent = MessageIndent;
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ const {
|
|||
} = require("devtools/client/shared/vendor/react");
|
||||
const { PluralForm } = require("devtools/shared/plural-form");
|
||||
const { l10n } = require("devtools/client/webconsole/new-console-output/utils/messages");
|
||||
const messageRepeatsTooltip = l10n.getStr("messageRepeats.tooltip2");
|
||||
|
||||
MessageRepeat.displayName = "MessageRepeat";
|
||||
|
||||
|
@ -23,13 +24,14 @@ MessageRepeat.propTypes = {
|
|||
|
||||
function MessageRepeat(props) {
|
||||
const { repeat } = props;
|
||||
const visibility = repeat > 1 ? "visible" : "hidden";
|
||||
|
||||
if (!repeat || repeat < 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return dom.span({
|
||||
className: "message-repeats",
|
||||
style: {visibility},
|
||||
title: PluralForm.get(repeat, l10n.getStr("messageRepeats.tooltip2"))
|
||||
.replace("#1", repeat)
|
||||
title: PluralForm.get(repeat, messageRepeatsTooltip).replace("#1", repeat)
|
||||
}, repeat);
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ const {
|
|||
DOM: dom,
|
||||
PropTypes
|
||||
} = require("devtools/client/shared/vendor/react");
|
||||
const GripMessageBody = createFactory(require("devtools/client/webconsole/new-console-output/components/grip-message-body"));
|
||||
const GripMessageBody = require("devtools/client/webconsole/new-console-output/components/grip-message-body");
|
||||
const ConsoleTable = createFactory(require("devtools/client/webconsole/new-console-output/components/console-table"));
|
||||
const {isGroupType, l10n} = require("devtools/client/webconsole/new-console-output/utils/messages");
|
||||
|
||||
|
@ -25,6 +25,7 @@ ConsoleApiCall.propTypes = {
|
|||
open: PropTypes.bool,
|
||||
serviceContainer: PropTypes.object.isRequired,
|
||||
indent: PropTypes.number.isRequired,
|
||||
timestampsVisible: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
ConsoleApiCall.defaultProps = {
|
||||
|
@ -40,6 +41,7 @@ function ConsoleApiCall(props) {
|
|||
tableData,
|
||||
serviceContainer,
|
||||
indent,
|
||||
timestampsVisible,
|
||||
} = props;
|
||||
const {
|
||||
id: messageId,
|
||||
|
@ -108,6 +110,7 @@ function ConsoleApiCall(props) {
|
|||
dispatch,
|
||||
indent,
|
||||
timeStamp,
|
||||
timestampsVisible,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ ConsoleCommand.propTypes = {
|
|||
message: PropTypes.object.isRequired,
|
||||
autoscroll: PropTypes.bool.isRequired,
|
||||
indent: PropTypes.number.isRequired,
|
||||
timestampsVisible: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
ConsoleCommand.defaultProps = {
|
||||
|
@ -29,7 +30,13 @@ ConsoleCommand.defaultProps = {
|
|||
* Displays input from the console.
|
||||
*/
|
||||
function ConsoleCommand(props) {
|
||||
const { autoscroll, indent, message } = props;
|
||||
const {
|
||||
autoscroll,
|
||||
indent,
|
||||
message,
|
||||
timestampsVisible,
|
||||
} = props;
|
||||
|
||||
const {
|
||||
source,
|
||||
type,
|
||||
|
@ -41,7 +48,7 @@ function ConsoleCommand(props) {
|
|||
serviceContainer,
|
||||
} = props;
|
||||
|
||||
const childProps = {
|
||||
return Message({
|
||||
source,
|
||||
type,
|
||||
level,
|
||||
|
@ -49,9 +56,9 @@ function ConsoleCommand(props) {
|
|||
messageBody,
|
||||
scrollToMessage: autoscroll,
|
||||
serviceContainer,
|
||||
indent: indent,
|
||||
};
|
||||
return Message(childProps);
|
||||
indent,
|
||||
timestampsVisible,
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = ConsoleCommand;
|
||||
|
|
|
@ -12,13 +12,14 @@ const {
|
|||
PropTypes
|
||||
} = require("devtools/client/shared/vendor/react");
|
||||
const Message = createFactory(require("devtools/client/webconsole/new-console-output/components/message"));
|
||||
const GripMessageBody = createFactory(require("devtools/client/webconsole/new-console-output/components/grip-message-body"));
|
||||
const GripMessageBody = require("devtools/client/webconsole/new-console-output/components/grip-message-body");
|
||||
|
||||
EvaluationResult.displayName = "EvaluationResult";
|
||||
|
||||
EvaluationResult.propTypes = {
|
||||
message: PropTypes.object.isRequired,
|
||||
indent: PropTypes.number.isRequired,
|
||||
timestampsVisible: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
EvaluationResult.defaultProps = {
|
||||
|
@ -26,7 +27,13 @@ EvaluationResult.defaultProps = {
|
|||
};
|
||||
|
||||
function EvaluationResult(props) {
|
||||
const { message, serviceContainer, indent } = props;
|
||||
const {
|
||||
message,
|
||||
serviceContainer,
|
||||
indent,
|
||||
timestampsVisible,
|
||||
} = props;
|
||||
|
||||
const {
|
||||
source,
|
||||
type,
|
||||
|
@ -60,7 +67,7 @@ function EvaluationResult(props) {
|
|||
|
||||
const topLevelClasses = ["cm-s-mozilla"];
|
||||
|
||||
const childProps = {
|
||||
return Message({
|
||||
source,
|
||||
type,
|
||||
level,
|
||||
|
@ -75,8 +82,8 @@ function EvaluationResult(props) {
|
|||
timeStamp,
|
||||
parameters,
|
||||
notes,
|
||||
};
|
||||
return Message(childProps);
|
||||
timestampsVisible,
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = EvaluationResult;
|
||||
|
|
|
@ -23,6 +23,7 @@ NetworkEventMessage.propTypes = {
|
|||
openNetworkPanel: PropTypes.func.isRequired,
|
||||
}),
|
||||
indent: PropTypes.number.isRequired,
|
||||
timestampsVisible: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
NetworkEventMessage.defaultProps = {
|
||||
|
@ -33,6 +34,7 @@ function NetworkEventMessage({
|
|||
indent,
|
||||
message = {},
|
||||
serviceContainer,
|
||||
timestampsVisible,
|
||||
}) {
|
||||
const {
|
||||
actor,
|
||||
|
@ -73,7 +75,7 @@ function NetworkEventMessage({
|
|||
|
||||
const messageBody = [method, xhr, url, statusBody];
|
||||
|
||||
const childProps = {
|
||||
return Message({
|
||||
source,
|
||||
type,
|
||||
level,
|
||||
|
@ -83,8 +85,8 @@ function NetworkEventMessage({
|
|||
messageBody,
|
||||
serviceContainer,
|
||||
request,
|
||||
};
|
||||
return Message(childProps);
|
||||
timestampsVisible,
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = NetworkEventMessage;
|
||||
|
|
|
@ -19,6 +19,7 @@ PageError.propTypes = {
|
|||
message: PropTypes.object.isRequired,
|
||||
open: PropTypes.bool,
|
||||
indent: PropTypes.number.isRequired,
|
||||
timestampsVisible: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
PageError.defaultProps = {
|
||||
|
@ -33,6 +34,7 @@ function PageError(props) {
|
|||
open,
|
||||
serviceContainer,
|
||||
indent,
|
||||
timestampsVisible,
|
||||
} = props;
|
||||
const {
|
||||
id: messageId,
|
||||
|
@ -55,7 +57,7 @@ function PageError(props) {
|
|||
messageBody = `${message.messageText.initial}…`;
|
||||
}
|
||||
|
||||
const childProps = {
|
||||
return Message({
|
||||
dispatch,
|
||||
messageId,
|
||||
open,
|
||||
|
@ -73,8 +75,8 @@ function PageError(props) {
|
|||
exceptionDocURL,
|
||||
timeStamp,
|
||||
notes,
|
||||
};
|
||||
return Message(childProps);
|
||||
timestampsVisible,
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = PageError;
|
||||
|
|
|
@ -16,10 +16,10 @@ const {
|
|||
const { l10n } = require("devtools/client/webconsole/new-console-output/utils/messages");
|
||||
const actions = require("devtools/client/webconsole/new-console-output/actions/index");
|
||||
const {MESSAGE_SOURCE} = require("devtools/client/webconsole/new-console-output/constants");
|
||||
const CollapseButton = createFactory(require("devtools/client/webconsole/new-console-output/components/collapse-button"));
|
||||
const MessageIndent = createFactory(require("devtools/client/webconsole/new-console-output/components/message-indent").MessageIndent);
|
||||
const MessageIcon = createFactory(require("devtools/client/webconsole/new-console-output/components/message-icon"));
|
||||
const MessageRepeat = createFactory(require("devtools/client/webconsole/new-console-output/components/message-repeat"));
|
||||
const CollapseButton = require("devtools/client/webconsole/new-console-output/components/collapse-button");
|
||||
const MessageIndent = require("devtools/client/webconsole/new-console-output/components/message-indent").MessageIndent;
|
||||
const MessageIcon = require("devtools/client/webconsole/new-console-output/components/message-icon");
|
||||
const MessageRepeat = require("devtools/client/webconsole/new-console-output/components/message-repeat");
|
||||
const FrameView = createFactory(require("devtools/client/shared/components/frame"));
|
||||
const StackTrace = createFactory(require("devtools/client/shared/components/stack-trace"));
|
||||
|
||||
|
@ -47,6 +47,7 @@ const Message = createClass({
|
|||
request: PropTypes.object,
|
||||
dispatch: PropTypes.func,
|
||||
timeStamp: PropTypes.number,
|
||||
timestampsVisible: PropTypes.bool.isRequired,
|
||||
serviceContainer: PropTypes.shape({
|
||||
emitNewMessage: PropTypes.func.isRequired,
|
||||
onViewSourceInDebugger: PropTypes.func,
|
||||
|
@ -116,6 +117,7 @@ const Message = createClass({
|
|||
dispatch,
|
||||
exceptionDocURL,
|
||||
timeStamp = Date.now(),
|
||||
timestampsVisible,
|
||||
notes,
|
||||
} = this.props;
|
||||
|
||||
|
@ -124,9 +126,12 @@ const Message = createClass({
|
|||
topLevelClasses.push("open");
|
||||
}
|
||||
|
||||
const timestampEl = dom.span({
|
||||
className: "timestamp devtools-monospace"
|
||||
}, l10n.timestampString(timeStamp));
|
||||
let timestampEl;
|
||||
if (timestampsVisible === true) {
|
||||
timestampEl = dom.span({
|
||||
className: "timestamp devtools-monospace"
|
||||
}, l10n.timestampString(timeStamp));
|
||||
}
|
||||
|
||||
const icon = MessageIcon({level});
|
||||
|
||||
|
@ -134,13 +139,17 @@ const Message = createClass({
|
|||
let attachment = null;
|
||||
if (this.props.attachment) {
|
||||
attachment = this.props.attachment;
|
||||
} else if (stacktrace) {
|
||||
const child = open ? StackTrace({
|
||||
stacktrace: stacktrace,
|
||||
onViewSourceInDebugger: serviceContainer.onViewSourceInDebugger,
|
||||
onViewSourceInScratchpad: serviceContainer.onViewSourceInScratchpad,
|
||||
}) : null;
|
||||
attachment = dom.div({ className: "stacktrace devtools-monospace" }, child);
|
||||
} else if (stacktrace && open) {
|
||||
attachment = dom.div(
|
||||
{
|
||||
className: "stacktrace devtools-monospace"
|
||||
},
|
||||
StackTrace({
|
||||
stacktrace: stacktrace,
|
||||
onViewSourceInDebugger: serviceContainer.onViewSourceInDebugger,
|
||||
onViewSourceInScratchpad: serviceContainer.onViewSourceInScratchpad,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// If there is an expandable part, make it collapsible.
|
||||
|
@ -182,7 +191,7 @@ const Message = createClass({
|
|||
notesNodes = [];
|
||||
}
|
||||
|
||||
const repeat = this.props.repeat ? MessageRepeat({repeat: this.props.repeat}) : null;
|
||||
const repeat = MessageRepeat({repeat: this.props.repeat});
|
||||
|
||||
let onFrameClick;
|
||||
if (serviceContainer && frame) {
|
||||
|
@ -230,11 +239,13 @@ const Message = createClass({
|
|||
dom.span({ className: "message-body-wrapper" },
|
||||
dom.span({ className: "message-flex-body" },
|
||||
// Add whitespaces for formatting when copying to the clipboard.
|
||||
" ", dom.span({ className: "message-body devtools-monospace" },
|
||||
timestampEl ? " " : null,
|
||||
dom.span({ className: "message-body devtools-monospace" },
|
||||
messageBody,
|
||||
learnMore
|
||||
),
|
||||
" ", repeat,
|
||||
repeat ? " " : null,
|
||||
repeat,
|
||||
" ", location
|
||||
),
|
||||
// Add a newline for formatting when copying to the clipboard.
|
||||
|
|
|
@ -19,8 +19,8 @@ VariablesViewLink.propTypes = {
|
|||
object: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
function VariablesViewLink(props) {
|
||||
const { className, object, children } = props;
|
||||
function VariablesViewLink(props, ...children) {
|
||||
const { className, object } = props;
|
||||
const classes = ["cm-variable"];
|
||||
if (className) {
|
||||
classes.push(className);
|
||||
|
@ -32,7 +32,7 @@ function VariablesViewLink(props) {
|
|||
"data-link-actor-id": object.actor,
|
||||
className: classes.join(" "),
|
||||
draggable: false,
|
||||
}, children)
|
||||
}, ...children)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -92,14 +92,28 @@ describe("ConsoleAPICall component:", () => {
|
|||
expect(wrapper.find(".indent").prop("style").width).toBe(`0`);
|
||||
});
|
||||
|
||||
it("renders a timestamp", () => {
|
||||
it("renders a timestamp when passed a truthy timestampsVisible prop", () => {
|
||||
const message = stubPreparedMessages.get("console.log('foobar', 'test')");
|
||||
const wrapper = render(ConsoleApiCall({ message, serviceContainer }));
|
||||
const wrapper = render(ConsoleApiCall({
|
||||
message,
|
||||
serviceContainer,
|
||||
timestampsVisible: true,
|
||||
}));
|
||||
const L10n = require("devtools/client/webconsole/new-console-output/test/fixtures/L10n");
|
||||
const { timestampString } = new L10n();
|
||||
|
||||
expect(wrapper.find(".timestamp").text()).toBe(timestampString(message.timeStamp));
|
||||
});
|
||||
|
||||
it("does not render a timestamp when not asked to", () => {
|
||||
const message = stubPreparedMessages.get("console.log('foobar', 'test')");
|
||||
const wrapper = render(ConsoleApiCall({
|
||||
message,
|
||||
serviceContainer,
|
||||
}));
|
||||
|
||||
expect(wrapper.find(".timestamp").length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("console.count", () => {
|
||||
|
|
|
@ -91,12 +91,25 @@ describe("EvaluationResult component:", () => {
|
|||
expect(locationLink.text()).toBe("debugger eval code:1:4");
|
||||
});
|
||||
|
||||
it("has a timestamp", () => {
|
||||
it("has a timestamp when passed a truthy timestampsVisible prop", () => {
|
||||
const message = stubPreparedMessages.get("new Date(0)");
|
||||
const wrapper = render(EvaluationResult({ message }));
|
||||
const wrapper = render(EvaluationResult({
|
||||
message,
|
||||
timestampsVisible: true,
|
||||
}));
|
||||
const L10n = require("devtools/client/webconsole/new-console-output/test/fixtures/L10n");
|
||||
const { timestampString } = new L10n();
|
||||
|
||||
expect(wrapper.find(".timestamp").text()).toBe(timestampString(message.timeStamp));
|
||||
});
|
||||
|
||||
it("does not have a timestamp when timestampsVisible prop is falsy", () => {
|
||||
const message = stubPreparedMessages.get("new Date(0)");
|
||||
const wrapper = render(EvaluationResult({
|
||||
message,
|
||||
timestampsVisible: false,
|
||||
}));
|
||||
|
||||
expect(wrapper.find(".timestamp").length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,11 +6,10 @@
|
|||
const expect = require("expect");
|
||||
const {
|
||||
renderComponent,
|
||||
shallowRenderComponent
|
||||
} = require("devtools/client/webconsole/new-console-output/test/helpers");
|
||||
|
||||
// Components under test.
|
||||
const { MessageContainer } = require("devtools/client/webconsole/new-console-output/components/message-container");
|
||||
const { MessageContainer, getMessageComponent } = require("devtools/client/webconsole/new-console-output/components/message-container");
|
||||
const ConsoleApiCall = require("devtools/client/webconsole/new-console-output/components/message-types/console-api-call");
|
||||
const EvaluationResult = require("devtools/client/webconsole/new-console-output/components/message-types/evaluation-result");
|
||||
const PageError = require("devtools/client/webconsole/new-console-output/components/message-types/page-error");
|
||||
|
@ -50,11 +49,7 @@ describe("MessageContainer component:", () => {
|
|||
|
||||
messageTypes.forEach(info => {
|
||||
const { component, message } = info;
|
||||
const rendered = shallowRenderComponent(MessageContainer, {
|
||||
message,
|
||||
serviceContainer,
|
||||
});
|
||||
expect(rendered.type).toBe(component);
|
||||
expect(getMessageComponent(message)).toBe(component);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -14,12 +14,10 @@ describe("MessageRepeat component:", () => {
|
|||
it("renders repeated value correctly", () => {
|
||||
const rendered = renderComponent(MessageRepeat, { repeat: 99 });
|
||||
expect(rendered.classList.contains("message-repeats")).toBe(true);
|
||||
expect(rendered.style.visibility).toBe("visible");
|
||||
expect(rendered.textContent).toBe("99");
|
||||
});
|
||||
|
||||
it("renders an un-repeated value correctly", () => {
|
||||
const rendered = renderComponent(MessageRepeat, { repeat: 1 });
|
||||
expect(rendered.style.visibility).toBe("hidden");
|
||||
it("does not render un-repeated value", () => {
|
||||
expect(MessageRepeat({ repeat: 1 })).toBe(null);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -24,7 +24,11 @@ describe("NetworkEventMessage component:", () => {
|
|||
describe("GET request", () => {
|
||||
it("renders as expected", () => {
|
||||
const message = stubPreparedMessages.get("GET request eventTimings");
|
||||
const wrapper = render(NetworkEventMessage({ message, serviceContainer }));
|
||||
const wrapper = render(NetworkEventMessage({
|
||||
message,
|
||||
serviceContainer,
|
||||
timestampsVisible: true,
|
||||
}));
|
||||
const L10n = require("devtools/client/webconsole/new-console-output/test/fixtures/L10n");
|
||||
const { timestampString } = new L10n();
|
||||
|
||||
|
@ -37,6 +41,17 @@ describe("NetworkEventMessage component:", () => {
|
|||
expect(wrapper.find(".message-body .status").text()).toMatch(EXPECTED_STATUS);
|
||||
});
|
||||
|
||||
it("does not have a timestamp when timestampsVisible prop is falsy", () => {
|
||||
const message = stubPreparedMessages.get("GET request eventTimings");
|
||||
const wrapper = render(NetworkEventMessage({
|
||||
message,
|
||||
serviceContainer,
|
||||
timestampsVisible: false,
|
||||
}));
|
||||
|
||||
expect(wrapper.find(".timestamp").length).toBe(0);
|
||||
});
|
||||
|
||||
it("has the expected indent", () => {
|
||||
const message = stubPreparedMessages.get("GET request");
|
||||
|
||||
|
|
|
@ -27,7 +27,11 @@ const serviceContainer = require("devtools/client/webconsole/new-console-output/
|
|||
describe("PageError component:", () => {
|
||||
it("renders", () => {
|
||||
const message = stubPreparedMessages.get("ReferenceError: asdf is not defined");
|
||||
const wrapper = render(PageError({ message, serviceContainer }));
|
||||
const wrapper = render(PageError({
|
||||
message,
|
||||
serviceContainer,
|
||||
timestampsVisible: true,
|
||||
}));
|
||||
const L10n = require("devtools/client/webconsole/new-console-output/test/fixtures/L10n");
|
||||
const { timestampString } = new L10n();
|
||||
|
||||
|
@ -47,6 +51,17 @@ describe("PageError component:", () => {
|
|||
expect(locationLink.text()).toBe("test-console-api.html:3:5");
|
||||
});
|
||||
|
||||
it("does not have a timestamp when timestampsVisible prop is falsy", () => {
|
||||
const message = stubPreparedMessages.get("ReferenceError: asdf is not defined");
|
||||
const wrapper = render(PageError({
|
||||
message,
|
||||
serviceContainer,
|
||||
timestampsVisible: false,
|
||||
}));
|
||||
|
||||
expect(wrapper.find(".timestamp").length).toBe(0);
|
||||
});
|
||||
|
||||
it("renders an error with a longString exception message", () => {
|
||||
const message = stubPreparedMessages.get("TypeError longString message");
|
||||
const wrapper = render(PageError({ message, serviceContainer }));
|
||||
|
|
|
@ -5,11 +5,16 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const {PrefObserver} = require("devtools/client/shared/prefs");
|
||||
|
||||
// RegExp that validates copied text for log lines.
|
||||
const LOG_FORMAT = /^[\d:.]+ .+ \d+ .+:\d+$/;
|
||||
const LOG_FORMAT_WITH_TIMESTAMP = /^[\d:.]+ .+ (\d+ )?.+:\d+$/;
|
||||
const LOG_FORMAT_WITHOUT_TIMESTAMP = /^.+ (\d+ )?.+:\d+$/;
|
||||
// RegExp that validates copied text for stacktrace lines.
|
||||
const TRACE_FORMAT = /^\t.+ .+:\d+:\d+$/;
|
||||
|
||||
const PREF_MESSAGE_TIMESTAMP = "devtools.webconsole.timestampMessages";
|
||||
|
||||
const TEST_URI = `data:text/html;charset=utf-8,<script>
|
||||
window.logStuff = function () {
|
||||
console.log("simple text message");
|
||||
|
@ -24,6 +29,11 @@ const TEST_URI = `data:text/html;charset=utf-8,<script>
|
|||
// different log messages.
|
||||
|
||||
add_task(function* () {
|
||||
let observer = new PrefObserver("");
|
||||
let onPrefUpdated = observer.once(PREF_MESSAGE_TIMESTAMP, () => {});
|
||||
Services.prefs.setBoolPref(PREF_MESSAGE_TIMESTAMP, true);
|
||||
yield onPrefUpdated;
|
||||
|
||||
let hud = yield openNewTabAndConsole(TEST_URI);
|
||||
hud.jsterm.clearOutput();
|
||||
|
||||
|
@ -41,7 +51,8 @@ add_task(function* () {
|
|||
let lines = clipboardText.split("\n");
|
||||
ok(lines.length, 2, "There are 2 lines in the copied text");
|
||||
is(lines[1], "", "The last line is an empty new line");
|
||||
ok(LOG_FORMAT.test(lines[0]), "Log line has the right format:\n" + lines[0]);
|
||||
ok(LOG_FORMAT_WITH_TIMESTAMP.test(lines[0]),
|
||||
"Log line has the right format:\n" + lines[0]);
|
||||
|
||||
info("Test copy menu item for the stack trace message");
|
||||
message = yield waitFor(() => findMessage(hud, "console.trace"));
|
||||
|
@ -52,9 +63,45 @@ add_task(function* () {
|
|||
lines = clipboardText.split("\n");
|
||||
ok(lines.length, 4, "There are 4 lines in the copied text");
|
||||
is(lines[3], "", "The last line is an empty new line");
|
||||
ok(LOG_FORMAT.test(lines[0]), "Log line has the right format:\n" + lines[0]);
|
||||
ok(LOG_FORMAT_WITH_TIMESTAMP.test(lines[0]),
|
||||
"Log line has the right format:\n" + lines[0]);
|
||||
ok(TRACE_FORMAT.test(lines[1]), "Stacktrace line has the right format:\n" + lines[1]);
|
||||
ok(TRACE_FORMAT.test(lines[2]), "Stacktrace line has the right format:\n" + lines[2]);
|
||||
|
||||
info("Test copy menu item without timestamp");
|
||||
|
||||
onPrefUpdated = observer.once(PREF_MESSAGE_TIMESTAMP, () => {});
|
||||
Services.prefs.setBoolPref(PREF_MESSAGE_TIMESTAMP, false);
|
||||
yield onPrefUpdated;
|
||||
|
||||
info("Test copy menu item for the simple log");
|
||||
message = yield waitFor(() => findMessage(hud, "simple text message"));
|
||||
clipboardText = yield copyMessageContent(hud, message);
|
||||
ok(true, "Clipboard text was found and saved");
|
||||
|
||||
info("Check copied text for simple log message");
|
||||
lines = clipboardText.split("\n");
|
||||
ok(lines.length, 2, "There are 2 lines in the copied text");
|
||||
is(lines[1], "", "The last line is an empty new line");
|
||||
ok(LOG_FORMAT_WITHOUT_TIMESTAMP.test(lines[0]),
|
||||
"Log line has the right format:\n" + lines[0]);
|
||||
|
||||
info("Test copy menu item for the stack trace message");
|
||||
message = yield waitFor(() => findMessage(hud, "console.trace"));
|
||||
clipboardText = yield copyMessageContent(hud, message);
|
||||
ok(true, "Clipboard text was found and saved");
|
||||
|
||||
info("Check copied text for stack trace message");
|
||||
lines = clipboardText.split("\n");
|
||||
ok(lines.length, 4, "There are 4 lines in the copied text");
|
||||
is(lines[3], "", "The last line is an empty new line");
|
||||
ok(LOG_FORMAT_WITHOUT_TIMESTAMP.test(lines[0]),
|
||||
"Log line has the right format:\n" + lines[0]);
|
||||
ok(TRACE_FORMAT.test(lines[1]), "Stacktrace line has the right format:\n" + lines[1]);
|
||||
ok(TRACE_FORMAT.test(lines[2]), "Stacktrace line has the right format:\n" + lines[2]);
|
||||
|
||||
observer.destroy();
|
||||
Services.prefs.clearUserPref(PREF_MESSAGE_TIMESTAMP);
|
||||
});
|
||||
|
||||
/**
|
||||
|
|
|
@ -10,16 +10,24 @@
|
|||
|
||||
const {PrefObserver} = require("devtools/client/shared/prefs");
|
||||
|
||||
const TEST_URI = "data:text/html;charset=utf-8,Web Console test for " +
|
||||
"bug 1307871 - preference for toggling timestamps in messages";
|
||||
const TEST_URI = `data:text/html;charset=utf-8,
|
||||
Web Console test for bug 1307871 - preference for toggling timestamps in messages
|
||||
<script>
|
||||
window.logMessage = function () {
|
||||
console.log("simple text message");
|
||||
};
|
||||
</script>`;
|
||||
const PREF_MESSAGE_TIMESTAMP = "devtools.webconsole.timestampMessages";
|
||||
|
||||
add_task(function* () {
|
||||
let hud = yield openNewTabAndConsole(TEST_URI);
|
||||
let outputNode = hud.ui.experimentalOutputNode;
|
||||
let outputEl = outputNode.querySelector(".webconsole-output");
|
||||
|
||||
testPrefDefaults(outputEl);
|
||||
info("Call the log function defined in the test page");
|
||||
yield ContentTask.spawn(gBrowser.selectedBrowser, null, () => {
|
||||
content.wrappedJSObject.logMessage();
|
||||
});
|
||||
|
||||
yield testPrefDefaults(hud);
|
||||
|
||||
let observer = new PrefObserver("");
|
||||
let toolbox = gDevTools.getToolbox(hud.target);
|
||||
|
@ -27,16 +35,17 @@ add_task(function* () {
|
|||
yield togglePref(optionsPanel, observer);
|
||||
observer.destroy();
|
||||
|
||||
yield testChangedPref(outputEl);
|
||||
yield testChangedPref(hud);
|
||||
|
||||
Services.prefs.clearUserPref(PREF_MESSAGE_TIMESTAMP);
|
||||
});
|
||||
|
||||
function testPrefDefaults(outputEl) {
|
||||
function* testPrefDefaults(hud) {
|
||||
let prefValue = Services.prefs.getBoolPref(PREF_MESSAGE_TIMESTAMP);
|
||||
ok(!prefValue, "Messages should have no timestamp by default (pref check)");
|
||||
ok(outputEl.classList.contains("hideTimestamps"),
|
||||
"Messages should have no timestamp (class name check)");
|
||||
let message = yield waitFor(() => findMessage(hud, "simple text message"));
|
||||
is(message.querySelectorAll(".timestamp").length, 0,
|
||||
"Messages should have no timestamp by default (element check)");
|
||||
}
|
||||
|
||||
function* togglePref(panel, observer) {
|
||||
|
@ -50,9 +59,10 @@ function* togglePref(panel, observer) {
|
|||
yield prefChanged;
|
||||
}
|
||||
|
||||
function* testChangedPref(outputEl) {
|
||||
function* testChangedPref(hud) {
|
||||
let prefValue = Services.prefs.getBoolPref(PREF_MESSAGE_TIMESTAMP);
|
||||
ok(prefValue, "Messages should have timestamps (pref check)");
|
||||
ok(!outputEl.classList.contains("hideTimestamps"),
|
||||
"Messages should have timestamps (class name check)");
|
||||
let message = yield waitFor(() => findMessage(hud, "simple text message"));
|
||||
is(message.querySelectorAll(".timestamp").length, 1,
|
||||
"Messages should have timestamp (element check)");
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<!DOCTYPE html><meta charset=utf-8>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<body style="margin: 0px">
|
||||
<body style="margin: 0px; overflow: hidden">
|
||||
<script>
|
||||
let args = location.search.substring(1).split(',');
|
||||
document.write(`<img src="${args[0]}" width="${args[1]}" height="${args[2]}">`);
|
||||
|
|
|
@ -194,14 +194,14 @@ fuzzy(18,128) == downscale-32px.html?-png-in.ico downscale-32px-ref.html
|
|||
|
||||
# Test images taller or wider than 32767 pixels.
|
||||
== huge-1.html?100x32768.png,100,100 huge-1.html?100x100.png,100,100
|
||||
fails-if(stylo) == huge-1.html?100x32768.png,100,32768 huge-1.html?100x100.png,100,32768
|
||||
== huge-1.html?100x32768.png,100,32768 huge-1.html?100x100.png,100,32768
|
||||
== huge-1.html?32768x100.png,100,100 huge-1.html?100x100.png,100,100
|
||||
fails-if(stylo) == huge-1.html?32768x100.png,32768,100 huge-1.html?100x100.png,32768,100
|
||||
== huge-1.html?32768x100.png,32768,100 huge-1.html?100x100.png,32768,100
|
||||
== huge-1.html?100x32768.gif,100,100 huge-1.html?100x100.gif,100,100
|
||||
fails-if(stylo) == huge-1.html?100x32768.gif,100,32768 huge-1.html?100x100.gif,100,32768
|
||||
== huge-1.html?100x32768.gif,100,32768 huge-1.html?100x100.gif,100,32768
|
||||
== huge-1.html?32768x100.gif,100,100 huge-1.html?100x100.gif,100,100
|
||||
fails-if(stylo) == huge-1.html?32768x100.gif,32768,100 huge-1.html?100x100.gif,32768,100
|
||||
== huge-1.html?32768x100.gif,32768,100 huge-1.html?100x100.gif,32768,100
|
||||
== huge-1.html?100x32768.jpg,100,100 huge-1.html?100x100.jpg,100,100
|
||||
fails-if(stylo) == huge-1.html?100x32768.jpg,100,32768 huge-1.html?100x100.jpg,100,32768
|
||||
== huge-1.html?100x32768.jpg,100,32768 huge-1.html?100x100.jpg,100,32768
|
||||
== huge-1.html?32768x100.jpg,100,100 huge-1.html?100x100.jpg,100,100
|
||||
fails-if(stylo) == huge-1.html?32768x100.jpg,32768,100 huge-1.html?100x100.jpg,32768,100
|
||||
== huge-1.html?32768x100.jpg,32768,100 huge-1.html?100x100.jpg,32768,100
|
||||
|
|
|
@ -299,11 +299,16 @@ this.XPCOMUtils = {
|
|||
* @param aOnUpdate
|
||||
* A function to call upon update. Receives as arguments
|
||||
* `(aPreference, previousValue, newValue)`
|
||||
* @param aTransform
|
||||
* An optional function to transform the value. If provided,
|
||||
* this function receives the new preference value as an argument
|
||||
* and its return value is used by the getter.
|
||||
*/
|
||||
defineLazyPreferenceGetter: function XPCU_defineLazyPreferenceGetter(
|
||||
aObject, aName, aPreference,
|
||||
aDefaultValue = null,
|
||||
aOnUpdate = null)
|
||||
aOnUpdate = null,
|
||||
aTransform = val => val)
|
||||
{
|
||||
// Note: We need to keep a reference to this observer alive as long
|
||||
// as aObject is alive. This means that all of our getters need to
|
||||
|
@ -343,7 +348,7 @@ this.XPCOMUtils = {
|
|||
|
||||
function lazyGetter() {
|
||||
if (observer.value === undefined) {
|
||||
observer.value = Preferences.get(aPreference, aDefaultValue);
|
||||
observer.value = aTransform(Preferences.get(aPreference, aDefaultValue));
|
||||
}
|
||||
return observer.value;
|
||||
}
|
||||
|
|
|
@ -151,6 +151,18 @@ add_test(function test_defineLazyPreferenceGetter()
|
|||
|
||||
equal(obj.pref, "defaultValue", "Should return default value after pref is reset");
|
||||
|
||||
obj = {};
|
||||
XPCOMUtils.defineLazyPreferenceGetter(obj, "pref", PREF, "a,b",
|
||||
null, value => value.split(","));
|
||||
|
||||
deepEqual(obj.pref, ["a", "b"], "transform is applied to default value");
|
||||
|
||||
Preferences.set(PREF, "x,y,z");
|
||||
deepEqual(obj.pref, ["x", "y", "z"], "transform is applied to updated value");
|
||||
|
||||
Preferences.reset(PREF);
|
||||
deepEqual(obj.pref, ["a", "b"], "transform is applied to reset default");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ fails-if(webrender) fails-if(stylo) != transform-floating-point-invalidation.htm
|
|||
fails-if(webrender) fails-if(stylo) != transform-floating-point-invalidation.html?reverse about:blank
|
||||
fails-if(webrender) fails-if(stylo) != nudge-to-integer-invalidation.html about:blank
|
||||
fails-if(webrender) fails-if(stylo) != nudge-to-integer-invalidation.html?reverse about:blank
|
||||
fails-if(webrender) != clipped-animated-transform-1.html about:blank
|
||||
fails-if(webrender) skip-if(stylo) != clipped-animated-transform-1.html about:blank # Bug 1334036 (stylo)
|
||||
fails-if(webrender) fails-if(stylo) != paintedlayer-recycling-1.html about:blank
|
||||
fails-if(webrender) fails-if(stylo) != paintedlayer-recycling-2.html about:blank
|
||||
pref(layers.single-tile.enabled,false) fails-if(webrender) fails-if(stylo) != paintedlayer-recycling-3.html about:blank
|
||||
|
|
|
@ -69,7 +69,6 @@ to mochitest command.
|
|||
* test_bug413958.html `monitorConsole` [3]
|
||||
* test_parser_diagnostics_unprintables.html [550]
|
||||
* Transition support:
|
||||
* test_compute_data_with_start_struct.html `transition` [2]
|
||||
* test_transitions.html: pseudo elements [12]
|
||||
* Events:
|
||||
* test_animations_event_order.html [2]
|
||||
|
@ -80,7 +79,6 @@ to mochitest command.
|
|||
* character not properly escaped servo/servo#15947
|
||||
* test_parse_url.html [1]
|
||||
* test_bug829816.html [8]
|
||||
* test_compute_data_with_start_struct.html `timing-function`: incorrectly computing keywords to bezier function servo/servo#15086 [2]
|
||||
* \@counter-style support bug 1328319
|
||||
* test_counter_descriptor_storage.html [1]
|
||||
* test_counter_style.html [5]
|
||||
|
|
|
@ -339,19 +339,24 @@ impl PropertyAnimation {
|
|||
|
||||
/// Update the given animation at a given point of progress.
|
||||
pub fn update(&self, style: &mut ComputedValues, time: f64) {
|
||||
let progress = match self.timing_function {
|
||||
let timing_function = match self.timing_function {
|
||||
TransitionTimingFunction::Keyword(keyword) =>
|
||||
keyword.to_non_keyword_value(),
|
||||
other => other,
|
||||
};
|
||||
let progress = match timing_function {
|
||||
TransitionTimingFunction::CubicBezier(p1, p2) => {
|
||||
// See `WebCore::AnimationBase::solveEpsilon(double)` in WebKit.
|
||||
let epsilon = 1.0 / (200.0 * (self.duration.seconds() as f64));
|
||||
Bezier::new(Point2D::new(p1.x as f64, p1.y as f64),
|
||||
Point2D::new(p2.x as f64, p2.y as f64)).solve(time, epsilon)
|
||||
}
|
||||
},
|
||||
TransitionTimingFunction::Steps(steps, StartEnd::Start) => {
|
||||
(time * (steps as f64)).ceil() / (steps as f64)
|
||||
}
|
||||
},
|
||||
TransitionTimingFunction::Steps(steps, StartEnd::End) => {
|
||||
(time * (steps as f64)).floor() / (steps as f64)
|
||||
}
|
||||
},
|
||||
TransitionTimingFunction::Frames(frames) => {
|
||||
// https://drafts.csswg.org/css-timing/#frames-timing-functions
|
||||
let mut out = (time * (frames as f64)).floor() / ((frames - 1) as f64);
|
||||
|
@ -367,7 +372,10 @@ impl PropertyAnimation {
|
|||
out = 1.0;
|
||||
}
|
||||
out
|
||||
}
|
||||
},
|
||||
TransitionTimingFunction::Keyword(_) => {
|
||||
panic!("Keyword function should not appear")
|
||||
},
|
||||
};
|
||||
|
||||
self.property.update(style, progress);
|
||||
|
|
|
@ -9,6 +9,7 @@ use properties::longhands::transition_timing_function::single_value::SpecifiedVa
|
|||
use properties::longhands::transition_timing_function::single_value::computed_value::StartEnd;
|
||||
use properties::longhands::transition_timing_function::single_value::computed_value::T as ComputedTimingFunction;
|
||||
use std::mem;
|
||||
use values::computed::ToComputedValue;
|
||||
|
||||
impl nsTimingFunction {
|
||||
fn set_as_step(&mut self, function_type: nsTimingFunction_Type, steps: u32) {
|
||||
|
@ -46,23 +47,7 @@ impl nsTimingFunction {
|
|||
|
||||
impl From<ComputedTimingFunction> for nsTimingFunction {
|
||||
fn from(function: ComputedTimingFunction) -> nsTimingFunction {
|
||||
let mut tf: nsTimingFunction = unsafe { mem::zeroed() };
|
||||
|
||||
match function {
|
||||
ComputedTimingFunction::Steps(steps, StartEnd::Start) => {
|
||||
tf.set_as_step(nsTimingFunction_Type::StepStart, steps);
|
||||
},
|
||||
ComputedTimingFunction::Steps(steps, StartEnd::End) => {
|
||||
tf.set_as_step(nsTimingFunction_Type::StepEnd, steps);
|
||||
},
|
||||
ComputedTimingFunction::Frames(frames) => {
|
||||
tf.set_as_frames(frames);
|
||||
},
|
||||
ComputedTimingFunction::CubicBezier(p1, p2) => {
|
||||
tf.set_as_bezier(nsTimingFunction_Type::CubicBezier, p1, p2);
|
||||
},
|
||||
}
|
||||
tf
|
||||
SpecifiedTimingFunction::from_computed_value(&function).into()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,7 +74,7 @@ impl From<SpecifiedTimingFunction> for nsTimingFunction {
|
|||
Point2D::new(p2.x.get(), p2.y.get()));
|
||||
},
|
||||
SpecifiedTimingFunction::Keyword(keyword) => {
|
||||
match keyword.to_computed_value() {
|
||||
match keyword.to_non_keyword_value() {
|
||||
ComputedTimingFunction::CubicBezier(p1, p2) => {
|
||||
match keyword {
|
||||
FunctionKeyword::Ease => {
|
||||
|
@ -120,7 +105,10 @@ impl From<SpecifiedTimingFunction> for nsTimingFunction {
|
|||
},
|
||||
ComputedTimingFunction::Frames(frames) => {
|
||||
tf.set_as_frames(frames)
|
||||
}
|
||||
},
|
||||
ComputedTimingFunction::Keyword(_) => {
|
||||
panic!("Keyword function should not appear")
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
@ -145,11 +133,21 @@ impl From<nsTimingFunction> for ComputedTimingFunction {
|
|||
ComputedTimingFunction::Frames(
|
||||
unsafe { function.__bindgen_anon_1.__bindgen_anon_1.as_ref().mStepsOrFrames })
|
||||
}
|
||||
nsTimingFunction_Type::Ease |
|
||||
nsTimingFunction_Type::Linear |
|
||||
nsTimingFunction_Type::EaseIn |
|
||||
nsTimingFunction_Type::EaseOut |
|
||||
nsTimingFunction_Type::EaseInOut |
|
||||
nsTimingFunction_Type::Ease => {
|
||||
ComputedTimingFunction::Keyword(FunctionKeyword::Ease)
|
||||
},
|
||||
nsTimingFunction_Type::Linear => {
|
||||
ComputedTimingFunction::Keyword(FunctionKeyword::Linear)
|
||||
},
|
||||
nsTimingFunction_Type::EaseIn => {
|
||||
ComputedTimingFunction::Keyword(FunctionKeyword::EaseIn)
|
||||
},
|
||||
nsTimingFunction_Type::EaseOut => {
|
||||
ComputedTimingFunction::Keyword(FunctionKeyword::EaseOut)
|
||||
},
|
||||
nsTimingFunction_Type::EaseInOut => {
|
||||
ComputedTimingFunction::Keyword(FunctionKeyword::EaseInOut)
|
||||
},
|
||||
nsTimingFunction_Type::CubicBezier => {
|
||||
ComputedTimingFunction::CubicBezier(
|
||||
TypedPoint2D::new(unsafe { function.__bindgen_anon_1.mFunc.as_ref().mX1 },
|
||||
|
|
|
@ -502,6 +502,7 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
|
|||
use parser::{Parse, ParserContext};
|
||||
use std::fmt;
|
||||
use style_traits::ToCss;
|
||||
use super::FunctionKeyword;
|
||||
use values::specified;
|
||||
|
||||
pub use super::parse;
|
||||
|
@ -512,6 +513,7 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
|
|||
CubicBezier(Point2D<f32>, Point2D<f32>),
|
||||
Steps(u32, StartEnd),
|
||||
Frames(u32),
|
||||
Keyword(FunctionKeyword),
|
||||
}
|
||||
|
||||
impl ToCss for T {
|
||||
|
@ -538,6 +540,9 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
|
|||
try!(frames.to_css(dest));
|
||||
dest.write_str(")")
|
||||
},
|
||||
T::Keyword(keyword) => {
|
||||
super::serialize_keyword(dest, keyword)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -651,6 +656,22 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
|
|||
dest.write_str(")")
|
||||
}
|
||||
|
||||
fn serialize_keyword<W>(dest: &mut W, keyword: FunctionKeyword) -> fmt::Result
|
||||
where W: fmt::Write,
|
||||
{
|
||||
match keyword {
|
||||
FunctionKeyword::StepStart => {
|
||||
serialize_steps(dest, specified::Integer::new(1), StartEnd::Start)
|
||||
},
|
||||
FunctionKeyword::StepEnd => {
|
||||
serialize_steps(dest, specified::Integer::new(1), StartEnd::End)
|
||||
},
|
||||
_ => {
|
||||
keyword.to_css(dest)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/css-transitions/#serializing-a-timing-function
|
||||
impl ToCss for SpecifiedValue {
|
||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||
|
@ -675,17 +696,7 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
|
|||
dest.write_str(")")
|
||||
},
|
||||
SpecifiedValue::Keyword(keyword) => {
|
||||
match keyword {
|
||||
FunctionKeyword::StepStart => {
|
||||
serialize_steps(dest, specified::Integer::new(1), StartEnd::Start)
|
||||
},
|
||||
FunctionKeyword::StepEnd => {
|
||||
serialize_steps(dest, specified::Integer::new(1), StartEnd::End)
|
||||
},
|
||||
_ => {
|
||||
keyword.to_css(dest)
|
||||
},
|
||||
}
|
||||
serialize_keyword(dest, keyword)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -708,7 +719,9 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
|
|||
SpecifiedValue::Frames(frames) => {
|
||||
computed_value::T::Frames(frames.to_computed_value(context) as u32)
|
||||
},
|
||||
SpecifiedValue::Keyword(keyword) => keyword.to_computed_value(),
|
||||
SpecifiedValue::Keyword(keyword) => {
|
||||
computed_value::T::Keyword(keyword)
|
||||
},
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
|
@ -729,13 +742,16 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
|
|||
let frames = frames as i32;
|
||||
SpecifiedValue::Frames(specified::Integer::from_computed_value(&frames))
|
||||
},
|
||||
computed_value::T::Keyword(keyword) => {
|
||||
SpecifiedValue::Keyword(keyword)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FunctionKeyword {
|
||||
#[inline]
|
||||
pub fn to_computed_value(&self) -> computed_value::T {
|
||||
pub fn to_non_keyword_value(&self) -> computed_value::T {
|
||||
match *self {
|
||||
FunctionKeyword::Ease => ease(),
|
||||
FunctionKeyword::Linear => linear(),
|
||||
|
@ -753,7 +769,7 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
|
|||
|
||||
#[inline]
|
||||
pub fn get_initial_value() -> computed_value::T {
|
||||
ease()
|
||||
computed_value::T::Keyword(FunctionKeyword::Ease)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
@ -538,13 +538,13 @@ function _execute_test() {
|
|||
run_next_test();
|
||||
}
|
||||
|
||||
if (coverageCollector != null) {
|
||||
coverageCollector.recordTestCoverage(_TEST_FILE[0]);
|
||||
}
|
||||
|
||||
do_test_finished("MAIN run_test");
|
||||
_do_main();
|
||||
_PromiseTestUtils.assertNoUncaughtRejections();
|
||||
|
||||
if (coverageCollector != null) {
|
||||
coverageCollector.recordTestCoverage(_TEST_FILE[0]);
|
||||
}
|
||||
} catch (e) {
|
||||
_passed = false;
|
||||
// do_check failures are already logged and set _quit to true and throw
|
||||
|
|
|
@ -407,6 +407,8 @@ FetchIconPerSpec(const RefPtr<Database>& aDB,
|
|||
"JOIN moz_icons_to_pages ON i.id = icon_id "
|
||||
"JOIN moz_pages_w_icons p ON p.id = page_id "
|
||||
"WHERE page_url_hash = hash(:url) AND page_url = :url "
|
||||
"OR (:hash_idx AND page_url_hash = hash(substr(:url, 0, :hash_idx)) "
|
||||
"AND page_url = substr(:url, 0, :hash_idx)) "
|
||||
"UNION ALL "
|
||||
"SELECT width, icon_url, root "
|
||||
"FROM moz_icons i "
|
||||
|
@ -425,6 +427,9 @@ FetchIconPerSpec(const RefPtr<Database>& aDB,
|
|||
rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("root_icon_url"),
|
||||
rootIconFixedUrl);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
int32_t hashIdx = PromiseFlatCString(aPageSpec).RFind("#");
|
||||
rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("hash_idx"), hashIdx + 1);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Return the biggest icon close to the preferred width. It may be bigger
|
||||
// or smaller if the preferred width isn't found.
|
||||
|
|
|
@ -92,7 +92,7 @@ PageIconProtocolHandler.prototype = {
|
|||
channel.contentStream = pipe.inputStream;
|
||||
channel.loadInfo = loadInfo;
|
||||
|
||||
let pageURI = NetUtil.newURI(uri.cloneIgnoringRef().path);
|
||||
let pageURI = NetUtil.newURI(uri.path.replace(/[&#]size=[^&]+$/, ""));
|
||||
let preferredSize = PlacesUtils.favicons.preferredSizeFromURI(uri);
|
||||
PlacesUtils.favicons.getFaviconDataForPage(pageURI, (iconURI, len, data, mimeType) => {
|
||||
channel.contentType = mimeType;
|
||||
|
|
|
@ -797,33 +797,44 @@ nsFaviconService::OptimizeIconSizes(IconData& aIcon)
|
|||
newPayload.mimeType = NS_LITERAL_CSTRING(PNG_MIME_TYPE);
|
||||
newPayload.width = frameInfo.width;
|
||||
for (uint16_t size : sFaviconSizes) {
|
||||
if (size <= frameInfo.width) {
|
||||
// The icon could be smaller than 16, that is our minimum.
|
||||
// Icons smaller than 16px are kept as-is.
|
||||
if (frameInfo.width >= 16) {
|
||||
if (size > frameInfo.width) {
|
||||
continue;
|
||||
}
|
||||
newPayload.width = size;
|
||||
}
|
||||
|
||||
// If the original payload is png and the size is the same, rescale the
|
||||
// image only if it's larger than the maximum allowed.
|
||||
if (newPayload.mimeType.Equals(payload.mimeType) &&
|
||||
newPayload.width == frameInfo.width &&
|
||||
payload.data.Length() < nsIFaviconService::MAX_FAVICON_BUFFER_SIZE) {
|
||||
newPayload.data = payload.data;
|
||||
} else {
|
||||
// Otherwise, scale and recompress.
|
||||
// Since EncodeScaledImage uses SYNC_DECODE, it will pick the best frame.
|
||||
nsCOMPtr<nsIInputStream> iconStream;
|
||||
rv = GetImgTools()->EncodeScaledImage(container,
|
||||
newPayload.mimeType,
|
||||
newPayload.width,
|
||||
newPayload.width,
|
||||
EmptyString(),
|
||||
getter_AddRefs(iconStream));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
// Read the stream into the new buffer.
|
||||
rv = NS_ConsumeStream(iconStream, UINT32_MAX, newPayload.data);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// If the icon size is good, we are done, otherwise try the next size.
|
||||
if (newPayload.data.Length() < nsIFaviconService::MAX_FAVICON_BUFFER_SIZE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If the original payload is png and the size is the same, no reason to
|
||||
// rescale the image.
|
||||
if (newPayload.mimeType.Equals(payload.mimeType) &&
|
||||
newPayload.width == frameInfo.width) {
|
||||
newPayload.data = payload.data;
|
||||
} else {
|
||||
// Scale and recompress.
|
||||
// Since EncodeScaledImage use SYNC_DECODE, it will pick the best frame.
|
||||
nsCOMPtr<nsIInputStream> iconStream;
|
||||
rv = GetImgTools()->EncodeScaledImage(container,
|
||||
newPayload.mimeType,
|
||||
newPayload.width,
|
||||
newPayload.width,
|
||||
EmptyString(),
|
||||
getter_AddRefs(iconStream));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
// Read the stream into the new buffer.
|
||||
rv = NS_ConsumeStream(iconStream, UINT32_MAX, newPayload.data);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(newPayload.data.Length() < nsIFaviconService::MAX_FAVICON_BUFFER_SIZE);
|
||||
if (newPayload.data.Length() < nsIFaviconService::MAX_FAVICON_BUFFER_SIZE) {
|
||||
aIcon.payloads.AppendElement(newPayload);
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ interface nsIFaviconService : nsISupports
|
|||
* The limit in bytes of the size of favicons in memory and passed via the
|
||||
* favicon protocol.
|
||||
*/
|
||||
const unsigned long MAX_FAVICON_BUFFER_SIZE = 35840;
|
||||
const unsigned long MAX_FAVICON_BUFFER_SIZE = 65536;
|
||||
|
||||
/**
|
||||
* For a given icon URI, this will return a URI that will result in the image.
|
||||
|
|
|
@ -20,6 +20,7 @@ Cu.import("resource://gre/modules/Services.jsm");
|
|||
|
||||
// Put any other stuff relative to this test folder below.
|
||||
|
||||
const systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
|
||||
|
||||
// This error icon must stay in sync with FAVICON_ERRORPAGE_URL in
|
||||
// nsIFaviconService.idl, aboutCertError.xhtml and netError.xhtml.
|
||||
|
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 156 KiB |
|
@ -47,9 +47,23 @@ add_task(function* () {
|
|||
yield compareFavicons(PlacesUtils.urlWithSizeRef(win, PAGE_ICON_URL, -1),
|
||||
PlacesUtils.favicons.getFaviconLinkForIcon(NetUtil.newURI(ICON32_URL)),
|
||||
"Invalid size should return the bigger icon");
|
||||
|
||||
// Ass the icon also for the page with ref.
|
||||
yield PlacesTestUtils.addVisits(PAGE_URL + "#other§=12");
|
||||
yield setFaviconForPage(PAGE_URL + "#other§=12", ICON16_URL, false);
|
||||
yield setFaviconForPage(PAGE_URL + "#other§=12", ICON32_URL, false);
|
||||
yield compareFavicons(PlacesUtils.urlWithSizeRef(win, PAGE_ICON_URL + "#other§=12", 16),
|
||||
PlacesUtils.favicons.getFaviconLinkForIcon(NetUtil.newURI(ICON16_URL)),
|
||||
"Pre-existing refs should be retained");
|
||||
yield compareFavicons(PlacesUtils.urlWithSizeRef(win, PAGE_ICON_URL + "#other§=12", 32),
|
||||
PlacesUtils.favicons.getFaviconLinkForIcon(NetUtil.newURI(ICON32_URL)),
|
||||
"Pre-existing refs should be ignored");
|
||||
"Pre-existing refs should be retained");
|
||||
|
||||
// If the ref-ed url is unknown, should still try to fetch icon for the unref-ed url.
|
||||
yield compareFavicons(PlacesUtils.urlWithSizeRef(win, PAGE_ICON_URL + "#randomstuff", 32),
|
||||
PlacesUtils.favicons.getFaviconLinkForIcon(NetUtil.newURI(ICON32_URL)),
|
||||
"Non-existing refs should be ignored");
|
||||
|
||||
win = { devicePixelRatio: 1.1 };
|
||||
yield compareFavicons(PlacesUtils.urlWithSizeRef(win, PAGE_ICON_URL, 16),
|
||||
PlacesUtils.favicons.getFaviconLinkForIcon(NetUtil.newURI(ICON32_URL)),
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*
|
||||
* Tests a png with a large file size that can't fit MAX_FAVICON_BUFFER_SIZE,
|
||||
* it should be downsized until it can be stored, rather than thrown away.
|
||||
*/
|
||||
|
||||
add_task(function* () {
|
||||
let file = do_get_file("noise.png");
|
||||
let icon = {
|
||||
file,
|
||||
uri: NetUtil.newURI(file),
|
||||
data: readFileData(file),
|
||||
mimetype: "image/png"
|
||||
};
|
||||
|
||||
// If this should fail, it means MAX_FAVICON_BUFFER_SIZE has been made bigger
|
||||
// than this icon. For this test to make sense the icon shoul always be
|
||||
// bigger than MAX_FAVICON_BUFFER_SIZE. Please update the icon!
|
||||
Assert.ok(icon.data.length > Ci.nsIFaviconService.MAX_FAVICON_BUFFER_SIZE,
|
||||
"The test icon file size must be larger than Ci.nsIFaviconService.MAX_FAVICON_BUFFER_SIZE")
|
||||
|
||||
let pageURI = uri("http://foo.bar/");
|
||||
yield PlacesTestUtils.addVisits(pageURI);
|
||||
|
||||
PlacesUtils.favicons.replaceFaviconData(icon.uri, icon.data, icon.data.length,
|
||||
icon.mimetype);
|
||||
yield setFaviconForPage(pageURI, icon.uri);
|
||||
Assert.equal(yield getFaviconUrlForPage(pageURI), icon.uri.spec,
|
||||
"A resampled version of the icon should be stored");
|
||||
});
|
|
@ -82,3 +82,16 @@ add_task(function* svg_icon() {
|
|||
Assert.equal(svgIcon.contentType, pageIcon.contentType);
|
||||
Assert.deepEqual(svgIcon.data, pageIcon.data, "Got the root favicon data");
|
||||
});
|
||||
|
||||
add_task(function* page_with_ref() {
|
||||
for (let url of ["http://places.test.ref/#myref",
|
||||
"http://places.test.ref/#!&b=16",
|
||||
"http://places.test.ref/#"]) {
|
||||
yield PlacesTestUtils.addVisits(url);
|
||||
yield setFaviconForPage(url, ICON_URI, false);
|
||||
let {data, contentType} = yield fetchIconForSpec("page-icon:" + url);
|
||||
Assert.equal(contentType, gFavicon.contentType);
|
||||
Assert.deepEqual(data, gFavicon.data, "Got the favicon data");
|
||||
yield PlacesUtils.history.remove(url);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
var iconsvc = PlacesUtils.favicons;
|
||||
var histsvc = PlacesUtils.history;
|
||||
var systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
|
||||
|
||||
var originalFavicon = {
|
||||
file: do_get_file("favicon-normal16.png"),
|
||||
|
|
|
@ -19,6 +19,7 @@ add_task(function* () {
|
|||
faviconURI.spec);
|
||||
|
||||
// Check database entries.
|
||||
yield PlacesTestUtils.promiseAsyncUpdates();
|
||||
let db = yield PlacesUtils.promiseDBConnection();
|
||||
let rows = yield db.execute("SELECT * FROM moz_icons");
|
||||
Assert.equal(rows.length, 1, "There should only be 1 icon entry");
|
||||
|
@ -70,6 +71,7 @@ add_task(function* test_removePagesByTimeframe() {
|
|||
);
|
||||
|
||||
// Check database entries.
|
||||
yield PlacesTestUtils.promiseAsyncUpdates();
|
||||
let db = yield PlacesUtils.promiseDBConnection();
|
||||
let rows = yield db.execute("SELECT * FROM moz_icons");
|
||||
Assert.equal(rows.length, 1, "There should only be 1 icon entry");
|
||||
|
|
|
@ -22,6 +22,7 @@ support-files =
|
|||
favicon-normal32.png
|
||||
favicon-scale160x3.jpg
|
||||
favicon-scale3x160.jpg
|
||||
noise.png
|
||||
|
||||
[test_expireAllFavicons.js]
|
||||
[test_expire_migrated_icons.js]
|
||||
|
@ -30,6 +31,7 @@ support-files =
|
|||
[test_favicons_protocols_ref.js]
|
||||
[test_getFaviconDataForPage.js]
|
||||
[test_getFaviconURLForPage.js]
|
||||
[test_heavy_favicon.js]
|
||||
[test_moz-anno_favicon_mime_type.js]
|
||||
[test_multiple_frames.js]
|
||||
[test_page-icon_protocol.js]
|
||||
|
|
|
@ -188,6 +188,10 @@
|
|||
<!ENTITY addon.details.label "More">
|
||||
<!ENTITY addon.details.tooltip "Show more details about this add-on">
|
||||
<!ENTITY addon.unknownDate "Unknown">
|
||||
<!-- LOCALIZATION NOTE (addon.legacy.label): This appears in a badge next
|
||||
to the add-on name for extensions that are not webextensions, which
|
||||
will stop working in Firefox 57. -->
|
||||
<!ENTITY addon.legacy.label "LEGACY">
|
||||
<!-- LOCALIZATION NOTE (addon.disabled.postfix): This is used in a normal list
|
||||
to signify that an add-on is disabled, in the form
|
||||
"<Addon name> <1.0> (disabled)" -->
|
||||
|
|
|
@ -157,12 +157,14 @@ setting[type="menulist"] {
|
|||
.addon[active="true"] .disabled-postfix,
|
||||
.addon[pending="install"] .update-postfix,
|
||||
.addon[pending="install"] .disabled-postfix,
|
||||
.addon[legacy="false"] .legacy-warning,
|
||||
#detail-view:not([notification="warning"]) .warning,
|
||||
#detail-view:not([notification="error"]) .error,
|
||||
#detail-view:not([notification="info"]) .info,
|
||||
#detail-view:not([pending]) .pending,
|
||||
#detail-view:not([upgrade="true"]) .update-postfix,
|
||||
#detail-view[active="true"] .disabled-postfix,
|
||||
#detail-view[legacy="false"] .legacy-warning,
|
||||
#detail-view[loading] .detail-view-container,
|
||||
#detail-view:not([loading]) .alert-container,
|
||||
.detail-row:not([value]),
|
||||
|
|
|
@ -56,6 +56,7 @@ const PREF_GETADDONS_CACHE_ENABLED = "extensions.getAddons.cache.enabled";
|
|||
const PREF_GETADDONS_CACHE_ID_ENABLED = "extensions.%ID%.getAddons.cache.enabled";
|
||||
const PREF_UI_TYPE_HIDDEN = "extensions.ui.%TYPE%.hidden";
|
||||
const PREF_UI_LASTCATEGORY = "extensions.ui.lastCategory";
|
||||
const PREF_LEGACY_EXCEPTIONS = "extensions.legacy.exceptions";
|
||||
|
||||
const LOADING_MSG_DELAY = 100;
|
||||
|
||||
|
@ -104,6 +105,10 @@ XPCOMUtils.defineLazyGetter(this, "gInlineOptionsStylesheets", () => {
|
|||
return stylesheets;
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyPreferenceGetter(this, "legacyWarningExceptions",
|
||||
PREF_LEGACY_EXCEPTIONS, "",
|
||||
raw => raw.split(","));
|
||||
|
||||
document.addEventListener("load", initialize, true);
|
||||
window.addEventListener("unload", shutdown);
|
||||
|
||||
|
@ -3059,6 +3064,32 @@ var gDetailView = {
|
|||
|
||||
this.node.setAttribute("type", aAddon.type);
|
||||
|
||||
let legacy = false;
|
||||
if (!aAddon.install) {
|
||||
if (aAddon.type == "extension" && !aAddon.isWebExtension) {
|
||||
legacy = true;
|
||||
}
|
||||
if (aAddon.type == "theme") {
|
||||
// The logic here is kind of clunky but we want to mark complete
|
||||
// themes as legacy. There's no explicit flag for complete
|
||||
// themes so explicitly check for new style themes (for which
|
||||
// isWebExtension is true) or lightweight themes (which have
|
||||
// ids that end with @personas.mozilla.org)
|
||||
legacy = !(aAddon.isWebExtension || aAddon.id.endsWith("@personas.mozilla.org"));
|
||||
}
|
||||
|
||||
if (legacy && aAddon.signedStatus == AddonManager.SIGNEDSTATE_PRIVILEGED) {
|
||||
legacy = false;
|
||||
}
|
||||
|
||||
// Exceptions that can slip through above: the default theme plus
|
||||
// test pilot addons until we get SIGNEDSTATE_PRIVILEGED deployed.
|
||||
if (legacy && legacyWarningExceptions.includes(aAddon.id)) {
|
||||
legacy = false;
|
||||
}
|
||||
}
|
||||
this.node.setAttribute("legacy", legacy);
|
||||
|
||||
// If the search category isn't selected then make sure to select the
|
||||
// correct category
|
||||
if (gCategories.selected != "addons://search/")
|
||||
|
|
|
@ -748,6 +748,38 @@
|
|||
<binding id="addon-base"
|
||||
extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
|
||||
<implementation>
|
||||
<property name="isLegacy" readonly="true">
|
||||
<getter><![CDATA[
|
||||
if (this.mAddon.install) {
|
||||
return false;
|
||||
}
|
||||
let legacy = false;
|
||||
if (this.mAddon.type == "extension" && !this.mAddon.isWebExtension) {
|
||||
legacy = true;
|
||||
}
|
||||
if (this.mAddon.type == "theme") {
|
||||
// The logic here is kind of clunky but we want to mark complete
|
||||
// themes as legacy. There's no explicit flag for complete
|
||||
// themes so explicitly check for new style themes (for which
|
||||
// isWebExtension is true) or lightweight themes (which have
|
||||
// ids that end with @personas.mozilla.org)
|
||||
legacy = !(this.mAddon.isWebExtension ||
|
||||
this.mAddon.id.endsWith("@personas.mozilla.org"));
|
||||
}
|
||||
|
||||
if (legacy && this.mAddon.signedStatus == AddonManager.SIGNEDSTATE_PRIVILEGED) {
|
||||
legacy = false;
|
||||
}
|
||||
|
||||
// Exceptions that can slip through above: the default theme plus
|
||||
// test pilot addons until we get SIGNEDSTATE_PRIVILEGED deployed.
|
||||
if (legacy && legacyWarningExceptions.includes(this.mAddon.id)) {
|
||||
legacy = false;
|
||||
}
|
||||
return legacy;
|
||||
]]></getter>
|
||||
</property>
|
||||
|
||||
<method name="hasPermission">
|
||||
<parameter name="aPerm"/>
|
||||
<body><![CDATA[
|
||||
|
@ -833,6 +865,7 @@
|
|||
<xul:hbox class="name-container">
|
||||
<xul:label anonid="name" class="name" crop="end" flex="1"
|
||||
tooltip="addonitem-tooltip" xbl:inherits="value=name"/>
|
||||
<xul:label anonid="legacy" class="legacy-warning" value="&addon.legacy.label;"/>
|
||||
<xul:label class="disabled-postfix" value="&addon.disabled.postfix;"/>
|
||||
<xul:label class="update-postfix" value="&addon.update.postfix;"/>
|
||||
<xul:spacer flex="5000"/> <!-- Necessary to make the name crop -->
|
||||
|
@ -1154,6 +1187,8 @@
|
|||
else
|
||||
this._description.hidden = true;
|
||||
|
||||
this.setAttribute("legacy", this.isLegacy);
|
||||
|
||||
if (!("applyBackgroundUpdates" in this.mAddon) ||
|
||||
(this.mAddon.applyBackgroundUpdates == AddonManager.AUTOUPDATE_DISABLE ||
|
||||
(this.mAddon.applyBackgroundUpdates == AddonManager.AUTOUPDATE_DEFAULT &&
|
||||
|
|
|
@ -567,6 +567,7 @@
|
|||
align="start">
|
||||
<label id="detail-name" flex="1"/>
|
||||
<label id="detail-version"/>
|
||||
<label class="legacy-warning" value="&addon.legacy.label;"/>
|
||||
<label class="disabled-postfix" value="&addon.disabled.postfix;"/>
|
||||
<label class="update-postfix" value="&addon.update.postfix;"/>
|
||||
<spacer flex="5000"/> <!-- Necessary to allow the name to wrap -->
|
||||
|
|
|
@ -64,6 +64,7 @@ skip-if = buildapp == 'mulet'
|
|||
skip-if = require_signing
|
||||
[browser_install.js]
|
||||
[browser_installssl.js]
|
||||
[browser_legacy.js]
|
||||
[browser_newaddon.js]
|
||||
[browser_non_mpc.js]
|
||||
[browser_searching.js]
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
|
||||
add_task(async function() {
|
||||
const NAMES = {
|
||||
fullTheme: "Full Theme",
|
||||
newTheme: "New LWT",
|
||||
legacy: "Legacy Extension",
|
||||
webextension: "WebExtension",
|
||||
dictionary: "Dictionary",
|
||||
langpack: "Language Pack",
|
||||
};
|
||||
let addons = [
|
||||
{
|
||||
id: "full-theme@tests.mozilla.org",
|
||||
name: NAMES.fullTheme,
|
||||
type: "theme",
|
||||
isWebExtension: false,
|
||||
},
|
||||
{
|
||||
id: "new-theme@tests.mozilla.org",
|
||||
name: NAMES.newTheme,
|
||||
type: "theme",
|
||||
isWebExtension: true,
|
||||
},
|
||||
{
|
||||
id: "legacy@tests.mozilla.org",
|
||||
name: NAMES.legacy,
|
||||
type: "extension",
|
||||
isWebExtension: false,
|
||||
},
|
||||
{
|
||||
id: "webextension@tests.mozilla.org",
|
||||
name: NAMES.webextension,
|
||||
type: "extension",
|
||||
isWebExtension: true,
|
||||
},
|
||||
{
|
||||
id: "dictionary@tests.mozilla.org",
|
||||
name: NAMES.dictionary,
|
||||
type: "dictionary",
|
||||
},
|
||||
];
|
||||
|
||||
let provider = new MockProvider();
|
||||
provider.createAddons(addons);
|
||||
|
||||
let mgrWin = await open_manager(null);
|
||||
let catUtils = new CategoryUtilities(mgrWin);
|
||||
|
||||
async function check(category, name, isLegacy) {
|
||||
await catUtils.openType(category);
|
||||
|
||||
let document = mgrWin.document;
|
||||
// First find the entry in the list.
|
||||
let item = Array.from(document.getElementById("addon-list").childNodes)
|
||||
.find(i => i.getAttribute("name") == name);
|
||||
|
||||
ok(item, `Found ${name} in list`);
|
||||
item.parentNode.ensureElementIsVisible(item);
|
||||
|
||||
// Check the badge
|
||||
let badge = document.getAnonymousElementByAttribute(item, "class", "legacy-warning");
|
||||
|
||||
if (isLegacy) {
|
||||
is_element_visible(badge, `Legacy badge is visible for ${name}`);
|
||||
} else {
|
||||
is_element_hidden(badge, `Legacy badge is hidden for ${name}`);
|
||||
}
|
||||
|
||||
// Click down to the details page.
|
||||
let detailsButton = document.getAnonymousElementByAttribute(item, "anonid", "details-btn");
|
||||
EventUtils.synthesizeMouseAtCenter(detailsButton, {}, mgrWin);
|
||||
await new Promise(resolve => wait_for_view_load(mgrWin, resolve));
|
||||
|
||||
// And check the badge
|
||||
let elements = document.getElementsByClassName("legacy-warning");
|
||||
is(elements.length, 1, "Found the legacy-warning element");
|
||||
badge = elements[0];
|
||||
|
||||
if (isLegacy) {
|
||||
is_element_visible(badge, `Legacy badge is visible for ${name}`);
|
||||
} else {
|
||||
is_element_hidden(badge, `Legacy badge is hidden for ${name}`);
|
||||
}
|
||||
}
|
||||
|
||||
await check("theme", NAMES.fullTheme, true);
|
||||
await check("theme", NAMES.newTheme, false);
|
||||
await check("extension", NAMES.legacy, true);
|
||||
await check("extension", NAMES.webextension, false);
|
||||
await check("dictionary", NAMES.dictionary, false);
|
||||
|
||||
await close_manager(mgrWin);
|
||||
});
|
|
@ -457,6 +457,18 @@ button.warning {
|
|||
-moz-box-flex: 1;
|
||||
}
|
||||
|
||||
.legacy-warning {
|
||||
background-color: #FFE900;
|
||||
color: #3E2800;
|
||||
padding: 4px 5px 3px;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
#detail-view .legacy-warning {
|
||||
margin-top: 0.78rem;
|
||||
}
|
||||
|
||||
.creator {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче