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

This commit is contained in:
Sebastian Hengst 2017-08-31 14:39:13 +02:00
Родитель beb48926f3 030c11d7dd
Коммит 444f7c3600
388 изменённых файлов: 6697 добавлений и 6966 удалений

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

@ -2,3 +2,5 @@
# See http://pep8.readthedocs.io/en/latest/intro.html#configuration # See http://pep8.readthedocs.io/en/latest/intro.html#configuration
ignore = E121, E123, E126, E129, E133, E226, E241, E242, E704, W503, E402 ignore = E121, E123, E126, E129, E133, E226, E241, E242, E704, W503, E402
max-line-length = 99 max-line-length = 99
exclude =
testing/mochitest/pywebsocket,

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

@ -22,4 +22,4 @@
# changes to stick? As of bug 928195, this shouldn't be necessary! Please # changes to stick? As of bug 928195, this shouldn't be necessary! Please
# don't change CLOBBER for WebIDL changes any more. # don't change CLOBBER for WebIDL changes any more.
Bug 1340910 - clobber required for Brotli 0.6.0 update due to changes in exported headers. Bug 1395286 - Fix packaging error for Android builds, presumably due to bug 1255404 and/or bug 863246.

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

@ -339,6 +339,7 @@ var BrowserPageActions = {
_makeUrlbarButtonNode(action) { _makeUrlbarButtonNode(action) {
let buttonNode = document.createElement("image"); let buttonNode = document.createElement("image");
buttonNode.classList.add("urlbar-icon", "urlbar-page-action"); buttonNode.classList.add("urlbar-icon", "urlbar-page-action");
buttonNode.setAttribute("role", "button");
if (action.tooltip) { if (action.tooltip) {
buttonNode.setAttribute("tooltiptext", action.tooltip); buttonNode.setAttribute("tooltiptext", action.tooltip);
} }
@ -721,7 +722,7 @@ var BrowserPageActionFeedback = {
// The timeout value used here allows the panel to stay open for // The timeout value used here allows the panel to stay open for
// 1 second after the text transition (duration=120ms) has finished. // 1 second after the text transition (duration=120ms) has finished.
setTimeout(() => { setTimeout(() => {
this.panelNode.hidePopup(); this.panelNode.hidePopup(true);
}, Services.prefs.getIntPref("browser.pageActions.feedbackTimeoutMS", 1120)); }, Services.prefs.getIntPref("browser.pageActions.feedbackTimeoutMS", 1120));
}, },
}; };

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

@ -881,11 +881,13 @@
</hbox> </hbox>
<image id="page-report-button" <image id="page-report-button"
class="urlbar-icon urlbar-page-action" class="urlbar-icon urlbar-page-action"
role="button"
hidden="true" hidden="true"
tooltiptext="&pageReportIcon.tooltip;" tooltiptext="&pageReportIcon.tooltip;"
onmousedown="gPopupBlockerObserver.onReportButtonMousedown(event);"/> onmousedown="gPopupBlockerObserver.onReportButtonMousedown(event);"/>
<image id="reader-mode-button" <image id="reader-mode-button"
class="urlbar-icon urlbar-page-action" class="urlbar-icon urlbar-page-action"
role="button"
hidden="true" hidden="true"
onclick="ReaderParent.buttonClick(event);"/> onclick="ReaderParent.buttonClick(event);"/>
<toolbarbutton id="urlbar-zoom-button" <toolbarbutton id="urlbar-zoom-button"
@ -895,19 +897,23 @@
<box id="pageActionSeparator" class="urlbar-page-action"/> <box id="pageActionSeparator" class="urlbar-page-action"/>
<image id="pageActionButton" <image id="pageActionButton"
class="urlbar-icon urlbar-page-action" class="urlbar-icon urlbar-page-action"
role="button"
tooltiptext="&pageActionButton.tooltip;" tooltiptext="&pageActionButton.tooltip;"
onclick="BrowserPageActions.mainButtonClicked(event);"/> onclick="BrowserPageActions.mainButtonClicked(event);"/>
<hbox id="star-button-box" <hbox id="star-button-box"
hidden="true" hidden="true"
class="urlbar-icon-wrapper urlbar-page-action" class="urlbar-icon-wrapper urlbar-page-action"
role="button"
context="pageActionPanelContextMenu" context="pageActionPanelContextMenu"
oncontextmenu="BrowserPageActions.onContextMenu(event);" oncontextmenu="BrowserPageActions.onContextMenu(event);"
onclick="BrowserPageActions.bookmark.onUrlbarNodeClicked(event);"> onclick="BrowserPageActions.bookmark.onUrlbarNodeClicked(event);">
<image id="star-button" <image id="star-button"
class="urlbar-icon" class="urlbar-icon"
role="button"
observes="bookmarkThisPageBroadcaster"/> observes="bookmarkThisPageBroadcaster"/>
<hbox id="star-button-animatable-box"> <hbox id="star-button-animatable-box">
<image id="star-button-animatable-image" <image id="star-button-animatable-image"
role="button"
observes="bookmarkThisPageBroadcaster"/> observes="bookmarkThisPageBroadcaster"/>
</hbox> </hbox>
</hbox> </hbox>

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

