merge autoland to mozilla-central. r=merge a=merge

MozReview-Commit-ID: H1xftEhaJOS
This commit is contained in:
Sebastian Hengst 2017-04-29 21:26:04 +02:00
Родитель 8961c1494e bc6fc1e239
Коммит 98ce3de2ed
53 изменённых файлов: 653 добавлений и 236 удалений

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

@ -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.

Двоичные данные
toolkit/components/places/tests/favicons/noise.png Normal file

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

После

Ширина:  |  Высота:  |  Размер: 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;
}