This commit is contained in:
Ryan VanderMeulen 2017-06-01 16:45:03 -04:00
Родитель 4d61fef769 187f1f3dfe
Коммит d85c7f6cc3
44 изменённых файлов: 1322 добавлений и 1080 удалений

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

@ -304,6 +304,7 @@ var gSync = {
this.remoteClients.map(client => client.id);
clients.forEach(clientId => this.sendTabToDevice(url, clientId, title));
gPageActionButton.panel.hidePopup();
}
function addTargetDevice(clientId, name, clientType) {

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

@ -129,8 +129,6 @@ var whitelist = new Set([
platforms: ["linux", "win"]},
{file: "chrome://browser/skin/customizableui/panelarrow-customizeTip@2x.png",
platforms: ["linux", "win"]},
// Bug 1320058
{file: "chrome://browser/skin/preferences/saveFile.png", platforms: ["win"]},
// Bug 1316187
{file: "chrome://global/content/customizeToolbar.xul"},
// Bug 1343837

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

@ -55,7 +55,6 @@
padding-inline-end: 4px;
}
#sidebar-icon,
#sidebar-switcher-arrow,
#sidebar-close > .toolbarbutton-icon {
-moz-context-properties: fill;
@ -106,19 +105,31 @@
}
%endif
/* Use bookmarks star as default icon for the sidebar box (including when opening a web page) */
#sidebar-box[sidebarcommand="viewWebPanelsSidebar"] > #sidebar-header > #sidebar-switcher-target > #sidebar-icon {
list-style-image: url(chrome://mozapps/skin/places/defaultFavicon.svg);
}
#sidebar-switcher-bookmarks > .toolbarbutton-icon,
#sidebar-box[sidebarcommand="viewWebPanelsSidebar"] > #sidebar-header > #sidebar-switcher-target > #sidebar-icon,
#sidebar-box[sidebarcommand="viewBookmarksSidebar"] > #sidebar-header > #sidebar-switcher-target > #sidebar-icon {
list-style-image: url(chrome://browser/skin/bookmark.svg);
-moz-context-properties: fill;
fill: currentColor;
opacity: 0.8;
}
#sidebar-switcher-history > .toolbarbutton-icon,
#sidebar-box[sidebarcommand="viewHistorySidebar"] > #sidebar-header > #sidebar-switcher-target > #sidebar-icon {
list-style-image: url(chrome://browser/skin/history.svg);
-moz-context-properties: fill;
fill: currentColor;
opacity: 0.8;
}
#sidebar-switcher-tabs > .toolbarbutton-icon,
#sidebar-box[sidebarcommand="viewTabsSidebar"] > #sidebar-header > #sidebar-switcher-target > #sidebar-icon {
list-style-image: url(chrome://browser/skin/sync.svg);
-moz-context-properties: fill;
fill: currentColor;
opacity: 0.8;
}

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