@ -1,4 +1,9 @@
add_task(async function() { add_task(async function() {
// When about:home is set to Activity Stream, there are no 'narrow' attributes
// therefore for this test, we want to ensure we're using the original about:home
await SpecialPowers.pushPrefEnv({set: [
["browser.newtabpage.activity-stream.aboutHome.enabled", false]
]});
let newWindow = await BrowserTestUtils.openNewBrowserWindow(); let newWindow = await BrowserTestUtils.openNewBrowserWindow();
let resizedPromise = BrowserTestUtils.waitForEvent(newWindow, "resize"); let resizedPromise = BrowserTestUtils.waitForEvent(newWindow, "resize");

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

@ -445,7 +445,7 @@ this.tabs = class extends ExtensionAPI {
} }
if (updateProperties.muted !== null) { if (updateProperties.muted !== null) {
if (nativeTab.muted != updateProperties.muted) { if (nativeTab.muted != updateProperties.muted) {
nativeTab.toggleMuteAudio(extension.uuid); nativeTab.toggleMuteAudio(extension.id);
} }
} }
if (updateProperties.pinned !== null) { if (updateProperties.pinned !== null) {

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

@ -115,8 +115,7 @@ add_task(async function() {
for (let obj of [nonMuted.changeInfo, nonMuted.tab, muted.changeInfo, muted.tab]) { for (let obj of [nonMuted.changeInfo, nonMuted.tab, muted.changeInfo, muted.tab]) {
browser.test.assertEq("extension", obj.mutedInfo.reason, "Mute state changed by extension"); browser.test.assertEq("extension", obj.mutedInfo.reason, "Mute state changed by extension");
// FIXME: browser.runtime.id is currently broken. browser.test.assertEq(browser.runtime.id,
browser.test.assertEq(browser.i18n.getMessage("@@extension_id"),
obj.mutedInfo.extensionId, obj.mutedInfo.extensionId,
"Mute state changed by extension"); "Mute state changed by extension");
} }
@ -128,8 +127,7 @@ add_task(async function() {
browser.test.assertEq("extension", tab.mutedInfo.reason, "Mute state changed by extension"); browser.test.assertEq("extension", tab.mutedInfo.reason, "Mute state changed by extension");
// FIXME: browser.runtime.id is currently broken. browser.test.assertEq(browser.runtime.id,
browser.test.assertEq(browser.i18n.getMessage("@@extension_id"),
tab.mutedInfo.extensionId, tab.mutedInfo.extensionId,
"Mute state changed by extension"); "Mute state changed by extension");

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

@ -1602,10 +1602,27 @@ var PlacesControllerDragHelper = {
// Adjust insertion index to prevent reversal of dragged items. When you // Adjust insertion index to prevent reversal of dragged items. When you
// drag multiple elts upward: need to increment index or each successive // drag multiple elts upward: need to increment index or each successive
// elt will be inserted at the same index, each above the previous. // elt will be inserted at the same index, each above the previous.
let dragginUp = insertionPoint.itemId == unwrapped.parent && if (index != -1 && unwrapped.itemGuid) {
index < (await PlacesUtils.bookmarks.fetch(unwrapped.itemGuid)).index; // Note: we use the parent from the existing bookmark as the sidebar
if (index != -1 && dragginUp) // gives us an unwrapped.parent that is actually a query and not the real
index += movedCount++; // parent.
let existingBookmark = await PlacesUtils.bookmarks.fetch(unwrapped.itemGuid);
let dragginUp = parentGuid == existingBookmark.parentGuid &&
index < existingBookmark.index;
if (dragginUp) {
index += movedCount++;
} else if (PlacesUIUtils.useAsyncTransactions) {
if (index == existingBookmark.index) {
// We're moving to the same index, so there's nothing for us to do.
continue;
}
// If we're dragging down, we need to go one lower to insert at
// the real point as moving the element changes the index of
// everything below by 1.
index--;
}
}
// If dragging over a tag container we should tag the item. // If dragging over a tag container we should tag the item.
if (insertionPoint.isTag) { if (insertionPoint.isTag) {
@ -1638,7 +1655,11 @@ var PlacesControllerDragHelper = {
} }
} }
} }
// Check if we actually have something to add, if we don't it probably wasn't
// valid, or it was moving to the same location, so just ignore it.
if (!transactions.length) {
return;
}
if (PlacesUIUtils.useAsyncTransactions) { if (PlacesUIUtils.useAsyncTransactions) {
await PlacesTransactions.batch(transactions); await PlacesTransactions.batch(transactions);
} else { } else {

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

@ -28,6 +28,7 @@ support-files =
[browser_click_bookmarks_on_toolbar.js] [browser_click_bookmarks_on_toolbar.js]
[browser_cutting_bookmarks.js] [browser_cutting_bookmarks.js]
subsuite = clipboard subsuite = clipboard
[browser_controller_onDrop.js]
[browser_copy_folder_tree.js] [browser_copy_folder_tree.js]
[browser_copy_query_without_tree.js] [browser_copy_query_without_tree.js]
subsuite = clipboard subsuite = clipboard

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

@ -0,0 +1,132 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
/* global sinon */
Services.scriptloader.loadSubScript("resource://testing-common/sinon-2.3.2.js");
const sandbox = sinon.sandbox.create();
var bookmarks;
var bookmarkIds;
add_task(async function setup() {
registerCleanupFunction(async function() {
sandbox.restore();
delete window.sinon;
await PlacesUtils.bookmarks.eraseEverything();
await PlacesTestUtils.clearHistory();
});
sandbox.stub(PlacesUIUtils, "getTransactionForData");
sandbox.stub(PlacesTransactions, "batch");
bookmarks = await PlacesUtils.bookmarks.insertTree({
guid: PlacesUtils.bookmarks.unfiledGuid,
children: [{
title: "bm1",
url: "http://example1.com"
}, {
title: "bm2",
url: "http://example2.com"
}, {
title: "bm3",
url: "http://example3.com"
}]
});
bookmarkIds = await PlacesUtils.promiseManyItemIds([
bookmarks[0].guid,
bookmarks[1].guid,
bookmarks[2].guid,
]);
});
async function run_drag_test(startBookmarkIndex, insertionIndex,
realInsertionIndex, expectTransactionCreated = true) {
// Reset the stubs so that previous test runs don't count against us.
PlacesUIUtils.getTransactionForData.reset();
PlacesTransactions.batch.reset();
let dragBookmark = bookmarks[startBookmarkIndex];
await withSidebarTree("bookmarks", async (tree) => {
tree.selectItems([PlacesUtils.unfiledBookmarksFolderId]);
PlacesUtils.asContainer(tree.selectedNode).containerOpen = true;
// Simulating a drag-drop with a tree view turns out to be really difficult
// as you can't get a node for the source/target. Hence, we fake the
// insertion point and drag data and call the function direct.
let ip = new InsertionPoint({
parentId: await PlacesUtils.promiseItemId(PlacesUtils.bookmarks.unfiledGuid),
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
index: insertionIndex,
orientation: Ci.nsITreeView.DROP_ON
});
let bookmarkWithId = JSON.stringify(Object.assign({
id: bookmarkIds.get(dragBookmark.guid),
itemGuid: dragBookmark.guid,
parent: PlacesUtils.unfiledBookmarksFolderId,
}, dragBookmark));
let dt = {
dropEffect: "move",
mozCursor: "auto",
mozItemCount: 1,
types: [ PlacesUtils.TYPE_X_MOZ_PLACE ],
mozTypesAt(i) {
return this.types;
},
mozGetDataAt(i) {
return bookmarkWithId;
}
};
await PlacesControllerDragHelper.onDrop(ip, dt);
if (!expectTransactionCreated) {
Assert.ok(PlacesUIUtils.getTransactionForData.notCalled,
"Should not have created transaction data");
return;
}
Assert.ok(PlacesUIUtils.getTransactionForData.calledOnce,
"Should have called getTransactionForData at least once.");
let args = PlacesUIUtils.getTransactionForData.args[0];
Assert.deepEqual(args[0], JSON.parse(bookmarkWithId),
"Should have called getTransactionForData with the correct unwrapped bookmark");
Assert.equal(args[1], PlacesUtils.TYPE_X_MOZ_PLACE,
"Should have called getTransactionForData with the correct flavor");
Assert.equal(args[2], PlacesUtils.bookmarks.unfiledGuid,
"Should have called getTransactionForData with the correct parent guid");
Assert.equal(args[3], realInsertionIndex,
"Should have called getTransactionForData with the correct index");
Assert.equal(args[4], false,
"Should have called getTransactionForData with a move");
});
}
add_task(async function test_simple_move_down() {
// When we move items down the list, we'll get a drag index that is one higher
// than where we actually want to insert to - as the item is being moved up,
// everything shifts down one. Hence the index to pass to the transaction should
// be one less than the supplied index.
await run_drag_test(0, 2, 1);
});
add_task(async function test_simple_move_up() {
// When we move items up the list, we want the matching index to be passed to
// the transaction as there's no changes below the item in the list.
await run_drag_test(2, 0, 0);
});
add_task(async function test_simple_move_to_same() {
// If we move to the same index, then we don't expect any transactions to be
// created.
await run_drag_test(1, 1, 1, false);
});

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

@ -230,7 +230,8 @@ function search(aQuery, aAttribute, aSubquery, aSubAttribute) {
let attributeValue = element.getAttribute(aAttribute); let attributeValue = element.getAttribute(aAttribute);
if (attributeValue == aQuery) { if (attributeValue == aQuery) {
if (!element.classList.contains("header") && if (!element.classList.contains("header") &&
aSubquery && aSubAttribute) { element.localName !== "preferences" &&
aSubquery && aSubAttribute) {
let subAttributeValue = element.getAttribute(aSubAttribute); let subAttributeValue = element.getAttribute(aSubAttribute);
element.hidden = subAttributeValue != aSubquery; element.hidden = subAttributeValue != aSubquery;
} else { } else {

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

@ -45,6 +45,49 @@ add_task(async function() {
await BrowserTestUtils.removeTab(gBrowser.selectedTab); await BrowserTestUtils.removeTab(gBrowser.selectedTab);
}); });
// Test opening to a subcategory displays the correct values for preferences
add_task(async function() {
// Skip if crash reporting isn't enabled since the checkbox will be missing.
if (!AppConstants.MOZ_CRASHREPORTER) {
return;
}
await SpecialPowers.pushPrefEnv({
set: [["browser.crashReports.unsubmittedCheck.autoSubmit", true]],
});
await openPreferencesViaOpenPreferencesAPI("privacy-reports", {leaveOpen: true});
let doc = gBrowser.contentDocument;
ok(
doc.querySelector("#automaticallySubmitCrashesBox").checked,
"Checkbox for automatically submitting crashes should be checked when the pref is true and only Reports are requested"
);
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
await SpecialPowers.popPrefEnv();
});
add_task(async function() {
// Skip if crash reporting isn't enabled since the checkbox will be missing.
if (!AppConstants.MOZ_CRASHREPORTER) {
return;
}
await SpecialPowers.pushPrefEnv({
set: [["browser.crashReports.unsubmittedCheck.autoSubmit", false]],
});
await openPreferencesViaOpenPreferencesAPI("privacy-reports", {leaveOpen: true});
let doc = gBrowser.contentDocument;
ok(
!doc.querySelector("#automaticallySubmitCrashesBox").checked,
"Checkbox for automatically submitting crashes should not be checked when the pref is false only Reports are requested"
);
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
await SpecialPowers.popPrefEnv();
});
function openPreferencesViaHash(aPane) { function openPreferencesViaHash(aPane) {
return new Promise(resolve => { return new Promise(resolve => {

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

@ -5,6 +5,11 @@
// This test checks that the Session Restore "Restore Previous Session" // This test checks that the Session Restore "Restore Previous Session"
// button on about:home is disabled in private mode // button on about:home is disabled in private mode
add_task(async function test_no_sessionrestore_button() { add_task(async function test_no_sessionrestore_button() {
// Activity Stream page does not have a restore session button.
// We want to run this test only when Activity Stream is disabled from about:home.
await SpecialPowers.pushPrefEnv({set: [
["browser.newtabpage.activity-stream.aboutHome.enabled", false]
]});
// Opening, then closing, a private window shouldn't create session data. // Opening, then closing, a private window shouldn't create session data.
(await BrowserTestUtils.openNewBrowserWindow({private: true})).close(); (await BrowserTestUtils.openNewBrowserWindow({private: true})).close();

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

@ -971,6 +971,8 @@ var SessionStoreInternal = {
SessionStoreInternal.restoreNextTab(); SessionStoreInternal.restoreNextTab();
this._sendTabRestoredNotification(tab, data.isRemotenessUpdate); this._sendTabRestoredNotification(tab, data.isRemotenessUpdate);
Services.obs.notifyObservers(null, "sessionstore-one-or-no-tab-restored");
break; break;
case "SessionStore:crashedTabRevived": case "SessionStore:crashedTabRevived":
// The browser was revived by navigating to a different page // The browser was revived by navigating to a different page
@ -1149,6 +1151,7 @@ var SessionStoreInternal = {
// Nothing to restore now, notify observers things are complete. // Nothing to restore now, notify observers things are complete.
Services.obs.notifyObservers(null, NOTIFY_WINDOWS_RESTORED); Services.obs.notifyObservers(null, NOTIFY_WINDOWS_RESTORED);
Services.obs.notifyObservers(null, "sessionstore-one-or-no-tab-restored");
this._deferredAllWindowsRestored.resolve(); this._deferredAllWindowsRestored.resolve();
} else { } else {
TelemetryTimestamps.add("sessionRestoreRestoring"); TelemetryTimestamps.add("sessionRestoreRestoring");
@ -1168,6 +1171,7 @@ var SessionStoreInternal = {
} else { } else {
// Nothing to restore, notify observers things are complete. // Nothing to restore, notify observers things are complete.
Services.obs.notifyObservers(null, NOTIFY_WINDOWS_RESTORED); Services.obs.notifyObservers(null, NOTIFY_WINDOWS_RESTORED);
Services.obs.notifyObservers(null, "sessionstore-one-or-no-tab-restored");
this._deferredAllWindowsRestored.resolve(); this._deferredAllWindowsRestored.resolve();
} }
// this window was opened by _openWindowWithState // this window was opened by _openWindowWithState

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

@ -0,0 +1,26 @@
[
{
"version": "Visual Studio 2015 Update 3 14.0.25425.01 / SDK 10.0.14393.0",
"size": 326656969,
"digest": "babc414ffc0457d27f5a1ed24a8e4873afbe2f1c1a4075469a27c005e1babc3b2a788f643f825efedff95b79686664c67ec4340ed535487168a3482e68559bc7",
"algorithm": "sha512",
"filename": "vs2015u3.zip",
"unpack": true
},
{
"version": "Ninja 1.7.1",
"size": 184821,
"digest": "e4f9a1ae624a2630e75264ba37d396d9c7407d6e6aea3763056210ba6e1387908bd31cf4037a6a3661a418e86c4d2761e0c333e6a3bd0d66549d2b0d72d3f43b",
"algorithm": "sha512",
"filename": "ninja171.zip",
"unpack": true
},
{
"version": "MinGit-2.13.3-64-bit",
"size": 21482885,
"digest": "929bb3c07be8487ee519422a312bdbfeec8f4db4b62c49d02f9aad9fd2a66c0ee5fad63d2b06c8744c336dc9d50446fa4457897333ad17ffd783ecabd1e2ddbb",
"algorithm": "sha512",
"filename": "git.zip",
"unpack": true
}
]

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

@ -372,10 +372,12 @@ var FormAutofillContent = {
* *
* @param {Object} profile Submitted form's address/creditcard guid and record. * @param {Object} profile Submitted form's address/creditcard guid and record.
* @param {Object} domWin Current content window. * @param {Object} domWin Current content window.
* @param {int} timeStartedFillingMS Time of form filling started.
*/ */
_onFormSubmit(profile, domWin) { _onFormSubmit(profile, domWin, timeStartedFillingMS) {
let mm = this._messageManagerFromWindow(domWin); let mm = this._messageManagerFromWindow(domWin);
mm.sendAsyncMessage("FormAutofill:OnFormSubmit", profile); mm.sendAsyncMessage("FormAutofill:OnFormSubmit",
{profile, timeStartedFillingMS});
}, },
/** /**
@ -407,7 +409,7 @@ var FormAutofillContent = {
return true; return true;
} }
this._onFormSubmit(records, domWin); this._onFormSubmit(records, domWin, handler.timeStartedFillingMS);
return true; return true;
}, },

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

@ -114,6 +114,11 @@ FormAutofillHandler.prototype = {
return this._formFieldCount != this.form.elements.length; return this._formFieldCount != this.form.elements.length;
}, },
/**
* Time in milliseconds since epoch when a user started filling in the form.
*/
timeStartedFillingMS: null,
/** /**
* Set fieldDetails from the form about fields that can be autofilled. * Set fieldDetails from the form about fields that can be autofilled.
* *
@ -127,6 +132,7 @@ FormAutofillHandler.prototype = {
this._formFieldCount = this.form.elements.length; this._formFieldCount = this.form.elements.length;
let fieldDetails = FormAutofillHeuristics.getFormInfo(this.form, allowDuplicates); let fieldDetails = FormAutofillHeuristics.getFormInfo(this.form, allowDuplicates);
this.fieldDetails = fieldDetails ? fieldDetails : []; this.fieldDetails = fieldDetails ? fieldDetails : [];
this.form.rootElement.addEventListener("input", this);
log.debug("Collected details on", this.fieldDetails.length, "fields"); log.debug("Collected details on", this.fieldDetails.length, "fields");
this.address.fieldDetails = this.fieldDetails.filter( this.address.fieldDetails = this.fieldDetails.filter(
@ -582,4 +588,43 @@ FormAutofillHandler.prototype = {
Services.cpmm.sendAsyncMessage("FormAutofill:GetDecryptedString", {cipherText, reauth}); Services.cpmm.sendAsyncMessage("FormAutofill:GetDecryptedString", {cipherText, reauth});
}); });
}, },
/**
* Find the fieldDetail by HTML element (assume all details were collected in collectFormFields).
*
* @param {HTMLElement} element
*
* @returns {Object|null}
* Return fieldDetail if fieldDetail's element ref could match the target.
* (or return null if the element could not match any fieldDetail).
*/
getFieldDetailsForElement(element) {
for (let detail of this.fieldDetails) {
if (detail.elementWeakRef.get() == element) {
return detail;
}
}
return null;
},
handleEvent(event) {
switch (event.type) {
case "input":
if (!event.isTrusted) {
return;
}
if (!FormAutofillUtils.isFieldEligibleForAutofill(event.target)) {
return;
}
if (!this.getFieldDetailsForElement(event.target)) {
return;
}
this.form.rootElement.removeEventListener("input", this);
this.timeStartedFillingMS = Date.now();
break;
}
},
}; };

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

@ -355,7 +355,7 @@ FormAutofillParent.prototype = {
this._updateStatus(); this._updateStatus();
}, },
_onAddressSubmit(address, target) { _onAddressSubmit(address, target, timeStartedFillingMS) {
if (address.guid) { if (address.guid) {
// Avoid updating the fields that users don't modify. // Avoid updating the fields that users don't modify.
let originalAddress = this.profileStorage.addresses.get(address.guid); let originalAddress = this.profileStorage.addresses.get(address.guid);
@ -366,6 +366,8 @@ FormAutofillParent.prototype = {
} }
if (!this.profileStorage.addresses.mergeIfPossible(address.guid, address.record)) { if (!this.profileStorage.addresses.mergeIfPossible(address.guid, address.record)) {
this._recordFormFillingTime("address", "autofill-update", timeStartedFillingMS);
FormAutofillDoorhanger.show(target, "update").then((state) => { FormAutofillDoorhanger.show(target, "update").then((state) => {
let changedGUIDs = this.profileStorage.addresses.mergeToStorage(address.record); let changedGUIDs = this.profileStorage.addresses.mergeToStorage(address.record);
switch (state) { switch (state) {
@ -389,6 +391,7 @@ FormAutofillParent.prototype = {
Services.telemetry.scalarAdd("formautofill.addresses.fill_type_autofill_update", 1); Services.telemetry.scalarAdd("formautofill.addresses.fill_type_autofill_update", 1);
return; return;
} }
this._recordFormFillingTime("address", "autofill", timeStartedFillingMS);
this.profileStorage.addresses.notifyUsed(address.guid); this.profileStorage.addresses.notifyUsed(address.guid);
// Address is merged successfully // Address is merged successfully
Services.telemetry.scalarAdd("formautofill.addresses.fill_type_autofill", 1); Services.telemetry.scalarAdd("formautofill.addresses.fill_type_autofill", 1);
@ -398,6 +401,7 @@ FormAutofillParent.prototype = {
changedGUIDs.push(this.profileStorage.addresses.add(address.record)); changedGUIDs.push(this.profileStorage.addresses.add(address.record));
} }
changedGUIDs.forEach(guid => this.profileStorage.addresses.notifyUsed(guid)); changedGUIDs.forEach(guid => this.profileStorage.addresses.notifyUsed(guid));
this._recordFormFillingTime("address", "manual", timeStartedFillingMS);
// Show first time use doorhanger // Show first time use doorhanger
if (Services.prefs.getBoolPref("extensions.formautofill.firstTimeUse")) { if (Services.prefs.getBoolPref("extensions.formautofill.firstTimeUse")) {
@ -441,13 +445,28 @@ FormAutofillParent.prototype = {
}, },
_onFormSubmit(data, target) { _onFormSubmit(data, target) {
let {address, creditCard} = data; let {profile: {address, creditCard}, timeStartedFillingMS} = data;
if (address) { if (address) {
this._onAddressSubmit(address, target); this._onAddressSubmit(address, target, timeStartedFillingMS);
} }
if (creditCard) { if (creditCard) {
this._onCreditCardSubmit(creditCard, target); this._onCreditCardSubmit(creditCard, target);
} }
}, },
/**
* Set the probes for the filling time with specific filling type and form type.
*
* @private
* @param {string} formType
* 3 type of form (address/creditcard/address-creditcard).
* @param {string} fillingType
* 3 filling type (manual/autofill/autofill-update).
* @param {int} startedFillingMS
* Time that form started to filling in ms.
*/
_recordFormFillingTime(formType, fillingType, startedFillingMS) {
let histogram = Services.telemetry.getKeyedHistogramById("FORM_FILLING_REQUIRED_TIME_MS");
histogram.add(`${formType}-${fillingType}`, Date.now() - startedFillingMS);
},
}; };

6
browser/extensions/pocket/bootstrap.js поставляемый
Просмотреть файл

@ -118,9 +118,15 @@ var PocketPageAction = {
animatableBox.id = "pocket-animatable-box"; animatableBox.id = "pocket-animatable-box";
let animatableImage = doc.createElement("image"); let animatableImage = doc.createElement("image");
animatableImage.id = "pocket-animatable-image"; animatableImage.id = "pocket-animatable-image";
animatableImage.setAttribute("role", "button");
let tooltip =
gPocketBundle.GetStringFromName("pocket-button.tooltiptext");
animatableImage.setAttribute("tooltiptext", tooltip);
let pocketButton = doc.createElement("image"); let pocketButton = doc.createElement("image");
pocketButton.id = "pocket-button"; pocketButton.id = "pocket-button";
pocketButton.classList.add("urlbar-icon"); pocketButton.classList.add("urlbar-icon");
pocketButton.setAttribute("role", "button");
pocketButton.setAttribute("tooltiptext", tooltip);
wrapper.appendChild(pocketButton); wrapper.appendChild(pocketButton);
wrapper.appendChild(animatableBox); wrapper.appendChild(animatableBox);

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

@ -43,8 +43,9 @@ const prefs = Services.prefs.getBranch("extensions.shield-recipe-client.");
const TIMER_NAME = "recipe-client-addon-run"; const TIMER_NAME = "recipe-client-addon-run";
const RUN_INTERVAL_PREF = "run_interval_seconds"; const RUN_INTERVAL_PREF = "run_interval_seconds";
const FIRST_RUN_PREF = "first_run"; const FIRST_RUN_PREF = "first_run";
const UI_AVAILABLE_NOTIFICATION = "sessionstore-windows-restored"; const UI_AVAILABLE_TOPIC = "sessionstore-windows-restored";
const SHIELD_INIT_NOTIFICATION = "shield-init-complete"; const SHIELD_INIT_TOPIC = "shield-init-complete";
const PREF_CHANGED_TOPIC = "nsPref:changed";
this.RecipeRunner = { this.RecipeRunner = {
init() { init() {
@ -60,19 +61,8 @@ this.RecipeRunner = {
if (prefs.getBoolPref(FIRST_RUN_PREF)) { if (prefs.getBoolPref(FIRST_RUN_PREF)) {
// Run once immediately after the UI is available. Do this before adding the // Run once immediately after the UI is available. Do this before adding the
// timer so we can't end up racing it. // timer so we can't end up racing it.
const observer = { Services.obs.addObserver(this, UI_AVAILABLE_TOPIC);
observe: async (subject, topic, data) => { CleanupManager.addCleanupHandler(() => Services.obs.removeObserver(this, UI_AVAILABLE_TOPIC));
Services.obs.removeObserver(observer, UI_AVAILABLE_NOTIFICATION);
await this.run();
this.registerTimer();
prefs.setBoolPref(FIRST_RUN_PREF, false);
Services.obs.notifyObservers(null, SHIELD_INIT_NOTIFICATION);
},
};
Services.obs.addObserver(observer, UI_AVAILABLE_NOTIFICATION);
CleanupManager.addCleanupHandler(() => Services.obs.removeObserver(observer, UI_AVAILABLE_NOTIFICATION));
} else { } else {
this.registerTimer(); this.registerTimer();
} }
@ -108,17 +98,38 @@ this.RecipeRunner = {
return true; return true;
}, },
observe(subject, topic, data) {
switch (topic) {
case PREF_CHANGED_TOPIC:
this.observePrefChange(data);
break;
case UI_AVAILABLE_TOPIC:
this.observeUIAvailable().catch(err => Cu.reportError(err));
break;
}
},
/** /**
* Watch for preference changes from Services.pref.addObserver. * Watch for preference changes from Services.pref.addObserver.
*/ */
observe(changedPrefBranch, action, changedPref) { observePrefChange(prefName) {
if (action === "nsPref:changed" && changedPref === RUN_INTERVAL_PREF) { if (prefName === RUN_INTERVAL_PREF) {
this.updateRunInterval(); this.updateRunInterval();
} else { } else {
log.debug(`Observer fired with unexpected pref change: ${action} ${changedPref}`); log.debug(`Observer fired with unexpected pref change: ${prefName}`);
} }
}, },
async observeUIAvailable() {
Services.obs.removeObserver(this, UI_AVAILABLE_TOPIC);
await this.run();
this.registerTimer();
prefs.setBoolPref(FIRST_RUN_PREF, false);
Services.obs.notifyObservers(null, SHIELD_INIT_TOPIC);
},
updateRunInterval() { updateRunInterval() {
// Run once every `runInterval` wall-clock seconds. This is managed by setting a "last ran" // Run once every `runInterval` wall-clock seconds. This is managed by setting a "last ran"
// timestamp, and running if it is more than `runInterval` seconds ago. Even with very short // timestamp, and running if it is more than `runInterval` seconds ago. Even with very short

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

@ -376,7 +376,7 @@ decorate_task(
ok(!runStub.called, "RecipeRunner.run is not called immediately"); ok(!runStub.called, "RecipeRunner.run is not called immediately");
ok(!registerTimerStub.called, "RecipeRunner.registerTimer is not called immediately"); ok(!registerTimerStub.called, "RecipeRunner.registerTimer is not called immediately");
Services.obs.notifyObservers(null, "sessionstore-windows-restored"); RecipeRunner.observe(null, "sessionstore-windows-restored");
await TestUtils.topicObserved("shield-init-complete"); await TestUtils.topicObserved("shield-init-complete");
ok(runStub.called, "RecipeRunner.run is called after the UI is available"); ok(runStub.called, "RecipeRunner.run is called after the UI is available");
ok(registerTimerStub.called, "RecipeRunner.registerTimer is called after the UI is available"); ok(registerTimerStub.called, "RecipeRunner.registerTimer is called after the UI is available");

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

@ -3,6 +3,11 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this # License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
ifndef ENABLE_TESTS
# We can't package tests if they aren't enabled.
MOZ_AUTOMATION_PACKAGE_TESTS = 0
endif
ifneq (,$(filter automation/%,$(MAKECMDGOALS))) ifneq (,$(filter automation/%,$(MAKECMDGOALS)))
ifeq (4.0,$(firstword $(sort 4.0 $(MAKE_VERSION)))) ifeq (4.0,$(firstword $(sort 4.0 $(MAKE_VERSION))))
MAKEFLAGS += --output-sync=target MAKEFLAGS += --output-sync=target

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

@ -31,6 +31,10 @@ def jemalloc(value, target, build_project, c_compiler):
if target.kernel == 'Linux': if target.kernel == 'Linux':
return True return True
if value and target.kernel not in ('WINNT', 'Linux', 'Darwin', 'kFreeBSD',
'FreeBSD', 'NetBSD'):
die('--enable-jemalloc is not supported on %s', target.kernel)
set_config('MOZ_MEMORY', jemalloc) set_config('MOZ_MEMORY', jemalloc)
set_define('MOZ_MEMORY', jemalloc) set_define('MOZ_MEMORY', jemalloc)
@ -47,29 +51,6 @@ def jemalloc_for_old_configure(jemalloc):
add_old_configure_arg(jemalloc_for_old_configure) add_old_configure_arg(jemalloc_for_old_configure)
@depends(jemalloc, target)
def jemalloc_os_define(jemalloc, target):
if jemalloc:
if target.kernel == 'WINNT':
return 'MOZ_MEMORY_WINDOWS'
if target.kernel == 'Linux':
return 'MOZ_MEMORY_LINUX'
if target.kernel == 'Darwin':
return 'MOZ_MEMORY_DARWIN'
if target.kernel in ('kFreeBSD', 'FreeBSD', 'NetBSD'):
return 'MOZ_MEMORY_BSD'
die('--enable-jemalloc is not supported on %s', target.kernel)
set_define(jemalloc_os_define, '1')
@depends(jemalloc, target)
def jemalloc_os_define_android(jemalloc, target):
if jemalloc and target.os == 'Android':
return 'MOZ_MEMORY_ANDROID'
set_define(jemalloc_os_define_android, '1')
option('--enable-replace-malloc', option('--enable-replace-malloc',
help='Enable ability to dynamically replace the malloc implementation') help='Enable ability to dynamically replace the malloc implementation')

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

@ -7,5 +7,6 @@ support-files =
!/caps/tests/mochitest/file_disableScript.html !/caps/tests/mochitest/file_disableScript.html
[test_bug995943.xul] [test_bug995943.xul]
skip-if = stylo && debug && os == 'linux' # bug 1384701
[test_addonMayLoad.html] [test_addonMayLoad.html]
[test_disableScript.xul] [test_disableScript.xul]

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

@ -11,7 +11,7 @@
const TAB_URL = EXAMPLE_URL + "doc_promise-get-allocation-stack.html"; const TAB_URL = EXAMPLE_URL + "doc_promise-get-allocation-stack.html";
const { PromisesFront } = require("devtools/shared/fronts/promises"); const { PromisesFront } = require("devtools/shared/fronts/promises");
var events = require("devtools/shared/event-emitter"); var EventEmitter = require("devtools/shared/event-emitter");
function test() { function test() {
Task.spawn(function* () { Task.spawn(function* () {
@ -48,7 +48,7 @@ function* testGetAllocationStack(client, form, tab) {
// Get the grip for promise p // Get the grip for promise p
let onNewPromise = new Promise(resolve => { let onNewPromise = new Promise(resolve => {
events.on(front, "new-promises", promises => { EventEmitter.on(front, "new-promises", promises => {
for (let p of promises) { for (let p of promises) {
if (p.preview.ownProperties.name && if (p.preview.ownProperties.name &&
p.preview.ownProperties.name.value === "p") { p.preview.ownProperties.name.value === "p") {

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

@ -12,7 +12,7 @@
const SOURCE_URL = "browser_dbg_promises-chrome-allocation-stack.js"; const SOURCE_URL = "browser_dbg_promises-chrome-allocation-stack.js";
const PromisesFront = require("devtools/shared/fronts/promises"); const PromisesFront = require("devtools/shared/fronts/promises");
var events = require("devtools/shared/event-emitter"); var EventEmitter = require("devtools/shared/event-emitter");
const STACK_DATA = [ const STACK_DATA = [
{ functionDisplayName: "test/</<" }, { functionDisplayName: "test/</<" },
@ -57,7 +57,7 @@ function* testGetAllocationStack(client, form, makePromises) {
// Get the grip for promise p // Get the grip for promise p
let onNewPromise = new Promise(resolve => { let onNewPromise = new Promise(resolve => {
events.on(front, "new-promises", promises => { EventEmitter.on(front, "new-promises", promises => {
for (let p of promises) { for (let p of promises) {
if (p.preview.ownProperties.name && if (p.preview.ownProperties.name &&
p.preview.ownProperties.name.value === "p") { p.preview.ownProperties.name.value === "p") {

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

@ -11,7 +11,7 @@
const TAB_URL = EXAMPLE_URL + "doc_promise-get-fulfillment-stack.html"; const TAB_URL = EXAMPLE_URL + "doc_promise-get-fulfillment-stack.html";
const { PromisesFront } = require("devtools/shared/fronts/promises"); const { PromisesFront } = require("devtools/shared/fronts/promises");
var events = require("devtools/shared/event-emitter"); var EventEmitter = require("devtools/shared/event-emitter");
const TEST_DATA = [ const TEST_DATA = [
{ {
@ -66,7 +66,7 @@ function* testGetFulfillmentStack(client, form, tab) {
// Get the grip for promise p // Get the grip for promise p
let onNewPromise = new Promise(resolve => { let onNewPromise = new Promise(resolve => {
events.on(front, "new-promises", promises => { EventEmitter.on(front, "new-promises", promises => {
for (let p of promises) { for (let p of promises) {
if (p.preview.ownProperties.name && if (p.preview.ownProperties.name &&
p.preview.ownProperties.name.value === "p") { p.preview.ownProperties.name.value === "p") {

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

@ -11,7 +11,7 @@
const TAB_URL = EXAMPLE_URL + "doc_promise-get-rejection-stack.html"; const TAB_URL = EXAMPLE_URL + "doc_promise-get-rejection-stack.html";
const { PromisesFront } = require("devtools/shared/fronts/promises"); const { PromisesFront } = require("devtools/shared/fronts/promises");
var events = require("devtools/shared/event-emitter"); var EventEmitter = require("devtools/shared/event-emitter");
// The code in the document above leaves an uncaught rejection. This is only // The code in the document above leaves an uncaught rejection. This is only
// reported to the testing framework if the code is loaded in the main process. // reported to the testing framework if the code is loaded in the main process.
@ -73,7 +73,7 @@ function* testGetRejectionStack(client, form, tab) {
// Get the grip for promise p // Get the grip for promise p
let onNewPromise = new Promise(resolve => { let onNewPromise = new Promise(resolve => {
events.on(front, "new-promises", promises => { EventEmitter.on(front, "new-promises", promises => {
for (let p of promises) { for (let p of promises) {
if (p.preview.ownProperties.name && if (p.preview.ownProperties.name &&
p.preview.ownProperties.name.value === "p") { p.preview.ownProperties.name.value === "p") {

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

@ -36,11 +36,14 @@ function SourceMapURLService(target, threadClient, sourceMapService) {
Services.prefs.addObserver(SOURCE_MAP_PREF, this._onPrefChanged); Services.prefs.addObserver(SOURCE_MAP_PREF, this._onPrefChanged);
// Start fetching the sources now. // Start fetching the sources now.
this._loadingPromise = new Promise(resolve => { this._loadingPromise = threadClient.getSources().then(({sources}) => {
threadClient.getSources(({sources}) => { // Ignore errors. Register the sources we got; we can't rely on
// Just ignore errors. // an event to arrive if the source actor already existed.
resolve(sources); for (let source of sources) {
}); this._onSourceUpdated(null, {source});
}
}, e => {
// Also ignore any protocol-based errors.
}); });
} }

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

@ -63,6 +63,7 @@ skip-if = debug # Bug 1282269
[browser_source_map-01.js] [browser_source_map-01.js]
[browser_source_map-absolute.js] [browser_source_map-absolute.js]
[browser_source_map-cross-domain.js] [browser_source_map-cross-domain.js]
[browser_source_map-init.js]
[browser_source_map-inline.js] [browser_source_map-inline.js]
[browser_source_map-no-race.js] [browser_source_map-no-race.js]
[browser_source_map-reload.js] [browser_source_map-reload.js]

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

@ -0,0 +1,43 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Test that the source map service initializes properly when source
// actors have already been created. Regression test for bug 1391768.
"use strict";
const JS_URL = URL_ROOT + "code_bundle_no_race.js";
const PAGE_URL = `data:text/html,
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<title>Empty test page to test race case</title>
</head>
<body>
<script src="${JS_URL}"></script>
</body>
</html>`;
const ORIGINAL_URL = "webpack:///code_no_race.js";
const GENERATED_LINE = 84;
const ORIGINAL_LINE = 11;
add_task(function* () {
// Opening the debugger causes the source actors to be created.
const toolbox = yield openNewTabAndToolbox(PAGE_URL, "jsdebugger");
// In bug 1391768, when the sourceMapURLService was created, it was
// ignoring any source actors that already existed, leading to
// source-mapping failures for those.
const service = toolbox.sourceMapURLService;
info(`checking original location for ${JS_URL}:${GENERATED_LINE}`);
let newLoc = yield service.originalPositionFor(JS_URL, GENERATED_LINE);
is(newLoc.sourceUrl, ORIGINAL_URL, "check mapped URL");
is(newLoc.line, ORIGINAL_LINE, "check mapped line number");
});

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

@ -7,7 +7,7 @@
// on NodeActor. Custom form property is set when 'form' event is sent // on NodeActor. Custom form property is set when 'form' event is sent
// by NodeActor actor (see 'onNodeActorForm' method). // by NodeActor actor (see 'onNodeActorForm' method).
const Events = require("devtools/shared/event-emitter"); const EventEmitter = require("devtools/shared/event-emitter");
const {ActorClassWithSpec, Actor, FrontClassWithSpec, Front, generateActorSpec} = const {ActorClassWithSpec, Actor, FrontClassWithSpec, Front, generateActorSpec} =
require("devtools/shared/protocol"); require("devtools/shared/protocol");
@ -34,11 +34,11 @@ var EventsFormActor = ActorClassWithSpec(eventsSpec, {
}, },
attach: function () { attach: function () {
Events.on(NodeActor, "form", this.onNodeActorForm); EventEmitter.on(NodeActor, "form", this.onNodeActorForm);
}, },
detach: function () { detach: function () {
Events.off(NodeActor, "form", this.onNodeActorForm); EventEmitter.off(NodeActor, "form", this.onNodeActorForm);
}, },
onNodeActorForm: function (event) { onNodeActorForm: function (event) {

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

@ -47,7 +47,6 @@ support-files =
!/devtools/client/shared/test/test-actor-registry.js !/devtools/client/shared/test/test-actor-registry.js
[browser_rules_add-property-and-reselect.js] [browser_rules_add-property-and-reselect.js]
skip-if = stylo # Bug 1387445
[browser_rules_add-property-cancel_01.js] [browser_rules_add-property-cancel_01.js]
[browser_rules_add-property-cancel_02.js] [browser_rules_add-property-cancel_02.js]
[browser_rules_add-property-cancel_03.js] [browser_rules_add-property-cancel_03.js]
@ -112,7 +111,7 @@ skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32
[browser_rules_cubicbezier-commit-on-ENTER.js] [browser_rules_cubicbezier-commit-on-ENTER.js]
[browser_rules_cubicbezier-revert-on-ESC.js] [browser_rules_cubicbezier-revert-on-ESC.js]
[browser_rules_custom.js] [browser_rules_custom.js]
skip-if = stylo # Bug 1387445 skip-if = stylo # Bug 1391198
[browser_rules_cycle-angle.js] [browser_rules_cycle-angle.js]
[browser_rules_cycle-color.js] [browser_rules_cycle-color.js]
[browser_rules_edit-display-grid-property.js] [browser_rules_edit-display-grid-property.js]
@ -132,7 +131,7 @@ skip-if = (os == "linux") # Bug 1356214
[browser_rules_edit-property_04.js] [browser_rules_edit-property_04.js]
[browser_rules_edit-property_05.js] [browser_rules_edit-property_05.js]
[browser_rules_edit-property_06.js] [browser_rules_edit-property_06.js]
skip-if = stylo # Bug 1387445 skip-if = stylo # Bug 1391198
[browser_rules_edit-property_07.js] [browser_rules_edit-property_07.js]
[browser_rules_edit-property_08.js] [browser_rules_edit-property_08.js]
[browser_rules_edit-property_09.js] [browser_rules_edit-property_09.js]
@ -183,17 +182,17 @@ skip-if = (os == "win" && debug) # bug 963492: win.
[browser_rules_invalid-source-map.js] [browser_rules_invalid-source-map.js]
[browser_rules_keybindings.js] [browser_rules_keybindings.js]
[browser_rules_keyframes-rule_01.js] [browser_rules_keyframes-rule_01.js]
skip-if = stylo # Bug 1387445 skip-if = stylo # Bug 1394994
[browser_rules_keyframes-rule_02.js] [browser_rules_keyframes-rule_02.js]
skip-if = stylo # Bug 1387445 skip-if = stylo # Bug 1394994
[browser_rules_keyframeLineNumbers.js] [browser_rules_keyframeLineNumbers.js]
skip-if = stylo # Bug 1387445 skip-if = stylo # Bug 1394994
[browser_rules_lineNumbers.js] [browser_rules_lineNumbers.js]
[browser_rules_livepreview.js] [browser_rules_livepreview.js]
[browser_rules_mark_overridden_01.js] [browser_rules_mark_overridden_01.js]
[browser_rules_mark_overridden_02.js] [browser_rules_mark_overridden_02.js]
[browser_rules_mark_overridden_03.js] [browser_rules_mark_overridden_03.js]
skip-if = stylo # Bug 1387445 skip-if = stylo # Bug 1391198
[browser_rules_mark_overridden_04.js] [browser_rules_mark_overridden_04.js]
[browser_rules_mark_overridden_05.js] [browser_rules_mark_overridden_05.js]
[browser_rules_mark_overridden_06.js] [browser_rules_mark_overridden_06.js]
@ -223,7 +222,7 @@ skip-if = stylo # Bug 1387445
[browser_rules_search-filter-overridden-property.js] [browser_rules_search-filter-overridden-property.js]
[browser_rules_search-filter_01.js] [browser_rules_search-filter_01.js]
[browser_rules_search-filter_02.js] [browser_rules_search-filter_02.js]
skip-if = stylo # Bug 1387445 skip-if = stylo # Bug 1394994
[browser_rules_search-filter_03.js] [browser_rules_search-filter_03.js]
[browser_rules_search-filter_04.js] [browser_rules_search-filter_04.js]
[browser_rules_search-filter_05.js] [browser_rules_search-filter_05.js]

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

@ -215,6 +215,12 @@ webconsole.menu.copyURL.accesskey=a
webconsole.menu.openURL.label=Open URL in New Tab webconsole.menu.openURL.label=Open URL in New Tab
webconsole.menu.openURL.accesskey=T webconsole.menu.openURL.accesskey=T
# LOCALIZATION NOTE (webconsole.menu.openInNetworkPanel.label)
# Label used for a context-menu item displayed for network message logs. Clicking on it
# opens the network message in the Network panel
webconsole.menu.openInNetworkPanel.label=Open in Network Panel
webconsole.menu.openInNetworkPanel.accesskey=N
# LOCALIZATION NOTE (webconsole.menu.openInVarView.label) # LOCALIZATION NOTE (webconsole.menu.openInVarView.label)
# Label used for a context-menu item displayed for object/variable logs. Clicking on it # Label used for a context-menu item displayed for object/variable logs. Clicking on it
# opens the webconsole variable view for the logged variable. # opens the webconsole variable view for the logged variable.

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

@ -203,11 +203,11 @@ const HeadersPanel = createClass({
className: "headers-summary learn-more-link", className: "headers-summary learn-more-link",
}), }),
button({ button({
className: "devtools-button", className: "devtools-button edit-and-resend-button",
onClick: cloneSelectedRequest, onClick: cloneSelectedRequest,
}, EDIT_AND_RESEND), }, EDIT_AND_RESEND),
button({ button({
className: "devtools-button", className: "devtools-button raw-headers-button",
onClick: this.toggleRawHeaders, onClick: this.toggleRawHeaders,
}, RAW_HEADERS), }, RAW_HEADERS),
) )

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

@ -41,9 +41,9 @@ function NetworkDetailsPanel({
request, request,
selectTab, selectTab,
sourceMapService, sourceMapService,
cloneSelectedRequest,
}) : }) :
CustomRequestPanel({ CustomRequestPanel({
cloneSelectedRequest,
request, request,
}) })
) )

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

@ -9,9 +9,8 @@ const {
PropTypes, PropTypes,
} = require("devtools/client/shared/vendor/react"); } = require("devtools/client/shared/vendor/react");
const { connect } = require("devtools/client/shared/vendor/react-redux"); const { connect } = require("devtools/client/shared/vendor/react-redux");
const Actions = require("../actions/index");
const { L10N } = require("../utils/l10n"); const { L10N } = require("../utils/l10n");
const { getSelectedRequest } = require("../selectors/index"); const { PANELS } = require("../constants");
// Components // Components
const Tabbar = createFactory(require("devtools/client/shared/components/tabs/tabbar")); const Tabbar = createFactory(require("devtools/client/shared/components/tabs/tabbar"));
@ -38,7 +37,7 @@ const TIMINGS_TITLE = L10N.getStr("netmonitor.tab.timings");
*/ */
function TabboxPanel({ function TabboxPanel({
activeTabId, activeTabId,
cloneSelectedRequest, cloneSelectedRequest = ()=>{},
request, request,
selectTab, selectTab,
sourceMapService, sourceMapService,
@ -56,45 +55,45 @@ function TabboxPanel({
showAllTabsMenu: true, showAllTabsMenu: true,
}, },
TabPanel({ TabPanel({
id: "headers", id: PANELS.HEADERS,
title: HEADERS_TITLE, title: HEADERS_TITLE,
}, },
HeadersPanel({ request, cloneSelectedRequest }), HeadersPanel({ request, cloneSelectedRequest }),
), ),
TabPanel({ TabPanel({
id: "cookies", id: PANELS.COOKIES,
title: COOKIES_TITLE, title: COOKIES_TITLE,
}, },
CookiesPanel({ request }), CookiesPanel({ request }),
), ),
TabPanel({ TabPanel({
id: "params", id: PANELS.PARAMS,
title: PARAMS_TITLE, title: PARAMS_TITLE,
}, },
ParamsPanel({ request }), ParamsPanel({ request }),
), ),
TabPanel({ TabPanel({
id: "response", id: PANELS.RESPONSE,
title: RESPONSE_TITLE, title: RESPONSE_TITLE,
}, },
ResponsePanel({ request }), ResponsePanel({ request }),
), ),
TabPanel({ TabPanel({
id: "timings", id: PANELS.TIMINGS,
title: TIMINGS_TITLE, title: TIMINGS_TITLE,
}, },
TimingsPanel({ request }), TimingsPanel({ request }),
), ),
request.cause && request.cause.stacktrace && request.cause.stacktrace.length > 0 && request.cause && request.cause.stacktrace && request.cause.stacktrace.length > 0 &&
TabPanel({ TabPanel({
id: "stack-trace", id: PANELS.STACK_TRACE,
title: STACK_TRACE_TITLE, title: STACK_TRACE_TITLE,
}, },
StackTracePanel({ request, sourceMapService }), StackTracePanel({ request, sourceMapService }),
), ),
request.securityState && request.securityState !== "insecure" && request.securityState && request.securityState !== "insecure" &&
TabPanel({ TabPanel({
id: "security", id: PANELS.SECURITY,
title: SECURITY_TITLE, title: SECURITY_TITLE,
}, },
SecurityPanel({ request }), SecurityPanel({ request }),
@ -107,20 +106,11 @@ TabboxPanel.displayName = "TabboxPanel";
TabboxPanel.propTypes = { TabboxPanel.propTypes = {
activeTabId: PropTypes.string, activeTabId: PropTypes.string,
cloneSelectedRequest: PropTypes.func.isRequired, cloneSelectedRequest: PropTypes.func,
request: PropTypes.object, request: PropTypes.object,
selectTab: PropTypes.func.isRequired, selectTab: PropTypes.func.isRequired,
// Service to enable the source map feature. // Service to enable the source map feature.
sourceMapService: PropTypes.object, sourceMapService: PropTypes.object,
}; };
module.exports = connect( module.exports = connect()(TabboxPanel);
(state) => ({
activeTabId: state.ui.detailsPanelSelectedTab,
request: getSelectedRequest(state),
}),
(dispatch) => ({
cloneSelectedRequest: () => dispatch(Actions.cloneSelectedRequest()),
selectTab: (tabId) => dispatch(Actions.selectDetailsPanelTab(tabId)),
}),
)(TabboxPanel);

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

@ -5,54 +5,29 @@
"use strict"; "use strict";
const Services = require("Services"); const Services = require("Services");
const { CurlUtils } = require("devtools/client/shared/curl");
const { TimelineFront } = require("devtools/shared/fronts/timeline"); const { TimelineFront } = require("devtools/shared/fronts/timeline");
const { ACTIVITY_TYPE, EVENTS } = require("../constants"); const { ACTIVITY_TYPE, EVENTS } = require("../constants");
const { getDisplayedRequestById } = require("../selectors/index"); const { getDisplayedRequestById } = require("../selectors/index");
const { fetchHeaders, formDataURI } = require("../utils/request-utils"); const FirefoxDataProvider = require("./firefox-data-provider");
class FirefoxConnector { class FirefoxConnector {
constructor() { constructor() {
// Internal properties
this.payloadQueue = [];
// Public methods // Public methods
this.connect = this.connect.bind(this); this.connect = this.connect.bind(this);
this.disconnect = this.disconnect.bind(this); this.disconnect = this.disconnect.bind(this);
this.willNavigate = this.willNavigate.bind(this); this.willNavigate = this.willNavigate.bind(this);
this.displayCachedEvents = this.displayCachedEvents.bind(this); this.displayCachedEvents = this.displayCachedEvents.bind(this);
this.onDocLoadingMarker = this.onDocLoadingMarker.bind(this); this.onDocLoadingMarker = this.onDocLoadingMarker.bind(this);
this.addRequest = this.addRequest.bind(this);
this.updateRequest = this.updateRequest.bind(this);
this.fetchImage = this.fetchImage.bind(this);
this.fetchRequestHeaders = this.fetchRequestHeaders.bind(this);
this.fetchResponseHeaders = this.fetchResponseHeaders.bind(this);
this.fetchPostData = this.fetchPostData.bind(this);
this.fetchResponseCookies = this.fetchResponseCookies.bind(this);
this.fetchRequestCookies = this.fetchRequestCookies.bind(this);
this.getPayloadFromQueue = this.getPayloadFromQueue.bind(this);
this.isQueuePayloadReady = this.isQueuePayloadReady.bind(this);
this.pushPayloadToQueue = this.pushPayloadToQueue.bind(this);
this.sendHTTPRequest = this.sendHTTPRequest.bind(this); this.sendHTTPRequest = this.sendHTTPRequest.bind(this);
this.setPreferences = this.setPreferences.bind(this); this.setPreferences = this.setPreferences.bind(this);
this.triggerActivity = this.triggerActivity.bind(this); this.triggerActivity = this.triggerActivity.bind(this);
this.inspectRequest = this.inspectRequest.bind(this); this.inspectRequest = this.inspectRequest.bind(this);
this.getLongString = this.getLongString.bind(this);
this.getNetworkRequest = this.getNetworkRequest.bind(this);
this.getTabTarget = this.getTabTarget.bind(this); this.getTabTarget = this.getTabTarget.bind(this);
this.viewSourceInDebugger = this.viewSourceInDebugger.bind(this); this.viewSourceInDebugger = this.viewSourceInDebugger.bind(this);
// Event handlers // Internals
this.onNetworkEvent = this.onNetworkEvent.bind(this); this.getLongString = this.getLongString.bind(this);
this.onNetworkEventUpdate = this.onNetworkEventUpdate.bind(this); this.getNetworkRequest = this.getNetworkRequest.bind(this);
this.onRequestHeaders = this.onRequestHeaders.bind(this);
this.onRequestCookies = this.onRequestCookies.bind(this);
this.onRequestPostData = this.onRequestPostData.bind(this);
this.onSecurityInfo = this.onSecurityInfo.bind(this);
this.onResponseHeaders = this.onResponseHeaders.bind(this);
this.onResponseCookies = this.onResponseCookies.bind(this);
this.onResponseContent = this.onResponseContent.bind(this);
this.onEventTimings = this.onEventTimings.bind(this);
} }
async connect(connection, actions, getState) { async connect(connection, actions, getState) {
@ -63,10 +38,17 @@ class FirefoxConnector {
this.webConsoleClient = this.tabTarget.activeConsole; this.webConsoleClient = this.tabTarget.activeConsole;
this.dataProvider = new FirefoxDataProvider({
webConsoleClient: this.webConsoleClient,
actions: this.actions,
});
this.tabTarget.on("will-navigate", this.willNavigate); this.tabTarget.on("will-navigate", this.willNavigate);
this.tabTarget.on("close", this.disconnect); this.tabTarget.on("close", this.disconnect);
this.webConsoleClient.on("networkEvent", this.onNetworkEvent); this.webConsoleClient.on("networkEvent",
this.webConsoleClient.on("networkEventUpdate", this.onNetworkEventUpdate); this.dataProvider.onNetworkEvent);
this.webConsoleClient.on("networkEventUpdate",
this.dataProvider.onNetworkEventUpdate);
// Don't start up waiting for timeline markers if the server isn't // Don't start up waiting for timeline markers if the server isn't
// recent enough to emit the markers we're interested in. // recent enough to emit the markers we're interested in.
@ -96,6 +78,7 @@ class FirefoxConnector {
this.webConsoleClient.off("networkEventUpdate"); this.webConsoleClient.off("networkEventUpdate");
this.webConsoleClient = null; this.webConsoleClient = null;
this.timelineFront = null; this.timelineFront = null;
this.dataProvider = null;
} }
willNavigate() { willNavigate() {
@ -114,10 +97,10 @@ class FirefoxConnector {
displayCachedEvents() { displayCachedEvents() {
for (let networkInfo of this.webConsoleClient.getNetworkEvents()) { for (let networkInfo of this.webConsoleClient.getNetworkEvents()) {
// First add the request to the timeline. // First add the request to the timeline.
this.onNetworkEvent("networkEvent", networkInfo); this.dataProvider.onNetworkEvent("networkEvent", networkInfo);
// Then replay any updates already received. // Then replay any updates already received.
for (let updateType of networkInfo.updates) { for (let updateType of networkInfo.updates) {
this.onNetworkEventUpdate("networkEventUpdate", { this.dataProvider.onNetworkEventUpdate("networkEventUpdate", {
packet: { updateType }, packet: { updateType },
networkInfo, networkInfo,
}); });
@ -135,222 +118,6 @@ class FirefoxConnector {
this.actions.addTimingMarker(marker); this.actions.addTimingMarker(marker);
} }
/**
* Add a new network request to application state.
*
* @param {string} id request id
* @param {object} data data payload will be added to application state
*/
addRequest(id, data) {
let {
method,
url,
isXHR,
cause,
startedDateTime,
fromCache,
fromServiceWorker,
} = data;
this.actions.addRequest(
id,
{
// Convert the received date/time string to a unix timestamp.
startedMillis: Date.parse(startedDateTime),
method,
url,
isXHR,
cause,
fromCache,
fromServiceWorker,
},
true,
)
.then(() => window.emit(EVENTS.REQUEST_ADDED, id));
}
/**
* Update a network request if it already exists in application state.
*
* @param {string} id request id
* @param {object} data data payload will be updated to application state
*/
async updateRequest(id, data) {
let {
mimeType,
responseContent,
responseCookies,
responseHeaders,
requestCookies,
requestHeaders,
requestPostData,
} = data;
// fetch request detail contents in parallel
let [
imageObj,
requestHeadersObj,
responseHeadersObj,
postDataObj,
requestCookiesObj,
responseCookiesObj,
] = await Promise.all([
this.fetchImage(mimeType, responseContent),
this.fetchRequestHeaders(requestHeaders),
this.fetchResponseHeaders(responseHeaders),
this.fetchPostData(requestPostData),
this.fetchRequestCookies(requestCookies),
this.fetchResponseCookies(responseCookies),
]);
let payload = Object.assign({}, data,
imageObj, requestHeadersObj, responseHeadersObj,
postDataObj, requestCookiesObj, responseCookiesObj);
this.pushPayloadToQueue(id, payload);
if (this.isQueuePayloadReady(id)) {
await this.actions.updateRequest(id, this.getPayloadFromQueue(id).payload, true);
}
}
async fetchImage(mimeType, responseContent) {
let payload = {};
if (mimeType && responseContent && responseContent.content) {
let { encoding, text } = responseContent.content;
let response = await this.getLongString(text);
if (mimeType.includes("image/")) {
payload.responseContentDataUri = formDataURI(mimeType, encoding, response);
}
responseContent.content.text = response;
payload.responseContent = responseContent;
}
return payload;
}
async fetchRequestHeaders(requestHeaders) {
let payload = {};
if (requestHeaders && requestHeaders.headers && requestHeaders.headers.length) {
let headers = await fetchHeaders(requestHeaders, this.getLongString);
if (headers) {
payload.requestHeaders = headers;
}
}
return payload;
}
async fetchResponseHeaders(responseHeaders) {
let payload = {};
if (responseHeaders && responseHeaders.headers && responseHeaders.headers.length) {
let headers = await fetchHeaders(responseHeaders, this.getLongString);
if (headers) {
payload.responseHeaders = headers;
}
}
return payload;
}
async fetchPostData(requestPostData) {
let payload = {};
if (requestPostData && requestPostData.postData) {
let { text } = requestPostData.postData;
let postData = await this.getLongString(text);
const headers = CurlUtils.getHeadersFromMultipartText(postData);
const headersSize = headers.reduce((acc, { name, value }) => {
return acc + name.length + value.length + 2;
}, 0);
requestPostData.postData.text = postData;
payload.requestPostData = Object.assign({}, requestPostData);
payload.requestHeadersFromUploadStream = { headers, headersSize };
}
return payload;
}
async fetchResponseCookies(responseCookies) {
let payload = {};
if (responseCookies) {
let resCookies = [];
// response store cookies in responseCookies or responseCookies.cookies
let cookies = responseCookies.cookies ?
responseCookies.cookies : responseCookies;
// make sure cookies is iterable
if (typeof cookies[Symbol.iterator] === "function") {
for (let cookie of cookies) {
resCookies.push(Object.assign({}, cookie, {
value: await this.getLongString(cookie.value),
}));
}
if (resCookies.length) {
payload.responseCookies = resCookies;
}
}
}
return payload;
}
async fetchRequestCookies(requestCookies) {
let payload = {};
if (requestCookies) {
let reqCookies = [];
// request store cookies in requestCookies or requestCookies.cookies
let cookies = requestCookies.cookies ?
requestCookies.cookies : requestCookies;
// make sure cookies is iterable
if (typeof cookies[Symbol.iterator] === "function") {
for (let cookie of cookies) {
reqCookies.push(Object.assign({}, cookie, {
value: await this.getLongString(cookie.value),
}));
}
if (reqCookies.length) {
payload.requestCookies = reqCookies;
}
}
}
return payload;
}
/**
* Access a payload item from payload queue.
*
* @param {string} id request id
* @return {boolean} return a queued payload item from queue.
*/
getPayloadFromQueue(id) {
return this.payloadQueue.find((item) => item.id === id);
}
/**
* Packet order of "networkUpdateEvent" is predictable, as a result we can wait for
* the last one "eventTimings" packet arrives to check payload is ready.
*
* @param {string} id request id
* @return {boolean} return whether a specific networkEvent has been updated completely.
*/
isQueuePayloadReady(id) {
let queuedPayload = this.getPayloadFromQueue(id);
return queuedPayload && queuedPayload.payload.eventTimings;
}
/**
* Push a request payload into a queue if request doesn't exist. Otherwise update the
* request itself.
*
* @param {string} id request id
* @param {object} payload request data payload
*/
pushPayloadToQueue(id, payload) {
let queuedPayload = this.getPayloadFromQueue(id);
if (!queuedPayload) {
this.payloadQueue.push({ id, payload });
} else {
// Merge upcoming networkEventUpdate payload into existing one
queuedPayload.payload = Object.assign({}, queuedPayload.payload, payload);
}
}
/** /**
* Send a HTTP request data payload * Send a HTTP request data payload
* *
@ -490,7 +257,7 @@ class FirefoxConnector {
* @return {object} networkInfo data packet * @return {object} networkInfo data packet
*/ */
getNetworkRequest(id) { getNetworkRequest(id) {
return this.webConsoleClient.getNetworkRequest(id); return this.dataProvider.getNetworkRequest(id);
} }
/** /**
@ -505,7 +272,7 @@ class FirefoxConnector {
* are available, or rejected if something goes wrong. * are available, or rejected if something goes wrong.
*/ */
getLongString(stringGrip) { getLongString(stringGrip) {
return this.webConsoleClient.getString(stringGrip); return this.dataProvider.getLongString(stringGrip);
} }
/** /**
@ -526,213 +293,6 @@ class FirefoxConnector {
this.toolbox.viewSourceInDebugger(sourceURL, sourceLine); this.toolbox.viewSourceInDebugger(sourceURL, sourceLine);
} }
} }
/**
* The "networkEvent" message type handler.
*
* @param {string} type message type
* @param {object} networkInfo network request information
*/
onNetworkEvent(type, networkInfo) {
let {
actor,
cause,
fromCache,
fromServiceWorker,
isXHR,
request: {
method,
url,
},
startedDateTime,
} = networkInfo;
this.addRequest(actor, {
cause,
fromCache,
fromServiceWorker,
isXHR,
method,
startedDateTime,
url,
});
window.emit(EVENTS.NETWORK_EVENT, actor);
}
/**
* The "networkEventUpdate" message type handler.
*
* @param {string} type message type
* @param {object} packet the message received from the server.
* @param {object} networkInfo the network request information.
*/
onNetworkEventUpdate(type, { packet, networkInfo }) {
let { actor } = networkInfo;
switch (packet.updateType) {
case "requestHeaders":
this.webConsoleClient.getRequestHeaders(actor, this.onRequestHeaders);
window.emit(EVENTS.UPDATING_REQUEST_HEADERS, actor);
break;
case "requestCookies":
this.webConsoleClient.getRequestCookies(actor, this.onRequestCookies);
window.emit(EVENTS.UPDATING_REQUEST_COOKIES, actor);
break;
case "requestPostData":
this.webConsoleClient.getRequestPostData(actor, this.onRequestPostData);
window.emit(EVENTS.UPDATING_REQUEST_POST_DATA, actor);
break;
case "securityInfo":
this.updateRequest(actor, {
securityState: networkInfo.securityInfo,
}).then(() => {
this.webConsoleClient.getSecurityInfo(actor, this.onSecurityInfo);
window.emit(EVENTS.UPDATING_SECURITY_INFO, actor);
});
break;
case "responseHeaders":
this.webConsoleClient.getResponseHeaders(actor, this.onResponseHeaders);
window.emit(EVENTS.UPDATING_RESPONSE_HEADERS, actor);
break;
case "responseCookies":
this.webConsoleClient.getResponseCookies(actor, this.onResponseCookies);
window.emit(EVENTS.UPDATING_RESPONSE_COOKIES, actor);
break;
case "responseStart":
this.updateRequest(actor, {
httpVersion: networkInfo.response.httpVersion,
remoteAddress: networkInfo.response.remoteAddress,
remotePort: networkInfo.response.remotePort,
status: networkInfo.response.status,
statusText: networkInfo.response.statusText,
headersSize: networkInfo.response.headersSize
}).then(() => {
window.emit(EVENTS.STARTED_RECEIVING_RESPONSE, actor);
});
break;
case "responseContent":
this.webConsoleClient.getResponseContent(actor,
this.onResponseContent.bind(this, {
contentSize: networkInfo.response.bodySize,
transferredSize: networkInfo.response.transferredSize,
mimeType: networkInfo.response.content.mimeType
}));
window.emit(EVENTS.UPDATING_RESPONSE_CONTENT, actor);
break;
case "eventTimings":
this.updateRequest(actor, { totalTime: networkInfo.totalTime })
.then(() => {
this.webConsoleClient.getEventTimings(actor, this.onEventTimings);
window.emit(EVENTS.UPDATING_EVENT_TIMINGS, actor);
});
break;
}
}
/**
* Handles additional information received for a "requestHeaders" packet.
*
* @param {object} response the message received from the server.
*/
onRequestHeaders(response) {
this.updateRequest(response.from, {
requestHeaders: response
}).then(() => {
window.emit(EVENTS.RECEIVED_REQUEST_HEADERS, response.from);
});
}
/**
* Handles additional information received for a "requestCookies" packet.
*
* @param {object} response the message received from the server.
*/
onRequestCookies(response) {
this.updateRequest(response.from, {
requestCookies: response
}).then(() => {
window.emit(EVENTS.RECEIVED_REQUEST_COOKIES, response.from);
});
}
/**
* Handles additional information received for a "requestPostData" packet.
*
* @param {object} response the message received from the server.
*/
onRequestPostData(response) {
this.updateRequest(response.from, {
requestPostData: response
}).then(() => {
window.emit(EVENTS.RECEIVED_REQUEST_POST_DATA, response.from);
});
}
/**
* Handles additional information received for a "securityInfo" packet.
*
* @param {object} response the message received from the server.
*/
onSecurityInfo(response) {
this.updateRequest(response.from, {
securityInfo: response.securityInfo
}).then(() => {
window.emit(EVENTS.RECEIVED_SECURITY_INFO, response.from);
});
}
/**
* Handles additional information received for a "responseHeaders" packet.
*
* @param {object} response the message received from the server.
*/
onResponseHeaders(response) {
this.updateRequest(response.from, {
responseHeaders: response
}).then(() => {
window.emit(EVENTS.RECEIVED_RESPONSE_HEADERS, response.from);
});
}
/**
* Handles additional information received for a "responseCookies" packet.
*
* @param {object} response the message received from the server.
*/
onResponseCookies(response) {
this.updateRequest(response.from, {
responseCookies: response
}).then(() => {
window.emit(EVENTS.RECEIVED_RESPONSE_COOKIES, response.from);
});
}
/**
* Handles additional information received for a "responseContent" packet.
*
* @param {object} data the message received from the server event.
* @param {object} response the message received from the server.
*/
onResponseContent(data, response) {
let payload = Object.assign({ responseContent: response }, data);
this.updateRequest(response.from, payload).then(() => {
window.emit(EVENTS.RECEIVED_RESPONSE_CONTENT, response.from);
});
}
/**
* Handles additional information received for a "eventTimings" packet.
*
* @param {object} response the message received from the server.
*/
onEventTimings(response) {
this.updateRequest(response.from, {
eventTimings: response
}).then(() => {
window.emit(EVENTS.RECEIVED_EVENT_TIMINGS, response.from);
});
}
} }
module.exports = new FirefoxConnector(); module.exports = new FirefoxConnector();

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

@ -0,0 +1,525 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* eslint-disable block-scoped-var */
"use strict";
const { EVENTS } = require("../constants");
const { CurlUtils } = require("devtools/client/shared/curl");
const { fetchHeaders, formDataURI } = require("../utils/request-utils");
/**
* This object is responsible for fetching additional HTTP
* data from the backend.
*/
class FirefoxDataProvider {
constructor({webConsoleClient, actions}) {
// Options
this.webConsoleClient = webConsoleClient;
this.actions = actions;
// Internal properties
this.payloadQueue = [];
// Public methods
this.addRequest = this.addRequest.bind(this);
this.updateRequest = this.updateRequest.bind(this);
// Internals
this.fetchImage = this.fetchImage.bind(this);
this.fetchRequestHeaders = this.fetchRequestHeaders.bind(this);
this.fetchResponseHeaders = this.fetchResponseHeaders.bind(this);
this.fetchPostData = this.fetchPostData.bind(this);
this.fetchResponseCookies = this.fetchResponseCookies.bind(this);
this.fetchRequestCookies = this.fetchRequestCookies.bind(this);
this.getPayloadFromQueue = this.getPayloadFromQueue.bind(this);
this.isQueuePayloadReady = this.isQueuePayloadReady.bind(this);
this.pushPayloadToQueue = this.pushPayloadToQueue.bind(this);
this.getLongString = this.getLongString.bind(this);
this.getNetworkRequest = this.getNetworkRequest.bind(this);
// Event handlers
this.onNetworkEvent = this.onNetworkEvent.bind(this);
this.onNetworkEventUpdate = this.onNetworkEventUpdate.bind(this);
this.onRequestHeaders = this.onRequestHeaders.bind(this);
this.onRequestCookies = this.onRequestCookies.bind(this);
this.onRequestPostData = this.onRequestPostData.bind(this);
this.onSecurityInfo = this.onSecurityInfo.bind(this);
this.onResponseHeaders = this.onResponseHeaders.bind(this);
this.onResponseCookies = this.onResponseCookies.bind(this);
this.onResponseContent = this.onResponseContent.bind(this);
this.onEventTimings = this.onEventTimings.bind(this);
}
/**
* Add a new network request to application state.
*
* @param {string} id request id
* @param {object} data data payload will be added to application state
*/
async addRequest(id, data) {
let {
method,
url,
isXHR,
cause,
startedDateTime,
fromCache,
fromServiceWorker,
} = data;
if (this.actions.addRequest) {
await this.actions.addRequest(id, {
// Convert the received date/time string to a unix timestamp.
startedMillis: Date.parse(startedDateTime),
method,
url,
isXHR,
cause,
fromCache,
fromServiceWorker},
true,
);
}
emit(EVENTS.REQUEST_ADDED, id);
}
/**
* Update a network request if it already exists in application state.
*
* @param {string} id request id
* @param {object} data data payload will be updated to application state
*/
async updateRequest(id, data) {
let {
mimeType,
responseContent,
responseCookies,
responseHeaders,
requestCookies,
requestHeaders,
requestPostData,
} = data;
// fetch request detail contents in parallel
let [
imageObj,
requestHeadersObj,
responseHeadersObj,
postDataObj,
requestCookiesObj,
responseCookiesObj,
] = await Promise.all([
this.fetchImage(mimeType, responseContent),
this.fetchRequestHeaders(requestHeaders),
this.fetchResponseHeaders(responseHeaders),
this.fetchPostData(requestPostData),
this.fetchRequestCookies(requestCookies),
this.fetchResponseCookies(responseCookies),
]);
let payload = Object.assign({},
data,
imageObj,
requestHeadersObj,
responseHeadersObj,
postDataObj,
requestCookiesObj,
responseCookiesObj
);
this.pushPayloadToQueue(id, payload);
if (this.actions.updateRequest && this.isQueuePayloadReady(id)) {
await this.actions.updateRequest(id, this.getPayloadFromQueue(id).payload, true);
}
}
async fetchImage(mimeType, responseContent) {
let payload = {};
if (mimeType && responseContent && responseContent.content) {
let { encoding, text } = responseContent.content;
let response = await this.getLongString(text);
if (mimeType.includes("image/")) {
payload.responseContentDataUri = formDataURI(mimeType, encoding, response);
}
responseContent.content.text = response;
payload.responseContent = responseContent;
}
return payload;
}
async fetchRequestHeaders(requestHeaders) {
let payload = {};
if (requestHeaders && requestHeaders.headers && requestHeaders.headers.length) {
let headers = await fetchHeaders(requestHeaders, this.getLongString);
if (headers) {
payload.requestHeaders = headers;
}
}
return payload;
}
async fetchResponseHeaders(responseHeaders) {
let payload = {};
if (responseHeaders && responseHeaders.headers && responseHeaders.headers.length) {
let headers = await fetchHeaders(responseHeaders, this.getLongString);
if (headers) {
payload.responseHeaders = headers;
}
}
return payload;
}
async fetchPostData(requestPostData) {
let payload = {};
if (requestPostData && requestPostData.postData) {
let { text } = requestPostData.postData;
let postData = await this.getLongString(text);
const headers = CurlUtils.getHeadersFromMultipartText(postData);
// Calculate total header size and don't forget to include
// two new-line characters at the end.
const headersSize = headers.reduce((acc, { name, value }) => {
return acc + name.length + value.length + 2;
}, 0);
requestPostData.postData.text = postData;
payload.requestPostData = Object.assign({}, requestPostData);
payload.requestHeadersFromUploadStream = { headers, headersSize };
}
return payload;
}
async fetchResponseCookies(responseCookies) {
let payload = {};
if (responseCookies) {
let resCookies = [];
// response store cookies in responseCookies or responseCookies.cookies
let cookies = responseCookies.cookies ?
responseCookies.cookies : responseCookies;
// make sure cookies is iterable
if (typeof cookies[Symbol.iterator] === "function") {
for (let cookie of cookies) {
resCookies.push(Object.assign({}, cookie, {
value: await this.getLongString(cookie.value),
}));
}
if (resCookies.length) {
payload.responseCookies = resCookies;
}
}
}
return payload;
}
async fetchRequestCookies(requestCookies) {
let payload = {};
if (requestCookies) {
let reqCookies = [];
// request store cookies in requestCookies or requestCookies.cookies
let cookies = requestCookies.cookies ?
requestCookies.cookies : requestCookies;
// make sure cookies is iterable
if (typeof cookies[Symbol.iterator] === "function") {
for (let cookie of cookies) {
reqCookies.push(Object.assign({}, cookie, {
value: await this.getLongString(cookie.value),
}));
}
if (reqCookies.length) {
payload.requestCookies = reqCookies;
}
}
}
return payload;
}
/**
* Access a payload item from payload queue.
*
* @param {string} id request id
* @return {boolean} return a queued payload item from queue.
*/
getPayloadFromQueue(id) {
return this.payloadQueue.find((item) => item.id === id);
}
/**
* Return true if payload is ready (all data fetched from the backend)
*
* @param {string} id request id
* @return {boolean} return whether a specific networkEvent has been updated completely.
*/
isQueuePayloadReady(id) {
let queuedPayload = this.getPayloadFromQueue(id);
// TODO we should find a better solution since it might happen
// that eventTimings is not the last update.
return queuedPayload && queuedPayload.payload.eventTimings;
}
/**
* Push a request payload into a queue if request doesn't exist. Otherwise update the
* request itself.
*
* @param {string} id request id
* @param {object} payload request data payload
*/
pushPayloadToQueue(id, payload) {
let queuedPayload = this.getPayloadFromQueue(id);
if (!queuedPayload) {
this.payloadQueue.push({ id, payload });
} else {
// Merge upcoming networkEventUpdate payload into existing one
queuedPayload.payload = Object.assign({}, queuedPayload.payload, payload);
}
}
/**
* Fetches the network information packet from actor server
*
* @param {string} id request id
* @return {object} networkInfo data packet
*/
getNetworkRequest(id) {
return this.webConsoleClient.getNetworkRequest(id);
}
/**
* Fetches the full text of a LongString.
*
* @param {object|string} stringGrip
* The long string grip containing the corresponding actor.
* If you pass in a plain string (by accident or because you're lazy),
* then a promise of the same string is simply returned.
* @return {object}
* A promise that is resolved when the full string contents
* are available, or rejected if something goes wrong.
*/
getLongString(stringGrip) {
return this.webConsoleClient.getString(stringGrip);
}
/**
* The "networkEvent" message type handler.
*
* @param {string} type message type
* @param {object} networkInfo network request information
*/
onNetworkEvent(type, networkInfo) {
let {
actor,
cause,
fromCache,
fromServiceWorker,
isXHR,
request: {
method,
url,
},
startedDateTime,
} = networkInfo;
this.addRequest(actor, {
cause,
fromCache,
fromServiceWorker,
isXHR,
method,
startedDateTime,
url,
});
emit(EVENTS.NETWORK_EVENT, actor);
}
/**
* The "networkEventUpdate" message type handler.
*
* @param {string} type message type
* @param {object} packet the message received from the server.
* @param {object} networkInfo the network request information.
*/
onNetworkEventUpdate(type, { packet, networkInfo }) {
let { actor } = networkInfo;
switch (packet.updateType) {
case "requestHeaders":
this.webConsoleClient.getRequestHeaders(actor, this.onRequestHeaders);
emit(EVENTS.UPDATING_REQUEST_HEADERS, actor);
break;
case "requestCookies":
this.webConsoleClient.getRequestCookies(actor, this.onRequestCookies);
emit(EVENTS.UPDATING_REQUEST_COOKIES, actor);
break;
case "requestPostData":
this.webConsoleClient.getRequestPostData(actor, this.onRequestPostData);
emit(EVENTS.UPDATING_REQUEST_POST_DATA, actor);
break;
case "securityInfo":
this.updateRequest(actor, {
securityState: networkInfo.securityInfo,
}).then(() => {
this.webConsoleClient.getSecurityInfo(actor, this.onSecurityInfo);
emit(EVENTS.UPDATING_SECURITY_INFO, actor);
});
break;
case "responseHeaders":
this.webConsoleClient.getResponseHeaders(actor, this.onResponseHeaders);
emit(EVENTS.UPDATING_RESPONSE_HEADERS, actor);
break;
case "responseCookies":
this.webConsoleClient.getResponseCookies(actor, this.onResponseCookies);
emit(EVENTS.UPDATING_RESPONSE_COOKIES, actor);
break;
case "responseStart":
this.updateRequest(actor, {
httpVersion: networkInfo.response.httpVersion,
remoteAddress: networkInfo.response.remoteAddress,
remotePort: networkInfo.response.remotePort,
status: networkInfo.response.status,
statusText: networkInfo.response.statusText,
headersSize: networkInfo.response.headersSize
}).then(() => {
emit(EVENTS.STARTED_RECEIVING_RESPONSE, actor);
});
break;
case "responseContent":
this.webConsoleClient.getResponseContent(actor,
this.onResponseContent.bind(this, {
contentSize: networkInfo.response.bodySize,
transferredSize: networkInfo.response.transferredSize,
mimeType: networkInfo.response.content.mimeType
}));
emit(EVENTS.UPDATING_RESPONSE_CONTENT, actor);
break;
case "eventTimings":
this.updateRequest(actor, { totalTime: networkInfo.totalTime })
.then(() => {
this.webConsoleClient.getEventTimings(actor, this.onEventTimings);
emit(EVENTS.UPDATING_EVENT_TIMINGS, actor);
});
break;
}
}
/**
* Handles additional information received for a "requestHeaders" packet.
*
* @param {object} response the message received from the server.
*/
onRequestHeaders(response) {
this.updateRequest(response.from, {
requestHeaders: response
}).then(() => {
emit(EVENTS.RECEIVED_REQUEST_HEADERS, response.from);
});
}
/**
* Handles additional information received for a "requestCookies" packet.
*
* @param {object} response the message received from the server.
*/
onRequestCookies(response) {
this.updateRequest(response.from, {
requestCookies: response
}).then(() => {
emit(EVENTS.RECEIVED_REQUEST_COOKIES, response.from);
});
}
/**
* Handles additional information received for a "requestPostData" packet.
*
* @param {object} response the message received from the server.
*/
onRequestPostData(response) {
this.updateRequest(response.from, {
requestPostData: response
}).then(() => {
emit(EVENTS.RECEIVED_REQUEST_POST_DATA, response.from);
});
}
/**
* Handles additional information received for a "securityInfo" packet.
*
* @param {object} response the message received from the server.
*/
onSecurityInfo(response) {
this.updateRequest(response.from, {
securityInfo: response.securityInfo
}).then(() => {
emit(EVENTS.RECEIVED_SECURITY_INFO, response.from);
});
}
/**
* Handles additional information received for a "responseHeaders" packet.
*
* @param {object} response the message received from the server.
*/
onResponseHeaders(response) {
this.updateRequest(response.from, {
responseHeaders: response
}).then(() => {
emit(EVENTS.RECEIVED_RESPONSE_HEADERS, response.from);
});
}
/**
* Handles additional information received for a "responseCookies" packet.
*
* @param {object} response the message received from the server.
*/
onResponseCookies(response) {
this.updateRequest(response.from, {
responseCookies: response
}).then(() => {
emit(EVENTS.RECEIVED_RESPONSE_COOKIES, response.from);
});
}
/**
* Handles additional information received for a "responseContent" packet.
*
* @param {object} data the message received from the server event.
* @param {object} response the message received from the server.
*/
onResponseContent(data, response) {
let payload = Object.assign({ responseContent: response }, data);
this.updateRequest(response.from, payload).then(() => {
emit(EVENTS.RECEIVED_RESPONSE_CONTENT, response.from);
});
}
/**
* Handles additional information received for a "eventTimings" packet.
*
* @param {object} response the message received from the server.
*/
onEventTimings(response) {
this.updateRequest(response.from, {
eventTimings: response
}).then(() => {
emit(EVENTS.RECEIVED_EVENT_TIMINGS, response.from);
});
}
}
/**
* Guard 'emit' to avoid exception in non-window environment.
*/
function emit(type, data) {
if (typeof window != "undefined") {
window.emit(type, data);
}
}
module.exports = FirefoxDataProvider;

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

@ -4,5 +4,6 @@
DevToolsModules( DevToolsModules(
'firefox-connector.js', 'firefox-connector.js',
'firefox-data-provider.js',
'index.js', 'index.js',
) )

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

@ -94,6 +94,44 @@ const EVENTS = {
CONNECTED: "connected", CONNECTED: "connected",
}; };
const UPDATE_PROPS = [
"method",
"url",
"remotePort",
"remoteAddress",
"status",
"statusText",
"httpVersion",
"securityState",
"securityInfo",
"mimeType",
"contentSize",
"transferredSize",
"totalTime",
"eventTimings",
"headersSize",
"customQueryValue",
"requestHeaders",
"requestHeadersFromUploadStream",
"requestCookies",
"requestPostData",
"responseHeaders",
"responseCookies",
"responseContent",
"responseContentDataUri",
"formDataSections",
];
const PANELS = {
COOKIES: "cookies",
HEADERS: "headers",
PARAMS: "params",
RESPONSE: "response",
SECURITY: "security",
STACK_TRACE: "stack-trace",
TIMINGS: "timings",
};
const RESPONSE_HEADERS = [ const RESPONSE_HEADERS = [
"Cache-Control", "Cache-Control",
"Connection", "Connection",
@ -246,11 +284,13 @@ const general = {
ACTIVITY_TYPE, ACTIVITY_TYPE,
EVENTS, EVENTS,
FILTER_SEARCH_DELAY: 200, FILTER_SEARCH_DELAY: 200,
UPDATE_PROPS,
HEADERS, HEADERS,
RESPONSE_HEADERS, RESPONSE_HEADERS,
FILTER_FLAGS, FILTER_FLAGS,
SOURCE_EDITOR_SYNTAX_HIGHLIGHT_MAX_SIZE: 51200, // 50 KB in bytes SOURCE_EDITOR_SYNTAX_HIGHLIGHT_MAX_SIZE: 51200, // 50 KB in bytes
REQUESTS_WATERFALL, REQUESTS_WATERFALL,
PANELS,
}; };
// flatten constants // flatten constants

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

@ -15,6 +15,7 @@ const {
SELECT_REQUEST, SELECT_REQUEST,
SEND_CUSTOM_REQUEST, SEND_CUSTOM_REQUEST,
UPDATE_REQUEST, UPDATE_REQUEST,
UPDATE_PROPS,
} = require("../constants"); } = require("../constants");
const Request = I.Record({ const Request = I.Record({
@ -68,34 +69,6 @@ const Requests = I.Record({
lastEndedMillis: -Infinity, lastEndedMillis: -Infinity,
}); });
const UPDATE_PROPS = [
"method",
"url",
"remotePort",
"remoteAddress",
"status",
"statusText",
"httpVersion",
"securityState",
"securityInfo",
"mimeType",
"contentSize",
"transferredSize",
"totalTime",
"eventTimings",
"headersSize",
"customQueryValue",
"requestHeaders",
"requestHeadersFromUploadStream",
"requestCookies",
"requestPostData",
"responseHeaders",
"responseCookies",
"responseContent",
"responseContentDataUri",
"formDataSections",
];
/** /**
* Remove the currently selected custom request. * Remove the currently selected custom request.
*/ */

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

@ -19,6 +19,7 @@ const {
SELECT_REQUEST, SELECT_REQUEST,
TOGGLE_COLUMN, TOGGLE_COLUMN,
WATERFALL_RESIZE, WATERFALL_RESIZE,
PANELS,
} = require("../constants"); } = require("../constants");
const cols = { const cols = {
@ -51,7 +52,7 @@ const Columns = I.Record(
const UI = I.Record({ const UI = I.Record({
columns: new Columns(), columns: new Columns(),
detailsPanelSelectedTab: "headers", detailsPanelSelectedTab: PANELS.HEADERS,
networkDetailsOpen: false, networkDetailsOpen: false,
browserCacheDisabled: Services.prefs.getBoolPref("devtools.cache.disabled"), browserCacheDisabled: Services.prefs.getBoolPref("devtools.cache.disabled"),
statisticsOpen: false, statisticsOpen: false,

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

@ -13,9 +13,6 @@ pref("devtools.devedition.promo.url", "https://www.mozilla.org/firefox/developer
pref("devtools.devedition.promo.enabled", false); pref("devtools.devedition.promo.enabled", false);
#endif #endif
// DevTools development workflow
pref("devtools.loader.hotreload", false);
// Developer toolbar preferences // Developer toolbar preferences
pref("devtools.toolbar.enabled", true); pref("devtools.toolbar.enabled", true);

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

@ -3,8 +3,7 @@ tags = devtools
subsuite = devtools subsuite = devtools
# !e10s: RDM only works for remote tabs # !e10s: RDM only works for remote tabs
# Win: Bug 1319248 # Win: Bug 1319248
# Stylo: Bug 1384701 skip-if = !e10s || os == "win"
skip-if = !e10s || os == "win" || (stylo && os == "linux" && debug)
support-files = support-files =
devices.json devices.json
doc_page_state.html doc_page_state.html

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

@ -8,7 +8,6 @@ const loaders = Cu.import("resource://gre/modules/commonjs/toolkit/loader.js", {
const { devtools } = Cu.import("resource://devtools/shared/Loader.jsm", {}); const { devtools } = Cu.import("resource://devtools/shared/Loader.jsm", {});
const { joinURI } = devtools.require("devtools/shared/path"); const { joinURI } = devtools.require("devtools/shared/path");
const { assert } = devtools.require("devtools/shared/DevToolsUtils"); const { assert } = devtools.require("devtools/shared/DevToolsUtils");
const Services = devtools.require("Services");
const { AppConstants } = devtools.require("resource://gre/modules/AppConstants.jsm"); const { AppConstants } = devtools.require("resource://gre/modules/AppConstants.jsm");
const BROWSER_BASED_DIRS = [ const BROWSER_BASED_DIRS = [
@ -37,10 +36,6 @@ const COMMON_LIBRARY_DIRS = [
const browserBasedDirsRegExp = const browserBasedDirsRegExp =
/^resource\:\/\/devtools\/client\/\S*\/components\//; /^resource\:\/\/devtools\/client\/\S*\/components\//;
function clearCache() {
Services.obs.notifyObservers(null, "startupcache-invalidate");
}
/* /*
* Create a loader to be used in a browser environment. This evaluates * Create a loader to be used in a browser environment. This evaluates
* modules in their own environment, but sets window (the normal * modules in their own environment, but sets window (the normal
@ -101,7 +96,6 @@ function BrowserLoaderBuilder({ baseURI, window, useOnlyShared, commonLibRequire
const loaderOptions = devtools.require("@loader/options"); const loaderOptions = devtools.require("@loader/options");
const dynamicPaths = {}; const dynamicPaths = {};
const componentProxies = new Map();
if (AppConstants.DEBUG || AppConstants.DEBUG_JS_MODULES) { if (AppConstants.DEBUG || AppConstants.DEBUG_JS_MODULES) {
dynamicPaths["devtools/client/shared/vendor/react"] = dynamicPaths["devtools/client/shared/vendor/react"] =
@ -169,39 +163,6 @@ function BrowserLoaderBuilder({ baseURI, window, useOnlyShared, commonLibRequire
} }
}; };
if (Services.prefs.getBoolPref("devtools.loader.hotreload")) {
opts.loadModuleHook = (module, require) => {
const { uri, exports } = module;
if (exports.prototype &&
exports.prototype.isReactComponent) {
const { createProxy, getForceUpdate } =
require("devtools/client/shared/vendor/react-proxy");
const React = require("devtools/client/shared/vendor/react");
if (!componentProxies.get(uri)) {
const proxy = createProxy(exports);
componentProxies.set(uri, proxy);
module.exports = proxy.get();
} else {
const proxy = componentProxies.get(uri);
const instances = proxy.update(exports);
instances.forEach(getForceUpdate(React));
module.exports = proxy.get();
}
}
return exports;
};
const watcher = devtools.require("devtools/client/shared/devtools-file-watcher");
let onFileChanged = (_, relativePath, path) => {
this.hotReloadFile(componentProxies, "resource://devtools/" + relativePath);
};
watcher.on("file-changed", onFileChanged);
window.addEventListener("unload", () => {
watcher.off("file-changed", onFileChanged);
});
}
const mainModule = loaders.Module(baseURI, joinURI(baseURI, "main.js")); const mainModule = loaders.Module(baseURI, joinURI(baseURI, "main.js"));
this.loader = loaders.Loader(opts); this.loader = loaders.Loader(opts);
this.require = loaders.Require(this.loader, mainModule); this.require = loaders.Require(this.loader, mainModule);
@ -228,20 +189,6 @@ BrowserLoaderBuilder.prototype = {
? this.require(module)[property] ? this.require(module)[property]
: this.require(module || property); : this.require(module || property);
}); });
},
hotReloadFile: function (componentProxies, fileURI) {
if (fileURI.match(/\.js$/)) {
// Test for React proxy components
const proxy = componentProxies.get(fileURI);
if (proxy) {
// Remove the old module and re-require the new one; the require
// hook in the loader will take care of the rest
delete this.loader.modules[fileURI];
clearCache();
this.require(fileURI);
}
}
} }
}; };

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

@ -1,142 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { Services } = require("resource://gre/modules/Services.jsm");
const { getTheme } = require("devtools/client/shared/theme");
function iterStyleNodes(window, func) {
for (let node of window.document.childNodes) {
// Look for ProcessingInstruction nodes.
if (node.nodeType === 7) {
func(node);
}
}
const links = window.document.getElementsByTagNameNS(
"http://www.w3.org/1999/xhtml", "link"
);
for (let node of links) {
func(node);
}
}
function replaceCSS(window, fileURI) {
const document = window.document;
const randomKey = Math.random();
Services.obs.notifyObservers(null, "startupcache-invalidate");
// Scan every CSS tag and reload ones that match the file we are
// looking for.
iterStyleNodes(window, node => {
if (node.nodeType === 7) {
// xml-stylesheet declaration
if (node.data.includes(fileURI)) {
const newNode = window.document.createProcessingInstruction(
"xml-stylesheet",
`href="${fileURI}?s=${randomKey}" type="text/css"`
);
document.insertBefore(newNode, node);
document.removeChild(node);
}
} else if (node.href.includes(fileURI)) {
const parentNode = node.parentNode;
const newNode = window.document.createElementNS(
"http://www.w3.org/1999/xhtml",
"link"
);
newNode.rel = "stylesheet";
newNode.type = "text/css";
newNode.href = fileURI + "?s=" + randomKey;
parentNode.insertBefore(newNode, node);
parentNode.removeChild(node);
}
});
}
function _replaceResourceInSheet(sheet, filename, randomKey) {
for (let i = 0; i < sheet.cssRules.length; i++) {
const rule = sheet.cssRules[i];
if (rule.type === rule.IMPORT_RULE) {
_replaceResourceInSheet(rule.styleSheet, filename);
} else if (rule.cssText.includes(filename)) {
// Strip off any existing query strings. This might lose
// updates for files if there are multiple resources
// referenced in the same rule, but the chances of someone hot
// reloading multiple resources in the same rule is very low.
const text = rule.cssText.replace(/\?s=0.\d+/g, "");
const newRule = (
text.replace(filename, filename + "?s=" + randomKey)
);
sheet.deleteRule(i);
sheet.insertRule(newRule, i);
}
}
}
function replaceCSSResource(window, fileURI) {
const document = window.document;
const randomKey = Math.random();
// Only match the filename. False positives are much better than
// missing updates, as all that would happen is we reload more
// resources than we need. We do this because many resources only
// use relative paths.
const parts = fileURI.split("/");
const file = parts[parts.length - 1];
// Scan every single rule in the entire page for any reference to
// this resource, and re-insert the rule to force it to update.
for (let sheet of document.styleSheets) {
_replaceResourceInSheet(sheet, file, randomKey);
}
for (let node of document.querySelectorAll("img,image")) {
if (node.src.startsWith(fileURI)) {
node.src = fileURI + "?s=" + randomKey;
}
}
}
function watchCSS(window) {
if (Services.prefs.getBoolPref("devtools.loader.hotreload")) {
const watcher = require("devtools/client/shared/devtools-file-watcher");
function onFileChanged(_, relativePath) {
if (relativePath.match(/\.css$/)) {
if (relativePath.startsWith("client/themes")) {
let path = relativePath.replace(/^client\/themes\//, "");
// Special-case a few files that get imported from other CSS
// files. We just manually hot reload the parent CSS file.
if (path === "variables.css" || path === "toolbars.css" ||
path === "common.css" || path === "splitters.css") {
replaceCSS(window, "chrome://devtools/skin/" + getTheme() + "-theme.css");
} else {
replaceCSS(window, "chrome://devtools/skin/" + path);
}
return;
}
replaceCSS(
window,
"chrome://devtools/content/" + relativePath.replace(/^client\//, "")
);
replaceCSS(window, "resource://devtools/" + relativePath);
} else if (relativePath.match(/\.(svg|png)$/)) {
relativePath = relativePath.replace(/^client\/themes\//, "");
replaceCSSResource(window, "chrome://devtools/skin/" + relativePath);
}
}
watcher.on("file-changed", onFileChanged);
window.addEventListener("unload", () => {
watcher.off("file-changed", onFileChanged);
});
}
}
module.exports = { watchCSS };

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

@ -1,78 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { Ci } = require("chrome");
const Services = require("Services");
const EventEmitter = require("devtools/shared/old-event-emitter");
loader.lazyImporter(this, "OS", "resource://gre/modules/osfile.jsm");
const HOTRELOAD_PREF = "devtools.loader.hotreload";
function resolveResourcePath(uri) {
const handler = Services.io.getProtocolHandler("resource")
.QueryInterface(Ci.nsIResProtocolHandler);
const resolved = handler.resolveURI(Services.io.newURI(uri));
return resolved.replace(/file:\/\//, "");
}
function findSourceDir(path) {
if (path === "" || path === "/") {
return Promise.resolve(null);
}
return OS.File.exists(
OS.Path.join(path, "devtools/client/shared/file-watcher.js")
).then(exists => {
if (exists) {
return path;
}
return findSourceDir(OS.Path.dirname(path));
});
}
let worker = null;
const onPrefChange = function () {
// We need to figure out a src dir to watch. These are the actual
// files the user is working with, not the files in the obj dir. We
// do this by walking up the filesystem and looking for the devtools
// directories, and falling back to the raw path. This means none of
// this will work for users who store their obj dirs outside of the
// src dir.
//
// We take care not to mess with the `devtoolsPath` if that's what
// we end up using, because it might be intentionally mapped to a
// specific place on the filesystem for loading devtools externally.
//
// `devtoolsPath` is currently the devtools directory inside of the
// obj dir, and we search for `devtools/client`, so go up 2 levels
// to skip that devtools dir and start searching for the src dir.
if (Services.prefs.getBoolPref(HOTRELOAD_PREF) && !worker) {
const devtoolsPath = resolveResourcePath("resource://devtools")
.replace(/\/$/, "");
const searchPoint = OS.Path.dirname(OS.Path.dirname(devtoolsPath));
findSourceDir(searchPoint)
.then(srcPath => {
const rootPath = srcPath ? OS.Path.join(srcPath, "devtools")
: devtoolsPath;
const watchPath = OS.Path.join(rootPath, "client");
const { watchFiles } = require("devtools/client/shared/file-watcher");
worker = watchFiles(watchPath, path => {
let relativePath = path.replace(rootPath + "/", "");
module.exports.emit("file-changed", relativePath, path);
});
});
} else if (worker) {
worker.terminate();
worker = null;
}
};
Services.prefs.addObserver(HOTRELOAD_PREF, {
observe: onPrefChange
});
onPrefChange();
EventEmitter.decorate(module.exports);

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

@ -1,81 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
/* eslint-env worker */
/* global OS */
importScripts("resource://gre/modules/osfile.jsm");
const modifiedTimes = new Map();
function gatherFiles(path, fileRegex) {
let files = [];
const iterator = new OS.File.DirectoryIterator(path);
try {
for (let child of iterator) {
// Don't descend into test directories. Saves us some time and
// there's no reason to.
if (child.isDir && !child.path.endsWith("/test")) {
files = files.concat(gatherFiles(child.path, fileRegex));
} else if (child.path.match(fileRegex)) {
let info;
try {
info = OS.File.stat(child.path);
} catch (e) {
// Just ignore it.
continue;
}
files.push(child.path);
modifiedTimes.set(child.path, info.lastModificationDate.getTime());
}
}
} finally {
iterator.close();
}
return files;
}
function scanFiles(files, onChangedFile) {
files.forEach(file => {
let info;
try {
info = OS.File.stat(file);
} catch (e) {
// Just ignore it. It was probably deleted.
return;
}
const lastTime = modifiedTimes.get(file);
if (info.lastModificationDate.getTime() > lastTime) {
modifiedTimes.set(file, info.lastModificationDate.getTime());
onChangedFile(file);
}
});
}
onmessage = function (event) {
const { path, fileRegex } = event.data;
const info = OS.File.stat(path);
if (!info.isDir) {
throw new Error("Watcher expects a directory as root path");
}
// We get a list of all the files upfront, which means we don't
// support adding new files. But you need to rebuild Firefox when
// adding a new file anyway.
const files = gatherFiles(path, fileRegex || /.*/);
// Every second, scan for file changes by stat-ing each of them and
// comparing modification time.
setInterval(() => {
scanFiles(files, changedFile => {
postMessage({ path: changedFile });
});
}, 1000);
};

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

@ -1,28 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { ChromeWorker } = require("chrome");
function watchFiles(path, onFileChanged) {
const watchWorker = new ChromeWorker(
"resource://devtools/client/shared/file-watcher-worker.js"
);
watchWorker.onmessage = event => {
// We need to turn a local path back into a resource URI (or
// chrome). This means that this system will only work when built
// files are symlinked, so that these URIs actually read from
// local sources. There might be a better way to do this.
const { path: newPath } = event.data;
onFileChanged(newPath);
};
watchWorker.postMessage({
path,
fileRegex: /\.(js|css|svg|png)$/
});
return watchWorker;
}
exports.watchFiles = watchFiles;

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

@ -20,18 +20,14 @@ DevToolsModules(
'autocomplete-popup.js', 'autocomplete-popup.js',
'browser-loader.js', 'browser-loader.js',
'css-angle.js', 'css-angle.js',
'css-reload.js',
'curl.js', 'curl.js',
'demangle.js', 'demangle.js',
'developer-toolbar.js', 'developer-toolbar.js',
'devices.js', 'devices.js',
'devtools-file-watcher.js',
'DOMHelpers.jsm', 'DOMHelpers.jsm',
'doorhanger.js', 'doorhanger.js',
'enum.js', 'enum.js',
'file-saver.js', 'file-saver.js',
'file-watcher-worker.js',
'file-watcher.js',
'getjson.js', 'getjson.js',
'inplace-editor.js', 'inplace-editor.js',
'Jsbeautify.jsm', 'Jsbeautify.jsm',

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

@ -9,7 +9,6 @@
const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {}); const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
const Services = require("Services"); const Services = require("Services");
const { gDevTools } = require("devtools/client/framework/devtools"); const { gDevTools } = require("devtools/client/framework/devtools");
const { watchCSS } = require("devtools/client/shared/css-reload");
const { appendStyleSheet } = require("devtools/client/shared/stylesheet-utils"); const { appendStyleSheet } = require("devtools/client/shared/stylesheet-utils");
let documentElement = document.documentElement; let documentElement = document.documentElement;
@ -154,6 +153,4 @@
Services.prefs.removeObserver("devtools.theme", handlePrefChange); Services.prefs.removeObserver("devtools.theme", handlePrefChange);
}, { once: true }); }, { once: true });
} }
watchCSS(window);
})(); })();

1
devtools/client/shared/vendor/moz.build поставляемый
Просмотреть файл

@ -25,7 +25,6 @@ if CONFIG['DEBUG_JS_MODULES'] or CONFIG['MOZ_DEBUG']:
modules += [ modules += [
'react-dom-server.js', 'react-dom-server.js',
'react-dom.js', 'react-dom.js',
'react-proxy.js',
'react-redux.js', 'react-redux.js',
'react-virtualized.js', 'react-virtualized.js',
'react.js', 'react.js',

1909
devtools/client/shared/vendor/react-proxy.js поставляемый

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -49,6 +49,8 @@
<hbox id="storage-toolbar" class="devtools-toolbar"> <hbox id="storage-toolbar" class="devtools-toolbar">
<button id="add-button" <button id="add-button"
class="devtools-button add-button"></button> class="devtools-button add-button"></button>
<button id="refresh-button"
class="devtools-button refresh-button"></button>
<spacer flex="1"/> <spacer flex="1"/>
<textbox id="storage-searchbox" <textbox id="storage-searchbox"
class="devtools-filterinput" class="devtools-filterinput"

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

@ -169,6 +169,7 @@ function StorageUI(front, target, panelWin, toolbox) {
this._tablePopup = this._panelDoc.getElementById("storage-table-popup"); this._tablePopup = this._panelDoc.getElementById("storage-table-popup");
this._tablePopup.addEventListener("popupshowing", this.onTablePopupShowing); this._tablePopup.addEventListener("popupshowing", this.onTablePopupShowing);
this.onRefreshTable = this.onRefreshTable.bind(this);
this.onAddItem = this.onAddItem.bind(this); this.onAddItem = this.onAddItem.bind(this);
this.onRemoveItem = this.onRemoveItem.bind(this); this.onRemoveItem = this.onRemoveItem.bind(this);
this.onRemoveAllFrom = this.onRemoveAllFrom.bind(this); this.onRemoveAllFrom = this.onRemoveAllFrom.bind(this);
@ -176,6 +177,9 @@ function StorageUI(front, target, panelWin, toolbox) {
this.onRemoveAllSessionCookies = this.onRemoveAllSessionCookies.bind(this); this.onRemoveAllSessionCookies = this.onRemoveAllSessionCookies.bind(this);
this.onRemoveTreeItem = this.onRemoveTreeItem.bind(this); this.onRemoveTreeItem = this.onRemoveTreeItem.bind(this);
this._refreshButton = this._panelDoc.getElementById("refresh-button");
this._refreshButton.addEventListener("command", this.onRefreshTable);
this._addButton = this._panelDoc.getElementById("add-button"); this._addButton = this._panelDoc.getElementById("add-button");
this._addButton.addEventListener("command", this.onAddItem); this._addButton.addEventListener("command", this.onAddItem);
@ -242,6 +246,7 @@ StorageUI.prototype = {
this.sidebarToggleBtn = null; this.sidebarToggleBtn = null;
this._treePopup.removeEventListener("popupshowing", this.onTreePopupShowing); this._treePopup.removeEventListener("popupshowing", this.onTreePopupShowing);
this._refreshButton.removeEventListener("command", this.onRefreshTable);
this._addButton.removeEventListener("command", this.onAddItem); this._addButton.removeEventListener("command", this.onAddItem);
this._tablePopupAddItem.removeEventListener("command", this.onAddItem); this._tablePopupAddItem.removeEventListener("command", this.onAddItem);
this._treePopupDeleteAll.removeEventListener("command", this.onRemoveAll); this._treePopupDeleteAll.removeEventListener("command", this.onRemoveAll);
@ -1162,6 +1167,13 @@ StorageUI.prototype = {
} }
}, },
/**
* Handles refreshing the selected storage
*/
onRefreshTable: function (event) {
this.onHostSelect(event, this.tree.selectedItem);
},
/** /**
* Handles adding an item from the storage * Handles adding an item from the storage
*/ */

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

@ -1,6 +1,7 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public <!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this - License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 14" width="14" height="14"> <svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<path d="M12,7H6l2.4-2.4C7.6,4,6.6,3.8,5.5,4.1C4.3,4.5,3.3,5.5,3,6.8 C2.6,9,4.3,11,6.5,11c1,0,2-0.5,2.6-1.2l1.7,1c-1.3,1.6-3.3,2.5-5.6,2c-2-0.5-3.6-2.1-4-4.1C0.4,5.1,3.1,2,6.5,2 c1.3,0,2.4,0.4,3.3,1.2L12,1V7z"/> <path d="M13.917 7C13.44 4.162 10.973 2 8 2 4.686 2 2 4.686 2 8s2.686 6 6 6c2.22 0 4.16-1.207 5.197-3H12c-.912 1.214-2.364 2-4 2-2.76 0-5-2.24-5-5s2.24-5 5-5c2.42 0 4.437 1.718 4.9 4h1.017z"/>
<path d="M14 1L8 7h6V1zm-1 1L9 6h4V2z" fill-rule="evenodd"/>
</svg> </svg>

До

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

После

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

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

@ -37,6 +37,11 @@
-moz-user-focus: normal; -moz-user-focus: normal;
} }
#storage-toolbar .refresh-button::before {
background-image: url("chrome://devtools/skin/images/reload.svg");
-moz-user-focus: normal;
}
#storage-toolbar .devtools-button { #storage-toolbar .devtools-button {
min-width: 0; min-width: 0;
} }

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

@ -854,14 +854,43 @@ a.learn-more-link.webconsole-learn-more-link {
background-position: -36px -36px; background-position: -36px -36px;
} }
/* Network Messages */
.message.network .method { .message.network .method {
margin-inline-end: 5px; margin-inline-end: 5px;
} }
.network.message .network-info {
display: none;
margin-top: 8px;
border: solid 1px var(--theme-splitter-color);
}
.network.message.open .network-info {
display: block;
}
.network.message .network-info .panels {
max-height: 250px;
min-height: 100px;
}
/* Hide 'Edit And Resend' button since the feature isn't
supported in the Console panel. */
.network.message #headers-panel .edit-and-resend-button {
display: none;
}
.network.message #response-panel .treeTable {
overflow-y: hidden;
}
.network .message-flex-body > .message-body { .network .message-flex-body > .message-body {
display: flex; display: flex;
} }
/* Output Wrapper */
.webconsole-output-wrapper .message .indent { .webconsole-output-wrapper .message .indent {
display: inline-block; display: inline-block;
border-inline-end: solid 1px var(--theme-splitter-color); border-inline-end: solid 1px var(--theme-splitter-color);

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

@ -15,6 +15,7 @@ const { batchActions } = require("devtools/client/shared/redux/middleware/deboun
const { const {
MESSAGE_ADD, MESSAGE_ADD,
NETWORK_MESSAGE_UPDATE, NETWORK_MESSAGE_UPDATE,
NETWORK_UPDATE_REQUEST,
MESSAGES_CLEAR, MESSAGES_CLEAR,
MESSAGE_OPEN, MESSAGE_OPEN,
MESSAGE_CLOSE, MESSAGE_CLOSE,
@ -94,7 +95,7 @@ function messageTableDataReceive(id, data) {
}; };
} }
function networkMessageUpdate(packet, idGenerator = null) { function networkMessageUpdate(packet, idGenerator = null, response) {
if (idGenerator == null) { if (idGenerator == null) {
idGenerator = defaultIdGenerator; idGenerator = defaultIdGenerator;
} }
@ -104,6 +105,15 @@ function networkMessageUpdate(packet, idGenerator = null) {
return { return {
type: NETWORK_MESSAGE_UPDATE, type: NETWORK_MESSAGE_UPDATE,
message, message,
response,
};
}
function networkUpdateRequest(id, data) {
return {
type: NETWORK_UPDATE_REQUEST,
id,
data,
}; };
} }
@ -179,6 +189,7 @@ module.exports = {
messageClose, messageClose,
messageTableDataGet, messageTableDataGet,
networkMessageUpdate, networkMessageUpdate,
networkUpdateRequest,
messageObjectPropertiesLoad, messageObjectPropertiesLoad,
messageObjectEntriesLoad, messageObjectEntriesLoad,
// for test purpose only. // for test purpose only.

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

@ -13,6 +13,7 @@ const {
FILTER_BAR_TOGGLE, FILTER_BAR_TOGGLE,
PREFS, PREFS,
TIMESTAMPS_TOGGLE, TIMESTAMPS_TOGGLE,
SELECT_NETWORK_MESSAGE_TAB,
} = require("devtools/client/webconsole/new-console-output/constants"); } = require("devtools/client/webconsole/new-console-output/constants");
function filterBarToggle(show) { function filterBarToggle(show) {
@ -32,7 +33,15 @@ function timestampsToggle(visible) {
}; };
} }
function selectNetworkMessageTab(id) {
return {
type: SELECT_NETWORK_MESSAGE_TAB,
id,
};
}
module.exports = { module.exports = {
filterBarToggle, filterBarToggle,
timestampsToggle, timestampsToggle,
selectNetworkMessageTab,
}; };

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

@ -46,6 +46,7 @@ const ConsoleOutput = createClass({
messagesRepeat: PropTypes.object.isRequired, messagesRepeat: PropTypes.object.isRequired,
networkMessagesUpdate: PropTypes.object.isRequired, networkMessagesUpdate: PropTypes.object.isRequired,
visibleMessages: PropTypes.array.isRequired, visibleMessages: PropTypes.array.isRequired,
networkMessageActiveTabId: PropTypes.string.isRequired,
}, },
componentDidMount() { componentDidMount() {
@ -67,7 +68,7 @@ const ConsoleOutput = createClass({
const visibleMessagesDelta = const visibleMessagesDelta =
nextProps.visibleMessages.length - this.props.visibleMessages.length; nextProps.visibleMessages.length - this.props.visibleMessages.length;
const messagesDelta = const messagesDelta =
nextProps.messages.length - this.props.messages.length; nextProps.messages.size - this.props.messages.size;
// We need to scroll to the bottom if: // We need to scroll to the bottom if:
// - the number of messages displayed changed // - the number of messages displayed changed
@ -102,6 +103,7 @@ const ConsoleOutput = createClass({
messagesObjectEntries, messagesObjectEntries,
messagesRepeat, messagesRepeat,
networkMessagesUpdate, networkMessagesUpdate,
networkMessageActiveTabId,
serviceContainer, serviceContainer,
timestampsVisible, timestampsVisible,
} = this.props; } = this.props;
@ -116,6 +118,7 @@ const ConsoleOutput = createClass({
timestampsVisible, timestampsVisible,
repeat: messagesRepeat[messageId], repeat: messagesRepeat[messageId],
networkMessageUpdate: networkMessagesUpdate[messageId], networkMessageUpdate: networkMessagesUpdate[messageId],
networkMessageActiveTabId,
getMessage: () => messages.get(messageId), getMessage: () => messages.get(messageId),
loadedObjectProperties: messagesObjectProperties.get(messageId), loadedObjectProperties: messagesObjectProperties.get(messageId),
loadedObjectEntries: messagesObjectEntries.get(messageId), loadedObjectEntries: messagesObjectEntries.get(messageId),
@ -156,6 +159,7 @@ function mapStateToProps(state, props) {
messagesRepeat: getAllRepeatById(state), messagesRepeat: getAllRepeatById(state),
networkMessagesUpdate: getAllNetworkMessagesUpdateById(state), networkMessagesUpdate: getAllNetworkMessagesUpdateById(state),
timestampsVisible: state.ui.timestampsVisible, timestampsVisible: state.ui.timestampsVisible,
networkMessageActiveTabId: state.ui.networkMessageActiveTabId,
}; };
} }

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

@ -13,7 +13,9 @@ const {
PropTypes PropTypes
} = require("devtools/client/shared/vendor/react"); } = require("devtools/client/shared/vendor/react");
const Message = createFactory(require("devtools/client/webconsole/new-console-output/components/message")); const Message = createFactory(require("devtools/client/webconsole/new-console-output/components/message"));
const actions = require("devtools/client/webconsole/new-console-output/actions/index");
const { l10n } = require("devtools/client/webconsole/new-console-output/utils/messages"); const { l10n } = require("devtools/client/webconsole/new-console-output/utils/messages");
const TabboxPanel = createFactory(require("devtools/client/netmonitor/src/components/tabbox-panel"));
NetworkEventMessage.displayName = "NetworkEventMessage"; NetworkEventMessage.displayName = "NetworkEventMessage";
@ -26,14 +28,30 @@ NetworkEventMessage.propTypes = {
networkMessageUpdate: PropTypes.object.isRequired, networkMessageUpdate: PropTypes.object.isRequired,
}; };
/**
* This component is responsible for rendering network messages
* in the Console panel.
*
* Network logs are expandable and the user can inspect it inline
* within the Console panel (no need to switch to the Network panel).
*
* HTTP details are rendered using `TabboxPanel` component used to
* render contents of the side bar in the Network panel.
*
* All HTTP details data are fetched from the backend on-demand
* when the user is expanding network log for the first time.
*/
function NetworkEventMessage({ function NetworkEventMessage({
message = {}, message = {},
serviceContainer, serviceContainer,
timestampsVisible, timestampsVisible,
networkMessageUpdate = {}, networkMessageUpdate = {},
networkMessageActiveTabId,
dispatch,
open,
}) { }) {
const { const {
actor, id,
indent, indent,
source, source,
type, type,
@ -61,27 +79,50 @@ function NetworkEventMessage({
statusInfo = `[${httpVersion} ${status} ${statusText} ${totalTime}ms]`; statusInfo = `[${httpVersion} ${status} ${statusText} ${totalTime}ms]`;
} }
const openNetworkMonitor = serviceContainer.openNetworkPanel const toggle = () => {
? () => serviceContainer.openNetworkPanel(actor) if (open) {
: null; dispatch(actions.messageClose(id));
} else {
dispatch(actions.messageOpen(id));
}
};
// Message body components.
const method = dom.span({className: "method" }, request.method); const method = dom.span({className: "method" }, request.method);
const xhr = isXHR const xhr = isXHR
? dom.span({ className: "xhr" }, l10n.getStr("webConsoleXhrIndicator")) ? dom.span({ className: "xhr" }, l10n.getStr("webConsoleXhrIndicator"))
: null; : null;
const url = dom.a({ className: "url", title: request.url, onClick: openNetworkMonitor }, const url = dom.a({ className: "url", title: request.url, onClick: toggle },
request.url.replace(/\?.+/, "")); request.url.replace(/\?.+/, ""));
const statusBody = statusInfo const statusBody = statusInfo
? dom.a({ className: "status", onClick: openNetworkMonitor }, statusInfo) ? dom.a({ className: "status", onClick: toggle }, statusInfo)
: null; : null;
const messageBody = [method, xhr, url, statusBody]; const messageBody = [method, xhr, url, statusBody];
// Only render the attachment if the network-event is
// actually opened (performance optimization).
const attachment = open && dom.div({className: "network-info devtools-monospace"},
TabboxPanel({
activeTabId: networkMessageActiveTabId,
request: networkMessageUpdate,
sourceMapService: serviceContainer.sourceMapService,
selectTab: (tabId) => {
dispatch(actions.selectNetworkMessageTab(tabId));
},
})
);
return Message({ return Message({
dispatch,
messageId: id,
source, source,
type, type,
level, level,
indent, indent,
collapsible: true,
open,
attachment,
topLevelClasses, topLevelClasses,
timeStamp, timeStamp,
messageBody, messageBody,

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

@ -89,10 +89,11 @@ const Message = createClass({
}, },
onContextMenu(e) { onContextMenu(e) {
let { serviceContainer, source, request } = this.props; let { serviceContainer, source, request, messageId } = this.props;
let messageInfo = { let messageInfo = {
source, source,
request, request,
messageId,
}; };
serviceContainer.openContextMenu(e, messageInfo); serviceContainer.openContextMenu(e, messageInfo);
e.stopPropagation(); e.stopPropagation();

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

@ -12,6 +12,7 @@ const actionTypes = {
MESSAGE_OPEN: "MESSAGE_OPEN", MESSAGE_OPEN: "MESSAGE_OPEN",
MESSAGE_CLOSE: "MESSAGE_CLOSE", MESSAGE_CLOSE: "MESSAGE_CLOSE",
NETWORK_MESSAGE_UPDATE: "NETWORK_MESSAGE_UPDATE", NETWORK_MESSAGE_UPDATE: "NETWORK_MESSAGE_UPDATE",
NETWORK_UPDATE_REQUEST: "NETWORK_UPDATE_REQUEST",
MESSAGE_TABLE_RECEIVE: "MESSAGE_TABLE_RECEIVE", MESSAGE_TABLE_RECEIVE: "MESSAGE_TABLE_RECEIVE",
MESSAGE_OBJECT_PROPERTIES_RECEIVE: "MESSAGE_OBJECT_PROPERTIES_RECEIVE", MESSAGE_OBJECT_PROPERTIES_RECEIVE: "MESSAGE_OBJECT_PROPERTIES_RECEIVE",
MESSAGE_OBJECT_ENTRIES_RECEIVE: "MESSAGE_OBJECT_ENTRIES_RECEIVE", MESSAGE_OBJECT_ENTRIES_RECEIVE: "MESSAGE_OBJECT_ENTRIES_RECEIVE",
@ -22,6 +23,7 @@ const actionTypes = {
FILTERS_CLEAR: "FILTERS_CLEAR", FILTERS_CLEAR: "FILTERS_CLEAR",
DEFAULT_FILTERS_RESET: "DEFAULT_FILTERS_RESET", DEFAULT_FILTERS_RESET: "DEFAULT_FILTERS_RESET",
FILTER_BAR_TOGGLE: "FILTER_BAR_TOGGLE", FILTER_BAR_TOGGLE: "FILTER_BAR_TOGGLE",
SELECT_NETWORK_MESSAGE_TAB: "SELECT_NETWORK_MESSAGE_TAB",
}; };
const prefs = { const prefs = {

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

@ -52,6 +52,11 @@ NewConsoleOutputWrapper.prototype = {
return; return;
} }
// Do not focus if an input field was clicked
if (target.closest("input")) {
return;
}
// Do not focus if something other than the output region was clicked // Do not focus if something other than the output region was clicked
if (!target.closest(".webconsole-output")) { if (!target.closest(".webconsole-output")) {
return; return;
@ -75,31 +80,35 @@ NewConsoleOutputWrapper.prototype = {
}])); }]));
}, },
hudProxyClient: this.jsterm.hud.proxy.client, hudProxyClient: this.jsterm.hud.proxy.client,
openContextMenu: (e, message) => {
let { screenX, screenY, target } = e;
let messageEl = target.closest(".message");
let clipboardText = messageEl ? messageEl.textContent : null;
// Retrieve closes actor id from the DOM.
let actorEl = target.closest("[data-link-actor-id]");
let actor = actorEl ? actorEl.dataset.linkActorId : null;
let menu = createContextMenu(this.jsterm, this.parentNode,
{ actor, clipboardText, message });
// Emit the "menu-open" event for testing.
menu.once("open", () => this.emit("menu-open"));
menu.popup(screenX, screenY, this.toolbox);
return menu;
},
openLink: url => this.jsterm.hud.owner.openLink(url), openLink: url => this.jsterm.hud.owner.openLink(url),
createElement: nodename => { createElement: nodename => {
return this.document.createElementNS("http://www.w3.org/1999/xhtml", nodename); return this.document.createElementNS("http://www.w3.org/1999/xhtml", nodename);
}, },
}; };
// Set `openContextMenu` this way so, `serviceContainer` variable
// is available in the current scope and we can pass it into
// `createContextMenu` method.
serviceContainer.openContextMenu = (e, message) => {
let { screenX, screenY, target } = e;
let messageEl = target.closest(".message");
let clipboardText = messageEl ? messageEl.textContent : null;
// Retrieve closes actor id from the DOM.
let actorEl = target.closest("[data-link-actor-id]");
let actor = actorEl ? actorEl.dataset.linkActorId : null;
let menu = createContextMenu(this.jsterm, this.parentNode,
{ actor, clipboardText, message, serviceContainer });
// Emit the "menu-open" event for testing.
menu.once("open", () => this.emit("menu-open"));
menu.popup(screenX, screenY, this.toolbox);
return menu;
};
if (this.toolbox) { if (this.toolbox) {
Object.assign(serviceContainer, { Object.assign(serviceContainer, {
onViewSourceInDebugger: frame => { onViewSourceInDebugger: frame => {
@ -219,6 +228,19 @@ NewConsoleOutputWrapper.prototype = {
} }
}, },
dispatchRequestUpdate: function (id, data) {
batchedMessageAdd(actions.networkUpdateRequest(id, data));
// Fire an event indicating that all data fetched from
// the backend has been received. This is based on
// 'FirefoxDataProvider.isQueuePayloadReady', see more
// comments in that method.
// (netmonitor/src/connector/firefox-data-provider).
// This event might be utilized in tests to find the right
// time when to finish.
this.jsterm.hud.emit("network-request-payload-ready", {id, data});
},
// Should be used for test purpose only. // Should be used for test purpose only.
getStore: function () { getStore: function () {
return store; return store;

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

@ -22,6 +22,10 @@ const {
const { getGripPreviewItems } = require("devtools/client/shared/components/reps/reps"); const { getGripPreviewItems } = require("devtools/client/shared/components/reps/reps");
const { getSourceNames } = require("devtools/client/shared/source-utils"); const { getSourceNames } = require("devtools/client/shared/source-utils");
const {
UPDATE_PROPS
} = require("devtools/client/netmonitor/src/constants");
const MessageState = Immutable.Record({ const MessageState = Immutable.Record({
// List of all the messages added to the console. // List of all the messages added to the console.
messagesById: Immutable.OrderedMap(), messagesById: Immutable.OrderedMap(),
@ -166,8 +170,10 @@ function messages(state = new MessageState(), action, filtersState, prefsState)
return state.withMutations(function (record) { return state.withMutations(function (record) {
record.set("messagesUiById", messagesUiById.push(action.id)); record.set("messagesUiById", messagesUiById.push(action.id));
let currMessage = messagesById.get(action.id);
// If the message is a group // If the message is a group
if (isGroupType(messagesById.get(action.id).type)) { if (isGroupType(currMessage.type)) {
// We want to make its children visible // We want to make its children visible
const messagesToShow = [...messagesById].reduce((res, [id, message]) => { const messagesToShow = [...messagesById].reduce((res, [id, message]) => {
if ( if (
@ -195,6 +201,21 @@ function messages(state = new MessageState(), action, filtersState, prefsState)
...visibleMessages.slice(insertIndex), ...visibleMessages.slice(insertIndex),
]); ]);
} }
// If the current message is a network event, mark it as opened-once,
// so HTTP details are not fetched again the next time the user
// opens the log.
if (currMessage.source == "network") {
record.set("messagesById",
messagesById.set(
action.id, Object.assign({},
currMessage, {
openedOnce: true
}
)
)
);
}
}); });
case constants.MESSAGE_CLOSE: case constants.MESSAGE_CLOSE:
@ -250,6 +271,44 @@ function messages(state = new MessageState(), action, filtersState, prefsState)
}) })
); );
case constants.NETWORK_UPDATE_REQUEST: {
let request = networkMessagesUpdateById[action.id];
if (!request) {
return state;
}
let values = {};
for (let [key, value] of Object.entries(action.data)) {
if (UPDATE_PROPS.includes(key)) {
values[key] = value;
switch (key) {
case "securityInfo":
values.securityState = value.state;
break;
case "totalTime":
values.totalTime = request.totalTime;
break;
case "requestPostData":
values.requestHeadersFromUploadStream = {
headers: [],
headersSize: 0,
};
break;
}
}
}
let newState = state.set(
"networkMessagesUpdateById",
Object.assign({}, networkMessagesUpdateById, {
[action.id]: Object.assign({}, request, values)
})
);
return newState;
}
case constants.REMOVED_ACTORS_CLEAR: case constants.REMOVED_ACTORS_CLEAR:
return state.set("removedActors", []); return state.set("removedActors", []);

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

@ -7,14 +7,20 @@
const { const {
FILTER_BAR_TOGGLE, FILTER_BAR_TOGGLE,
TIMESTAMPS_TOGGLE TIMESTAMPS_TOGGLE,
SELECT_NETWORK_MESSAGE_TAB,
} = require("devtools/client/webconsole/new-console-output/constants"); } = require("devtools/client/webconsole/new-console-output/constants");
const Immutable = require("devtools/client/shared/vendor/immutable"); const Immutable = require("devtools/client/shared/vendor/immutable");
const {
PANELS,
} = require("devtools/client/netmonitor/src/constants");
const UiState = Immutable.Record({ const UiState = Immutable.Record({
filterBarVisible: false, filterBarVisible: false,
filteredMessageVisible: false, filteredMessageVisible: false,
timestampsVisible: true, timestampsVisible: true,
networkMessageActiveTabId: PANELS.HEADERS,
}); });
function ui(state = new UiState(), action) { function ui(state = new UiState(), action) {
@ -23,6 +29,8 @@ function ui(state = new UiState(), action) {
return state.set("filterBarVisible", !state.filterBarVisible); return state.set("filterBarVisible", !state.filterBarVisible);
case TIMESTAMPS_TOGGLE: case TIMESTAMPS_TOGGLE:
return state.set("timestampsVisible", action.visible); return state.set("timestampsVisible", action.visible);
case SELECT_NETWORK_MESSAGE_TAB:
return state.set("networkMessageActiveTabId", action.id);
} }
return state; return state;

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

@ -17,13 +17,24 @@ const {
} = require("devtools/client/shared/redux/middleware/debounce"); } = require("devtools/client/shared/redux/middleware/debounce");
const { const {
MESSAGE_ADD, MESSAGE_ADD,
MESSAGE_OPEN,
MESSAGES_CLEAR, MESSAGES_CLEAR,
REMOVED_ACTORS_CLEAR, REMOVED_ACTORS_CLEAR,
NETWORK_MESSAGE_UPDATE,
PREFS, PREFS,
} = require("devtools/client/webconsole/new-console-output/constants"); } = require("devtools/client/webconsole/new-console-output/constants");
const { reducers } = require("./reducers/index"); const { reducers } = require("./reducers/index");
const Services = require("Services"); const Services = require("Services");
const {
getMessage,
getAllMessagesUiById,
} = require("devtools/client/webconsole/new-console-output/selectors/messages");
const DataProvider = require("devtools/client/netmonitor/src/connector/firefox-data-provider");
/**
* Create and configure store for the Console panel. This is the place
* where various enhancers and middleware can be registered.
*/
function configureStore(hud, options = {}) { function configureStore(hud, options = {}) {
const logLimit = options.logLimit const logLimit = options.logLimit
|| Math.max(Services.prefs.getIntPref("devtools.hud.loglimit"), 1); || Math.max(Services.prefs.getIntPref("devtools.hud.loglimit"), 1);
@ -42,13 +53,19 @@ function configureStore(hud, options = {}) {
}), }),
ui: new UiState({ ui: new UiState({
filterBarVisible: Services.prefs.getBoolPref(PREFS.UI.FILTER_BAR), filterBarVisible: Services.prefs.getBoolPref(PREFS.UI.FILTER_BAR),
networkMessageActiveTabId: "headers",
}) })
}; };
return createStore( return createStore(
createRootReducer(), createRootReducer(),
initialState, initialState,
compose(applyMiddleware(thunk), enableActorReleaser(hud), enableBatching()) compose(
applyMiddleware(thunk),
enableActorReleaser(hud),
enableBatching(),
enableNetProvider(hud)
)
); );
} }
@ -125,6 +142,69 @@ function enableActorReleaser(hud) {
}; };
} }
/**
* This enhancer is responsible for fetching HTTP details data
* collected by the backend. The fetch happens on-demand
* when the user expands network log in order to inspect it.
*
* This way we don't slow down the Console logging by fetching.
* unnecessary data over RDP.
*/
function enableNetProvider(hud) {
let dataProvider;
return next => (reducer, initialState, enhancer) => {
function netProviderEnhancer(state, action) {
let proxy = hud ? hud.proxy : null;
if (!proxy) {
return reducer(state, action);
}
let actions = {
updateRequest: (id, data, batch) => {
proxy.dispatchRequestUpdate(id, data);
}
};
// Data provider implements async logic for fetching
// data from the backend. It's created the first
// time it's needed.
if (!dataProvider) {
dataProvider = new DataProvider({
actions,
webConsoleClient: proxy.webConsoleClient
});
}
let type = action.type;
// If network message has been opened, fetch all
// HTTP details from the backend.
if (type == MESSAGE_OPEN) {
let message = getMessage(state, action.id);
if (!message.openedOnce && message.source == "network") {
message.updates.forEach(updateType => {
dataProvider.onNetworkEventUpdate(null, {
packet: { updateType: updateType },
networkInfo: message,
});
});
}
}
// Process all incoming HTTP details packets.
if (type == NETWORK_MESSAGE_UPDATE) {
let open = getAllMessagesUiById(state).includes(action.id);
if (open) {
dataProvider.onNetworkEventUpdate(null, action.response);
}
}
return reducer(state, action);
}
return next(netProviderEnhancer, initialState, enhancer);
};
}
/** /**
* Helper function for releasing backend actors. * Helper function for releasing backend actors.
*/ */

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

@ -109,4 +109,16 @@ describe("NetworkEventMessage component:", () => {
expect(wrapper.find(".message-body .status").text()).toMatch(EXPECTED_STATUS); expect(wrapper.find(".message-body .status").text()).toMatch(EXPECTED_STATUS);
}); });
}); });
describe("is expandable", () => {
it("renders as expected", () => {
const message = stubPreparedMessages.get("XHR POST request");
const wrapper = render(NetworkEventMessage({
message,
serviceContainer,
}));
expect(wrapper.find(".message .theme-twisty")).toExist();
});
});
}); });

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

@ -27,7 +27,48 @@ stubPreparedMessages.set("GET request", new NetworkEventMessage({
"type": "log", "type": "log",
"groupId": null, "groupId": null,
"timeStamp": 1487022056850, "timeStamp": 1487022056850,
"indent": 0 "indent": 0,
"updates": [],
"openedOnce": false,
"securityState": null,
"securityInfo": null,
"requestHeadersFromUploadStream": null,
"url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
"urlDetails": {
"baseNameWithQuery": "inexistent.html",
"host": "example.com",
"scheme": "http",
"unicodeUrl": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
"isLocal": null
},
"method": "GET",
"cause": {
"type": "img",
"loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
"stacktrace": [
{
"filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
"lineNumber": 3,
"columnNumber": 1,
"functionName": "triggerPacket",
"asyncCause": null
},
{
"filename": "resource://testing-common/content-task.js line 52 > eval",
"lineNumber": 8,
"columnNumber": 9,
"functionName": null,
"asyncCause": null
},
{
"filename": "resource://testing-common/content-task.js",
"lineNumber": 53,
"columnNumber": 20,
"functionName": null,
"asyncCause": null
}
]
}
})); }));
stubPreparedMessages.set("GET request update", new NetworkEventMessage({ stubPreparedMessages.set("GET request update", new NetworkEventMessage({
@ -56,7 +97,20 @@ stubPreparedMessages.set("GET request update", new NetworkEventMessage({
"type": "log", "type": "log",
"groupId": null, "groupId": null,
"totalTime": 16, "totalTime": 16,
"indent": 0 "indent": 0,
"openedOnce": false,
"securityState": null,
"securityInfo": null,
"requestHeadersFromUploadStream": null,
"url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
"urlDetails": {
"baseNameWithQuery": "inexistent.html",
"host": "example.com",
"scheme": "http",
"unicodeUrl": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
"isLocal": null
},
"method": "GET"
})); }));
stubPreparedMessages.set("XHR GET request", new NetworkEventMessage({ stubPreparedMessages.set("XHR GET request", new NetworkEventMessage({
@ -73,7 +127,48 @@ stubPreparedMessages.set("XHR GET request", new NetworkEventMessage({
"type": "log", "type": "log",
"groupId": null, "groupId": null,
"timeStamp": 1487022057746, "timeStamp": 1487022057746,
"indent": 0 "indent": 0,
"updates": [],
"openedOnce": false,
"securityState": null,
"securityInfo": null,
"requestHeadersFromUploadStream": null,
"url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
"urlDetails": {
"baseNameWithQuery": "inexistent.html",
"host": "example.com",
"scheme": "http",
"unicodeUrl": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
"isLocal": null
},
"method": "GET",
"cause": {
"type": "xhr",
"loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
"stacktrace": [
{
"filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
"lineNumber": 4,
"columnNumber": 1,
"functionName": "triggerPacket",
"asyncCause": null
},
{
"filename": "resource://testing-common/content-task.js line 52 > eval",
"lineNumber": 8,
"columnNumber": 9,
"functionName": null,
"asyncCause": null
},
{
"filename": "resource://testing-common/content-task.js",
"lineNumber": 53,
"columnNumber": 20,
"functionName": null,
"asyncCause": null
}
]
}
})); }));
stubPreparedMessages.set("XHR GET request update", new NetworkEventMessage({ stubPreparedMessages.set("XHR GET request update", new NetworkEventMessage({
@ -102,7 +197,20 @@ stubPreparedMessages.set("XHR GET request update", new NetworkEventMessage({
"type": "log", "type": "log",
"groupId": null, "groupId": null,
"totalTime": 16, "totalTime": 16,
"indent": 0 "indent": 0,
"openedOnce": false,
"securityState": null,
"securityInfo": null,
"requestHeadersFromUploadStream": null,
"url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
"urlDetails": {
"baseNameWithQuery": "inexistent.html",
"host": "example.com",
"scheme": "http",
"unicodeUrl": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
"isLocal": null
},
"method": "GET"
})); }));
stubPreparedMessages.set("XHR POST request", new NetworkEventMessage({ stubPreparedMessages.set("XHR POST request", new NetworkEventMessage({
@ -119,7 +227,48 @@ stubPreparedMessages.set("XHR POST request", new NetworkEventMessage({
"type": "log", "type": "log",
"groupId": null, "groupId": null,
"timeStamp": 1487022058414, "timeStamp": 1487022058414,
"indent": 0 "indent": 0,
"updates": [],
"openedOnce": false,
"securityState": null,
"securityInfo": null,
"requestHeadersFromUploadStream": null,
"url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
"urlDetails": {
"baseNameWithQuery": "inexistent.html",
"host": "example.com",
"scheme": "http",
"unicodeUrl": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
"isLocal": null
},
"method": "POST",
"cause": {
"type": "xhr",
"loadingDocumentUri": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
"stacktrace": [
{
"filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
"lineNumber": 4,
"columnNumber": 1,
"functionName": "triggerPacket",
"asyncCause": null
},
{
"filename": "resource://testing-common/content-task.js line 52 > eval",
"lineNumber": 8,
"columnNumber": 9,
"functionName": null,
"asyncCause": null
},
{
"filename": "resource://testing-common/content-task.js",
"lineNumber": 53,
"columnNumber": 20,
"functionName": null,
"asyncCause": null
}
]
}
})); }));
stubPreparedMessages.set("XHR POST request update", new NetworkEventMessage({ stubPreparedMessages.set("XHR POST request update", new NetworkEventMessage({
@ -148,7 +297,20 @@ stubPreparedMessages.set("XHR POST request update", new NetworkEventMessage({
"type": "log", "type": "log",
"groupId": null, "groupId": null,
"totalTime": 10, "totalTime": 10,
"indent": 0 "indent": 0,
"openedOnce": false,
"securityState": null,
"securityInfo": null,
"requestHeadersFromUploadStream": null,
"url": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
"urlDetails": {
"baseNameWithQuery": "inexistent.html",
"host": "example.com",
"scheme": "http",
"unicodeUrl": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html",
"isLocal": null
},
"method": "POST"
})); }));
stubPackets.set("GET request", { stubPackets.set("GET request", {

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

@ -48,7 +48,8 @@ skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32
[browser_webconsole_location_scratchpad_link.js] [browser_webconsole_location_scratchpad_link.js]
[browser_webconsole_location_styleeditor_link.js] [browser_webconsole_location_styleeditor_link.js]
[browser_webconsole_logErrorInPage.js] [browser_webconsole_logErrorInPage.js]
[browser_webconsole_network_messages_click.js] [browser_webconsole_network_messages_openinnet.js]
[browser_webconsole_network_messages_expand.js]
[browser_webconsole_nodes_highlight.js] [browser_webconsole_nodes_highlight.js]
[browser_webconsole_nodes_select.js] [browser_webconsole_nodes_select.js]
[browser_webconsole_object_inspector_entries.js] [browser_webconsole_object_inspector_entries.js]

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

@ -0,0 +1,67 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const TEST_URI = "data:text/html;charset=utf8,Test that clicking on a network message " +
"in the console toggles the HTTP inspection.";
const TEST_FILE = "test-network-request.html";
const TEST_PATH = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/mochitest/";
const NET_PREF = "devtools.webconsole.filter.net";
const XHR_PREF = "devtools.webconsole.filter.netxhr";
Services.prefs.setBoolPref(NET_PREF, true);
Services.prefs.setBoolPref(XHR_PREF, true);
registerCleanupFunction(() => {
Services.prefs.clearUserPref(NET_PREF);
Services.prefs.clearUserPref(XHR_PREF);
});
add_task(async function task() {
const hud = await openNewTabAndConsole(TEST_URI);
const currentTab = gBrowser.selectedTab;
let target = TargetFactory.forTab(currentTab);
let toolbox = gDevTools.getToolbox(target);
const documentUrl = TEST_PATH + TEST_FILE;
await loadDocument(documentUrl);
info("Document loaded.");
await testNetworkMessage(hud, documentUrl);
await waitForNetworkUpdates(toolbox);
});
async function testNetworkMessage(hud, url) {
let messageNode = await waitFor(() => findMessage(hud, url));
let urlNode = messageNode.querySelector(".url");
info("Network message found.");
EventUtils.sendMouseEvent({ type: "click" }, urlNode);
let headersTab = messageNode.querySelector("#headers-tab");
let cookiesTab = messageNode.querySelector("#cookies-tab");
let paramsTab = messageNode.querySelector("#params-tab");
let responseTab = messageNode.querySelector("#response-tab");
let timingsTab = messageNode.querySelector("#timings-tab");
ok(headersTab, "Headers tab is available");
ok(cookiesTab, "Cookies tab is available");
ok(paramsTab, "Params tab is available");
ok(responseTab, "Response tab is available");
ok(timingsTab, "Timings tab is available");
}
async function waitForNetworkUpdates(toolbox) {
let panel = toolbox.getCurrentPanel();
let hud = panel.hud;
let ui = hud.ui;
return new Promise(resolve => {
ui.jsterm.hud.on("network-request-payload-ready", () => {
resolve();
});
});
}

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

@ -3,8 +3,8 @@
"use strict"; "use strict";
const TEST_URI = "data:text/html;charset=utf8,Test that clicking on a network message " + const TEST_URI = "data:text/html;charset=utf8,Test that 'Open in Network Panel' " +
"in the console opens the netmonitor panel."; "context menu item opens the selected request in netmonitor panel.";
const TEST_FILE = "test-network-request.html"; const TEST_FILE = "test-network-request.html";
const JSON_TEST_URL = "test-network-request.html"; const JSON_TEST_URL = "test-network-request.html";
@ -12,6 +12,7 @@ const TEST_PATH = "http://example.com/browser/devtools/client/webconsole/new-con
const NET_PREF = "devtools.webconsole.filter.net"; const NET_PREF = "devtools.webconsole.filter.net";
const XHR_PREF = "devtools.webconsole.filter.netxhr"; const XHR_PREF = "devtools.webconsole.filter.netxhr";
Services.prefs.setBoolPref(NET_PREF, true); Services.prefs.setBoolPref(NET_PREF, true);
Services.prefs.setBoolPref(XHR_PREF, true); Services.prefs.setBoolPref(XHR_PREF, true);
registerCleanupFunction(() => { registerCleanupFunction(() => {
@ -47,17 +48,16 @@ add_task(async function task() {
async function testNetmonitorLink(toolbox, hud, url) { async function testNetmonitorLink(toolbox, hud, url) {
let messageNode = await waitFor(() => findMessage(hud, url)); let messageNode = await waitFor(() => findMessage(hud, url));
let urlNode = messageNode.querySelector(".url");
info("Network message found."); info("Network message found.");
let onNetmonitorSelected = new Promise((resolve) => { let onNetmonitorSelected = toolbox.once("netmonitor-selected", (event, panel) => {
toolbox.once("netmonitor-selected", (event, panel) => { return panel;
resolve(panel);
});
}); });
info("Simulate click on the network message url."); let menuPopup = await openContextMenu(hud, messageNode);
EventUtils.sendMouseEvent({ type: "click" }, urlNode); let openInNetMenuItem = menuPopup.querySelector("#console-menu-open-in-network-panel");
ok(openInNetMenuItem, "open in network panel item is enabled");
openInNetMenuItem.click();
const {panelWin} = await onNetmonitorSelected; const {panelWin} = await onNetmonitorSelected;
ok(true, "The netmonitor panel is selected when clicking on the network message"); ok(true, "The netmonitor panel is selected when clicking on the network message");

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

@ -53,7 +53,7 @@ var openNewTabAndConsole = Task.async(function* (url) {
}); });
/** /**
* Wait for messages in the web console output, resolving once they are receieved. * Wait for messages in the web console output, resolving once they are received.
* *
* @param object options * @param object options
* - hud: the webconsole * - hud: the webconsole
@ -100,7 +100,7 @@ function waitForMessages({ hud, messages }) {
* idempotent function, since we have to run it a second time after it returns * idempotent function, since we have to run it a second time after it returns
* true in order to return the value. * true in order to return the value.
* @param string message [optional] * @param string message [optional]
* A message to output if the condition failes. * A message to output if the condition fails.
* @param number interval [optional] * @param number interval [optional]
* How often the predicate is invoked, in milliseconds. * How often the predicate is invoked, in milliseconds.
* @return object * @return object
@ -144,6 +144,7 @@ function findMessages(hud, text, selector = ".message") {
); );
return elements; return elements;
} }
/** /**
* Simulate a context menu event on the provided element, and wait for the console context * Simulate a context menu event on the provided element, and wait for the console context
* menu to open. Returns a promise that resolves the menu popup element. * menu to open. Returns a promise that resolves the menu popup element.
@ -154,10 +155,10 @@ function findMessages(hud, text, selector = ".message") {
* The dom element on which the context menu event should be synthesized. * The dom element on which the context menu event should be synthesized.
* @return promise * @return promise
*/ */
function* openContextMenu(hud, element) { async function openContextMenu(hud, element) {
let onConsoleMenuOpened = hud.ui.newConsoleOutput.once("menu-open"); let onConsoleMenuOpened = hud.ui.newConsoleOutput.once("menu-open");
synthesizeContextMenuEvent(element); synthesizeContextMenuEvent(element);
yield onConsoleMenuOpened; await onConsoleMenuOpened;
return hud.ui.newConsoleOutput.toolbox.doc.getElementById("webconsole-menu"); return hud.ui.newConsoleOutput.toolbox.doc.getElementById("webconsole-menu");
} }

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

@ -36,6 +36,8 @@ requireHacker.global_hook("default", path => {
return `module.exports = require("devtools/client/webconsole/new-console-output/test/fixtures/Services")`; return `module.exports = require("devtools/client/webconsole/new-console-output/test/fixtures/Services")`;
case "devtools/shared/client/main": case "devtools/shared/client/main":
return `module.exports = require("devtools/client/webconsole/new-console-output/test/fixtures/ObjectClient")`; return `module.exports = require("devtools/client/webconsole/new-console-output/test/fixtures/ObjectClient")`;
case "devtools/client/netmonitor/src/components/tabbox-panel":
return "{}";
} }
return undefined; return undefined;
}); });

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

@ -0,0 +1,85 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const {
getAllNetworkMessagesUpdateById,
} = require("devtools/client/webconsole/new-console-output/selectors/messages");
const {
setupActions,
setupStore,
clonePacket
} = require("devtools/client/webconsole/new-console-output/test/helpers");
const { stubPackets } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
const expect = require("expect");
describe("Network message reducer:", () => {
let actions;
let getState;
let dispatch;
before(() => {
actions = setupActions();
});
beforeEach(() => {
const store = setupStore([]);
getState = store.getState;
dispatch = store.dispatch;
let packet = clonePacket(stubPackets.get("GET request"));
let updatePacket = clonePacket(stubPackets.get("GET request update"));
packet.actor = "message1";
updatePacket.networkInfo.actor = "message1";
dispatch(actions.messageAdd(packet));
dispatch(actions.networkMessageUpdate(updatePacket.networkInfo));
});
describe("networkMessagesUpdateById", () => {
it("adds fetched HTTP request headers", () => {
let headers = {
headers: []
};
dispatch(actions.networkUpdateRequest("message1", {
requestHeaders: headers
}));
let networkUpdates = getAllNetworkMessagesUpdateById(getState());
expect(networkUpdates.message1.requestHeaders).toBe(headers);
});
it("adds fetched HTTP security info", () => {
let securityInfo = {
state: "insecure"
};
dispatch(actions.networkUpdateRequest("message1", {
securityInfo: securityInfo
}));
let networkUpdates = getAllNetworkMessagesUpdateById(getState());
expect(networkUpdates.message1.securityInfo).toBe(securityInfo);
expect(networkUpdates.message1.securityState).toBe("insecure");
});
it("adds fetched HTTP post data", () => {
let requestPostData = {
postData: {
text: ""
}
};
dispatch(actions.networkUpdateRequest("message1", {
requestPostData: requestPostData
}));
let networkUpdates = getAllNetworkMessagesUpdateById(getState());
expect(networkUpdates.message1.requestPostData).toBe(requestPostData);
expect(networkUpdates.message1.requestHeadersFromUploadStream).toExist();
});
});
});

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

@ -60,5 +60,10 @@ exports.NetworkEventMessage = function (props) {
timeStamp: null, timeStamp: null,
totalTime: null, totalTime: null,
indent: 0, indent: 0,
updates: null,
openedOnce: false,
securityState: null,
securityInfo: null,
requestHeadersFromUploadStream: null,
}, props); }, props);
}; };

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

@ -31,7 +31,12 @@ const { l10n } = require("devtools/client/webconsole/new-console-output/utils/me
* - {String} source * - {String} source
* - {String} request * - {String} request
*/ */
function createContextMenu(jsterm, parentNode, { actor, clipboardText, message }) { function createContextMenu(jsterm, parentNode, {
actor,
clipboardText,
message,
serviceContainer
}) {
let win = parentNode.ownerDocument.defaultView; let win = parentNode.ownerDocument.defaultView;
let selection = win.getSelection(); let selection = win.getSelection();
@ -55,6 +60,19 @@ function createContextMenu(jsterm, parentNode, { actor, clipboardText, message }
}, },
})); }));
// Open Network message in the Network panel.
menu.append(new MenuItem({
id: "console-menu-open-in-network-panel",
label: l10n.getStr("webconsole.menu.openInNetworkPanel.label"),
accesskey: l10n.getStr("webconsole.menu.openInNetworkPanel.accesskey"),
visible: source === MESSAGE_SOURCE.NETWORK,
click: () => {
if (request && serviceContainer.openNetworkPanel) {
serviceContainer.openNetworkPanel(message.messageId);
}
},
}));
// Open URL in a new tab for a network request. // Open URL in a new tab for a network request.
menu.append(new MenuItem({ menu.append(new MenuItem({
id: "console-menu-open-url", id: "console-menu-open-url",

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

@ -7,6 +7,7 @@
"use strict"; "use strict";
const l10n = require("devtools/client/webconsole/webconsole-l10n"); const l10n = require("devtools/client/webconsole/webconsole-l10n");
const { getUrlDetails } = require("devtools/client/netmonitor/src/utils/request-utils");
const { const {
MESSAGE_SOURCE, MESSAGE_SOURCE,
@ -232,6 +233,11 @@ function transformNetworkEventPacket(packet) {
response: networkEvent.response, response: networkEvent.response,
timeStamp: networkEvent.timeStamp, timeStamp: networkEvent.timeStamp,
totalTime: networkEvent.totalTime, totalTime: networkEvent.totalTime,
url: networkEvent.request.url,
urlDetails: getUrlDetails(networkEvent.request.url),
method: networkEvent.request.method,
updates: networkEvent.updates,
cause: networkEvent.cause,
}); });
} }

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

@ -88,7 +88,7 @@ WebConsolePanel.prototype = {
let msg = "WebConsolePanel open failed. " + let msg = "WebConsolePanel open failed. " +
reason.error + ": " + reason.message; reason.error + ": " + reason.message;
dump(msg + "\n"); dump(msg + "\n");
console.error(msg); console.error(msg, reason);
}); });
}, },

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

@ -243,6 +243,10 @@ WebConsoleConnectionProxy.prototype = {
this.webConsoleFrame.newConsoleOutput.dispatchMessageUpdate(networkInfo, response); this.webConsoleFrame.newConsoleOutput.dispatchMessageUpdate(networkInfo, response);
}, },
dispatchRequestUpdate: function (id, data) {
this.webConsoleFrame.newConsoleOutput.dispatchRequestUpdate(id, data);
},
/** /**
* The "cachedMessages" response handler. * The "cachedMessages" response handler.
* *

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

@ -11,6 +11,10 @@
<link rel="stylesheet" href="chrome://devtools/skin/webconsole.css"/> <link rel="stylesheet" href="chrome://devtools/skin/webconsole.css"/>
<link rel="stylesheet" href="chrome://devtools/skin/components-frame.css"/> <link rel="stylesheet" href="chrome://devtools/skin/components-frame.css"/>
<link rel="stylesheet" href="resource://devtools/client/shared/components/reps/reps.css"/> <link rel="stylesheet" href="resource://devtools/client/shared/components/reps/reps.css"/>
<link rel="stylesheet" href="resource://devtools/client/shared/components/tabs/tabs.css"/>
<link rel="stylesheet" href="resource://devtools/client/shared/components/tabs/tabbar.css"/>
<link rel="stylesheet" href="chrome://devtools/content/netmonitor/src/assets/styles/netmonitor.css"/>
<script src="chrome://devtools/content/shared/theme-switching.js"></script> <script src="chrome://devtools/content/shared/theme-switching.js"></script>
<script type="application/javascript" <script type="application/javascript"
src="resource://devtools/client/webconsole/new-console-output/main.js"></script> src="resource://devtools/client/webconsole/new-console-output/main.js"></script>

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

@ -82,6 +82,7 @@ webpackConfig.resolve = {
"devtools/shared/old-event-emitter": "devtools-modules/src/utils/event-emitter", "devtools/shared/old-event-emitter": "devtools-modules/src/utils/event-emitter",
"devtools/shared/client/main": path.join(__dirname, "new-console-output/test/fixtures/ObjectClient"), "devtools/shared/client/main": path.join(__dirname, "new-console-output/test/fixtures/ObjectClient"),
"devtools/shared/platform/clipboard": path.join(__dirname, "../../shared/platform/content/clipboard"), "devtools/shared/platform/clipboard": path.join(__dirname, "../../shared/platform/content/clipboard"),
"devtools/shared/platform/stack": path.join(__dirname, "../../shared/platform/content/stack"),
} }
}; };

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

@ -30,10 +30,8 @@
* [Panel SVGs](frontend/svgs.md) * [Panel SVGs](frontend/svgs.md)
* [React](frontend/react.md) * [React](frontend/react.md)
* [Guidelines](frontend/react-guidelines.md) * [Guidelines](frontend/react-guidelines.md)
* [Tips](frontend/react-tips.md)
* [Redux](frontend/redux.md) * [Redux](frontend/redux.md)
* [Guidelines](frontend/redux-guidelines.md) * [Guidelines](frontend/redux-guidelines.md)
* [Tips](frontend/redux-tips.md)
* [Telemetry](frontend/telemetry.md) * [Telemetry](frontend/telemetry.md)
* [Backend](backend/backend.md) * [Backend](backend/backend.md)
* [Remote Debugging Protocol](backend/protocol.md) * [Remote Debugging Protocol](backend/protocol.md)

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

@ -19,8 +19,8 @@ Ensure that the actor's destroy is really destroying everything that it should.
```js ```js
destroy: function() { destroy: function() {
Actor.prototype.destroy.call(this); Actor.prototype.destroy.call(this);
events.off(this.tabActor, "will-navigate", this.onWillNavigate); this.tabActor.off("will-navigate", this.onWillNavigate);
events.off(this.tabActor, "navigate", this.onNavigate); this.tabActor.off("navigate", this.onNavigate);
this.stopAnimationPlayerUpdates(); this.stopAnimationPlayerUpdates();
this.tabActor = this.observer = this.actors = null; this.tabActor = this.observer = this.actors = null;

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

@ -487,11 +487,11 @@ Here's how you'd set it up in a spec:
Here's how the implementation would look: Here's how the implementation would look:
const event = require("devtools/shared/event-emitter"); const EventEmitter = require("devtools/shared/event-emitter");
// In your protocol.ActorClassWithSpec definition: // In your protocol.ActorClassWithSpec definition:
giveGoodNews: function (news) { giveGoodNews: function (news) {
event.emit(this, "good-news", news); EventEmitter.emit(this, "good-news", news);
} }
Now you can listen to events on a front: Now you can listen to events on a front:

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

@ -1,19 +0,0 @@
# React Tips
Learn various tips and tricks for working with our React code.
## Hot Reloading
If you followed [this
rule](react-guidelines.md#export-the-component-directly-and-create-factory-on-import)
about exporting components, and are using the BrowserLoader, you can
hot reload any React component. Just turn on the pref
`devtools.loader.hotreload`, re-open the devtools, and you should be
able to save any React component and instantly see changes.
This does not reset the state of your app, so you can quickly se
changes in the same context.
## Profiling
We need information here about how to use React.addons.Perf to profile the app.

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

@ -1,5 +0,0 @@
Need to document:
* How to attach various redux loggers
* How to hot reload redux code

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

@ -30,7 +30,6 @@ const promise = require("promise");
const protocol = require("devtools/shared/protocol"); const protocol = require("devtools/shared/protocol");
const {Actor} = protocol; const {Actor} = protocol;
const {animationPlayerSpec, animationsSpec} = require("devtools/shared/specs/animation"); const {animationPlayerSpec, animationsSpec} = require("devtools/shared/specs/animation");
const events = require("devtools/shared/event-emitter");
// Types of animations. // Types of animations.
const ANIMATION_TYPES = { const ANIMATION_TYPES = {
@ -385,7 +384,7 @@ var AnimationPlayerActor = protocol.ActorClassWithSpec(animationPlayerSpec, {
} }
if (hasChanged) { if (hasChanged) {
events.emit(this, "changed", this.getCurrentState()); this.emit("changed", this.getCurrentState());
} }
}, },
@ -595,14 +594,14 @@ exports.AnimationsActor = protocol.ActorClassWithSpec(animationsSpec, {
this.onAnimationMutation = this.onAnimationMutation.bind(this); this.onAnimationMutation = this.onAnimationMutation.bind(this);
this.allAnimationsPaused = false; this.allAnimationsPaused = false;
events.on(this.tabActor, "will-navigate", this.onWillNavigate); this.tabActor.on("will-navigate", this.onWillNavigate);
events.on(this.tabActor, "navigate", this.onNavigate); this.tabActor.on("navigate", this.onNavigate);
}, },
destroy: function () { destroy: function () {
Actor.prototype.destroy.call(this); Actor.prototype.destroy.call(this);
events.off(this.tabActor, "will-navigate", this.onWillNavigate); this.tabActor.off("will-navigate", this.onWillNavigate);
events.off(this.tabActor, "navigate", this.onNavigate); this.tabActor.off("navigate", this.onNavigate);
this.stopAnimationPlayerUpdates(); this.stopAnimationPlayerUpdates();
this.tabActor = this.observer = this.actors = this.walker = null; this.tabActor = this.observer = this.actors = this.walker = null;
@ -729,7 +728,7 @@ exports.AnimationsActor = protocol.ActorClassWithSpec(animationsSpec, {
// Let's wait for all added animations to be ready before telling the // Let's wait for all added animations to be ready before telling the
// front-end. // front-end.
Promise.all(readyPromises).then(() => { Promise.all(readyPromises).then(() => {
events.emit(this, "mutations", eventData); this.emit("mutations", eventData);
}); });
} }
}, },

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

@ -6,12 +6,9 @@
/* global XPCNativeWrapper */ /* global XPCNativeWrapper */
const {Ci, Cu} = require("chrome"); const {Ci, Cu} = require("chrome");
const events = require("devtools/shared/event-emitter");
const protocol = require("devtools/shared/protocol"); const protocol = require("devtools/shared/protocol");
const {serializeStack, parseStack} = require("toolkit/loader"); const {serializeStack, parseStack} = require("toolkit/loader");
const {on, off, emit} = events;
const { functionCallSpec, callWatcherSpec } = require("devtools/shared/specs/call-watcher"); const { functionCallSpec, callWatcherSpec } = require("devtools/shared/specs/call-watcher");
const { CallWatcherFront } = require("devtools/shared/fronts/call-watcher"); const { CallWatcherFront } = require("devtools/shared/fronts/call-watcher");
@ -237,13 +234,13 @@ exports.CallWatcherActor = protocol.ActorClassWithSpec(callWatcherSpec, {
this._onGlobalCreated = this._onGlobalCreated.bind(this); this._onGlobalCreated = this._onGlobalCreated.bind(this);
this._onGlobalDestroyed = this._onGlobalDestroyed.bind(this); this._onGlobalDestroyed = this._onGlobalDestroyed.bind(this);
this._onContentFunctionCall = this._onContentFunctionCall.bind(this); this._onContentFunctionCall = this._onContentFunctionCall.bind(this);
on(this.tabActor, "window-ready", this._onGlobalCreated); this.tabActor.on("window-ready", this._onGlobalCreated);
on(this.tabActor, "window-destroyed", this._onGlobalDestroyed); this.tabActor.on("window-destroyed", this._onGlobalDestroyed);
}, },
destroy: function (conn) { destroy: function (conn) {
protocol.Actor.prototype.destroy.call(this, conn); protocol.Actor.prototype.destroy.call(this, conn);
off(this.tabActor, "window-ready", this._onGlobalCreated); this.tabActor.off("window-ready", this._onGlobalCreated);
off(this.tabActor, "window-destroyed", this._onGlobalDestroyed); this.tabActor.off("window-destroyed", this._onGlobalDestroyed);
this.finalize(); this.finalize();
}, },
@ -543,7 +540,7 @@ exports.CallWatcherActor = protocol.ActorClassWithSpec(callWatcherSpec, {
if (this.onCall) { if (this.onCall) {
this.onCall(functionCall); this.onCall(functionCall);
} else { } else {
emit(this, "call", functionCall); this.emit("call", functionCall);
} }
} }
}); });

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

@ -9,7 +9,6 @@ const { Cc, Ci } = require("chrome");
const Services = require("Services"); const Services = require("Services");
const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm"); const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
const events = require("devtools/shared/event-emitter");
const protocol = require("devtools/shared/protocol"); const protocol = require("devtools/shared/protocol");
const { cssUsageSpec } = require("devtools/shared/specs/csscoverage"); const { cssUsageSpec } = require("devtools/shared/specs/csscoverage");
@ -135,7 +134,7 @@ var CSSUsageActor = protocol.ActorClassWithSpec(cssUsageSpec, {
this._tabActor.window.location.reload(); this._tabActor.window.location.reload();
} }
events.emit(this, "state-change", { isRunning: true }); this.emit("state-change", { isRunning: true });
}, },
/** /**
@ -150,7 +149,7 @@ var CSSUsageActor = protocol.ActorClassWithSpec(cssUsageSpec, {
this._progress = undefined; this._progress = undefined;
this._running = false; this._running = false;
events.emit(this, "state-change", { isRunning: false }); this.emit("state-change", { isRunning: false });
}, },
/** /**

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

@ -14,7 +14,6 @@ const {Ci} = require("chrome");
const Services = require("Services"); const Services = require("Services");
const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm"); const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
const {Actor, ActorClassWithSpec} = require("devtools/shared/protocol"); const {Actor, ActorClassWithSpec} = require("devtools/shared/protocol");
const events = require("devtools/shared/event-emitter");
const {eventLoopLagSpec} = require("devtools/shared/specs/eventlooplag"); const {eventLoopLagSpec} = require("devtools/shared/specs/eventlooplag");
exports.EventLoopLagActor = ActorClassWithSpec(eventLoopLagSpec, { exports.EventLoopLagActor = ActorClassWithSpec(eventLoopLagSpec, {
@ -52,7 +51,7 @@ exports.EventLoopLagActor = ActorClassWithSpec(eventLoopLagSpec, {
observe: function (subject, topic, data) { observe: function (subject, topic, data) {
if (topic == "event-loop-lag") { if (topic == "event-loop-lag") {
// Forward event loop lag event // Forward event loop lag event
events.emit(this, "event-loop-lag", data); this.emit("event-loop-lag", data);
} }
}, },

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

@ -6,7 +6,6 @@
const { Actor, ActorClassWithSpec } = require("devtools/shared/protocol"); const { Actor, ActorClassWithSpec } = require("devtools/shared/protocol");
const { gcliSpec } = require("devtools/shared/specs/gcli"); const { gcliSpec } = require("devtools/shared/specs/gcli");
const events = require("devtools/shared/event-emitter");
const { createSystem } = require("gcli/system"); const { createSystem } = require("gcli/system");
/** /**
@ -222,7 +221,7 @@ const GcliActor = ActorClassWithSpec(gcliSpec, {
* Pass events from requisition.system.commands.onCommandsChange upwards * Pass events from requisition.system.commands.onCommandsChange upwards
*/ */
_commandsChanged: function () { _commandsChanged: function () {
events.emit(this, "commands-changed"); this.emit("commands-changed");
}, },
}); });

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

@ -8,7 +8,6 @@ const { Ci, Cu } = require("chrome");
const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm"); const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
const EventEmitter = require("devtools/shared/old-event-emitter"); const EventEmitter = require("devtools/shared/old-event-emitter");
const events = require("devtools/shared/event-emitter");
const protocol = require("devtools/shared/protocol"); const protocol = require("devtools/shared/protocol");
const Services = require("Services"); const Services = require("Services");
const { isWindowIncluded } = require("devtools/shared/layout/utils"); const { isWindowIncluded } = require("devtools/shared/layout/utils");
@ -106,7 +105,7 @@ exports.HighlighterActor = protocol.ActorClassWithSpec(highlighterSpec, {
// Listen to navigation events to switch from the BoxModelHighlighter to the // Listen to navigation events to switch from the BoxModelHighlighter to the
// SimpleOutlineHighlighter, and back, if the top level window changes. // SimpleOutlineHighlighter, and back, if the top level window changes.
events.on(this._tabActor, "navigate", this._onNavigate); this._tabActor.on("navigate", this._onNavigate);
}, },
get conn() { get conn() {
@ -165,7 +164,7 @@ exports.HighlighterActor = protocol.ActorClassWithSpec(highlighterSpec, {
this.hideBoxModel(); this.hideBoxModel();
this._destroyHighlighter(); this._destroyHighlighter();
events.off(this._tabActor, "navigate", this._onNavigate); this._tabActor.off("navigate", this._onNavigate);
this._highlighterEnv.destroy(); this._highlighterEnv.destroy();
this._highlighterEnv = null; this._highlighterEnv = null;
@ -255,7 +254,7 @@ exports.HighlighterActor = protocol.ActorClassWithSpec(highlighterSpec, {
// If shift is pressed, this is only a preview click, send the event to // If shift is pressed, this is only a preview click, send the event to
// the client, but don't stop picking. // the client, but don't stop picking.
if (event.shiftKey) { if (event.shiftKey) {
events.emit(this._walker, "picker-node-previewed", this._walker.emit("picker-node-previewed",
this._findAndAttachElement(event)); this._findAndAttachElement(event));
return; return;
} }
@ -270,7 +269,7 @@ exports.HighlighterActor = protocol.ActorClassWithSpec(highlighterSpec, {
if (!this._currentNode) { if (!this._currentNode) {
this._currentNode = this._findAndAttachElement(event); this._currentNode = this._findAndAttachElement(event);
} }
events.emit(this._walker, "picker-node-picked", this._currentNode); this._walker.emit("picker-node-picked", this._currentNode);
}; };
this._onHovered = event => { this._onHovered = event => {
@ -283,7 +282,7 @@ exports.HighlighterActor = protocol.ActorClassWithSpec(highlighterSpec, {
this._currentNode = this._findAndAttachElement(event); this._currentNode = this._findAndAttachElement(event);
if (this._hoveredNode !== this._currentNode.node) { if (this._hoveredNode !== this._currentNode.node) {
this._highlighter.show(this._currentNode.node.rawNode); this._highlighter.show(this._currentNode.node.rawNode);
events.emit(this._walker, "picker-node-hovered", this._currentNode); this._walker.emit("picker-node-hovered", this._currentNode);
this._hoveredNode = this._currentNode.node; this._hoveredNode = this._currentNode.node;
} }
}; };
@ -345,13 +344,13 @@ exports.HighlighterActor = protocol.ActorClassWithSpec(highlighterSpec, {
// Cancel pick mode. // Cancel pick mode.
case Ci.nsIDOMKeyEvent.DOM_VK_ESCAPE: case Ci.nsIDOMKeyEvent.DOM_VK_ESCAPE:
this.cancelPick(); this.cancelPick();
events.emit(this._walker, "picker-node-canceled"); this._walker.emit("picker-node-canceled");
return; return;
case Ci.nsIDOMKeyEvent.DOM_VK_C: case Ci.nsIDOMKeyEvent.DOM_VK_C:
if ((IS_OSX && event.metaKey && event.altKey) || if ((IS_OSX && event.metaKey && event.altKey) ||
(!IS_OSX && event.ctrlKey && event.shiftKey)) { (!IS_OSX && event.ctrlKey && event.shiftKey)) {
this.cancelPick(); this.cancelPick();
events.emit(this._walker, "picker-node-canceled"); this._walker.emit("picker-node-canceled");
} }
return; return;
default: return; default: return;
@ -360,7 +359,7 @@ exports.HighlighterActor = protocol.ActorClassWithSpec(highlighterSpec, {
// Store currently attached element // Store currently attached element
this._currentNode = this._walker.attachElement(currentNode); this._currentNode = this._walker.attachElement(currentNode);
this._highlighter.show(this._currentNode.node.rawNode); this._highlighter.show(this._currentNode.node.rawNode);
events.emit(this._walker, "picker-node-hovered", this._currentNode); this._walker.emit("picker-node-hovered", this._currentNode);
}; };
this._startPickerListeners(); this._startPickerListeners();
@ -414,11 +413,11 @@ exports.HighlighterActor = protocol.ActorClassWithSpec(highlighterSpec, {
}, },
_highlighterReady: function () { _highlighterReady: function () {
events.emit(this._inspector.walker, "highlighter-ready"); this._inspector.walker.emit("highlighter-ready");
}, },
_highlighterHidden: function () { _highlighterHidden: function () {
events.emit(this._inspector.walker, "highlighter-hide"); this._inspector.walker.emit("highlighter-hide");
}, },
cancelPick: function () { cancelPick: function () {
@ -518,7 +517,7 @@ exports.CustomHighlighterActor = protocol.ActorClassWithSpec(customHighlighterSp
* Upon receiving an event from the highlighter, forward it to the client. * Upon receiving an event from the highlighter, forward it to the client.
*/ */
_onHighlighterEvent: function (type, data) { _onHighlighterEvent: function (type, data) {
events.emit(this, "highlighter-event", data); this.emit("highlighter-event", data);
}, },
/** /**
@ -567,9 +566,9 @@ exports.HighlighterEnvironment = HighlighterEnvironment;
HighlighterEnvironment.prototype = { HighlighterEnvironment.prototype = {
initFromTabActor: function (tabActor) { initFromTabActor: function (tabActor) {
this._tabActor = tabActor; this._tabActor = tabActor;
events.on(this._tabActor, "window-ready", this.relayTabActorWindowReady); this._tabActor.on("window-ready", this.relayTabActorWindowReady);
events.on(this._tabActor, "navigate", this.relayTabActorNavigate); this._tabActor.on("navigate", this.relayTabActorNavigate);
events.on(this._tabActor, "will-navigate", this.relayTabActorWillNavigate); this._tabActor.on("will-navigate", this.relayTabActorWillNavigate);
}, },
initFromWindow: function (win) { initFromWindow: function (win) {
@ -686,9 +685,9 @@ HighlighterEnvironment.prototype = {
destroy: function () { destroy: function () {
if (this._tabActor) { if (this._tabActor) {
events.off(this._tabActor, "window-ready", this.relayTabActorWindowReady); this._tabActor.off("window-ready", this.relayTabActorWindowReady);
events.off(this._tabActor, "navigate", this.relayTabActorNavigate); this._tabActor.off("navigate", this.relayTabActorNavigate);
events.off(this._tabActor, "will-navigate", this.relayTabActorWillNavigate); this._tabActor.off("will-navigate", this.relayTabActorWillNavigate);
} }
// In case the environment was initialized from a window, we need to remove // In case the environment was initialized from a window, we need to remove

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

@ -4,7 +4,7 @@
"use strict"; "use strict";
const events = require("devtools/shared/event-emitter"); const EventEmitter = require("devtools/shared/event-emitter");
const { getCurrentZoom, getWindowDimensions, const { getCurrentZoom, getWindowDimensions,
setIgnoreLayoutChanges } = require("devtools/shared/layout/utils"); setIgnoreLayoutChanges } = require("devtools/shared/layout/utils");
const { const {
@ -212,7 +212,7 @@ MeasuringToolHighlighter.prototype = {
this.markup.destroy(); this.markup.destroy();
events.emit(this, "destroy"); EventEmitter.emit(this, "destroy");
}, },
show() { show() {

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

@ -4,7 +4,7 @@
"use strict"; "use strict";
const events = require("devtools/shared/event-emitter"); const EventEmitter = require("devtools/shared/event-emitter");
const { getCurrentZoom, const { getCurrentZoom,
setIgnoreLayoutChanges } = require("devtools/shared/layout/utils"); setIgnoreLayoutChanges } = require("devtools/shared/layout/utils");
const { const {
@ -277,7 +277,7 @@ RulersHighlighter.prototype = {
this.markup.destroy(); this.markup.destroy();
events.emit(this, "destroy"); EventEmitter.emit(this, "destroy");
}, },
show: function () { show: function () {

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

@ -7,7 +7,7 @@
const { Cc, Ci, Cu, Cr } = require("chrome"); const { Cc, Ci, Cu, Cr } = require("chrome");
const { getCurrentZoom, getWindowDimensions, getViewportDimensions, const { getCurrentZoom, getWindowDimensions, getViewportDimensions,
getRootBindingParent, loadSheet } = require("devtools/shared/layout/utils"); getRootBindingParent, loadSheet } = require("devtools/shared/layout/utils");
const { on, emit } = require("devtools/shared/event-emitter"); const EventEmitter = require("devtools/shared/event-emitter");
const lazyContainer = {}; const lazyContainer = {};
@ -61,7 +61,7 @@ ClassList.prototype = {
if (!this.contains(token)) { if (!this.contains(token)) {
this[_tokens].push(token); this[_tokens].push(token);
} }
emit(this, "update"); EventEmitter.emit(this, "update");
}, },
remove(token) { remove(token) {
let index = this[_tokens].indexOf(token); let index = this[_tokens].indexOf(token);
@ -69,7 +69,7 @@ ClassList.prototype = {
if (index > -1) { if (index > -1) {
this[_tokens].splice(index, 1); this[_tokens].splice(index, 1);
} }
emit(this, "update"); EventEmitter.emit(this, "update");
}, },
toggle(token) { toggle(token) {
if (this.contains(token)) { if (this.contains(token)) {
@ -482,7 +482,7 @@ CanvasFrameAnonymousContentHelper.prototype = {
let classList = new ClassList(this.getAttributeForElement(id, "class")); let classList = new ClassList(this.getAttributeForElement(id, "class"));
on(classList, "update", () => { EventEmitter.on(classList, "update", () => {
this.setAttributeForElement(id, "class", classList.toString()); this.setAttributeForElement(id, "class", classList.toString());
}); });

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

@ -58,7 +58,7 @@ const {LongStringActor} = require("devtools/server/actors/string");
const promise = require("promise"); const promise = require("promise");
const defer = require("devtools/shared/defer"); const defer = require("devtools/shared/defer");
const {Task} = require("devtools/shared/task"); const {Task} = require("devtools/shared/task");
const events = require("devtools/shared/event-emitter"); const EventEmitter = require("devtools/shared/event-emitter");
const {WalkerSearch} = require("devtools/server/actors/utils/walker-search"); const {WalkerSearch} = require("devtools/server/actors/utils/walker-search");
const {PageStyleActor, getFontPreviewData} = require("devtools/server/actors/styles"); const {PageStyleActor, getFontPreviewData} = require("devtools/server/actors/styles");
const { const {
@ -298,7 +298,7 @@ var NodeActor = exports.NodeActor = protocol.ActorClassWithSpec(nodeSpec, {
// Fire an event so, other modules can create its own properties // Fire an event so, other modules can create its own properties
// that should be passed to the client (within the form.props field). // that should be passed to the client (within the form.props field).
events.emit(NodeActor, "form", { EventEmitter.emit(NodeActor, "form", {
target: this, target: this,
data: form data: form
}); });
@ -890,8 +890,8 @@ var WalkerActor = protocol.ActorClassWithSpec(walkerSpec, {
this.onFrameLoad = this.onFrameLoad.bind(this); this.onFrameLoad = this.onFrameLoad.bind(this);
this.onFrameUnload = this.onFrameUnload.bind(this); this.onFrameUnload = this.onFrameUnload.bind(this);
events.on(tabActor, "will-navigate", this.onFrameUnload); tabActor.on("will-navigate", this.onFrameUnload);
events.on(tabActor, "window-ready", this.onFrameLoad); tabActor.on("window-ready", this.onFrameLoad);
// Ensure that the root document node actor is ready and // Ensure that the root document node actor is ready and
// managed. // managed.
@ -981,8 +981,8 @@ var WalkerActor = protocol.ActorClassWithSpec(walkerSpec, {
this._retainedOrphans = null; this._retainedOrphans = null;
this._refMap = null; this._refMap = null;
events.off(this.tabActor, "will-navigate", this.onFrameUnload); this.tabActor.off("will-navigate", this.onFrameUnload);
events.off(this.tabActor, "window-ready", this.onFrameLoad); this.tabActor.off("window-ready", this.onFrameLoad);
this.onFrameLoad = null; this.onFrameLoad = null;
this.onFrameUnload = null; this.onFrameUnload = null;
@ -1002,7 +1002,7 @@ var WalkerActor = protocol.ActorClassWithSpec(walkerSpec, {
this.layoutActor = null; this.layoutActor = null;
this.tabActor = null; this.tabActor = null;
events.emit(this, "destroyed"); this.emit("destroyed");
} catch (e) { } catch (e) {
console.error(e); console.error(e);
} }
@ -1077,7 +1077,7 @@ var WalkerActor = protocol.ActorClassWithSpec(walkerSpec, {
} }
if (changes.length) { if (changes.length) {
events.emit(this, "display-change", changes); this.emit("display-change", changes);
} }
}, },
@ -1085,7 +1085,7 @@ var WalkerActor = protocol.ActorClassWithSpec(walkerSpec, {
* When the browser window gets resized, relay the event to the front. * When the browser window gets resized, relay the event to the front.
*/ */
_onResize: function () { _onResize: function () {
events.emit(this, "resize"); this.emit("resize");
}, },
/** /**
@ -2333,7 +2333,7 @@ var WalkerActor = protocol.ActorClassWithSpec(walkerSpec, {
this._pendingMutations.push(mutation); this._pendingMutations.push(mutation);
if (needEvent) { if (needEvent) {
events.emit(this, "new-mutations"); this.emit("new-mutations");
} }
}, },
@ -2347,7 +2347,7 @@ var WalkerActor = protocol.ActorClassWithSpec(walkerSpec, {
// Notify any observers that want *all* mutations (even on nodes that aren't // Notify any observers that want *all* mutations (even on nodes that aren't
// referenced). This is not sent over the protocol so can only be used by // referenced). This is not sent over the protocol so can only be used by
// scripts running in the server process. // scripts running in the server process.
events.emit(this, "any-mutation"); this.emit("any-mutation");
for (let change of mutations) { for (let change of mutations) {
let targetActor = this.getNode(change.target); let targetActor = this.getNode(change.target);
@ -2760,7 +2760,7 @@ exports.InspectorActor = protocol.ActorClassWithSpec(inspectorSpec, {
window.removeEventListener("DOMContentLoaded", domReady, true); window.removeEventListener("DOMContentLoaded", domReady, true);
this.walker = WalkerActor(this.conn, tabActor, options); this.walker = WalkerActor(this.conn, tabActor, options);
this.manage(this.walker); this.manage(this.walker);
events.once(this.walker, "destroyed", () => { this.walker.once("destroyed", () => {
this._walkerPromise = null; this._walkerPromise = null;
this._pageStylePromise = null; this._pageStylePromise = null;
}); });
@ -2913,7 +2913,7 @@ exports.InspectorActor = protocol.ActorClassWithSpec(inspectorSpec, {
this._eyeDropper.show(this.window.document.documentElement, options); this._eyeDropper.show(this.window.document.documentElement, options);
this._eyeDropper.once("selected", this._onColorPicked); this._eyeDropper.once("selected", this._onColorPicked);
this._eyeDropper.once("canceled", this._onColorPickCanceled); this._eyeDropper.once("canceled", this._onColorPickCanceled);
events.once(this.tabActor, "will-navigate", this.destroyEyeDropper); this.tabActor.once("will-navigate", this.destroyEyeDropper);
}, },
/** /**
@ -2926,7 +2926,7 @@ exports.InspectorActor = protocol.ActorClassWithSpec(inspectorSpec, {
this._eyeDropper.hide(); this._eyeDropper.hide();
this._eyeDropper.off("selected", this._onColorPicked); this._eyeDropper.off("selected", this._onColorPicked);
this._eyeDropper.off("canceled", this._onColorPickCanceled); this._eyeDropper.off("canceled", this._onColorPickCanceled);
events.off(this.tabActor, "will-navigate", this.destroyEyeDropper); this.tabActor.off("will-navigate", this.destroyEyeDropper);
} }
}, },
@ -2954,11 +2954,11 @@ exports.InspectorActor = protocol.ActorClassWithSpec(inspectorSpec, {
}, },
_onColorPicked: function (e, color) { _onColorPicked: function (e, color) {
events.emit(this, "color-picked", color); this.emit("color-picked", color);
}, },
_onColorPickCanceled: function () { _onColorPickCanceled: function () {
events.emit(this, "color-pick-canceled"); this.emit("color-pick-canceled");
} }
}); });

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

@ -8,7 +8,6 @@ const protocol = require("devtools/shared/protocol");
const { Memory } = require("devtools/server/performance/memory"); const { Memory } = require("devtools/server/performance/memory");
const { actorBridgeWithSpec } = require("devtools/server/actors/common"); const { actorBridgeWithSpec } = require("devtools/server/actors/common");
const { memorySpec } = require("devtools/shared/specs/memory"); const { memorySpec } = require("devtools/shared/specs/memory");
loader.lazyRequireGetter(this, "events", "devtools/shared/event-emitter");
loader.lazyRequireGetter(this, "StackFrameCache", loader.lazyRequireGetter(this, "StackFrameCache",
"devtools/server/actors/utils/stack", true); "devtools/server/actors/utils/stack", true);
@ -71,13 +70,13 @@ exports.MemoryActor = protocol.ActorClassWithSpec(memorySpec, {
_onGarbageCollection: function (data) { _onGarbageCollection: function (data) {
if (this.conn.transport) { if (this.conn.transport) {
events.emit(this, "garbage-collection", data); this.emit("garbage-collection", data);
} }
}, },
_onAllocations: function (data) { _onAllocations: function (data) {
if (this.conn.transport) { if (this.conn.transport) {
events.emit(this, "allocations", data); this.emit("allocations", data);
} }
}, },
}); });

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