@ -37,7 +37,7 @@ menuitem[appHandlerIcon="ask"] {
richlistitem[appHandlerIcon="save"],
menuitem[appHandlerIcon="save"] {
list-style-image: url("chrome://browser/skin/preferences/application.png");
list-style-image: url("chrome://browser/skin/preferences/saveFile.png");
}
richlistitem[appHandlerIcon="feed"],

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

@ -28,8 +28,8 @@ add_task(function* removeLegacyExtension() {
// Click the remove button and wait for the DOM to change.
const addonListMutation = waitForMutation(
getTemporaryAddonList(document),
{ childList: true });
getTemporaryAddonList(document).parentNode,
{ childList: true, subtree: true });
getRemoveButton(document, addonID).click();
yield addonListMutation;
@ -57,8 +57,8 @@ add_task(function* removeWebextension() {
// Click the remove button and wait for the DOM to change.
const addonListMutation = waitForMutation(
getTemporaryAddonList(document),
{ childList: true });
getTemporaryAddonList(document).parentNode,
{ childList: true, subtree: true });
getRemoveButton(document, addonID).click();
yield addonListMutation;

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

@ -213,7 +213,8 @@ function* installAddon({document, path, name, isWebExtension}) {
function* uninstallAddon({document, id, name}) {
let addonList = getAddonListWithAddon(document, id);
let addonListMutation = waitForMutation(addonList, { childList: true });
let addonListMutation = waitForMutation(addonList.parentNode,
{ childList: true, subtree: true });
// Now uninstall this addon
yield new Promise(done => {
@ -233,13 +234,18 @@ function* uninstallAddon({document, id, name}) {
});
});
// Ensure that the UI removes the addon from the list
yield addonListMutation;
let names = [...addonList.querySelectorAll(".target-name")];
names = names.map(element => element.textContent);
ok(!names.includes(name),
"After uninstall, the addon name disappears from the list of addons: "
+ names);
// If parentNode is none, that means the entire addonList was removed from the
// document. This happens when the addon we are removing is the last one.
if (addonList.parentNode !== null) {
// Ensure that the UI removes the addon from the list
let names = [...addonList.querySelectorAll(".target-name")];
names = names.map(element => element.textContent);
ok(!names.includes(name),
"After uninstall, the addon name disappears from the list of addons: "
+ names);
}
}
/**

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

@ -277,7 +277,9 @@ HighlightersOverlay.prototype = {
try {
highlighter = yield utils.getHighlighterByType(type);
} catch (e) {}
} catch (e) {
// Ignore any error
}
if (!highlighter) {
return null;

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

@ -111,16 +111,20 @@ EventTooltip.prototype = {
if (location) {
sourceMapService.originalPositionFor(location.url, location.line)
.then((originalLocation) => {
if (originalLocation) {
const { sourceUrl, line } = originalLocation;
let newURI = sourceUrl + ":" + line;
filename.textContent = newURI;
filename.setAttribute("title", newURI);
let eventEditor = this._eventEditors.get(content);
eventEditor.uri = newURI;
// Do nothing if the tooltip was destroyed while we were
// waiting for a response.
if (this._tooltip) {
if (originalLocation) {
const { sourceUrl, line } = originalLocation;
let newURI = sourceUrl + ":" + line;
filename.textContent = newURI;
filename.setAttribute("title", newURI);
let eventEditor = this._eventEditors.get(content);
eventEditor.uri = newURI;
}
// This is emitted for testing.
this._tooltip.emit("event-tooltip-source-map-ready");
}
// This is emitted for testing.
this._tooltip.emit("event-tooltip-source-map-ready");
});
}
}

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

@ -12,9 +12,9 @@ const {
const { connect } = require("devtools/client/shared/vendor/react-redux");
const {
getAllMessages,
getAllMessagesUiById,
getAllMessagesTableDataById,
getVisibleMessages,
} = require("devtools/client/webconsole/new-console-output/selectors/messages");
const MessageContainer = createFactory(require("devtools/client/webconsole/new-console-output/components/message-container").MessageContainer);
@ -23,7 +23,6 @@ const ConsoleOutput = createClass({
displayName: "ConsoleOutput",
propTypes: {
messages: PropTypes.object.isRequired,
messagesUi: PropTypes.object.isRequired,
serviceContainer: PropTypes.shape({
attachRefToHud: PropTypes.func.isRequired,
@ -33,6 +32,7 @@ const ConsoleOutput = createClass({
dispatch: PropTypes.func.isRequired,
timestampsVisible: PropTypes.bool,
messagesTableData: PropTypes.object.isRequired,
visibleMessages: PropTypes.array.isRequired,
},
componentDidMount() {
@ -53,7 +53,7 @@ const ConsoleOutput = createClass({
// Figure out if we are at the bottom. If so, then any new message should be scrolled
// into view.
const lastChild = outputNode.lastChild;
const delta = nextProps.messages.size - this.props.messages.size;
const delta = nextProps.visibleMessages.length - this.props.visibleMessages.length;
this.shouldScrollBottom = delta > 0 && isScrolledToBottom(lastChild, outputNode);
},
@ -72,14 +72,14 @@ const ConsoleOutput = createClass({
render() {
let {
dispatch,
messages,
visibleMessages,
messagesUi,
messagesTableData,
serviceContainer,
timestampsVisible,
} = this.props;
let messageNodes = messages.map((message) => {
let messageNodes = visibleMessages.map((message) => {
return (
MessageContainer({
dispatch,
@ -120,7 +120,7 @@ function isScrolledToBottom(outputNode, scrollNode) {
function mapStateToProps(state, props) {
return {
messages: getAllMessages(state),
visibleMessages: getVisibleMessages(state),
messagesUi: getAllMessagesUiById(state),
messagesTableData: getAllMessagesTableDataById(state),
timestampsVisible: state.ui.timestampsVisible,

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

@ -6,15 +6,22 @@
"use strict";
const Immutable = require("devtools/client/shared/vendor/immutable");
const { l10n } = require("devtools/client/webconsole/new-console-output/utils/messages");
const constants = require("devtools/client/webconsole/new-console-output/constants");
const {isGroupType} = require("devtools/client/webconsole/new-console-output/utils/messages");
const Services = require("Services");
const logLimit = Math.max(Services.prefs.getIntPref("devtools.hud.loglimit"), 1);
const {
MESSAGE_TYPE,
MESSAGE_SOURCE
} = require("devtools/client/webconsole/new-console-output/constants");
const { getGripPreviewItems } = require("devtools/client/shared/components/reps/reps");
const { getSourceNames } = require("devtools/client/shared/source-utils");
const MessageState = Immutable.Record({
// List of all the messages added to the console.
messagesById: Immutable.List(),
messagesById: Immutable.OrderedMap(),
// Array of the visible messages.
visibleMessages: [],
// List of the message ids which are opened.
messagesUiById: Immutable.List(),
// Map of the form {messageId : tableData}, which represent the data passed
@ -30,15 +37,18 @@ const MessageState = Immutable.Record({
removedMessages: [],
});
function messages(state = new MessageState(), action) {
function messages(state = new MessageState(), action, filtersState, prefsState) {
const {
messagesById,
messagesUiById,
messagesTableDataById,
groupsById,
currentGroup,
visibleMessages,
} = state;
const {logLimit} = prefsState;
switch (action.type) {
case constants.MESSAGE_ADD:
let newMessage = action.message;
@ -56,23 +66,27 @@ function messages(state = new MessageState(), action) {
if (newMessage.allowRepeating && messagesById.size > 0) {
let lastMessage = messagesById.last();
if (lastMessage.repeatId === newMessage.repeatId) {
return state.withMutations(function (record) {
record.set("messagesById", messagesById.pop().push(
newMessage.set("repeat", lastMessage.repeat + 1)
));
});
return state.set(
"messagesById",
messagesById.set(
lastMessage.id,
lastMessage.set("repeat", lastMessage.repeat + 1)
)
);
}
}
let parentGroups = getParentGroups(currentGroup, groupsById);
newMessage = newMessage.withMutations(function (message) {
message.set("groupId", currentGroup);
message.set("indent", parentGroups.length);
});
return state.withMutations(function (record) {
// Add the new message with a reference to the parent group.
record.set("messagesById", messagesById.push(newMessage));
let parentGroups = getParentGroups(currentGroup, groupsById);
const addedMessage = newMessage.withMutations(function (message) {
message.set("groupId", currentGroup);
message.set("indent", parentGroups.length);
});
record.set(
"messagesById",
messagesById.set(newMessage.id, addedMessage)
);
if (newMessage.type === "trace") {
// We want the stacktrace to be open by default.
@ -87,39 +101,109 @@ function messages(state = new MessageState(), action) {
}
}
if (shouldMessageBeVisible(addedMessage, record, filtersState)) {
record.set("visibleMessages", [...visibleMessages, newMessage.id]);
}
// Remove top level message if the total count of top level messages
// exceeds the current limit.
limitTopLevelMessageCount(state, record);
if (record.messagesById.size > logLimit) {
limitTopLevelMessageCount(state, record, logLimit);
}
});
case constants.MESSAGES_CLEAR:
return state.withMutations(function (record) {
return new MessageState({
// Store all removed messages associated with some arguments.
// This array is used by `releaseActorsEnhancer` to release
// all related backend actors.
record.set("removedMessages",
record.messagesById.filter(msg => msg.parameters).toArray());
// Clear immutable state.
record.set("messagesById", Immutable.List());
record.set("messagesUiById", Immutable.List());
record.set("groupsById", Immutable.Map());
record.set("currentGroup", null);
"removedMessages": [...state.messagesById].reduce((res, [id, msg]) => {
if (msg.parameters) {
res.push(msg);
}
return res;
}, [])
});
case constants.MESSAGE_OPEN:
return state.set("messagesUiById", messagesUiById.push(action.id));
return state.withMutations(function (record) {
record.set("messagesUiById", messagesUiById.push(action.id));
// If the message is a group
if (isGroupType(messagesById.get(action.id).type)) {
// We want to make its children visible
const messagesToShow = [...messagesById].reduce((res, [id, message]) => {
if (
!visibleMessages.includes(message.id)
&& getParentGroups(message.groupId, groupsById).includes(action.id)
&& shouldMessageBeVisible(
message,
record,
filtersState,
// We want to check if the message is in an open group
// only if it is not a direct child of the group we're opening.
message.groupId !== action.id
)
) {
res.push(id);
}
return res;
}, []);
// We can then insert the messages ids right after the one of the group.
const insertIndex = visibleMessages.indexOf(action.id) + 1;
record.set("visibleMessages", [
...visibleMessages.slice(0, insertIndex),
...messagesToShow,
...visibleMessages.slice(insertIndex),
]);
}
});
case constants.MESSAGE_CLOSE:
let index = state.messagesUiById.indexOf(action.id);
return state.deleteIn(["messagesUiById", index]);
return state.withMutations(function (record) {
let messageId = action.id;
let index = record.messagesUiById.indexOf(messageId);
record.deleteIn(["messagesUiById", index]);
// If the message is a group
if (isGroupType(messagesById.get(messageId).type)) {
// Hide all its children
record.set(
"visibleMessages",
[...visibleMessages].filter(id => getParentGroups(
messagesById.get(id).groupId,
groupsById
).includes(messageId) === false
)
);
}
});
case constants.MESSAGE_TABLE_RECEIVE:
const {id, data} = action;
return state.set("messagesTableDataById", messagesTableDataById.set(id, data));
case constants.NETWORK_MESSAGE_UPDATE:
let updateMessage = action.message;
return state.set("messagesById", messagesById.map((message) =>
(message.id === updateMessage.id) ? updateMessage : message
return state.set("messagesById", messagesById.set(
updateMessage.id,
updateMessage
));
case constants.REMOVED_MESSAGES_CLEAR:
return state.set("removedMessages", []);
case constants.FILTER_TOGGLE:
case constants.FILTER_TEXT_SET:
return state.set(
"visibleMessages",
[...messagesById].reduce((res, [messageId, message]) => {
if (shouldMessageBeVisible(message, state, filtersState)) {
res.push(messageId);
}
return res;
}, [])
);
}
return state;
@ -156,36 +240,88 @@ function getParentGroups(currentGroup, groupsById) {
/**
* Remove all top level messages that exceeds message limit.
* Also release all backend actors associated with these
* messages.
* Also populate an array of all backend actors associated with these
* messages so they can be released.
*/
function limitTopLevelMessageCount(state, record) {
let tempRecord = {
messagesById: record.messagesById,
messagesUiById: record.messagesUiById,
messagesTableDataById: record.messagesTableDataById,
groupsById: record.groupsById,
};
function limitTopLevelMessageCount(state, record, logLimit) {
let topLevelCount = record.groupsById.size === 0
? record.messagesById.size
: getToplevelMessageCount(record);
let removedMessages = state.removedMessages;
// Remove top level messages logged over the limit.
let topLevelCount = getToplevelMessageCount(tempRecord);
while (topLevelCount > logLimit) {
removedMessages.push(...removeFirstMessage(tempRecord));
topLevelCount--;
if (topLevelCount <= logLimit) {
return record;
}
// Filter out messages with no arguments. Only actual arguments
// can be associated with backend actors.
removedMessages = state.removedMessages.filter(msg => msg.parameters);
const removedMessagesId = [];
const removedMessages = [];
let visibleMessages = [...record.visibleMessages];
// Update original record object
record.set("messagesById", tempRecord.messagesById);
record.set("messagesUiById", tempRecord.messagesUiById);
record.set("messagesTableDataById", tempRecord.messagesTableDataById);
record.set("groupsById", tempRecord.groupsById);
record.set("removedMessages", removedMessages);
let cleaningGroup = false;
record.messagesById.forEach((message, id) => {
// If we were cleaning a group and the current message does not have
// a groupId, we're done cleaning.
if (cleaningGroup === true && !message.groupId) {
cleaningGroup = false;
}
// If we're not cleaning a group and the message count is below the logLimit,
// we exit the forEach iteration.
if (cleaningGroup === false && topLevelCount <= logLimit) {
return false;
}
// If we're not currently cleaning a group, and the current message is identified
// as a group, set the cleaning flag to true.
if (cleaningGroup === false && record.groupsById.has(id)) {
cleaningGroup = true;
}
if (!message.groupId) {
topLevelCount--;
}
removedMessagesId.push(id);
// Filter out messages with no arguments. Only actual arguments
// can be associated with backend actors.
if (message && message.parameters) {
removedMessages.push(message);
}
const index = visibleMessages.indexOf(id);
if (index > -1) {
visibleMessages.splice(index, 1);
}
return true;
});
if (removedMessages.length > 0) {
record.set("removedMessages", record.removedMessages.concat(removedMessages));
}
if (record.visibleMessages.length > visibleMessages.length) {
record.set("visibleMessages", visibleMessages);
}
const isInRemovedId = id => removedMessagesId.includes(id);
const mapHasRemovedIdKey = map => map.findKey((value, id) => isInRemovedId(id));
const cleanUpCollection = map => removedMessagesId.forEach(id => map.remove(id));
record.set("messagesById", record.messagesById.withMutations(cleanUpCollection));
if (record.messagesUiById.find(isInRemovedId)) {
record.set("messagesUiById", record.messagesUiById.withMutations(cleanUpCollection));
}
if (mapHasRemovedIdKey(record.messagesTableDataById)) {
record.set("messagesTableDataById",
record.messagesTableDataById.withMutations(cleanUpCollection));
}
if (mapHasRemovedIdKey(record.groupsById)) {
record.set("groupsById", record.groupsById.withMutations(cleanUpCollection));
}
return record;
}
/**
@ -193,48 +329,209 @@ function limitTopLevelMessageCount(state, record) {
* within a group).
*/
function getToplevelMessageCount(record) {
return [...record.messagesById].filter(message => !message.groupId).length;
return record.messagesById.count(message => !message.groupId);
}
function shouldMessageBeVisible(message, messagesState, filtersState, checkGroup = true) {
return (
(
checkGroup === false
|| isInOpenedGroup(message, messagesState.groupsById, messagesState.messagesUiById)
)
&& (
isUnfilterable(message)
|| (
matchLevelFilters(message, filtersState)
&& matchCssFilters(message, filtersState)
&& matchNetworkFilters(message, filtersState)
&& matchSearchFilters(message, filtersState)
)
)
);
}
function isUnfilterable(message) {
return [
MESSAGE_TYPE.COMMAND,
MESSAGE_TYPE.RESULT,
MESSAGE_TYPE.START_GROUP,
MESSAGE_TYPE.START_GROUP_COLLAPSED,
].includes(message.type);
}
function isInOpenedGroup(message, groupsById, messagesUI) {
return !message.groupId
|| (
!isGroupClosed(message.groupId, messagesUI)
&& !hasClosedParentGroup(groupsById.get(message.groupId), messagesUI)
);
}
function hasClosedParentGroup(group, messagesUI) {
return group.some(groupId => isGroupClosed(groupId, messagesUI));
}
function isGroupClosed(groupId, messagesUI) {
return messagesUI.includes(groupId) === false;
}
function matchLevelFilters(message, filters) {
return filters.get(message.level) === true;
}
function matchNetworkFilters(message, filters) {
return (
message.source !== MESSAGE_SOURCE.NETWORK
|| (filters.get("net") === true && message.isXHR === false)
|| (filters.get("netxhr") === true && message.isXHR === true)
);
}
function matchCssFilters(message, filters) {
return (
message.source != MESSAGE_SOURCE.CSS
|| filters.get("css") === true
);
}
function matchSearchFilters(message, filters) {
let text = filters.text || "";
return (
text === ""
// Look for a match in parameters.
|| isTextInParameters(text, message.parameters)
// Look for a match in location.
|| isTextInFrame(text, message.frame)
// Look for a match in net events.
|| isTextInNetEvent(text, message.request)
// Look for a match in stack-trace.
|| isTextInStackTrace(text, message.stacktrace)
// Look for a match in messageText.
|| isTextInMessageText(text, message.messageText)
// Look for a match in notes.
|| isTextInNotes(text, message.notes)
);
}
/**
* Remove first (the oldest) message from the store. The methods removes
* also all its references and children from the store.
* Returns true if given text is included in provided stack frame.
*/
function isTextInFrame(text, frame) {
if (!frame) {
return false;
}
const {
functionName,
line,
column,
source
} = frame;
const { short } = getSourceNames(source);
return `${functionName ? functionName + " " : ""}${short}:${line}:${column}`
.toLocaleLowerCase()
.includes(text.toLocaleLowerCase());
}
/**
* Returns true if given text is included in provided parameters.
*/
function isTextInParameters(text, parameters) {
if (!parameters) {
return false;
}
text = text.toLocaleLowerCase();
return getAllProps(parameters).some(prop =>
(prop + "").toLocaleLowerCase().includes(text)
);
}
/**
* Returns true if given text is included in provided net event grip.
*/
function isTextInNetEvent(text, request) {
if (!request) {
return false;
}
text = text.toLocaleLowerCase();
let method = request.method.toLocaleLowerCase();
let url = request.url.toLocaleLowerCase();
return method.includes(text) || url.includes(text);
}
/**
* Returns true if given text is included in provided stack trace.
*/
function isTextInStackTrace(text, stacktrace) {
if (!Array.isArray(stacktrace)) {
return false;
}
// isTextInFrame expect the properties of the frame object to be in the same
// order they are rendered in the Frame component.
return stacktrace.some(frame => isTextInFrame(text, {
functionName: frame.functionName || l10n.getStr("stacktrace.anonymousFunction"),
source: frame.filename,
lineNumber: frame.lineNumber,
columnNumber: frame.columnNumber
}));
}
/**
* Returns true if given text is included in `messageText` field.
*/
function isTextInMessageText(text, messageText) {
if (!messageText) {
return false;
}
return messageText.toLocaleLowerCase().includes(text.toLocaleLowerCase());
}
/**
* Returns true if given text is included in notes.
*/
function isTextInNotes(text, notes) {
if (!Array.isArray(notes)) {
return false;
}
return notes.some(note =>
// Look for a match in location.
isTextInFrame(text, note.frame) ||
// Look for a match in messageBody.
(
note.messageBody &&
note.messageBody.toLocaleLowerCase().includes(text.toLocaleLowerCase())
)
);
}
/**
* Get a flat array of all the grips and their properties.
*
* @return {Array} Flat array of removed messages.
* @param {Array} Grips
* @return {Array} Flat array of the grips and their properties.
*/
function removeFirstMessage(record) {
let firstMessage = record.messagesById.first();
record.messagesById = record.messagesById.shift();
function getAllProps(grips) {
let result = grips.reduce((res, grip) => {
let previewItems = getGripPreviewItems(grip);
let allProps = previewItems.length > 0 ? getAllProps(previewItems) : [];
return [...res, grip, grip.class, ...allProps];
}, []);
// Remove from list of opened groups.
let uiIndex = record.messagesUiById.indexOf(firstMessage);
if (uiIndex >= 0) {
record.messagesUiById = record.messagesUiById.delete(uiIndex);
}
// We are interested only in primitive props (to search for)
// not in objects and undefined previews.
result = result.filter(grip =>
typeof grip != "object" &&
typeof grip != "undefined"
);
// Remove from list of tables.
if (record.messagesTableDataById.has(firstMessage.id)) {
record.messagesTableDataById = record.messagesTableDataById.delete(firstMessage.id);
}
// Remove from list of parent groups.
if (record.groupsById.has(firstMessage.id)) {
record.groupsById = record.groupsById.delete(firstMessage.id);
}
let removedMessages = [firstMessage];
// Remove all children. This loop assumes that children of removed
// group immediately follows the group. We use recursion since
// there might be inner groups.
let message = record.messagesById.first();
while (message.groupId == firstMessage.id) {
removedMessages.push(...removeFirstMessage(record));
message = record.messagesById.first();
}
// Return array with all removed messages.
return removedMessages;
return [...new Set(result)];
}
exports.messages = messages;

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

@ -5,42 +5,14 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { l10n } = require("devtools/client/webconsole/new-console-output/utils/messages");
const { getAllFilters } = require("devtools/client/webconsole/new-console-output/selectors/filters");
const { getGripPreviewItems } = require("devtools/client/shared/components/reps/reps");
const { getSourceNames } = require("devtools/client/shared/source-utils");
const {
MESSAGE_TYPE,
MESSAGE_SOURCE
} = require("devtools/client/webconsole/new-console-output/constants");
function getAllMessages(state) {
let messages = getAllMessagesById(state);
let filters = getAllFilters(state);
let groups = getAllGroupsById(state);
let messagesUI = getAllMessagesUiById(state);
return messages.filter(message => {
return (
isInOpenedGroup(message, groups, messagesUI)
&& (
isUnfilterable(message)
|| (
matchLevelFilters(message, filters)
&& matchCssFilters(message, filters)
&& matchNetworkFilters(message, filters)
&& matchSearchFilters(message, filters)
)
)
);
});
}
function getAllMessagesById(state) {
return state.messages.messagesById;
}
function getMessage(state, id) {
return getAllMessagesById(state).get(id);
}
function getAllMessagesUiById(state) {
return state.messages.messagesUiById;
}
@ -57,184 +29,16 @@ function getCurrentGroup(state) {
return state.messages.currentGroup;
}
function isUnfilterable(message) {
return [
MESSAGE_TYPE.COMMAND,
MESSAGE_TYPE.RESULT,
MESSAGE_TYPE.START_GROUP,
MESSAGE_TYPE.START_GROUP_COLLAPSED,
].includes(message.type);
function getVisibleMessages(state) {
return state.messages.visibleMessages.map(id => getMessage(state, id));
}
function isInOpenedGroup(message, groups, messagesUI) {
return !message.groupId
|| (
!isGroupClosed(message.groupId, messagesUI)
&& !hasClosedParentGroup(groups.get(message.groupId), messagesUI)
);
}
function hasClosedParentGroup(group, messagesUI) {
return group.some(groupId => isGroupClosed(groupId, messagesUI));
}
function isGroupClosed(groupId, messagesUI) {
return messagesUI.includes(groupId) === false;
}
function matchLevelFilters(message, filters) {
return filters.get(message.level) === true;
}
function matchNetworkFilters(message, filters) {
return (
message.source !== MESSAGE_SOURCE.NETWORK
|| (filters.get("net") === true && message.isXHR === false)
|| (filters.get("netxhr") === true && message.isXHR === true)
);
}
function matchCssFilters(message, filters) {
return (
message.source != MESSAGE_SOURCE.CSS
|| filters.get("css") === true
);
}
function matchSearchFilters(message, filters) {
let text = filters.text || "";
return (
text === ""
// Look for a match in parameters.
|| isTextInParameters(text, message.parameters)
// Look for a match in location.
|| isTextInFrame(text, message.frame)
// Look for a match in net events.
|| isTextInNetEvent(text, message.request)
// Look for a match in stack-trace.
|| isTextInStackTrace(text, message.stacktrace)
// Look for a match in messageText.
|| isTextInMessageText(text, message.messageText)
// Look for a match in notes.
|| isTextInNotes(text, message.notes)
);
}
/**
* Returns true if given text is included in provided stack frame.
*/
function isTextInFrame(text, frame) {
if (!frame) {
return false;
}
const { short } = getSourceNames(frame.source);
return `${short}:${frame.line}:${frame.column}`
.toLocaleLowerCase()
.includes(text.toLocaleLowerCase());
}
/**
* Returns true if given text is included in provided parameters.
*/
function isTextInParameters(text, parameters) {
if (!parameters) {
return false;
}
text = text.toLocaleLowerCase();
return getAllProps(parameters).find(prop =>
(prop + "").toLocaleLowerCase().includes(text)
);
}
/**
* Returns true if given text is included in provided net event grip.
*/
function isTextInNetEvent(text, request) {
if (!request) {
return false;
}
text = text.toLocaleLowerCase();
let method = request.method.toLocaleLowerCase();
let url = request.url.toLocaleLowerCase();
return method.includes(text) || url.includes(text);
}
/**
* Returns true if given text is included in provided stack trace.
*/
function isTextInStackTrace(text, stacktrace) {
if (!Array.isArray(stacktrace)) {
return false;
}
// isTextInFrame expect the properties of the frame object to be in the same
// order they are rendered in the Frame component.
return stacktrace.some(frame => isTextInFrame(text, {
functionName: frame.functionName || l10n.getStr("stacktrace.anonymousFunction"),
filename: frame.filename,
lineNumber: frame.lineNumber,
columnNumber: frame.columnNumber
}));
}
/**
* Returns true if given text is included in `messageText` field.
*/
function isTextInMessageText(text, messageText) {
if (!messageText) {
return false;
}
return messageText.toLocaleLowerCase().includes(text.toLocaleLowerCase());
}
/**
* Returns true if given text is included in notes.
*/
function isTextInNotes(text, notes) {
if (!Array.isArray(notes)) {
return false;
}
return notes.some(note =>
// Look for a match in location.
isTextInFrame(text, note.frame) ||
// Look for a match in messageBody.
(note.messageBody &&
note.messageBody.toLocaleLowerCase()
.includes(text.toLocaleLowerCase()))
);
}
/**
* Get a flat array of all the grips and their properties.
*
* @param {Array} Grips
* @return {Array} Flat array of the grips and their properties.
*/
function getAllProps(grips) {
let result = grips.reduce((res, grip) => {
let previewItems = getGripPreviewItems(grip);
let allProps = previewItems.length > 0 ? getAllProps(previewItems) : [];
return [...res, grip, grip.class, ...allProps];
}, []);
// We are interested only in primitive props (to search for)
// not in objects and undefined previews.
result = result.filter(grip =>
typeof grip != "object" &&
typeof grip != "undefined"
);
return [...new Set(result)];
}
exports.getAllMessages = getAllMessages;
exports.getAllMessagesUiById = getAllMessagesUiById;
exports.getAllMessagesTableDataById = getAllMessagesTableDataById;
exports.getAllGroupsById = getAllGroupsById;
exports.getCurrentGroup = getCurrentGroup;
module.exports = {
getMessage,
getAllMessagesById,
getAllMessagesUiById,
getAllMessagesTableDataById,
getAllGroupsById,
getCurrentGroup,
getVisibleMessages,
};

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

@ -8,7 +8,6 @@ const {PrefState} = require("devtools/client/webconsole/new-console-output/reduc
const {UiState} = require("devtools/client/webconsole/new-console-output/reducers/ui");
const {
applyMiddleware,
combineReducers,
compose,
createStore
} = require("devtools/client/shared/vendor/redux");
@ -23,11 +22,12 @@ const {
const { reducers } = require("./reducers/index");
const Services = require("Services");
function configureStore(hud) {
function configureStore(hud, options = {}) {
const logLimit = options.logLimit
|| Math.max(Services.prefs.getIntPref("devtools.hud.loglimit"), 1);
const initialState = {
prefs: new PrefState({
logLimit: Math.max(Services.prefs.getIntPref("devtools.hud.loglimit"), 1),
}),
prefs: new PrefState({ logLimit }),
filters: new FilterState({
error: Services.prefs.getBoolPref(PREFS.FILTER.ERROR),
warn: Services.prefs.getBoolPref(PREFS.FILTER.WARN),
@ -44,12 +44,34 @@ function configureStore(hud) {
};
return createStore(
combineReducers(reducers),
createRootReducer(),
initialState,
compose(applyMiddleware(thunk), enableActorReleaser(hud), enableBatching())
);
}
function createRootReducer() {
return function rootReducer(state, action) {
// We want to compute the new state for all properties except "messages".
const newState = [...Object.entries(reducers)].reduce((res, [key, reducer]) => {
if (key !== "messages") {
res[key] = reducer(state[key], action);
}
return res;
}, {});
return Object.assign(newState, {
// specifically pass the updated filters and prefs state as additional arguments.
messages: reducers.messages(
state.messages,
action,
newState.filters,
newState.prefs,
),
});
};
}
/**
* A enhancer for the store to handle batched actions.
*/

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

@ -31,8 +31,8 @@ function setupActions() {
/**
* Prepare the store for use in testing.
*/
function setupStore(input, hud) {
const store = configureStore(hud);
function setupStore(input, hud, options) {
const store = configureStore(hud, options);
// Add the messages from the input commands to the store.
input.forEach((cmd) => {

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

@ -16,19 +16,19 @@ add_task(function* () {
let hud = toolbox.getCurrentPanel().hud;
const store = hud.ui.newConsoleOutput.getStore();
// Adding loggin each time the store is modified in order to check
// Adding logging each time the store is modified in order to check
// the store state in case of failure.
store.subscribe(() => {
const messages = store.getState().messages.messagesById.toJS()
.map(message => {
return {
const messages = store.getState().messages.messagesById
.reduce(function (res, message) {
res.push({
id: message.id,
type: message.type,
parameters: message.parameters,
messageText: message.messageText
};
}
);
});
return res;
}, []);
info("messages : " + JSON.stringify(messages));
});

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

@ -8,7 +8,7 @@ const expect = require("expect");
const actions = require("devtools/client/webconsole/new-console-output/actions/index");
const { messageAdd } = require("devtools/client/webconsole/new-console-output/actions/index");
const { ConsoleCommand } = require("devtools/client/webconsole/new-console-output/types");
const { getAllMessages } = require("devtools/client/webconsole/new-console-output/selectors/messages");
const { getVisibleMessages } = require("devtools/client/webconsole/new-console-output/selectors/messages");
const { getAllFilters } = require("devtools/client/webconsole/new-console-output/selectors/filters");
const { setupStore } = require("devtools/client/webconsole/new-console-output/test/helpers");
const { MESSAGE_LEVEL } = require("devtools/client/webconsole/new-console-output/constants");
@ -25,22 +25,22 @@ describe("Filtering", () => {
beforeEach(() => {
store = prepareBaseStore();
store.dispatch(actions.filtersClear());
numMessages = getAllMessages(store.getState()).size;
numMessages = getVisibleMessages(store.getState()).length;
});
describe("Level filter", () => {
it("filters log messages", () => {
store.dispatch(actions.filterToggle(MESSAGE_LEVEL.LOG));
let messages = getAllMessages(store.getState());
expect(messages.size).toEqual(numMessages - 3);
let messages = getVisibleMessages(store.getState());
expect(messages.length).toEqual(numMessages - 3);
});
it("filters debug messages", () => {
store.dispatch(actions.filterToggle(MESSAGE_LEVEL.DEBUG));
let messages = getAllMessages(store.getState());
expect(messages.size).toEqual(numMessages - 1);
let messages = getVisibleMessages(store.getState());
expect(messages.length).toEqual(numMessages - 1);
});
// @TODO add info stub
@ -49,15 +49,15 @@ describe("Filtering", () => {
it("filters warning messages", () => {
store.dispatch(actions.filterToggle(MESSAGE_LEVEL.WARN));
let messages = getAllMessages(store.getState());
expect(messages.size).toEqual(numMessages - 1);
let messages = getVisibleMessages(store.getState());
expect(messages.length).toEqual(numMessages - 1);
});
it("filters error messages", () => {
store.dispatch(actions.filterToggle(MESSAGE_LEVEL.ERROR));
let messages = getAllMessages(store.getState());
expect(messages.size).toEqual(numMessages - 1);
let messages = getVisibleMessages(store.getState());
expect(messages.length).toEqual(numMessages - 1);
});
it("filters css messages", () => {
@ -66,52 +66,51 @@ describe("Filtering", () => {
);
store.dispatch(messageAdd(message));
let messages = getAllMessages(store.getState());
expect(messages.size).toEqual(numMessages);
let messages = getVisibleMessages(store.getState());
expect(messages.length).toEqual(numMessages);
store.dispatch(actions.filterToggle("css"));
messages = getAllMessages(store.getState());
expect(messages.size).toEqual(numMessages + 1);
messages = getVisibleMessages(store.getState());
expect(messages.length).toEqual(numMessages + 1);
});
it("filters xhr messages", () => {
let message = stubPreparedMessages.get("XHR GET request");
store.dispatch(messageAdd(message));
let messages = getAllMessages(store.getState());
expect(messages.size).toEqual(numMessages);
let messages = getVisibleMessages(store.getState());
expect(messages.length).toEqual(numMessages);
store.dispatch(actions.filterToggle("netxhr"));
messages = getAllMessages(store.getState());
expect(messages.size).toEqual(numMessages + 1);
messages = getVisibleMessages(store.getState());
expect(messages.length).toEqual(numMessages + 1);
});
it("filters network messages", () => {
let message = stubPreparedMessages.get("GET request");
store.dispatch(messageAdd(message));
let messages = getAllMessages(store.getState());
expect(messages.size).toEqual(numMessages);
let messages = getVisibleMessages(store.getState());
expect(messages.length).toEqual(numMessages);
store.dispatch(actions.filterToggle("net"));
messages = getAllMessages(store.getState());
expect(messages.size).toEqual(numMessages + 1);
messages = getVisibleMessages(store.getState());
expect(messages.length).toEqual(numMessages + 1);
});
});
describe("Text filter", () => {
it("matches on value grips", () => {
store.dispatch(actions.filterTextSet("danger"));
let messages = getAllMessages(store.getState());
expect(messages.size - numUnfilterableMessages).toEqual(1);
let messages = getVisibleMessages(store.getState());
expect(messages.length - numUnfilterableMessages).toEqual(1);
});
it("matches unicode values", () => {
store.dispatch(actions.filterTextSet("鼬"));
let messages = getAllMessages(store.getState());
expect(messages.size - numUnfilterableMessages).toEqual(1);
let messages = getVisibleMessages(store.getState());
expect(messages.length - numUnfilterableMessages).toEqual(1);
});
it("matches locations", () => {
@ -124,8 +123,8 @@ describe("Filtering", () => {
store.dispatch(actions.filterTextSet("search-location-test.js"));
let messages = getAllMessages(store.getState());
expect(messages.size - numUnfilterableMessages).toEqual(1);
let messages = getVisibleMessages(store.getState());
expect(messages.length - numUnfilterableMessages).toEqual(1);
});
it("matches stacktrace functionName", () => {
@ -134,8 +133,8 @@ describe("Filtering", () => {
store.dispatch(actions.filterTextSet("testStacktraceFiltering"));
let messages = getAllMessages(store.getState());
expect(messages.size - numUnfilterableMessages).toEqual(1);
let messages = getVisibleMessages(store.getState());
expect(messages.length - numUnfilterableMessages).toEqual(1);
});
it("matches stacktrace location", () => {
@ -151,16 +150,16 @@ describe("Filtering", () => {
store.dispatch(actions.filterTextSet("search-location-test.js:85:13"));
let messages = getAllMessages(store.getState());
expect(messages.size - numUnfilterableMessages).toEqual(1);
let messages = getVisibleMessages(store.getState());
expect(messages.length - numUnfilterableMessages).toEqual(1);
});
it("restores all messages once text is cleared", () => {
store.dispatch(actions.filterTextSet("danger"));
store.dispatch(actions.filterTextSet(""));
let messages = getAllMessages(store.getState());
expect(messages.size).toEqual(numMessages);
let messages = getVisibleMessages(store.getState());
expect(messages.length).toEqual(numMessages);
});
});
@ -189,7 +188,7 @@ describe("Clear filters", () => {
"net": false,
"netxhr": true,
"warn": true,
"text": "foobar"
"text": "foobar",
});
store.dispatch(actions.filtersClear());
@ -204,7 +203,7 @@ describe("Clear filters", () => {
"net": false,
"netxhr": false,
"warn": true,
"text": ""
"text": "",
});
});
});

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

@ -3,10 +3,12 @@
"use strict";
const {
getAllMessages,
getAllMessagesUiById,
getAllGroupsById,
getAllMessagesById,
getAllMessagesTableDataById,
getAllMessagesUiById,
getCurrentGroup,
getVisibleMessages,
} = require("devtools/client/webconsole/new-console-output/selectors/messages");
const {
setupActions,
@ -35,18 +37,16 @@ describe("Message reducer:", () => {
const message = stubPreparedMessages.get("console.log('foobar', 'test')");
dispatch(actions.messageAdd(packet));
const messages = getAllMessages(getState());
const messages = getAllMessagesById(getState());
expect(messages.first()).toEqual(message);
});
it("increments repeat on a repeating message", () => {
const { dispatch, getState } = setupStore([
"console.log('foobar', 'test')",
"console.log('foobar', 'test')"
]);
const key1 = "console.log('foobar', 'test')";
const { dispatch, getState } = setupStore([key1, key1]);
const packet = clonePacket(stubPackets.get("console.log('foobar', 'test')"));
const packet = clonePacket(stubPackets.get(key1));
// Repeat ID must be the same even if the timestamp is different.
packet.message.timeStamp = 1;
@ -54,26 +54,23 @@ describe("Message reducer:", () => {
packet.message.timeStamp = 2;
dispatch(actions.messageAdd(packet));
const messages = getAllMessages(getState());
const messages = getAllMessagesById(getState());
expect(messages.size).toBe(1);
expect(messages.first().repeat).toBe(4);
});
it("does not clobber a unique message", () => {
const { dispatch, getState } = setupStore([
"console.log('foobar', 'test')",
"console.log('foobar', 'test')"
]);
const key1 = "console.log('foobar', 'test')";
const { dispatch, getState } = setupStore([key1, key1]);
const packet = stubPackets.get("console.log('foobar', 'test')");
const packet = stubPackets.get(key1);
dispatch(actions.messageAdd(packet));
const packet2 = stubPackets.get("console.log(undefined)");
dispatch(actions.messageAdd(packet2));
const messages = getAllMessages(getState());
const messages = getAllMessagesById(getState());
expect(messages.size).toBe(2);
expect(messages.first().repeat).toBe(3);
expect(messages.last().repeat).toBe(1);
@ -84,7 +81,7 @@ describe("Message reducer:", () => {
dispatch(actions.messageAdd(stubPackets.get("console.clear()")));
const messages = getAllMessages(getState());
const messages = getAllMessagesById(getState());
expect(messages.size).toBe(1);
expect(messages.first().parameters[0]).toBe("Console was cleared.");
@ -93,13 +90,20 @@ describe("Message reducer:", () => {
it("clears the messages list in response to MESSAGES_CLEAR action", () => {
const { dispatch, getState } = setupStore([
"console.log('foobar', 'test')",
"console.log(undefined)"
"console.log(undefined)",
"console.table(['red', 'green', 'blue']);",
"console.group('bar')",
]);
dispatch(actions.messagesClear());
const messages = getAllMessages(getState());
expect(messages.size).toBe(0);
const state = getState();
expect(getAllMessagesById(state).size).toBe(0);
expect(getVisibleMessages(state).length).toBe(0);
expect(getAllMessagesUiById(state).size).toBe(0);
expect(getAllGroupsById(state).size).toBe(0);
expect(getAllMessagesTableDataById(state).size).toBe(0);
expect(getCurrentGroup(state)).toBe(null);
});
it("properly limits number of messages", () => {
@ -113,13 +117,13 @@ describe("Message reducer:", () => {
dispatch(actions.messageAdd(packet));
}
const messages = getAllMessages(getState());
const messages = getAllMessagesById(getState());
expect(messages.count()).toBe(logLimit);
expect(messages.first().parameters[0]).toBe(`message num 3`);
expect(messages.last().parameters[0]).toBe(`message num ${logLimit + 2}`);
});
it("properly limits number of groups", () => {
it("properly limits number of messages when there are nested groups", () => {
const { dispatch, getState } = setupStore([]);
const logLimit = 1000;
@ -128,23 +132,74 @@ describe("Message reducer:", () => {
const packetGroup = clonePacket(stubPackets.get("console.group('bar')"));
const packetGroupEnd = clonePacket(stubPackets.get("console.groupEnd()"));
packetGroup.message.arguments = [`group-1`];
dispatch(actions.messageAdd(packetGroup));
packetGroup.message.arguments = [`group-1-1`];
dispatch(actions.messageAdd(packetGroup));
packetGroup.message.arguments = [`group-1-1-1`];
dispatch(actions.messageAdd(packetGroup));
packet.message.arguments = [`message-in-group-1`];
dispatch(actions.messageAdd(packet));
packet.message.arguments = [`message-in-group-2`];
dispatch(actions.messageAdd(packet));
// Closing group-1-1-1
dispatch(actions.messageAdd(packetGroupEnd));
// Closing group-1-1
dispatch(actions.messageAdd(packetGroupEnd));
// Closing group-1
dispatch(actions.messageAdd(packetGroupEnd));
for (let i = 0; i < logLimit; i++) {
packet.message.arguments = [`message-${i}`];
dispatch(actions.messageAdd(packet));
}
const visibleMessages = getVisibleMessages(getState());
const messages = getAllMessagesById(getState());
expect(messages.count()).toBe(logLimit);
expect(visibleMessages.length).toBe(logLimit);
expect(visibleMessages[0].parameters[0]).toBe(`message-0`);
expect(visibleMessages[logLimit - 1].parameters[0]).toBe(`message-${logLimit - 1}`);
// The groups were cleaned up.
const groups = getAllGroupsById(getState());
expect(groups.count()).toBe(0);
});
it("properly limits number of groups", () => {
const logLimit = 100;
const { dispatch, getState } = setupStore([], null, {logLimit});
const packet = clonePacket(stubPackets.get("console.log(undefined)"));
const packetGroup = clonePacket(stubPackets.get("console.group('bar')"));
const packetGroupEnd = clonePacket(stubPackets.get("console.groupEnd()"));
for (let i = 0; i < logLimit + 2; i++) {
packet.message.arguments = [`message num ${i}`];
dispatch(actions.messageAdd(packetGroup));
packet.message.arguments = [`message-${i}-a`];
dispatch(actions.messageAdd(packet));
packet.message.arguments = [`message-${i}-b`];
dispatch(actions.messageAdd(packet));
dispatch(actions.messageAdd(packetGroupEnd));
}
const messages = getAllMessages(getState());
expect(messages.count()).toBe(logLimit * 2);
expect(messages.get(1).parameters[0]).toBe(`message num 2`);
expect(messages.last().parameters[0]).toBe(`message num ${logLimit + 1}`);
const visibleMessages = getVisibleMessages(getState());
const messages = getAllMessagesById(getState());
// We should have three times the logLimit since each group has one message inside.
expect(messages.count()).toBe(logLimit * 3);
// We should have logLimit number of groups
const groups = getAllGroupsById(getState());
expect(groups.count()).toBe(logLimit);
expect(visibleMessages[1].parameters[0]).toBe(`message-2-a`);
expect(messages.last().parameters[0]).toBe(`message-${logLimit + 1}-b`);
});
it("properly limits number of collapsed groups", () => {
const { dispatch, getState } = setupStore([]);
const logLimit = 1000;
const logLimit = 100;
const { dispatch, getState } = setupStore([], null, {logLimit});
const packet = clonePacket(stubPackets.get("console.log(undefined)"));
const packetGroupCollapsed = clonePacket(
@ -152,16 +207,30 @@ describe("Message reducer:", () => {
const packetGroupEnd = clonePacket(stubPackets.get("console.groupEnd()"));
for (let i = 0; i < logLimit + 2; i++) {
packetGroupCollapsed.message.arguments = [`message num ${i}`];
packetGroupCollapsed.message.arguments = [`group-${i}`];
dispatch(actions.messageAdd(packetGroupCollapsed));
packet.message.arguments = [`message-${i}-a`];
dispatch(actions.messageAdd(packet));
packet.message.arguments = [`message-${i}-b`];
dispatch(actions.messageAdd(packet));
dispatch(actions.messageAdd(packetGroupEnd));
}
const messages = getAllMessages(getState());
expect(messages.count()).toBe(logLimit);
expect(messages.first().parameters[0]).toBe(`message num 2`);
expect(messages.last().parameters[0]).toBe(`message num ${logLimit + 1}`);
const messages = getAllMessagesById(getState());
// We should have three times the logLimit since each group has two message inside.
expect(messages.size).toBe(logLimit * 3);
// We should have logLimit number of groups
const groups = getAllGroupsById(getState());
expect(groups.count()).toBe(logLimit);
expect(messages.first().parameters[0]).toBe(`group-2`);
expect(messages.last().parameters[0]).toBe(`message-${logLimit + 1}-b`);
const visibleMessages = getVisibleMessages(getState());
expect(visibleMessages.length).toBe(logLimit);
const lastVisibleMessage = visibleMessages[visibleMessages.length - 1];
expect(lastVisibleMessage.parameters[0]).toBe(`group-${logLimit + 1}`);
});
it("does not add null messages to the store", () => {
@ -170,7 +239,7 @@ describe("Message reducer:", () => {
const message = stubPackets.get("console.time('bar')");
dispatch(actions.messageAdd(message));
const messages = getAllMessages(getState());
const messages = getAllMessagesById(getState());
expect(messages.size).toBe(0);
});
@ -180,7 +249,7 @@ describe("Message reducer:", () => {
const packet = stubPackets.get("console.table('bar')");
dispatch(actions.messageAdd(packet));
const messages = getAllMessages(getState());
const messages = getAllMessagesById(getState());
const tableMessage = messages.last();
expect(tableMessage.level).toEqual(MESSAGE_TYPE.LOG);
});
@ -191,7 +260,7 @@ describe("Message reducer:", () => {
const message = stubPackets.get("console.group('bar')");
dispatch(actions.messageAdd(message));
const messages = getAllMessages(getState());
const messages = getAllMessagesById(getState());
expect(messages.size).toBe(1);
});
@ -204,7 +273,7 @@ describe("Message reducer:", () => {
const packet = stubPackets.get("console.log('foobar', 'test')");
dispatch(actions.messageAdd(packet));
const messages = getAllMessages(getState());
const messages = getAllMessagesById(getState());
expect(messages.size).toBe(2);
expect(messages.last().groupId).toBe(messages.first().id);
});
@ -215,7 +284,7 @@ describe("Message reducer:", () => {
const message = stubPackets.get("console.groupEnd('bar')");
dispatch(actions.messageAdd(message));
const messages = getAllMessages(getState());
const messages = getAllMessagesById(getState());
expect(messages.size).toBe(0);
});
@ -228,8 +297,8 @@ describe("Message reducer:", () => {
dispatch(actions.messageAdd(
stubPackets.get("console.log('foobar', 'test')")));
const messages = getAllMessages(getState());
expect(messages.size).toBe(1);
const messages = getVisibleMessages(getState());
expect(messages.length).toBe(1);
});
it("adds console.dirxml call as console.log", () => {
@ -238,7 +307,7 @@ describe("Message reducer:", () => {
const packet = stubPackets.get("console.dirxml(window)");
dispatch(actions.messageAdd(packet));
const messages = getAllMessages(getState());
const messages = getAllMessagesById(getState());
const dirxmlMessage = messages.last();
expect(dirxmlMessage.level).toEqual(MESSAGE_TYPE.LOG);
});
@ -251,7 +320,7 @@ describe("Message reducer:", () => {
const message = stubPackets.get("console.trace()");
dispatch(actions.messageAdd(message));
const messages = getAllMessages(getState());
const messages = getAllMessagesById(getState());
const messagesUi = getAllMessagesUiById(getState());
expect(messagesUi.size).toBe(1);
expect(messagesUi.first()).toBe(messages.first().id);
@ -278,7 +347,7 @@ describe("Message reducer:", () => {
const message = stubPackets.get("console.group('bar')");
dispatch(actions.messageAdd(message));
const messages = getAllMessages(getState());
const messages = getAllMessagesById(getState());
const messagesUi = getAllMessagesUiById(getState());
expect(messagesUi.size).toBe(1);
expect(messagesUi.first()).toBe(messages.first().id);
@ -302,7 +371,7 @@ describe("Message reducer:", () => {
const packet = stubPackets.get("console.group('bar')");
dispatch(actions.messageAdd(packet));
const messages = getAllMessages(getState());
const messages = getAllMessagesById(getState());
const currentGroup = getCurrentGroup(getState());
expect(currentGroup).toBe(messages.first().id);
});
@ -313,19 +382,19 @@ describe("Message reducer:", () => {
"console.groupCollapsed('foo')"
]);
let messages = getAllMessages(getState());
let messages = getAllMessagesById(getState());
let currentGroup = getCurrentGroup(getState());
expect(currentGroup).toBe(messages.last().id);
const endFooPacket = stubPackets.get("console.groupEnd('foo')");
dispatch(actions.messageAdd(endFooPacket));
messages = getAllMessages(getState());
messages = getAllMessagesById(getState());
currentGroup = getCurrentGroup(getState());
expect(currentGroup).toBe(messages.first().id);
const endBarPacket = stubPackets.get("console.groupEnd('bar')");
dispatch(actions.messageAdd(endBarPacket));
messages = getAllMessages(getState());
messages = getAllMessagesById(getState());
currentGroup = getCurrentGroup(getState());
expect(currentGroup).toBe(null);
});
@ -349,7 +418,7 @@ describe("Message reducer:", () => {
const barPacket = stubPackets.get("console.group('bar')");
dispatch(actions.messageAdd(barPacket));
let messages = getAllMessages(getState());
let messages = getAllMessagesById(getState());
let groupsById = getAllGroupsById(getState());
expect(groupsById.size).toBe(1);
expect(groupsById.has(messages.first().id)).toBe(true);
@ -357,7 +426,7 @@ describe("Message reducer:", () => {
const fooPacket = stubPackets.get("console.groupCollapsed('foo')");
dispatch(actions.messageAdd(fooPacket));
messages = getAllMessages(getState());
messages = getAllMessagesById(getState());
groupsById = getAllGroupsById(getState());
expect(groupsById.size).toBe(2);
expect(groupsById.has(messages.last().id)).toBe(true);

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

@ -6,7 +6,7 @@
const expect = require("expect");
const actions = require("devtools/client/webconsole/new-console-output/actions/index");
const { getAllMessages } = require("devtools/client/webconsole/new-console-output/selectors/messages");
const { getVisibleMessages } = require("devtools/client/webconsole/new-console-output/selectors/messages");
const { setupStore } = require("devtools/client/webconsole/new-console-output/test/helpers");
describe("Searching in grips", () => {
@ -20,42 +20,42 @@ describe("Searching in grips", () => {
describe("Search in table & array & object props", () => {
it("matches on value grips", () => {
store.dispatch(actions.filterTextSet("red"));
expect(getAllMessages(store.getState()).size).toEqual(3);
expect(getVisibleMessages(store.getState()).length).toEqual(3);
});
});
describe("Search in object value", () => {
it("matches on value grips", () => {
store.dispatch(actions.filterTextSet("redValue"));
expect(getAllMessages(store.getState()).size).toEqual(1);
expect(getVisibleMessages(store.getState()).length).toEqual(1);
});
});
describe("Search in regex", () => {
it("matches on value grips", () => {
store.dispatch(actions.filterTextSet("a.b.c"));
expect(getAllMessages(store.getState()).size).toEqual(1);
expect(getVisibleMessages(store.getState()).length).toEqual(1);
});
});
describe("Search in map values", () => {
it("matches on value grips", () => {
store.dispatch(actions.filterTextSet("value1"));
expect(getAllMessages(store.getState()).size).toEqual(1);
expect(getVisibleMessages(store.getState()).length).toEqual(1);
});
});
describe("Search in map keys", () => {
it("matches on value grips", () => {
store.dispatch(actions.filterTextSet("key1"));
expect(getAllMessages(store.getState()).size).toEqual(1);
expect(getVisibleMessages(store.getState()).length).toEqual(1);
});
});
describe("Search in text", () => {
it("matches on value grips", () => {
store.dispatch(actions.filterTextSet("myobj"));
expect(getAllMessages(store.getState()).size).toEqual(1);
expect(getVisibleMessages(store.getState()).length).toEqual(1);
});
});
@ -63,19 +63,19 @@ describe("Searching in grips", () => {
it("matches on network messages", () => {
store.dispatch(actions.filterToggle("net"));
store.dispatch(actions.filterTextSet("get"));
expect(getAllMessages(store.getState()).size).toEqual(1);
expect(getVisibleMessages(store.getState()).length).toEqual(1);
});
});
describe("Search in frame", () => {
it("matches on file name", () => {
store.dispatch(actions.filterTextSet("test-console-api.html:1:27"));
expect(getAllMessages(store.getState()).size).toEqual(7);
expect(getVisibleMessages(store.getState()).length).toEqual(7);
});
it("do not match on full url", () => {
store.dispatch(actions.filterTextSet("http://example.com/browser/devtools"));
expect(getAllMessages(store.getState()).size).toEqual(0);
expect(getVisibleMessages(store.getState()).length).toEqual(0);
});
});
});

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

@ -4,7 +4,6 @@
"use strict";
const promise = require("promise");
const {AddonManager} = require("resource://gre/modules/AddonManager.jsm");
const Services = require("Services");
const {getJSON} = require("devtools/client/shared/getjson");
@ -51,24 +50,24 @@ AddonManager.addAddonListener(addonsListener);
var GetAvailableAddons_promise = null;
var GetAvailableAddons = exports.GetAvailableAddons = function () {
if (!GetAvailableAddons_promise) {
let deferred = promise.defer();
GetAvailableAddons_promise = deferred.promise;
let addons = {
simulators: [],
adb: null
};
getJSON(ADDONS_URL).then(json => {
for (let stability in json) {
for (let version of json[stability]) {
addons.simulators.push(new SimulatorAddon(stability, version));
GetAvailableAddons_promise = new Promise((resolve, reject) => {
let addons = {
simulators: [],
adb: null
};
getJSON(ADDONS_URL).then(json => {
for (let stability in json) {
for (let version of json[stability]) {
addons.simulators.push(new SimulatorAddon(stability, version));
}
}
}
addons.adb = new ADBAddon();
addons.adapters = new AdaptersAddon();
deferred.resolve(addons);
}, e => {
GetAvailableAddons_promise = null;
deferred.reject(e);
addons.adb = new ADBAddon();
addons.adapters = new AdaptersAddon();
resolve(addons);
}, e => {
GetAvailableAddons_promise = null;
reject(e);
});
});
}
return GetAvailableAddons_promise;

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

@ -4,7 +4,6 @@
const {Cu} = require("chrome");
const promise = require("promise");
const {TargetFactory} = require("devtools/client/framework/target");
const Services = require("Services");
const {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm", {});
@ -277,7 +276,7 @@ var AppManager = exports.AppManager = {
reloadTab: function () {
if (this.selectedProject && this.selectedProject.type != "tab") {
return promise.reject("tried to reload non-tab project");
return Promise.reject("tried to reload non-tab project");
}
return this.getTarget().then(target => {
target.activeTab.reload();
@ -313,7 +312,7 @@ var AppManager = exports.AppManager = {
let app = this._getProjectFront(this.selectedProject);
if (!app) {
return promise.reject("Can't find app front for selected project");
return Promise.reject("Can't find app front for selected project");
}
return Task.spawn(function* () {
@ -325,9 +324,9 @@ var AppManager = exports.AppManager = {
try {
return yield app.getTarget();
} catch (e) {}
let deferred = promise.defer();
setTimeout(deferred.resolve, 500);
yield deferred.promise;
return new Promise(resolve => {
setTimeout(resolve, 500);
});
}
AppManager.reportError("error_cantConnectToApp", app.manifest.manifestURL);
@ -460,36 +459,36 @@ var AppManager = exports.AppManager = {
if (this.connected && this.selectedRuntime === runtime) {
// Already connected
return promise.resolve();
return Promise.resolve();
}
let deferred = promise.defer();
let deferred = new Promise((resolve, reject) => {
this.disconnectRuntime().then(() => {
this.selectedRuntime = runtime;
this.disconnectRuntime().then(() => {
this.selectedRuntime = runtime;
let onConnectedOrDisconnected = () => {
this.connection.off(Connection.Events.CONNECTED, onConnectedOrDisconnected);
this.connection.off(Connection.Events.DISCONNECTED, onConnectedOrDisconnected);
if (this.connected) {
deferred.resolve();
} else {
deferred.reject();
let onConnectedOrDisconnected = () => {
this.connection.off(Connection.Events.CONNECTED, onConnectedOrDisconnected);
this.connection.off(Connection.Events.DISCONNECTED, onConnectedOrDisconnected);
if (this.connected) {
resolve();
} else {
reject();
}
};
this.connection.on(Connection.Events.CONNECTED, onConnectedOrDisconnected);
this.connection.on(Connection.Events.DISCONNECTED, onConnectedOrDisconnected);
try {
// Reset the connection's state to defaults
this.connection.resetOptions();
// Only watch for errors here. Final resolution occurs above, once
// we've reached the CONNECTED state.
this.selectedRuntime.connect(this.connection)
.then(null, e => reject(e));
} catch (e) {
reject(e);
}
};
this.connection.on(Connection.Events.CONNECTED, onConnectedOrDisconnected);
this.connection.on(Connection.Events.DISCONNECTED, onConnectedOrDisconnected);
try {
// Reset the connection's state to defaults
this.connection.resetOptions();
// Only watch for errors here. Final resolution occurs above, once
// we've reached the CONNECTED state.
this.selectedRuntime.connect(this.connection)
.then(null, e => deferred.reject(e));
} catch (e) {
deferred.reject(e);
}
}, deferred.reject);
}, reject);
});
// Record connection result in telemetry
let logResult = result => {
@ -499,10 +498,10 @@ var AppManager = exports.AppManager = {
"_CONNECTION_RESULT", result);
}
};
deferred.promise.then(() => logResult(true), () => logResult(false));
deferred.then(() => logResult(true), () => logResult(false));
// If successful, record connection time in telemetry
deferred.promise.then(() => {
deferred.then(() => {
const timerId = "DEVTOOLS_WEBIDE_CONNECTION_TIME_SECONDS";
this._telemetry.startTimer(timerId);
this.connection.once(Connection.Events.STATUS_CHANGED, () => {
@ -514,7 +513,7 @@ var AppManager = exports.AppManager = {
// Bug 1121100 may find a better way to silence these.
});
return deferred.promise;
return deferred;
},
_recordRuntimeInfo: Task.async(function* () {
@ -570,17 +569,18 @@ var AppManager = exports.AppManager = {
disconnectRuntime: function () {
if (!this.connected) {
return promise.resolve();
return Promise.resolve();
}
let deferred = promise.defer();
this.connection.once(Connection.Events.DISCONNECTED, () => deferred.resolve());
this.connection.disconnect();
return deferred.promise;
return new Promise(resolve => {
this.connection.once(Connection.Events.DISCONNECTED, () => resolve());
this.connection.disconnect();
});
},
launchRuntimeApp: function () {
if (this.selectedProject && this.selectedProject.type != "runtimeApp") {
return promise.reject("attempting to launch a non-runtime app");
return Promise.reject("attempting to launch a non-runtime app");
}
let app = this._getProjectFront(this.selectedProject);
return app.launch();
@ -588,7 +588,7 @@ var AppManager = exports.AppManager = {
launchOrReloadRuntimeApp: function () {
if (this.selectedProject && this.selectedProject.type != "runtimeApp") {
return promise.reject("attempting to launch / reload a non-runtime app");
return Promise.reject("attempting to launch / reload a non-runtime app");
}
let app = this._getProjectFront(this.selectedProject);
if (!app.running) {
@ -607,17 +607,17 @@ var AppManager = exports.AppManager = {
if (!project || (project.type != "packaged" && project.type != "hosted")) {
console.error("Can't install project. Unknown type of project.");
return promise.reject("Can't install");
return Promise.reject("Can't install");
}
if (!this._listTabsResponse) {
this.reportError("error_cantInstallNotFullyConnected");
return promise.reject("Can't install");
return Promise.reject("Can't install");
}
if (!this._appsFront) {
console.error("Runtime doesn't have a webappsActor");
return promise.reject("Can't install");
return Promise.reject("Can't install");
}
return Task.spawn(function* () {
@ -635,7 +635,7 @@ var AppManager = exports.AppManager = {
let installPromise;
if (project.type != "packaged" && project.type != "hosted") {
return promise.reject("Don't know how to install project");
return Promise.reject("Don't know how to install project");
}
let response;
@ -674,15 +674,16 @@ var AppManager = exports.AppManager = {
let {app} = response;
if (!app.running) {
let deferred = promise.defer();
self.on("app-manager-update", function onUpdate(event, what) {
if (what == "project-started") {
self.off("app-manager-update", onUpdate);
deferred.resolve();
}
let deferred = new Promise(resolve => {
self.on("app-manager-update", function onUpdate(event, what) {
if (what == "project-started") {
self.off("app-manager-update", onUpdate);
resolve();
}
});
});
yield app.launch();
yield deferred.promise;
yield deferred;
} else {
yield app.reload();
}
@ -698,7 +699,7 @@ var AppManager = exports.AppManager = {
validateAndUpdateProject: function (project) {
if (!project) {
return promise.reject();
return Promise.reject();
}
return Task.spawn(function* () {
@ -823,7 +824,7 @@ var AppManager = exports.AppManager = {
writeManifest: function (project) {
if (project.type != "packaged") {
return promise.reject("Not a packaged app");
return Promise.reject("Not a packaged app");
}
if (!project.manifest) {

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

@ -3,7 +3,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const {Cc, Ci, Cu, Cr} = require("chrome");
const promise = require("promise");
const EventEmitter = require("devtools/shared/event-emitter");
const {generateUUID} = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
@ -21,145 +20,135 @@ const IDB = {
databaseName: "AppProjects",
open: function () {
let deferred = promise.defer();
return new Promise((resolve, reject) => {
let request = indexedDB.open(IDB.databaseName, 5);
request.onerror = function (event) {
reject("Unable to open AppProjects indexedDB: " +
this.error.name + " - " + this.error.message);
};
request.onupgradeneeded = function (event) {
let db = event.target.result;
db.createObjectStore("projects", { keyPath: "location" });
};
let request = indexedDB.open(IDB.databaseName, 5);
request.onerror = function (event) {
deferred.reject("Unable to open AppProjects indexedDB: " +
this.error.name + " - " + this.error.message);
};
request.onupgradeneeded = function (event) {
let db = event.target.result;
db.createObjectStore("projects", { keyPath: "location" });
};
request.onsuccess = function () {
let db = IDB._db = request.result;
let objectStore = db.transaction("projects").objectStore("projects");
let projects = [];
let toRemove = [];
objectStore.openCursor().onsuccess = function (event) {
let cursor = event.target.result;
if (cursor) {
if (cursor.value.location) {
request.onsuccess = function () {
let db = IDB._db = request.result;
let objectStore = db.transaction("projects").objectStore("projects");
let projects = [];
let toRemove = [];
objectStore.openCursor().onsuccess = function (event) {
let cursor = event.target.result;
if (cursor) {
if (cursor.value.location) {
// We need to make sure this object has a `.location` property.
// The UI depends on this property.
// This should not be needed as we make sure to register valid
// projects, but in the past (before bug 924568), we might have
// registered invalid objects.
// We need to make sure this object has a `.location` property.
// The UI depends on this property.
// This should not be needed as we make sure to register valid
// projects, but in the past (before bug 924568), we might have
// registered invalid objects.
// We also want to make sure the location is valid.
// If the location doesn't exist, we remove the project.
// We also want to make sure the location is valid.
// If the location doesn't exist, we remove the project.
try {
let file = FileUtils.File(cursor.value.location);
if (file.exists()) {
projects.push(cursor.value);
} else {
toRemove.push(cursor.value.location);
}
} catch (e) {
if (e.result == Cr.NS_ERROR_FILE_UNRECOGNIZED_PATH) {
// A URL
projects.push(cursor.value);
try {
let file = FileUtils.File(cursor.value.location);
if (file.exists()) {
projects.push(cursor.value);
} else {
toRemove.push(cursor.value.location);
}
} catch (e) {
if (e.result == Cr.NS_ERROR_FILE_UNRECOGNIZED_PATH) {
// A URL
projects.push(cursor.value);
}
}
}
cursor.continue();
} else {
let removePromises = [];
for (let location of toRemove) {
removePromises.push(IDB.remove(location));
}
Promise.all(removePromises).then(() => {
resolve(projects);
});
}
cursor.continue();
} else {
let removePromises = [];
for (let location of toRemove) {
removePromises.push(IDB.remove(location));
}
promise.all(removePromises).then(() => {
deferred.resolve(projects);
});
}
};
};
};
return deferred.promise;
});
},
add: function (project) {
let deferred = promise.defer();
if (!project.location) {
// We need to make sure this object has a `.location` property.
deferred.reject("Missing location property on project object.");
} else {
let transaction = IDB._db.transaction(["projects"], "readwrite");
let objectStore = transaction.objectStore("projects");
let request = objectStore.add(project);
request.onerror = function (event) {
deferred.reject("Unable to add project to the AppProjects indexedDB: " +
this.error.name + " - " + this.error.message);
};
request.onsuccess = function () {
deferred.resolve();
};
}
return deferred.promise;
return new Promise((resolve, reject) => {
if (!project.location) {
// We need to make sure this object has a `.location` property.
reject("Missing location property on project object.");
} else {
let transaction = IDB._db.transaction(["projects"], "readwrite");
let objectStore = transaction.objectStore("projects");
let request = objectStore.add(project);
request.onerror = function (event) {
reject("Unable to add project to the AppProjects indexedDB: " +
this.error.name + " - " + this.error.message);
};
request.onsuccess = function () {
resolve();
};
}
});
},
update: function (project) {
let deferred = promise.defer();
var transaction = IDB._db.transaction(["projects"], "readwrite");
var objectStore = transaction.objectStore("projects");
var request = objectStore.put(project);
request.onerror = function (event) {
deferred.reject("Unable to update project to the AppProjects indexedDB: " +
this.error.name + " - " + this.error.message);
};
request.onsuccess = function () {
deferred.resolve();
};
return deferred.promise;
return new Promise((resolve, reject) => {
var transaction = IDB._db.transaction(["projects"], "readwrite");
var objectStore = transaction.objectStore("projects");
var request = objectStore.put(project);
request.onerror = function (event) {
reject("Unable to update project to the AppProjects indexedDB: " +
this.error.name + " - " + this.error.message);
};
request.onsuccess = function () {
resolve();
};
});
},
remove: function (location) {
let deferred = promise.defer();
let request = IDB._db.transaction(["projects"], "readwrite")
return new Promise((resolve, reject) => {
let request = IDB._db.transaction(["projects"], "readwrite")
.objectStore("projects")
.delete(location);
request.onsuccess = function (event) {
deferred.resolve();
};
request.onerror = function () {
deferred.reject("Unable to delete project to the AppProjects indexedDB: " +
this.error.name + " - " + this.error.message);
};
return deferred.promise;
request.onsuccess = function (event) {
resolve();
};
request.onerror = function () {
reject("Unable to delete project to the AppProjects indexedDB: " +
this.error.name + " - " + this.error.message);
};
});
}
};
var loadDeferred = promise.defer();
loadDeferred.resolve(IDB.open().then(function (projects) {
var loadDeferred = IDB.open().then(function (projects) {
AppProjects.projects = projects;
AppProjects.emit("ready", projects);
}));
});
const AppProjects = {
load: function () {
return loadDeferred.promise;
return loadDeferred;
},
addPackaged: function (folder) {
let file = FileUtils.File(folder.path);
if (!file.exists()) {
return promise.reject("path doesn't exist");
return Promise.reject("path doesn't exist");
}
let existingProject = this.get(folder.path);
if (existingProject) {
return promise.reject("Already added");
return Promise.reject("Already added");
}
let project = {
type: "packaged",
@ -182,7 +171,7 @@ const AppProjects = {
addHosted: function (manifestURL) {
let existingProject = this.get(manifestURL);
if (existingProject) {
return promise.reject("Already added");
return Promise.reject("Already added");
}
let project = {
type: "hosted",

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

@ -4,7 +4,6 @@
"use strict";
var {Ci, Cu, CC} = require("chrome");
const promise = require("promise");
const {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm", {});
const Services = require("Services");
@ -64,46 +63,44 @@ AppValidator.prototype._getPackagedManifestURL = function () {
};
AppValidator.checkManifest = function (manifestURL) {
let deferred = promise.defer();
let error;
return new Promise((resolve, reject) => {
let error;
let req = new XMLHttpRequest();
req.overrideMimeType("text/plain");
let req = new XMLHttpRequest();
req.overrideMimeType("text/plain");
try {
req.open("GET", manifestURL, true);
req.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE | Ci.nsIRequest.INHIBIT_CACHING;
} catch (e) {
error = strings.formatStringFromName("validator.invalidManifestURL", [manifestURL], 1);
deferred.reject(error);
return deferred.promise;
}
req.onload = function () {
let manifest = null;
try {
manifest = JSON.parse(req.responseText);
req.open("GET", manifestURL, true);
req.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE | Ci.nsIRequest.INHIBIT_CACHING;
} catch (e) {
error = strings.formatStringFromName("validator.invalidManifestJSON", [e, manifestURL], 2);
deferred.reject(error);
error = strings.formatStringFromName("validator.invalidManifestURL", [manifestURL], 1);
return reject(error);
}
deferred.resolve({manifest, manifestURL});
};
req.onload = function () {
let manifest = null;
try {
manifest = JSON.parse(req.responseText);
} catch (e) {
error = strings.formatStringFromName("validator.invalidManifestJSON", [e, manifestURL], 2);
reject(error);
}
req.onerror = function () {
error = strings.formatStringFromName("validator.noAccessManifestURL", [req.statusText, manifestURL], 2);
deferred.reject(error);
};
resolve({manifest, manifestURL});
};
try {
req.send(null);
} catch (e) {
error = strings.formatStringFromName("validator.noAccessManifestURL", [e, manifestURL], 2);
deferred.reject(error);
}
req.onerror = function () {
error = strings.formatStringFromName("validator.noAccessManifestURL", [req.statusText, manifestURL], 2);
reject(error);
};
return deferred.promise;
try {
req.send(null);
} catch (e) {
error = strings.formatStringFromName("validator.noAccessManifestURL", [e, manifestURL], 2);
reject(error);
}
});
};
AppValidator.findManifestAtOrigin = function (manifestURL) {
@ -112,16 +109,14 @@ AppValidator.findManifestAtOrigin = function (manifestURL) {
};
AppValidator.findManifestPath = function (manifestURL) {
let deferred = promise.defer();
if (manifestURL.endsWith("manifest.webapp")) {
deferred.reject();
} else {
let fixedManifest = manifestURL + "/manifest.webapp";
deferred.resolve(AppValidator.checkManifest(fixedManifest));
}
return deferred.promise;
return new Promise((resolve, reject) => {
if (manifestURL.endsWith("manifest.webapp")) {
reject();
} else {
let fixedManifest = manifestURL + "/manifest.webapp";
resolve(AppValidator.checkManifest(fixedManifest));
}
});
};
AppValidator.checkAlternateManifest = function (manifestURL) {
@ -138,24 +133,23 @@ AppValidator.checkAlternateManifest = function (manifestURL) {
};
AppValidator.prototype._fetchManifest = function (manifestURL) {
let deferred = promise.defer();
this.manifestURL = manifestURL;
return new Promise(resolve => {
this.manifestURL = manifestURL;
AppValidator.checkManifest(manifestURL)
.then(({manifest, manifestURL}) => {
deferred.resolve(manifest);
}, error => {
AppValidator.checkAlternateManifest(manifestURL)
.then(({manifest, manifestURL}) => {
this.manifestURL = manifestURL;
deferred.resolve(manifest);
}, () => {
this.error(error);
deferred.resolve(null);
});
});
return deferred.promise;
AppValidator.checkManifest(manifestURL)
.then(({manifest, manifestURL}) => {
resolve(manifest);
}, error => {
AppValidator.checkAlternateManifest(manifestURL)
.then(({manifest, manifestURL}) => {
this.manifestURL = manifestURL;
resolve(manifest);
}, () => {
this.error(error);
resolve(null);
});
});
});
};
AppValidator.prototype._getManifest = function () {
@ -163,18 +157,18 @@ AppValidator.prototype._getManifest = function () {
if (this.type == "packaged") {
manifestURL = this._getPackagedManifestURL();
if (!manifestURL)
return promise.resolve(null);
return Promise.resolve(null);
} else if (this.type == "hosted") {
manifestURL = this.location;
try {
Services.io.newURI(manifestURL);
} catch (e) {
this.error(strings.formatStringFromName("validator.invalidHostedManifestURL", [manifestURL, e.message], 2));
return promise.resolve(null);
return Promise.resolve(null);
}
} else {
this.error(strings.formatStringFromName("validator.invalidProjectType", [this.type], 1));
return promise.resolve(null);
return Promise.resolve(null);
}
return this._fetchManifest(manifestURL);
};
@ -201,57 +195,53 @@ AppValidator.prototype._getOriginURL = function () {
};
AppValidator.prototype.validateLaunchPath = function (manifest) {
let deferred = promise.defer();
// The launch_path field has to start with a `/`
if (manifest.launch_path && manifest.launch_path[0] !== "/") {
this.error(strings.formatStringFromName("validator.nonAbsoluteLaunchPath", [manifest.launch_path], 1));
deferred.resolve();
return deferred.promise;
}
let origin = this._getOriginURL();
let path;
if (this.type == "packaged") {
path = "." + (manifest.launch_path || "/index.html");
} else if (this.type == "hosted") {
path = manifest.launch_path || "/";
}
let indexURL;
try {
indexURL = Services.io.newURI(path, null, Services.io.newURI(origin)).spec;
} catch (e) {
this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [origin + path], 1));
deferred.resolve();
return deferred.promise;
}
return new Promise(resolve => {
// The launch_path field has to start with a `/`
if (manifest.launch_path && manifest.launch_path[0] !== "/") {
this.error(strings.formatStringFromName("validator.nonAbsoluteLaunchPath", [manifest.launch_path], 1));
resolve();
}
let origin = this._getOriginURL();
let path;
if (this.type == "packaged") {
path = "." + (manifest.launch_path || "/index.html");
} else if (this.type == "hosted") {
path = manifest.launch_path || "/";
}
let indexURL;
try {
indexURL = Services.io.newURI(path, null, Services.io.newURI(origin)).spec;
} catch (e) {
this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [origin + path], 1));
return resolve();
}
let req = new XMLHttpRequest();
req.overrideMimeType("text/plain");
try {
req.open("HEAD", indexURL, true);
req.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE | Ci.nsIRequest.INHIBIT_CACHING;
} catch (e) {
this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [indexURL], 1));
deferred.resolve();
return deferred.promise;
}
req.onload = () => {
if (req.status >= 400)
this.error(strings.formatStringFromName("validator.accessFailedLaunchPathBadHttpCode", [indexURL, req.status], 2));
deferred.resolve();
};
req.onerror = () => {
this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [indexURL], 1));
deferred.resolve();
};
let req = new XMLHttpRequest();
req.overrideMimeType("text/plain");
try {
req.open("HEAD", indexURL, true);
req.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE | Ci.nsIRequest.INHIBIT_CACHING;
} catch (e) {
this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [indexURL], 1));
return resolve();
}
req.onload = () => {
if (req.status >= 400)
this.error(strings.formatStringFromName("validator.accessFailedLaunchPathBadHttpCode", [indexURL, req.status], 2));
resolve();
};
req.onerror = () => {
this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [indexURL], 1));
resolve();
};
try {
req.send(null);
} catch (e) {
this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [indexURL], 1));
deferred.resolve();
}
return deferred.promise;
try {
req.send(null);
} catch (e) {
this.error(strings.formatStringFromName("validator.accessFailedLaunchPath", [indexURL], 1));
resolve();
}
});
};
AppValidator.prototype.validateType = function (manifest) {
@ -279,7 +269,7 @@ AppValidator.prototype.validate = function () {
// Skip validations for add-ons
if (manifest.role === "addon" || manifest.manifest_version) {
return promise.resolve();
return Promise.resolve();
}
this.validateManifest(manifest);

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

@ -4,7 +4,6 @@
const {Cu, Cc, Ci} = require("chrome");
const promise = require("promise");
const { Task } = require("devtools/shared/task");
const { OS } = Cu.import("resource://gre/modules/osfile.jsm", {});
const Subprocess = require("sdk/system/child_process/subprocess");
@ -149,31 +148,30 @@ const ProjectBuilding = exports.ProjectBuilding = {
// Subprocess changes CWD, we have to save and restore it.
let originalCwd = yield OS.File.getCurrentDirectory();
try {
let defer = promise.defer();
Subprocess.call({
command: shell,
arguments: args,
environment: env,
workdir: cwd,
yield new Promise((resolve, reject) => {
Subprocess.call({
command: shell,
arguments: args,
environment: env,
workdir: cwd,
stdout: data =>
logger(data),
stderr: data =>
logger(data),
stdout: data =>
logger(data),
stderr: data =>
logger(data),
done: result => {
logger("Terminated with error code: " + result.exitCode);
if (result.exitCode == 0) {
defer.resolve();
} else {
defer.reject("pre-package command failed with error code " + result.exitCode);
done: result => {
logger("Terminated with error code: " + result.exitCode);
if (result.exitCode == 0) {
resolve();
} else {
reject("pre-package command failed with error code " + result.exitCode);
}
}
}
});
defer.promise.then(() => {
});
}).then(() => {
OS.File.setCurrentDirectory(originalCwd);
});
yield defer.promise;
} catch (e) {
throw new Error("Unable to run pre-package command '" + command + "' " +
args.join(" ") + ":\n" + (e.message || e));

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

@ -7,7 +7,6 @@ const {Cu} = require("chrome");
const Services = require("Services");
const {AppProjects} = require("devtools/client/webide/modules/app-projects");
const {AppManager} = require("devtools/client/webide/modules/app-manager");
const promise = require("promise");
const EventEmitter = require("devtools/shared/event-emitter");
const {Task} = require("devtools/shared/task");
const utils = require("devtools/client/webide/modules/utils");
@ -200,7 +199,7 @@ ProjectList.prototype = {
}, true);
}
return promise.resolve();
return Promise.resolve();
},
updateApps: function () {
@ -263,7 +262,7 @@ ProjectList.prototype = {
}, true);
}
return promise.resolve();
return Promise.resolve();
},
updateCommands: function () {
@ -299,69 +298,67 @@ ProjectList.prototype = {
* what is updated to only those sections.
*/
update: function (options) {
let deferred = promise.defer();
if (options && options.type === "apps") {
return this.updateApps();
} else if (options && options.type === "tabs") {
return this.updateTabs();
}
let doc = this._doc;
let projectsNode = doc.querySelector("#project-panel-projects");
return new Promise((resolve, reject) => {
let doc = this._doc;
let projectsNode = doc.querySelector("#project-panel-projects");
while (projectsNode.hasChildNodes()) {
projectsNode.firstChild.remove();
}
AppProjects.load().then(() => {
let projects = AppProjects.projects;
for (let i = 0; i < projects.length; i++) {
let project = projects[i];
let panelItemNode = doc.createElement(this._panelNodeEl);
panelItemNode.className = "panel-item";
projectsNode.appendChild(panelItemNode);
if (!project.validationStatus) {
// The result of the validation process (storing names, icons, …) is not stored in
// the IndexedDB database when App Manager v1 is used.
// We need to run the validation again and update the name and icon of the app.
AppManager.validateAndUpdateProject(project).then(() => {
this._renderProjectItem({
panel: panelItemNode,
name: project.name,
icon: project.icon
});
});
} else {
this._renderProjectItem({
panel: panelItemNode,
name: project.name || AppManager.DEFAULT_PROJECT_NAME,
icon: project.icon || AppManager.DEFAULT_PROJECT_ICON
});
}
panelItemNode.addEventListener("click", () => {
AppManager.selectedProject = project;
}, true);
while (projectsNode.hasChildNodes()) {
projectsNode.firstChild.remove();
}
deferred.resolve();
}, deferred.reject);
AppProjects.load().then(() => {
let projects = AppProjects.projects;
for (let i = 0; i < projects.length; i++) {
let project = projects[i];
let panelItemNode = doc.createElement(this._panelNodeEl);
panelItemNode.className = "panel-item";
projectsNode.appendChild(panelItemNode);
if (!project.validationStatus) {
// The result of the validation process (storing names, icons, …) is not stored in
// the IndexedDB database when App Manager v1 is used.
// We need to run the validation again and update the name and icon of the app.
AppManager.validateAndUpdateProject(project).then(() => {
this._renderProjectItem({
panel: panelItemNode,
name: project.name,
icon: project.icon
});
});
} else {
this._renderProjectItem({
panel: panelItemNode,
name: project.name || AppManager.DEFAULT_PROJECT_NAME,
icon: project.icon || AppManager.DEFAULT_PROJECT_ICON
});
}
panelItemNode.addEventListener("click", () => {
AppManager.selectedProject = project;
}, true);
}
// List remote apps and the main process, if they exist
this.updateApps();
resolve();
}, reject);
// Build the tab list right now, so it's fast...
this.updateTabs();
// List remote apps and the main process, if they exist
this.updateApps();
// But re-list them and rebuild, in case any tabs navigated since the last
// time they were listed.
if (AppManager.connected) {
AppManager.listTabs().then(() => {
this.updateTabs();
}).catch(console.error);
}
// Build the tab list right now, so it's fast...
this.updateTabs();
return deferred.promise;
// But re-list them and rebuild, in case any tabs navigated since the last
// time they were listed.
if (AppManager.connected) {
AppManager.listTabs().then(() => {
this.updateTabs();
}).catch(console.error);
}
});
},
destroy: function () {

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

@ -9,7 +9,6 @@ const { Cc, Ci, Cu } = require("chrome");
const Environment = require("sdk/system/environment").env;
const EventEmitter = require("devtools/shared/event-emitter");
const promise = require("promise");
const Subprocess = require("sdk/system/child_process/subprocess");
const Services = require("Services");
@ -104,21 +103,21 @@ SimulatorProcess.prototype = {
// Request a B2G instance kill.
kill() {
let deferred = promise.defer();
if (this.process) {
this.once("exit", (e, exitCode) => {
this.shuttingDown = false;
deferred.resolve(exitCode);
});
if (!this.shuttingDown) {
this.shuttingDown = true;
this.emit("kill", null);
this.process.kill();
return new Promise(resolve => {
if (this.process) {
this.once("exit", (e, exitCode) => {
this.shuttingDown = false;
resolve(exitCode);
});
if (!this.shuttingDown) {
this.shuttingDown = true;
this.emit("kill", null);
this.process.kill();
}
} else {
return resolve(undefined);
}
return deferred.promise;
} else {
return promise.resolve(undefined);
}
});
},
// Maybe log output messages.

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

@ -12,7 +12,6 @@ loader.lazyRequireGetter(this, "OldAddonSimulatorProcess", "devtools/client/webi
loader.lazyRequireGetter(this, "CustomSimulatorProcess", "devtools/client/webide/modules/simulator-process", true);
const asyncStorage = require("devtools/shared/async-storage");
const EventEmitter = require("devtools/shared/event-emitter");
const promise = require("promise");
const Services = require("Services");
const SimulatorRegExp = new RegExp(Services.prefs.getCharPref("devtools.webide.simulatorAddonRegExp"));
@ -46,18 +45,19 @@ var Simulators = {
// If the simulator had a reference to an addon, fix it.
if (options.addonID) {
let deferred = promise.defer();
AddonManager.getAddonByID(options.addonID, addon => {
simulator.addon = addon;
delete simulator.options.addonID;
deferred.resolve();
let deferred = new Promise(resolve => {
AddonManager.getAddonByID(options.addonID, addon => {
simulator.addon = addon;
delete simulator.options.addonID;
resolve();
});
});
jobs.push(deferred.promise);
jobs.push(deferred);
}
});
}
yield promise.all(jobs);
yield Promise.all(jobs);
yield Simulators._addUnusedAddons();
Simulators.emitUpdated();
return Simulators._simulators;
@ -79,7 +79,7 @@ var Simulators = {
jobs.push(Simulators.addIfUnusedAddon(addon, true));
});
yield promise.all(jobs);
yield Promise.all(jobs);
}),
/**
@ -117,19 +117,19 @@ var Simulators = {
* @return Promised addon list.
*/
findSimulatorAddons() {
let deferred = promise.defer();
AddonManager.getAllAddons(all => {
let addons = [];
for (let addon of all) {
if (Simulators.isSimulatorAddon(addon)) {
addons.push(addon);
return new Promise(resolve => {
AddonManager.getAllAddons(all => {
let addons = [];
for (let addon of all) {
if (Simulators.isSimulatorAddon(addon)) {
addons.push(addon);
}
}
}
// Sort simulator addons by name.
addons.sort(LocaleCompare);
deferred.resolve(addons);
// Sort simulator addons by name.
addons.sort(LocaleCompare);
resolve(addons);
});
});
return deferred.promise;
},
/**
@ -139,7 +139,7 @@ var Simulators = {
let simulators = this._simulators;
let matching = simulators.filter(s => s.addon && s.addon.id == addon.id);
if (matching.length > 0) {
return promise.resolve();
return Promise.resolve();
}
let options = {};
options.name = addon.name.replace(" Simulator", "");
@ -176,7 +176,7 @@ var Simulators = {
if (!silently) {
this.emitUpdated();
}
return promise.resolve(simulator);
return Promise.resolve(simulator);
},
/**
@ -332,13 +332,13 @@ Simulator.prototype = {
}
this.process.run();
return promise.resolve(this.options.port);
return Promise.resolve(this.options.port);
},
kill() {
let process = this.process;
if (!process) {
return promise.resolve();
return Promise.resolve();
}
this.process = null;
return process.kill();

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

@ -7,7 +7,6 @@ const { Cu } = require("chrome");
const { TargetFactory } = require("devtools/client/framework/target");
const EventEmitter = require("devtools/shared/event-emitter");
const { Connection } = require("devtools/shared/client/connection-manager");
const promise = require("promise");
const { Task } = require("devtools/shared/task");
const _knownTabStores = new WeakMap();
@ -95,25 +94,26 @@ TabStore.prototype = {
listTabs: function () {
if (!this._connection || !this._connection.client) {
return promise.reject(new Error("Can't listTabs, not connected."));
return Promise.reject(new Error("Can't listTabs, not connected."));
}
let deferred = promise.defer();
this._connection.client.listTabs(response => {
if (response.error) {
this._connection.disconnect();
deferred.reject(response.error);
return;
}
let tabsChanged = JSON.stringify(this.tabs) !== JSON.stringify(response.tabs);
this.response = response;
this.tabs = response.tabs;
this._checkSelectedTab();
if (tabsChanged) {
this.emit("tab-list");
}
deferred.resolve(response);
return new Promise((resolve, reject) => {
this._connection.client.listTabs(response => {
if (response.error) {
this._connection.disconnect();
reject(response.error);
return;
}
let tabsChanged = JSON.stringify(this.tabs) !== JSON.stringify(response.tabs);
this.response = response;
this.tabs = response.tabs;
this._checkSelectedTab();
if (tabsChanged) {
this.emit("tab-list");
}
resolve(response);
});
});
return deferred.promise;
},
// TODO: Tab "selection" should really take place by creating a TabProject

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

@ -64,12 +64,12 @@ function test() {
}
function connectToLocal(win, docRuntime) {
let deferred = promise.defer();
win.AppManager.connection.once(
return new Promise(resolve => {
win.AppManager.connection.once(
win.Connection.Events.CONNECTED,
() => deferred.resolve());
docRuntime.querySelectorAll(".runtime-panel-item-other")[1].click();
return deferred.promise;
resolve);
docRuntime.querySelectorAll(".runtime-panel-item-other")[1].click();
});
}
function selectTabProject(win, docProject) {

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

@ -8,7 +8,6 @@ var {utils: Cu, classes: Cc, interfaces: Ci} = Components;
const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
const { FileUtils } = require("resource://gre/modules/FileUtils.jsm");
const { gDevTools } = require("devtools/client/framework/devtools");
const promise = require("promise");
const Services = require("Services");
const { Task } = require("devtools/shared/task");
const { AppProjects } = require("devtools/client/webide/modules/app-projects");
@ -71,18 +70,14 @@ var openWebIDE = Task.async(function* (autoInstallAddons) {
function closeWebIDE(win) {
info("Closing WebIDE");
let deferred = promise.defer();
return new Promise(resolve => {
win.addEventListener("unload", function () {
info("WebIDE closed");
SimpleTest.executeSoon(resolve);
}, {once: true});
win.addEventListener("unload", function () {
info("WebIDE closed");
SimpleTest.executeSoon(() => {
deferred.resolve();
});
}, {once: true});
win.close();
return deferred.promise;
win.close();
});
}
function removeAllProjects() {
@ -98,93 +93,87 @@ function removeAllProjects() {
}
function nextTick() {
let deferred = promise.defer();
SimpleTest.executeSoon(() => {
deferred.resolve();
return new Promise(resolve => {
SimpleTest.executeSoon(resolve);
});
return deferred.promise;
}
function waitForUpdate(win, update) {
info("Wait: " + update);
let deferred = promise.defer();
win.AppManager.on("app-manager-update", function onUpdate(e, what) {
info("Got: " + what);
if (what !== update) {
return;
}
win.AppManager.off("app-manager-update", onUpdate);
deferred.resolve(win.UI._updatePromise);
return new Promise(resolve => {
win.AppManager.on("app-manager-update", function onUpdate(e, what) {
info("Got: " + what);
if (what !== update) {
return;
}
win.AppManager.off("app-manager-update", onUpdate);
resolve(win.UI._updatePromise);
});
});
return deferred.promise;
}
function waitForTime(time) {
let deferred = promise.defer();
setTimeout(() => {
deferred.resolve();
}, time);
return deferred.promise;
return new Promise(resolve => {
setTimeout(resolve, time);
});
}
function documentIsLoaded(doc) {
let deferred = promise.defer();
if (doc.readyState == "complete") {
deferred.resolve();
} else {
doc.addEventListener("readystatechange", function onChange() {
if (doc.readyState == "complete") {
doc.removeEventListener("readystatechange", onChange);
deferred.resolve();
}
});
}
return deferred.promise;
return new Promise(resolve => {
if (doc.readyState == "complete") {
resolve();
} else {
doc.addEventListener("readystatechange", function onChange() {
if (doc.readyState == "complete") {
doc.removeEventListener("readystatechange", onChange);
resolve();
}
});
}
});
}
function lazyIframeIsLoaded(iframe) {
let deferred = promise.defer();
iframe.addEventListener("load", function () {
deferred.resolve(nextTick());
}, {capture: true, once: true});
return deferred.promise;
return new Promise(resolve => {
iframe.addEventListener("load", function () {
resolve(nextTick());
}, {capture: true, once: true});
});
}
function addTab(aUrl, aWindow) {
info("Adding tab: " + aUrl);
let deferred = promise.defer();
let targetWindow = aWindow || window;
let targetBrowser = targetWindow.gBrowser;
return new Promise(resolve => {
let targetWindow = aWindow || window;
let targetBrowser = targetWindow.gBrowser;
targetWindow.focus();
let tab = targetBrowser.selectedTab = targetBrowser.addTab(aUrl);
let linkedBrowser = tab.linkedBrowser;
targetWindow.focus();
let tab = targetBrowser.selectedTab = targetBrowser.addTab(aUrl);
let linkedBrowser = tab.linkedBrowser;
BrowserTestUtils.browserLoaded(linkedBrowser).then(function () {
info("Tab added and finished loading: " + aUrl);
deferred.resolve(tab);
BrowserTestUtils.browserLoaded(linkedBrowser).then(function () {
info("Tab added and finished loading: " + aUrl);
resolve(tab);
});
});
return deferred.promise;
}
function removeTab(aTab, aWindow) {
info("Removing tab.");
let deferred = promise.defer();
let targetWindow = aWindow || window;
let targetBrowser = targetWindow.gBrowser;
let tabContainer = targetBrowser.tabContainer;
return new Promise(resolve => {
let targetWindow = aWindow || window;
let targetBrowser = targetWindow.gBrowser;
let tabContainer = targetBrowser.tabContainer;
tabContainer.addEventListener("TabClose", function (aEvent) {
info("Tab removed and finished closing.");
deferred.resolve();
}, {once: true});
tabContainer.addEventListener("TabClose", function (aEvent) {
info("Tab removed and finished closing.");
resolve();
}, {once: true});
targetBrowser.removeTab(aTab);
return deferred.promise;
targetBrowser.removeTab(aTab);
});
}
function getRuntimeDocument(win) {

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

@ -22,30 +22,26 @@
const {Devices} = Cu.import("resource://devtools/shared/apps/Devices.jsm");
const {Simulators} = require("devtools/client/webide/modules/simulators");
let adbAddonsInstalled = promise.defer();
Devices.on("addon-status-updated", function onUpdate1() {
Devices.off("addon-status-updated", onUpdate1);
adbAddonsInstalled.resolve();
});
let adbAddonsInstalled;
function getVersion(name) {
return name.match(/(\d+\.\d+)/)[0];
}
function onSimulatorInstalled(name) {
let deferred = promise.defer();
Simulators.on("updated", function onUpdate() {
Simulators.findSimulatorAddons().then(addons => {
for (let addon of addons) {
if (name == addon.name.replace(" Simulator", "")) {
Simulators.off("updated", onUpdate);
nextTick().then(deferred.resolve);
return;
return new Promise(resolve => {
Simulators.on("updated", function onUpdate() {
Simulators.findSimulatorAddons().then(addons => {
for (let addon of addons) {
if (name == addon.name.replace(" Simulator", "")) {
Simulators.off("updated", onUpdate);
nextTick().then(resolve);
return;
}
}
}
});
});
});
return deferred.promise;
}
function installSimulatorFromUI(doc, name) {
@ -55,39 +51,39 @@
}
function uninstallSimulatorFromUI(doc, name) {
let deferred = promise.defer();
Simulators.on("updated", function onUpdate() {
nextTick().then(() => {
let li = doc.querySelector('[status="uninstalled"][addon="simulator-' + getVersion(name) + '"]');
if (li) {
Simulators.off("updated", onUpdate);
deferred.resolve();
} else {
deferred.reject("Can't find item");
}
return new Promise((resolve, reject) => {
Simulators.on("updated", function onUpdate() {
nextTick().then(() => {
let li = doc.querySelector('[status="uninstalled"][addon="simulator-' + getVersion(name) + '"]');
if (li) {
Simulators.off("updated", onUpdate);
resolve();
} else {
reject("Can't find item");
}
});
});
let li = doc.querySelector('[status="installed"][addon="simulator-' + getVersion(name) + '"]');
li.querySelector(".uninstall-button").click();
});
let li = doc.querySelector('[status="installed"][addon="simulator-' + getVersion(name) + '"]');
li.querySelector(".uninstall-button").click();
return deferred.promise;
}
function uninstallADBFromUI(doc) {
let deferred = promise.defer();
Devices.on("addon-status-updated", function onUpdate() {
nextTick().then(() => {
let li = doc.querySelector('[status="uninstalled"][addon="adb"]');
if (li) {
Devices.off("addon-status-updated", onUpdate);
deferred.resolve();
} else {
deferred.reject("Can't find item");
}
})
return new Promise((resolve, reject) => {
Devices.on("addon-status-updated", function onUpdate() {
nextTick().then(() => {
let li = doc.querySelector('[status="uninstalled"][addon="adb"]');
if (li) {
Devices.off("addon-status-updated", onUpdate);
resolve();
} else {
reject("Can't find item");
}
})
});
let li = doc.querySelector('[status="installed"][addon="adb"]');
li.querySelector(".uninstall-button").click();
});
let li = doc.querySelector('[status="installed"][addon="adb"]');
li.querySelector(".uninstall-button").click();
return deferred.promise;
}
Task.spawn(function*() {
@ -97,7 +93,12 @@
let win = yield openWebIDE(true);
let docRuntime = getRuntimeDocument(win);
yield adbAddonsInstalled.promise;
adbAddonsInstalled = new Promise(resolve => {
Devices.on("addon-status-updated", function onUpdate1() {
Devices.off("addon-status-updated", onUpdate1);
resolve();
});
});
ok(Devices.helperAddonInstalled, "Helper has been auto-installed");

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

@ -33,7 +33,7 @@
is(connection, win.AppManager.connection, "connection is valid");
connection.host = null; // force connectPipe
connection.connect();
return promise.resolve();
return Promise.resolve();
},
get id() {

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

@ -35,12 +35,13 @@
let nbox = win.document.querySelector("#notificationbox");
let notification = nbox.getNotificationWithValue("webide:errornotification");
ok(!notification, "No notification yet");
let deferred = promise.defer();
nextTick().then(() => {
deferred.reject("BOOM!");
let deferred = new Promise((resolve, reject) => {
nextTick().then(() => {
reject("BOOM!");
});
});
try {
yield win.UI.busyUntil(deferred.promise, "xx");
yield win.UI.busyUntil(deferred, "xx");
} catch(e) {/* This *will* fail */}
notification = nbox.getNotificationWithValue("webide:errornotification");
ok(notification, "Error has been reported");

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

@ -16,12 +16,12 @@
<script type="application/javascript">
function connectToLocal(win, docRuntime) {
let deferred = promise.defer();
win.AppManager.connection.once(
win.Connection.Events.CONNECTED,
() => deferred.resolve());
docRuntime.querySelectorAll(".runtime-panel-item-other")[1].click();
return deferred.promise;
return new Promise(resolve => {
win.AppManager.connection.once(
win.Connection.Events.CONNECTED,
resolve);
docRuntime.querySelectorAll(".runtime-panel-item-other")[1].click();
});
}
window.onload = function() {
@ -33,7 +33,7 @@
let docRuntime = getRuntimeDocument(win);
win.AppManager.update("runtime-list");
yield connectToLocal(win, docRuntime);
connectToLocal(win, docRuntime);
// Select main process
yield waitForUpdate(win, "runtime-targets");

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

@ -62,7 +62,7 @@
is(connection, win.AppManager.connection, "connection is valid");
connection.host = null; // force connectPipe
connection.connect();
return promise.resolve();
return Promise.resolve();
},
get name() {
@ -72,8 +72,7 @@
win.AppManager.runtimeList.usb.push({
connect: function(connection) {
let deferred = promise.defer();
return deferred.promise;
return new Promise(() => {});
},
get name() {
@ -83,8 +82,7 @@
win.AppManager.runtimeList.usb.push({
connect: function(connection) {
let deferred = promise.defer();
return deferred.promise;
return new Promise(() => {});
},
prolongedConnection: true,
@ -164,36 +162,35 @@
Services.prefs.setIntPref("devtools.webide.busyTimeout", 100);
// Wait for error message since connection never completes
let errorDeferred = promise.defer();
win.UI.reportError = errorName => {
if (errorName === "error_operationTimeout") {
errorDeferred.resolve();
}
};
// Click the infinite runtime
items[1].click();
ok(win.document.querySelector("window").className, "busy", "UI is busy");
yield errorDeferred.promise;
// Check for unexpected error message since this is prolonged
let noErrorDeferred = promise.defer();
win.UI.reportError = errorName => {
if (errorName === "error_operationTimeout") {
noErrorDeferred.reject();
}
};
// Wait for error message since connection never completes
let errorDeferred = new Promise(resolve => {
win.UI.reportError = errorName => {
if (errorName === "error_operationTimeout") {
resolve();
}
};
});
// Click the prolonged runtime
items[2].click();
ok(win.document.querySelector("window").className, "busy", "UI is busy");
setTimeout(() => {
noErrorDeferred.resolve();
}, 1000);
// Check for unexpected error message since this is prolonged
let noErrorDeferred = new Promise((resolve, reject) => {
win.UI.reportError = errorName => {
if (errorName === "error_operationTimeout") {
reject();
}
};
yield noErrorDeferred.promise;
setTimeout(() => {
resolve();
}, 1000);
});
SimpleTest.finish();
});

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

@ -29,31 +29,31 @@
function addonStatus(addon, status) {
if (addon.status == status) {
return promise.resolve();
return Promise.resolve();
}
let deferred = promise.defer();
addon.on("update", function onUpdate() {
if (addon.status == status) {
addon.off("update", onUpdate);
nextTick().then(() => deferred.resolve());
}
return new Promise(resolve => {
addon.on("update", function onUpdate() {
if (addon.status == status) {
addon.off("update", onUpdate);
nextTick().then(() => resolve());
}
});
});
return deferred.promise;
}
function waitForUpdate(length) {
info(`Wait for update with length ${length}`);
let deferred = promise.defer();
let handler = (_, data) => {
if (data.length != length) {
return;
}
info(`Got update with length ${length}`);
Simulators.off("updated", handler);
deferred.resolve();
};
Simulators.on("updated", handler);
return deferred.promise;
return new Promise(resolve => {
let handler = (_, data) => {
if (data.length != length) {
return;
}
info(`Got update with length ${length}`);
Simulators.off("updated", handler);
resolve();
};
Simulators.on("updated", handler);
});
}
Task.spawn(function* () {
@ -70,9 +70,9 @@
// Hack SimulatorProcesses to spy on simulation parameters.
let runPromise;
function fakeRun() {
runPromise.resolve({
let resolver;
function fakeRun() {
resolver({
path: this.b2gBinary.path,
args: this.args
});
@ -85,9 +85,10 @@
CustomSimulatorProcess.prototype.run = fakeRun;
function runSimulator(i) {
runPromise = promise.defer();
findAll(".runtime-panel-item-simulator")[i].click();
return runPromise.promise;
return new Promise(resolve => {
resolver = resolve;
findAll(".runtime-panel-item-simulator")[i].click();
});
}
// Install fake "Firefox OS 1.0" simulator addon.

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

@ -42,10 +42,11 @@
win.AppManager.update("runtime-list");
let deferred = promise.defer();
win.AppManager.connection.once(
win.Connection.Events.CONNECTED,
() => deferred.resolve());
let deferred = new Promise(resolve => {
win.AppManager.connection.once(
win.Connection.Events.CONNECTED,
resolve);
});
docRuntime.querySelectorAll(".runtime-panel-item-other")[1].click();

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

@ -4449,12 +4449,13 @@ nsContentUtils::GetSubdocumentWithOuterWindowId(nsIDocument *aDocument,
return nullptr;
}
nsCOMPtr<nsPIDOMWindowOuter> window = nsGlobalWindow::GetOuterWindowWithId(aOuterWindowId)->AsOuter();
RefPtr<nsGlobalWindow> window = nsGlobalWindow::GetOuterWindowWithId(aOuterWindowId);
if (!window) {
return nullptr;
}
nsCOMPtr<nsIDocument> foundDoc = window->GetDoc();
nsCOMPtr<nsPIDOMWindowOuter> outerWindow = window->AsOuter();
nsCOMPtr<nsIDocument> foundDoc = outerWindow->GetDoc();
if (nsContentUtils::ContentIsCrossDocDescendantOf(foundDoc, aDocument)) {
// Note that ContentIsCrossDocDescendantOf will return true if
// foundDoc == aDocument.

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

@ -7631,6 +7631,7 @@ protected:
virtual nsresult
DispatchToWorkThread() = 0;
// Should only be called by Run().
virtual void
SendResults() = 0;
@ -21579,6 +21580,11 @@ FactoryOp::NoteDatabaseBlocked(Database* aDatabase)
NS_IMPL_ISUPPORTS_INHERITED0(FactoryOp, DatabaseOperationBase)
// Run() assumes that the caller holds a strong reference to the object that
// can't be cleared while Run() is being executed.
// So if you call Run() directly (as opposed to dispatching to an event queue)
// you need to make sure there's such a reference.
// See bug 1356824 for more details.
NS_IMETHODIMP
FactoryOp::Run()
{
@ -21663,8 +21669,11 @@ FactoryOp::DirectoryLockAcquired(DirectoryLock* aLock)
mResultCode = rv;
}
// The caller holds a strong reference to us, no need for a self reference
// before calling Run().
mState = State::SendingResults;
SendResults();
MOZ_ALWAYS_SUCCEEDS(Run());
return;
}
@ -21682,8 +21691,11 @@ FactoryOp::DirectoryLockFailed()
mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
// The caller holds a strong reference to us, no need for a self reference
// before calling Run().
mState = State::SendingResults;
SendResults();
MOZ_ALWAYS_SUCCEEDS(Run());
}
void
@ -22475,12 +22487,18 @@ OpenDatabaseOp::NoteDatabaseClosed(Database* aDatabase)
rv = NS_OK;
}
// We are being called with an assuption that mWaitingFactoryOp holds a strong
// reference to us.
RefPtr<OpenDatabaseOp> kungFuDeathGrip;
if (mMaybeBlockedDatabases.RemoveElement(aDatabase) &&
mMaybeBlockedDatabases.IsEmpty()) {
if (actorDestroyed) {
DatabaseActorInfo* info;
MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable->Get(mDatabaseId, &info));
MOZ_ASSERT(info->mWaitingFactoryOp == this);
kungFuDeathGrip =
static_cast<OpenDatabaseOp*>(info->mWaitingFactoryOp.get());
info->mWaitingFactoryOp = nullptr;
} else {
WaitForTransactions();
@ -22492,6 +22510,9 @@ OpenDatabaseOp::NoteDatabaseClosed(Database* aDatabase)
mResultCode = rv;
}
// A strong reference is held in kungFuDeathGrip, so it's safe to call Run()
// directly.
mState = State::SendingResults;
MOZ_ALWAYS_SUCCEEDS(Run());
}
@ -22611,17 +22632,15 @@ OpenDatabaseOp::SendResults()
mMaybeBlockedDatabases.Clear();
// Only needed if we're being called from within NoteDatabaseDone() since this
// OpenDatabaseOp is only held alive by the gLiveDatabaseHashtable.
RefPtr<OpenDatabaseOp> kungFuDeathGrip;
DatabaseActorInfo* info;
if (gLiveDatabaseHashtable &&
gLiveDatabaseHashtable->Get(mDatabaseId, &info) &&
info->mWaitingFactoryOp) {
MOZ_ASSERT(info->mWaitingFactoryOp == this);
kungFuDeathGrip =
static_cast<OpenDatabaseOp*>(info->mWaitingFactoryOp.get());
// SendResults() should only be called by Run() and Run() should only be
// called if there's a strong reference to the object that can't be cleared
// here, so it's safe to clear mWaitingFactoryOp without adding additional
// strong reference.
info->mWaitingFactoryOp = nullptr;
}
@ -23295,12 +23314,18 @@ DeleteDatabaseOp::NoteDatabaseClosed(Database* aDatabase)
rv = NS_OK;
}
// We are being called with an assuption that mWaitingFactoryOp holds a strong
// reference to us.
RefPtr<OpenDatabaseOp> kungFuDeathGrip;
if (mMaybeBlockedDatabases.RemoveElement(aDatabase) &&
mMaybeBlockedDatabases.IsEmpty()) {
if (actorDestroyed) {
DatabaseActorInfo* info;
MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable->Get(mDatabaseId, &info));
MOZ_ASSERT(info->mWaitingFactoryOp == this);
kungFuDeathGrip =
static_cast<OpenDatabaseOp*>(info->mWaitingFactoryOp.get());
info->mWaitingFactoryOp = nullptr;
} else {
WaitForTransactions();
@ -23312,6 +23337,9 @@ DeleteDatabaseOp::NoteDatabaseClosed(Database* aDatabase)
mResultCode = rv;
}
// A strong reference is held in kungFuDeathGrip, so it's safe to call Run()
// directly.
mState = State::SendingResults;
MOZ_ALWAYS_SUCCEEDS(Run());
}
@ -23637,6 +23665,8 @@ VersionChangeOp::RunOnOwningThread()
}
}
// We hold a strong ref to the deleteOp, so it's safe to call Run() directly.
deleteOp->mState = State::SendingResults;
MOZ_ALWAYS_SUCCEEDS(deleteOp->Run());
@ -23851,6 +23881,11 @@ TransactionDatabaseOperationBase::NoteContinueReceived()
mInternalState = InternalState::SendingResults;
// This TransactionDatabaseOperationBase can only be held alive by the IPDL.
// Run() can end up with clearing that last reference. So we need to add
// a self reference here.
RefPtr<TransactionDatabaseOperationBase> kungFuDeathGrip = this;
Unused << this->Run();
}
@ -23896,11 +23931,6 @@ TransactionDatabaseOperationBase::SendPreprocessInfoOrResults(
mInternalState == InternalState::SendingResults);
MOZ_ASSERT(mTransaction);
// Only needed if we're being called from within NoteContinueReceived() since
// this TransactionDatabaseOperationBase is only held alive by the IPDL.
// SendSuccessResult/SendFailureResult releases that last reference.
RefPtr<TransactionDatabaseOperationBase> kungFuDeathGrip;
if (NS_WARN_IF(IsActorDestroyed())) {
// Don't send any notifications if the actor was destroyed already.
if (NS_SUCCEEDED(mResultCode)) {
@ -23908,10 +23938,6 @@ TransactionDatabaseOperationBase::SendPreprocessInfoOrResults(
mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
} else {
if (!aSendPreprocessInfo) {
kungFuDeathGrip = this;
}
if (mTransaction->IsInvalidated() || mTransaction->IsAborted()) {
// Aborted transactions always see their requests fail with ABORT_ERR,
// even if the request succeeded or failed with another error.

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

@ -219,6 +219,19 @@ AOMDecoder::IsAV1(const nsACString& aMimeType)
|| aMimeType.EqualsLiteral("video/av1");
}
/* static */
bool
AOMDecoder::IsSupportedCodec(const nsAString& aCodecType)
{
// While AV1 is under development, we describe support
// for a specific aom commit hash so sites can check
// compatibility.
auto version = NS_ConvertASCIItoUTF16("av1.experimental.");
version.AppendLiteral("4d668d7feb1f8abd809d1bca0418570a7f142a36");
return aCodecType.EqualsLiteral("av1") ||
aCodecType.Equals(version);
}
/* static */
bool
AOMDecoder::IsKeyframe(Span<const uint8_t> aBuffer) {

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

@ -33,6 +33,9 @@ public:
// by our demuxers to identify AV1 streams.
static bool IsAV1(const nsACString& aMimeType);
// Return true if aCodecType is a supported codec description.
static bool IsSupportedCodec(const nsAString& aCodecType);
// Return true if a sample is a keyframe.
static bool IsKeyframe(Span<const uint8_t> aBuffer);

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

@ -5,6 +5,9 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/Preferences.h"
#ifdef MOZ_AV1
#include "AOMDecoder.h"
#endif
#include "MediaContainerType.h"
#include "MediaDecoderStateMachine.h"
#include "WebMDemuxer.h"
@ -51,6 +54,11 @@ WebMDecoder::IsSupportedType(const MediaContainerType& aContainerType)
codec.EqualsLiteral("vp9") || codec.EqualsLiteral("vp9.0"))) {
continue;
}
#ifdef MOZ_AV1
if (isVideo && AOMDecoder::IsSupportedCodec(codec)) {
continue;
}
#endif
// Some unsupported codec.
return false;
}

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

@ -30,6 +30,7 @@ const TOPICS = [
"weave:service:sync:start",
"weave:service:sync:finish",
"weave:service:sync:error",
"fxaccounts:onverified",
"fxaccounts:onlogin", // Defined in FxAccountsCommon, pulling it is expensive.
"fxaccounts:onlogout",
"fxaccounts:profilechange",

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

@ -169,8 +169,9 @@ add_task(async function test_observer_refreshState() {
let refreshState = sinon.spy(UIStateInternal, "refreshState");
let shouldRefresh = ["weave:service:login:change", "weave:service:login:error",
"weave:service:ready", "fxaccounts:onlogin",
"fxaccounts:onlogout", "fxaccounts:profilechange"];
"weave:service:ready", "fxaccounts:onverified",
"fxaccounts:onlogin", "fxaccounts:onlogout",
"fxaccounts:profilechange"];
for (let topic of shouldRefresh) {
let uiUpdateObserved = observeUIUpdate();

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

@ -776,6 +776,17 @@ def parseKeyValue(strings, separator='=', context='key, value: '):
return [string.split(separator, 1) for string in strings]
def create_zip(path):
"""
Takes a `path` on disk and creates a zipfile with its contents. Returns a
path to the location of the temporary zip file.
"""
with tempfile.NamedTemporaryFile() as f:
# `shutil.make_archive` writes to "{f.name}.zip", so we're really just
# using `NamedTemporaryFile` as a way to get a random path.
return shutil.make_archive(f.name, "zip", path)
class MochitestDesktop(object):
"""
Mochitest class for desktop firefox.
@ -2106,12 +2117,14 @@ toolbar#nav-bar {
self.marionette = Marionette(**marionette_args)
self.marionette.start_session(timeout=port_timeout)
# install specialpowers and mochikit as temporary addons
# install specialpowers and mochikit addons
addons = Addons(self.marionette)
if mozinfo.info.get('toolkit') != 'gonk':
addons.install(os.path.join(here, 'extensions', 'specialpowers'), temp=True)
addons.install(self.mochijar, temp=True)
addons.install(create_zip(
os.path.join(here, 'extensions', 'specialpowers')
))
addons.install(create_zip(self.mochijar))
self.execute_start_script()

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

@ -8,7 +8,9 @@ function test() {
requestLongerTimeout(2);
waitForExplicitFinish();
Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
SpecialPowers.pushPrefEnv({"set": [
["extensions.checkUpdateSecurity", false],
]});
run_next_test();
}
@ -43,8 +45,6 @@ add_test(function() {
// Finally, cleanup things
add_test(function() {
Services.prefs.setBoolPref("xpinstall.signatures.required", true);
AddonManager.getAddonByID("update1@tests.mozilla.org", function(aAddon) {
aAddon.uninstall();