Merge autoland to central, a=merge

MozReview-Commit-ID: 9FDTT0Vkl0L
This commit is contained in:
Wes Kocher 2017-02-15 15:26:11 -08:00
commit ec985117e6
315 changed files with 13590 additions and 8293 deletions

View File

@ -186,11 +186,6 @@ $(addprefix install-,$(subst /,_,$(filter dist/%,$(install_manifests)))): instal
.PHONY: install-tests
install-tests: install-test-files
# We no longer run "make install-tests" directly before running tests, but we still
# want to depend on things like config.status, hence this target.
.PHONY: run-tests-deps
run-tests-deps: $(install_manifest_depends)
# Force --no-remove, because $objdir/_tests is handled by multiple manifests.
.PHONY: install-test-files
install-test-files:

View File

@ -327,7 +327,8 @@ isColumnSelectedCB(AtkTable *aTable, gint aColIdx)
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable));
if (accWrap) {
return static_cast<gboolean>(accWrap->AsTable()->IsColSelected(aColIdx));
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
return static_cast<gboolean>(proxy->TableColumnSelected(aColIdx));
}
@ -340,7 +341,8 @@ isRowSelectedCB(AtkTable *aTable, gint aRowIdx)
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable));
if (accWrap) {
return static_cast<gboolean>(accWrap->AsTable()->IsRowSelected(aRowIdx));
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
return static_cast<gboolean>(proxy->TableRowSelected(aRowIdx));
}
@ -354,7 +356,8 @@ isCellSelectedCB(AtkTable *aTable, gint aRowIdx, gint aColIdx)
if (accWrap) {
return static_cast<gboolean>(accWrap->AsTable()->
IsCellSelected(aRowIdx, aColIdx));
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
return static_cast<gboolean>(proxy->TableCellSelected(aRowIdx, aColIdx));
}

View File

@ -500,7 +500,8 @@ getTextSelectionCB(AtkText *aText, gint aSelectionNum,
*aEndOffset = endOffset;
return getTextCB(aText, *aStartOffset, *aEndOffset);
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
nsString data;
proxy->SelectionBoundsAt(aSelectionNum, data, &startOffset, &endOffset);
*aStartOffset = startOffset;
@ -526,7 +527,8 @@ addTextSelectionCB(AtkText *aText,
}
return text->AddToSelection(aStartOffset, aEndOffset);
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
return proxy->AddToSelection(aStartOffset, aEndOffset);
}
@ -545,7 +547,8 @@ removeTextSelectionCB(AtkText *aText,
}
return text->RemoveFromSelection(aSelectionNum);
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
return proxy->RemoveFromSelection(aSelectionNum);
}
@ -564,7 +567,8 @@ setTextSelectionCB(AtkText *aText, gint aSelectionNum,
}
return text->SetSelectionBoundsAt(aSelectionNum, aStartOffset, aEndOffset);
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
return proxy->SetSelectionBoundsAt(aSelectionNum, aStartOffset, aEndOffset);
}

View File

@ -1586,6 +1586,7 @@ pref("services.sync.validation.enabled", true);
// Preferences for the form autofill system extension
pref("browser.formautofill.experimental", false);
pref("browser.formautofill.enabled", false);
pref("browser.formautofill.loglevel", "Warn");
// Enable safebrowsing v4 tables (suffixed by "-proto") update.
#ifdef NIGHTLY_BUILD

View File

@ -99,6 +99,7 @@
const event = new CustomEvent("AboutNetErrorResetPreferences", {bubbles:true});
document.dispatchEvent(event);
});
addAutofocus("prefResetButton", "beforeend");
}
function setupAdvancedButton() {
@ -198,6 +199,7 @@
initPageCertError();
return;
}
addAutofocus("errorTryAgain");
document.body.className = "neterror";
@ -317,6 +319,7 @@
document.dispatchEvent(event);
});
addAutofocus("openPortalLoginPageButton");
setupAdvancedButton();
addDomainErrorLinks();
@ -335,6 +338,7 @@
host.textContent = document.location.hostname;
}
addAutofocus("returnButton");
setupAdvancedButton();
document.getElementById("learnMoreContainer").style.display = "block";
@ -364,6 +368,22 @@
addDomainErrorLinks();
}
/* Only do autofocus if we're the toplevel frame; otherwise we
don't want to call attention to ourselves! The key part is
that autofocus happens on insertion into the tree, so we
can remove the button, add @autofocus, and reinsert the
button.
*/
function addAutofocus(buttonId, position = "afterbegin") {
if (window.top == window) {
var button = document.getElementById(buttonId);
var parent = button.parentNode;
button.remove();
button.setAttribute("autofocus", "true");
parent.insertAdjacentElement(position, button);
}
}
/* Try to preserve the links contained in the error description, like
the error code.
@ -595,10 +615,10 @@
</div>
<div id="certErrorAndCaptivePortalButtonContainer" class="button-container">
<button id="returnButton" class="primary" autocomplete="off" autofocus="true">&returnToPreviousPage.label;</button>
<button id="openPortalLoginPageButton" class="primary" autocomplete="off" autofocus="true">&openPortalLoginPage.label2;</button>
<button id="returnButton" class="primary" autocomplete="off">&returnToPreviousPage.label;</button>
<button id="openPortalLoginPageButton" class="primary" autocomplete="off">&openPortalLoginPage.label2;</button>
<div class="button-spacer"></div>
<button id="advancedButton" autocomplete="off" autofocus="true">&advanced.label;</button>
<button id="advancedButton" autocomplete="off">&advanced.label;</button>
</div>
</div>
@ -606,21 +626,6 @@
<button id="errorTryAgain" class="primary" autocomplete="off" onclick="retryThis(this);">&retry.label;</button>
</div>
<script>
// Only do autofocus if we're the toplevel frame; otherwise we
// don't want to call attention to ourselves! The key part is
// that autofocus happens on insertion into the tree, so we
// can remove the button, add @autofocus, and reinsert the
// button.
if (window.top == window) {
var button = document.getElementById("errorTryAgain");
var parent = button.parentNode;
button.remove();
button.setAttribute("autofocus", "true");
parent.appendChild(button);
}
</script>
<!-- UI for option to report certificate errors to Mozilla. Removed on
init for other error types .-->
<div id="certificateErrorReporting">

View File

@ -2367,7 +2367,7 @@ function BrowserViewSourceOfDocument(aArgsOrDocument) {
relatedToCurrent: true,
inBackground: false,
preferredRemoteType,
relatedBrowser: args.browser
sameProcessAsFrameLoader: args.browser ? args.browser.frameLoader : null
});
args.viewSourceBrowser = tabBrowser.getBrowserForTab(tab);
top.gViewSourceUtils.viewSourceInBrowser(args);
@ -3347,19 +3347,21 @@ var PrintPreviewListener = {
let browser = gBrowser.selectedBrowser;
let preferredRemoteType = browser.remoteType;
this._tabBeforePrintPreview = gBrowser.selectedTab;
this._printPreviewTab = gBrowser.loadOneTab("about:blank",
{ inBackground: false,
preferredRemoteType,
relatedBrowser: browser });
this._printPreviewTab = gBrowser.loadOneTab("about:blank", {
inBackground: false,
preferredRemoteType,
sameProcessAsFrameLoader: browser.frameLoader
});
gBrowser.selectedTab = this._printPreviewTab;
}
return gBrowser.getBrowserForTab(this._printPreviewTab);
},
createSimplifiedBrowser() {
let browser = this._tabBeforePrintPreview.linkedBrowser;
this._simplifyPageTab = gBrowser.loadOneTab("about:blank",
{ inBackground: true,
relatedBrowser: browser });
this._simplifyPageTab = gBrowser.loadOneTab("about:blank", {
inBackground: true,
sameProcessAsFrameLoader: browser.frameLoader
});
return this.getSimplifiedSourceBrowser();
},
getSourceBrowser() {

View File

@ -1511,32 +1511,32 @@
var aPreferredRemoteType;
var aNoReferrer;
var aUserContextId;
var aRelatedBrowser;
var aSameProcessAsFrameLoader;
var aOriginPrincipal;
var aOpener;
if (arguments.length == 2 &&
typeof arguments[1] == "object" &&
!(arguments[1] instanceof Ci.nsIURI)) {
let params = arguments[1];
aTriggeringPrincipal = params.triggeringPrincipal
aReferrerURI = params.referrerURI;
aReferrerPolicy = params.referrerPolicy;
aCharset = params.charset;
aPostData = params.postData;
aLoadInBackground = params.inBackground;
aAllowThirdPartyFixup = params.allowThirdPartyFixup;
aFromExternal = params.fromExternal;
aRelatedToCurrent = params.relatedToCurrent;
aAllowMixedContent = params.allowMixedContent;
aSkipAnimation = params.skipAnimation;
aForceNotRemote = params.forceNotRemote;
aPreferredRemoteType = params.preferredRemoteType;
aNoReferrer = params.noReferrer;
aUserContextId = params.userContextId;
aRelatedBrowser = params.relatedBrowser;
aOriginPrincipal = params.originPrincipal;
aOpener = params.opener;
aIsPrerendered = params.isPrerendered;
aTriggeringPrincipal = params.triggeringPrincipal
aReferrerURI = params.referrerURI;
aReferrerPolicy = params.referrerPolicy;
aCharset = params.charset;
aPostData = params.postData;
aLoadInBackground = params.inBackground;
aAllowThirdPartyFixup = params.allowThirdPartyFixup;
aFromExternal = params.fromExternal;
aRelatedToCurrent = params.relatedToCurrent;
aAllowMixedContent = params.allowMixedContent;
aSkipAnimation = params.skipAnimation;
aForceNotRemote = params.forceNotRemote;
aPreferredRemoteType = params.preferredRemoteType;
aNoReferrer = params.noReferrer;
aUserContextId = params.userContextId;
aSameProcessAsFrameLoader = params.sameProcessAsFrameLoader;
aOriginPrincipal = params.originPrincipal;
aOpener = params.opener;
aIsPrerendered = params.isPrerendered;
}
var bgLoad = (aLoadInBackground != null) ? aLoadInBackground :
@ -1560,7 +1560,7 @@
noReferrer: aNoReferrer,
userContextId: aUserContextId,
originPrincipal: aOriginPrincipal,
relatedBrowser: aRelatedBrowser,
sameProcessAsFrameLoader: aSameProcessAsFrameLoader,
opener: aOpener,
isPrerendered: aIsPrerendered });
if (!bgLoad)
@ -1736,9 +1736,9 @@
aBrowser.destroy();
// Make sure to restore the original droppedLinkHandler and
// relatedBrowser.
// sameProcessAsFrameLoader.
let droppedLinkHandler = aBrowser.droppedLinkHandler;
let relatedBrowser = aBrowser.relatedBrowser;
let sameProcessAsFrameLoader = aBrowser.sameProcessAsFrameLoader;
// Change the "remote" attribute.
let parent = aBrowser.parentNode;
@ -1756,7 +1756,7 @@
// type copying related browser would stop the switch and the
// previously related browser will no longer be relevant.
if (!aShouldBeRemote || currentRemoteType == aOptions.remoteType) {
aBrowser.relatedBrowser = relatedBrowser;
aBrowser.sameProcessAsFrameLoader = sameProcessAsFrameLoader;
}
if (aOptions.opener) {
@ -1996,8 +1996,8 @@
b.setAttribute("autoscrollpopup", this._autoScrollPopup.id);
if (aParams.relatedBrowser) {
b.relatedBrowser = aParams.relatedBrowser;
if (aParams.sameProcessAsFrameLoader) {
b.sameProcessAsFrameLoader = aParams.sameProcessAsFrameLoader;
}
// Create the browserStack container
@ -2078,7 +2078,7 @@
remoteType,
uriIsAboutBlank,
userContextId: aParams.userContextId,
relatedBrowser: aParams.relatedBrowser,
sameProcessAsFrameLoader: aParams.sameProcessAsFrameLoader,
opener: aParams.opener,
isPrerendered: aParams.isPrerendered});
}
@ -2157,7 +2157,7 @@
var aNoReferrer;
var aUserContextId;
var aEventDetail;
var aRelatedBrowser;
var aSameProcessAsFrameLoader;
var aOriginPrincipal;
var aDisallowInheritPrincipal;
var aOpener;
@ -2181,7 +2181,7 @@
aNoReferrer = params.noReferrer;
aUserContextId = params.userContextId;
aEventDetail = params.eventDetail;
aRelatedBrowser = params.relatedBrowser;
aSameProcessAsFrameLoader = params.sameProcessAsFrameLoader;
aOriginPrincipal = params.originPrincipal;
aDisallowInheritPrincipal = params.disallowInheritPrincipal;
aOpener = params.opener;
@ -2273,7 +2273,7 @@
forceNotRemote: aForceNotRemote,
preferredRemoteType: aPreferredRemoteType,
userContextId: aUserContextId,
relatedBrowser: aRelatedBrowser,
sameProcessAsFrameLoader: aSameProcessAsFrameLoader,
opener: aOpener,
isPrerendered: aIsPrerendered,
};

View File

@ -46,7 +46,9 @@ add_task(function* checkCaptivePortalCertErrorUI() {
"Captive portal error page UI is visible.");
info("Clicking the Open Login Page button.");
doc.getElementById("openPortalLoginPageButton").click();
let loginButton = doc.getElementById("openPortalLoginPageButton");
is(loginButton.getAttribute("autofocus"), "true", "openPortalLoginPageButton has autofocus");
loginButton.click();
});
let portalTab = yield portalTabPromise;

View File

@ -39,6 +39,7 @@ add_task(function* checkReturnToAboutHome() {
yield ContentTask.spawn(browser, null, function* () {
let doc = content.document;
let returnButton = doc.getElementById("returnButton");
is(returnButton.getAttribute("autofocus"), "true", "returnButton has autofocus");
returnButton.click();
});
yield pageshowPromise;

View File

@ -31,7 +31,10 @@ add_task(function* checkReturnToPreviousPage() {
let pageshowPromise = promiseWaitForEvent(browser, "pageshow");
yield ContentTask.spawn(browser, null, function* () {
content.document.getElementById("prefResetButton").click();
let doc = content.document;
let prefResetButton = doc.getElementById("prefResetButton");
Assert.equal(prefResetButton.getAttribute("autofocus"), "true", "prefResetButton has autofocus");
prefResetButton.click();
});
yield pageshowPromise;

View File

@ -2,6 +2,8 @@
// Obtain the blob, and read the string contained in it.
// Post back the string.
/* eslint-env worker */
var postStringInBlob = function(blobObject) {
var fileReader = new FileReaderSync();
var result = fileReader.readAsText(blobObject);

View File

@ -877,9 +877,15 @@ var gCookiesWindow = {
_updateRemoveAllButton: function gCookiesWindow__updateRemoveAllButton() {
let removeAllCookies = document.getElementById("removeAllCookies");
removeAllCookies.disabled = this._view._rowCount == 0;
let label = this._view._filtered ?
this._bundle.getString("removeAllShownCookies.label") : this._bundle.getString("removeAllCookies.label");
removeAllCookies.setAttribute("label", label);
let labelStringID = "removeAllCookies.label";
let accessKeyStringID = "removeAllCookies.accesskey";
if (this._view._filtered) {
labelStringID = "removeAllShownCookies.label";
accessKeyStringID = "removeAllShownCookies.accesskey";
}
removeAllCookies.setAttribute("label", this._bundle.getString(labelStringID));
removeAllCookies.setAttribute("accesskey", this._bundle.getString(accessKeyStringID));
},
filter() {

View File

@ -45,9 +45,6 @@ let gSiteDataSettings = {
Services.obs.notifyObservers(null, "sitedata-settings-init", null);
});
let removeAllBtn = document.getElementById("removeAll");
removeAllBtn.setAttribute("accesskey", this._prefStrBundle.getString("removeAll.accesskey"));
setEventListener("hostCol", "click", this.onClickTreeCol);
setEventListener("usageCol", "click", this.onClickTreeCol);
setEventListener("statusCol", "click", this.onClickTreeCol);
@ -64,9 +61,15 @@ let gSiteDataSettings = {
let removeAllBtn = document.getElementById("removeAll");
removeSelectedBtn.disabled = items.length == 0;
removeAllBtn.disabled = removeSelectedBtn.disabled;
let removeAllBtnLabel = this._searchBox.value ?
this._prefStrBundle.getString("removeAllShown.label") : this._prefStrBundle.getString("removeAll.label");
removeAllBtn.setAttribute("label", removeAllBtnLabel);
let removeAllBtnLabelStringID = "removeAllSiteData.label";
let removeAllBtnAccesskeyStringID = "removeAllSiteData.accesskey";
if (this._searchBox.value) {
removeAllBtnLabelStringID = "removeAllSiteDataShown.label";
removeAllBtnAccesskeyStringID = "removeAllSiteDataShown.accesskey";
}
removeAllBtn.setAttribute("label", this._prefStrBundle.getString(removeAllBtnLabelStringID));
removeAllBtn.setAttribute("accesskey", this._prefStrBundle.getString(removeAllBtnAccesskeyStringID));
},
/**

View File

@ -16,6 +16,7 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr, manager: Cm} = Compo
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://formautofill/FormAutofillUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ProfileAutoCompleteResult",
"resource://formautofill/ProfileAutoCompleteResult.jsm");
@ -67,7 +68,7 @@ AutocompleteFactory.prototype = {
* @implements {nsIAutoCompleteSearch}
*/
function AutofillProfileAutoCompleteSearch() {
FormAutofillUtils.defineLazyLogGetter(this, "AutofillProfileAutoCompleteSearch");
}
AutofillProfileAutoCompleteSearch.prototype = {
classID: Components.ID("4f9f1e4c-7f2c-439e-9c9e-566b68bc187d"),
@ -87,6 +88,7 @@ AutofillProfileAutoCompleteSearch.prototype = {
* @param {Object} listener the listener to notify when the search is complete
*/
startSearch(searchString, searchParam, previousResult, listener) {
this.log.debug("startSearch: for", searchString, "with input", formFillController.focusedInput);
let focusedInput = formFillController.focusedInput;
this.forceStop = false;
let info = this._serializeInfo(FormAutofillContent.getInputDetails(focusedInput));
@ -130,6 +132,7 @@ AutofillProfileAutoCompleteSearch.prototype = {
* Promise that resolves when profiles returned from parent process.
*/
_getProfiles(data) {
this.log.debug("_getProfiles with data:", data);
return new Promise((resolve) => {
Services.cpmm.addMessageListener("FormAutofill:Profiles", function getResult(result) {
Services.cpmm.removeMessageListener("FormAutofill:Profiles", getResult);
@ -161,6 +164,8 @@ let ProfileAutocomplete = {
return;
}
FormAutofillUtils.defineLazyLogGetter(this, "ProfileAutocomplete");
this.log.debug("ensureRegistered");
this._factory = new AutocompleteFactory();
this._factory.register(AutofillProfileAutoCompleteSearch);
this._registered = true;
@ -173,6 +178,7 @@ let ProfileAutocomplete = {
return;
}
this.log.debug("ensureUnregistered");
this._factory.unregister();
this._factory = null;
this._registered = false;
@ -206,6 +212,7 @@ let ProfileAutocomplete = {
},
_fillFromAutocompleteRow(focusedInput) {
this.log.debug("_fillFromAutocompleteRow:", focusedInput);
let formDetails = FormAutofillContent.getFormDetails(focusedInput);
if (!formDetails) {
// The observer notification is for a different frame.
@ -250,11 +257,13 @@ let ProfileAutocomplete = {
*/
var FormAutofillContent = {
/**
* @type {WeakMap} mapping FormLike root HTML elements to form details.
* @type {WeakMap} mapping FormLike root HTML elements to FormAutofillHandler objects.
*/
_formsDetails: new WeakMap(),
init() {
FormAutofillUtils.defineLazyLogGetter(this, "FormAutofillContent");
Services.cpmm.addMessageListener("FormAutofill:enabledStatus", (result) => {
if (result.data) {
ProfileAutocomplete.ensureRegistered();
@ -306,6 +315,7 @@ var FormAutofillContent = {
},
_identifyAutofillFields(doc) {
this.log.debug("_identifyAutofillFields:", "" + doc.location);
let forms = [];
// Collects root forms from inputs.
@ -322,18 +332,22 @@ var FormAutofillContent = {
}
}
this.log.debug("Found", forms.length, "forms");
// Collects the fields that can be autofilled from each form and marks them
// as autofill fields if the amount is above the threshold.
forms.forEach(form => {
let formHandler = new FormAutofillHandler(form);
formHandler.collectFormFields();
if (formHandler.fieldDetails.length < AUTOFILL_FIELDS_THRESHOLD) {
this.log.debug("Ignoring form since it has only", formHandler.fieldDetails.length,
"field(s)");
return;
}
this._formsDetails.set(form.rootElement, formHandler);
formHandler.fieldDetails.forEach(
detail => this._markAsAutofillField(detail.element));
this.log.debug("Adding form handler to _formsDetails:", formHandler);
formHandler.fieldDetails.forEach(detail => this._markAsAutofillField(detail.element));
});
},

View File

@ -14,9 +14,14 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://formautofill/FormAutofillUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "FormAutofillHeuristics",
"resource://formautofill/FormAutofillHeuristics.jsm");
this.log = null;
FormAutofillUtils.defineLazyLogGetter(this, this.EXPORTED_SYMBOLS[0]);
/**
* Handles profile autofill for a DOM Form element.
* @param {FormLike} form Form that need to be auto filled
@ -71,6 +76,7 @@ FormAutofillHandler.prototype = {
f.contactType == info.contactType &&
f.fieldName == info.fieldName)) {
// A field with the same identifier already exists.
log.debug("Not collecting a field matching another with the same info:", info);
return null;
}
@ -90,6 +96,8 @@ FormAutofillHandler.prototype = {
this.fieldDetails.push(formatWithElement);
}
log.debug("Collected details on", autofillData.length, "fields");
return autofillData;
},
@ -110,6 +118,7 @@ FormAutofillHandler.prototype = {
* }
*/
autofillFormFields(autofillResult) {
log.debug("autofillFormFields:", autofillResult);
for (let field of autofillResult) {
// TODO: Skip filling the value of focused input which is filled in
// FormFillController.

View File

@ -29,12 +29,16 @@
"use strict";
this.EXPORTED_SYMBOLS = ["FormAutofillParent"];
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://formautofill/FormAutofillUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "OS",
"resource://gre/modules/osfile.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ProfileStorage",
@ -42,6 +46,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "ProfileStorage",
XPCOMUtils.defineLazyModuleGetter(this, "FormAutofillPreferences",
"resource://formautofill/FormAutofillPreferences.jsm");
this.log = null;
FormAutofillUtils.defineLazyLogGetter(this, this.EXPORTED_SYMBOLS[0]);
const PROFILE_JSON_FILE_NAME = "autofill-profiles.json";
const ENABLED_PREF = "browser.formautofill.enabled";
@ -63,6 +70,7 @@ FormAutofillParent.prototype = {
* Initializes ProfileStorage and registers the message handler.
*/
init() {
log.debug("init");
let storePath = OS.Path.join(OS.Constants.Path.profileDir, PROFILE_JSON_FILE_NAME);
this._profileStore = new ProfileStorage(storePath);
this._profileStore.initialize();
@ -79,6 +87,7 @@ FormAutofillParent.prototype = {
},
observe(subject, topic, data) {
log.debug("observe:", topic, "with data:", data);
switch (topic) {
case "advanced-pane-loaded": {
let formAutofillPreferences = new FormAutofillPreferences();
@ -111,6 +120,7 @@ FormAutofillParent.prototype = {
* form autofill status changed.
*/
_onStatusChanged() {
log.debug("_onStatusChanged: Status changed to", this._enabled);
if (this._enabled) {
Services.ppmm.addMessageListener("FormAutofill:GetProfiles", this);
} else {
@ -199,5 +209,3 @@ FormAutofillParent.prototype = {
target.sendAsyncMessage("FormAutofill:Profiles", profiles);
},
};
this.EXPORTED_SYMBOLS = ["FormAutofillParent"];

View File

@ -6,7 +6,21 @@
this.EXPORTED_SYMBOLS = ["FormAutofillUtils"];
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
this.FormAutofillUtils = {
defineLazyLogGetter(scope, logPrefix) {
XPCOMUtils.defineLazyGetter(scope, "log", () => {
let ConsoleAPI = Cu.import("resource://gre/modules/Console.jsm", {}).ConsoleAPI;
return new ConsoleAPI({
maxLogLevelPref: "browser.formautofill.loglevel",
prefix: logPrefix,
});
});
},
generateFullName(firstName, lastName, middleName) {
// TODO: The implementation should depend on the L10N spec, but a simplified
// rule is used here.

View File

@ -9,15 +9,18 @@ this.EXPORTED_SYMBOLS = ["ProfileAutoCompleteResult"];
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://formautofill/FormAutofillUtils.jsm");
this.log = null;
FormAutofillUtils.defineLazyLogGetter(this, this.EXPORTED_SYMBOLS[0]);
XPCOMUtils.defineLazyModuleGetter(this, "FormAutofillUtils",
"resource://formautofill/FormAutofillUtils.jsm");
this.ProfileAutoCompleteResult = function(searchString,
focusedFieldName,
allFieldNames,
matchingProfiles,
{resultCode = null}) {
log.debug("Constructing new ProfileAutoCompleteResult:", [...arguments]);
this.searchString = searchString;
this._focusedFieldName = focusedFieldName;
this._allFieldNames = allFieldNames;

View File

@ -39,12 +39,16 @@
"use strict";
this.EXPORTED_SYMBOLS = ["ProfileStorage"];
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://formautofill/FormAutofillUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "JSONFile",
"resource://gre/modules/JSONFile.jsm");
@ -52,6 +56,9 @@ XPCOMUtils.defineLazyServiceGetter(this, "gUUIDGenerator",
"@mozilla.org/uuid-generator;1",
"nsIUUIDGenerator");
this.log = null;
FormAutofillUtils.defineLazyLogGetter(this, this.EXPORTED_SYMBOLS[0]);
const SCHEMA_VERSION = 1;
// Name-related fields will be handled in follow-up bugs due to the complexity.
@ -107,6 +114,7 @@ ProfileStorage.prototype = {
* The new profile for saving.
*/
add(profile) {
log.debug("add:", profile);
this._store.ensureDataReady();
let profileToSave = this._normalizeProfile(profile);
@ -135,6 +143,7 @@ ProfileStorage.prototype = {
* The new profile used to overwrite the old one.
*/
update(guid, profile) {
log.debug("update:", guid, profile);
this._store.ensureDataReady();
let profileFound = this._findByGUID(guid);
@ -184,6 +193,7 @@ ProfileStorage.prototype = {
* Indicates which profile to remove.
*/
remove(guid) {
log.debug("remove:", guid);
this._store.ensureDataReady();
this._store.data.profiles =
@ -200,6 +210,7 @@ ProfileStorage.prototype = {
* A clone of the profile.
*/
get(guid) {
log.debug("get:", guid);
this._store.ensureDataReady();
let profileFound = this._findByGUID(guid);
@ -218,6 +229,7 @@ ProfileStorage.prototype = {
* An array containing clones of all profiles.
*/
getAll() {
log.debug("getAll");
this._store.ensureDataReady();
// Profiles are cloned to avoid accidental modifications from outside.
@ -231,10 +243,13 @@ ProfileStorage.prototype = {
* An array containing clones of matched profiles.
*/
getByFilter({info, searchString}) {
log.debug("getByFilter:", info, searchString);
this._store.ensureDataReady();
// Profiles are cloned to avoid accidental modifications from outside.
return this._findByFilter({info, searchString}).map(this._clone);
let result = this._findByFilter({info, searchString}).map(this._clone);
log.debug("getByFilter: Returning", result.length, "result(s)");
return result;
},
_clone(profile) {
@ -291,5 +306,3 @@ ProfileStorage.prototype = {
return this._store._save();
},
};
this.EXPORTED_SYMBOLS = ["ProfileStorage"];

View File

@ -636,6 +636,7 @@
@RESPATH@/greprefs.js
@RESPATH@/defaults/autoconfig/prefcalls.js
@RESPATH@/browser/defaults/permissions
@RESPATH@/browser/defaults/blocklists
; Warning: changing the path to channel-prefs.js can cause bugs (Bug 756325)
; Technically this is an app pref file, but we are keeping it in the original

View File

@ -125,12 +125,15 @@ cookiesAll=The following cookies are stored on your computer:
cookiesFiltered=The following cookies match your search:
# LOCALIZATION NOTE (removeAllCookies, removeAllShownCookies):
# removeAllCookies and removeAllShownCookies are both used on the same one button,
# never displayed together and can share the same accesskey.
# When only partial cookies are shown as a result of keyword search,
# removeAllShownCookies is displayed as button label.
# removeAllCookies is displayed when no keyword search and all cookies are shown.
removeAllCookies.label=Remove All
removeAllShownCookies.label=Remove All Shown
removeAllCookies.accesskey=A
removeAllShownCookies.label=Remove All Shown
removeAllShownCookies.accesskey=A
# LOCALIZATION NOTE (removeSelectedCookies):
# Semicolon-separated list of plural forms. See:
@ -187,13 +190,16 @@ clearSiteDataNow=Clear Now
important=Important
default=Default
siteUsage=%1$S %2$S
# LOCALIZATION NOTE (removeAll, removeAllShown):
# LOCALIZATION NOTE (removeAllSiteData, removeAllSiteDataShown):
# removeAllSiteData and removeAllSiteDataShown are both used on the same one button,
# never displayed together and can share the same accesskey.
# When only partial sites are shown as a result of keyword search,
# removeAllShown is displayed as button label.
# removeAll is displayed when no keyword search and all sites are shown.
removeAll.label=Remove All
removeAllShown.label=Remove All Shown
removeAll.accesskey=e
removeAllSiteData.label=Remove All
removeAllSiteData.accesskey=e
removeAllSiteDataShown.label=Remove All Shown
removeAllSiteDataShown.accesskey=e
# LOCALIZATION NOTE (featureEnableRequiresRestart, featureDisableRequiresRestart, restartTitle): %S = brandShortName
featureEnableRequiresRestart=%S must restart to enable this feature.

32
build/gen_test_backend.py Normal file
View File

@ -0,0 +1,32 @@
# 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/.
import sys
from mozbuild.backend.test_manifest import TestManifestBackend
from mozbuild.base import BuildEnvironmentNotFoundException, MozbuildObject
from mozbuild.frontend.emitter import TreeMetadataEmitter
from mozbuild.frontend.reader import BuildReader, EmptyConfig
def gen_test_backend():
build_obj = MozbuildObject.from_environment()
try:
config = build_obj.config_environment
except BuildEnvironmentNotFoundException:
print("No build detected, test metadata may be incomplete.")
config = EmptyConfig(build_obj.topsrcdir)
config.topobjdir = build_obj.topobjdir
reader = BuildReader(config)
emitter = TreeMetadataEmitter(config)
backend = TestManifestBackend(config)
context = reader.read_topsrcdir()
data = emitter.emit(context, emitfn=emitter._process_test_manifests)
backend.consume(data)
if __name__ == '__main__':
sys.exit(gen_test_backend())

View File

@ -25,9 +25,7 @@ $(subst .,%,$(BUILD_BACKEND_FILES)):
$(PYTHON) $(BACKEND_GENERATION_SCRIPT)
define build_backend_rule
$(1)_files := $$(shell cat $(1).in)
$(1): $$($(1)_files)
$$($(1)_files):
$(1): $$(wildcard $$(shell cat $(1).in))
endef
$(foreach file,$(BUILD_BACKEND_FILES),$(eval $(call build_backend_rule,$(file))))

View File

@ -307,7 +307,7 @@ Tools.netMonitor = {
visibilityswitch: "devtools.netmonitor.enabled",
icon: "chrome://devtools/skin/images/tool-network.svg",
invertIconForDarkTheme: true,
url: "chrome://devtools/content/netmonitor/netmonitor.xul",
url: "chrome://devtools/content/netmonitor/netmonitor.xhtml",
label: l10n("netmonitor.label"),
panelLabel: l10n("netmonitor.panelLabel"),
get tooltip() {

View File

@ -24,7 +24,6 @@ add_task(function* () {
let monitor = yield toolbox.selectTool("netmonitor");
let { gStore, windowRequire } = monitor.panelWin;
let Actions = windowRequire("devtools/client/netmonitor/actions/index");
is(gStore.getState().requests.requests.size, 0, "No network requests appear in the network panel");

View File

@ -10,6 +10,17 @@ module.exports = {
// code. Some files in the inspector disable this rule still. The
// goal is to enable the rule globally on all files.
/* eslint-disable max-len */
"mozilla/reject-some-requires": ["error", "^(chrome|chrome:.*|resource:.*|devtools/server/.*|.*\\.jsm|devtools/shared/platform/(chome|content)/.*)$"],
"mozilla/reject-some-requires": ["error",
"^(chrome|chrome:.*|resource:.*|devtools/server/.*|.*\\.jsm|devtools/shared/platform/(chome|content)/.*)$"],
// The webpack bundle does not support trailing commas in functions, so disabled it
// explicitly with linting here.
"comma-dangle": ["error", {
"arrays": "ignore",
"objects": "ignore",
"imports": "ignore",
"exports": "ignore",
"functions": "never",
}]
},
};

View File

@ -54,8 +54,7 @@ module.exports = createClass({
boxModel,
})
:
null,
null
);
},
});

View File

@ -65,15 +65,15 @@ module.exports = createClass({
className: "boxmodel-properties-expander theme-twisty",
open: this.state.isOpen,
onClick: this.onToggleExpander,
},
}
),
dom.span(
{
className: "boxmodel-properties-label",
title: BOXMODEL_L10N.getStr("boxmodel.propertiesLabel"),
},
BOXMODEL_L10N.getStr("boxmodel.propertiesLabel"),
),
BOXMODEL_L10N.getStr("boxmodel.propertiesLabel")
)
),
dom.div(
{
@ -81,8 +81,8 @@ module.exports = createClass({
hidden: !this.state.isOpen,
tabIndex: 0,
},
properties,
),
properties
)
);
},

View File

@ -7,7 +7,7 @@
},
"author": "",
"dependencies": {
"devtools-launchpad": "0.0.29",
"devtools-launchpad": "=0.0.43",
"raw-loader": "^0.5.1",
"json-loader": "^0.5.4"
}

View File

@ -37,7 +37,7 @@ add_task(function* () {
function* isHiddenByDefault({isElementHidden}) {
info("Checking the highlighter is hidden by default");
let hidden = yield isElementHidden("elements");
let hidden = yield isElementHidden("root");
ok(hidden, "highlighter's root is hidden by default");
hidden = yield isElementHidden("label-size");

View File

@ -9,22 +9,15 @@
const TEST_URL = "data:text/html;charset=utf-8,<div>zoom me</div>";
// TEST_LEVELS entries should contain the following properties:
// - level: the zoom level to test
// - expected: the style attribute value to check for on the root highlighter
// element.
const TEST_LEVELS = [{
level: 2,
expected: "position:absolute;transform-origin:top left;" +
"transform:scale(0.5);width:200%;height:200%;"
}, {
level: 1,
expected: "position:absolute;width:100%;height:100%;"
}, {
level: .5,
expected: "position:absolute;transform-origin:top left;" +
"transform:scale(2);width:50%;height:50%;"
}];
// TEST_LEVELS entries should contain the zoom level to test.
const TEST_LEVELS = [2, 1, .5];
// Returns the expected style attribute value to check for on the root highlighter
// element, for the values given.
const expectedStyle = (w, h, z) =>
(z !== 1 ? `transform-origin:top left; transform:scale(${1 / z}); ` : "") +
`position:absolute; width:${w * z}px;height:${h * z}px; ` +
"overflow:hidden";
add_task(function* () {
let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
@ -35,7 +28,7 @@ add_task(function* () {
let isVisible = yield testActor.isHighlighting();
ok(isVisible, "The highlighter is visible");
for (let {level, expected} of TEST_LEVELS) {
for (let level of TEST_LEVELS) {
info("Zoom to level " + level +
" and check that the highlighter is correct");
@ -48,7 +41,9 @@ add_task(function* () {
info("Check that the highlighter root wrapper node was scaled down");
let style = yield getRootNodeStyle(testActor);
is(style, expected, "The style attribute of the root element is correct");
let { width, height } = yield testActor.getWindowDimensions();
is(style, expectedStyle(width, height, level),
"The style attribute of the root element is correct");
}
});

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,7 @@ devtools.jar:
content/shared/widgets/VariablesView.xul (shared/widgets/VariablesView.xul)
content/projecteditor/chrome/content/projecteditor.xul (projecteditor/chrome/content/projecteditor.xul)
content/projecteditor/lib/helpers/readdir.js (projecteditor/lib/helpers/readdir.js)
content/netmonitor/netmonitor.xul (netmonitor/netmonitor.xul)
content/netmonitor/netmonitor.xhtml (netmonitor/netmonitor.xhtml)
content/netmonitor/netmonitor.js (netmonitor/netmonitor.js)
content/webconsole/webconsole.xul (webconsole/webconsole.xul)
* content/scratchpad/scratchpad.xul (scratchpad/scratchpad.xul)

View File

@ -5,6 +5,10 @@
"use strict";
module.exports = {
"env": {
"browser": true,
},
// Extend from the devtools eslintrc.
"extends": "../../.eslintrc.js",

View File

@ -2,8 +2,6 @@
* 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/. */
/* globals NetMonitorController */
"use strict";
const {
@ -48,7 +46,7 @@ function cloneSelectedRequest() {
* Send a new HTTP request using the data in the custom request form.
*/
function sendCustomRequest() {
if (!NetMonitorController.supportsCustomRequest) {
if (!window.NetMonitorController.supportsCustomRequest) {
return cloneSelectedRequest();
}
@ -72,7 +70,7 @@ function sendCustomRequest() {
data.body = selected.requestPostData.postData.text;
}
NetMonitorController.webConsoleClient.sendHTTPRequest(data, (response) => {
window.NetMonitorController.webConsoleClient.sendHTTPRequest(data, (response) => {
return dispatch({
type: SEND_CUSTOM_REQUEST,
id: response.eventActor.actor,

View File

@ -5,6 +5,7 @@
"use strict";
const {
ACTIVITY_TYPE,
OPEN_NETWORK_DETAILS,
OPEN_STATISTICS,
SELECT_DETAILS_PANEL_TAB,
@ -29,6 +30,9 @@ function openNetworkDetails(open) {
* @param {boolean} visible - expected performance statistics panel open state
*/
function openStatistics(open) {
if (open) {
window.NetMonitorController.triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED);
}
return {
type: OPEN_STATISTICS,
open,

View File

@ -0,0 +1,136 @@
/* 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 {
createClass,
createFactory,
DOM,
PropTypes,
} = require("devtools/client/shared/vendor/react");
const { connect } = require("devtools/client/shared/vendor/react-redux");
const { findDOMNode } = require("devtools/client/shared/vendor/react-dom");
const Actions = require("../actions/index");
const { Prefs } = require("../prefs");
const { getFormDataSections } = require("../request-utils");
const { getSelectedRequest } = require("../selectors/index");
// Components
const SplitBox = createFactory(require("devtools/client/shared/components/splitter/split-box"));
const NetworkDetailsPanel = createFactory(require("../shared/components/network-details-panel"));
const RequestList = createFactory(require("./request-list"));
const Toolbar = createFactory(require("./toolbar"));
const { div } = DOM;
const MediaQueryList = window.matchMedia("(min-width: 700px)");
/*
* Monitor panel component
* The main panel for displaying various network request information
*/
const MonitorPanel = createClass({
displayName: "MonitorPanel",
propTypes: {
isEmpty: PropTypes.bool.isRequired,
networkDetailsOpen: PropTypes.bool.isRequired,
openNetworkDetails: PropTypes.func.isRequired,
request: PropTypes.object,
updateRequest: PropTypes.func.isRequired,
},
getInitialState() {
return {
isVerticalSpliter: MediaQueryList.matches,
};
},
componentDidMount() {
MediaQueryList.addListener(this.onLayoutChange);
},
componentWillReceiveProps(nextProps) {
let {
request = {},
updateRequest,
} = nextProps;
let {
formDataSections,
requestHeaders,
requestHeadersFromUploadStream,
requestPostData,
} = request;
if (!formDataSections && requestHeaders &&
requestHeadersFromUploadStream && requestPostData) {
getFormDataSections(
requestHeaders,
requestHeadersFromUploadStream,
requestPostData,
window.gNetwork.getString.bind(window.gNetwork),
).then((newFormDataSections) => {
updateRequest(
request.id,
{ formDataSections: newFormDataSections },
true,
);
});
}
},
componentWillUnmount() {
MediaQueryList.removeListener(this.onLayoutChange);
let { clientWidth, clientHeight } = findDOMNode(this.refs.networkDetailsPanel) || {};
if (this.state.isVerticalSpliter && clientWidth) {
Prefs.networkDetailsWidth = clientWidth;
} else if (clientHeight) {
Prefs.networkDetailsHeight = clientHeight;
}
},
onLayoutChange() {
this.setState({
isVerticalSpliter: MediaQueryList.matches,
});
},
render() {
let { isEmpty, networkDetailsOpen } = this.props;
return (
div({ className: "monitor-panel" },
Toolbar(),
SplitBox({
className: "devtools-responsive-container",
initialWidth: `${Prefs.networkDetailsWidth}px`,
initialHeight: `${Prefs.networkDetailsHeight}px`,
minSize: "50px",
maxSize: "80%",
splitterSize: "1px",
startPanel: RequestList({ isEmpty }),
endPanel: networkDetailsOpen ?
NetworkDetailsPanel({
ref: "networkDetailsPanel",
toolbox: window.NetMonitorController._toolbox,
}) : null,
endPanelControl: true,
vert: this.state.isVerticalSpliter,
}),
)
);
}
});
module.exports = connect(
(state) => ({
isEmpty: state.requests.requests.isEmpty(),
networkDetailsOpen: state.ui.networkDetailsOpen,
request: getSelectedRequest(state),
}),
(dispatch) => ({
openNetworkDetails: (open) => dispatch(Actions.openNetworkDetails(open)),
updateRequest: (id, data, batch) => dispatch(Actions.updateRequest(id, data, batch)),
}),
)(MonitorPanel);

View File

@ -3,12 +3,12 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DevToolsModules(
'monitor-panel.js',
'network-monitor.js',
'request-list-content.js',
'request-list-context-menu.js',
'request-list-empty.js',
'request-list-header.js',
'request-list-item.js',
'request-list-tooltip.js',
'request-list.js',
'statistics-panel.js',
'toolbar.js',

View File

@ -0,0 +1,39 @@
/* 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 {
createFactory,
DOM,
PropTypes,
} = require("devtools/client/shared/vendor/react");
const { connect } = require("devtools/client/shared/vendor/react-redux");
// Components
const MonitoPanel = createFactory(require("./monitor-panel"));
const StatisticsPanel = createFactory(require("./statistics-panel"));
const { div } = DOM;
/*
* Network monitor component
*/
function NetworkMonitor({ statisticsOpen }) {
return (
div({ className: "network-monitor" },
!statisticsOpen ? MonitoPanel() : StatisticsPanel()
)
);
}
NetworkMonitor.displayName = "NetworkMonitor";
NetworkMonitor.propTypes = {
statisticsOpen: PropTypes.bool.isRequired,
};
module.exports = connect(
(state) => ({ statisticsOpen: state.ui.statisticsOpen }),
)(NetworkMonitor);

View File

@ -2,8 +2,6 @@
* 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/. */
/* globals NetMonitorController */
"use strict";
const { KeyCodes } = require("devtools/client/shared/keycodes");
@ -15,12 +13,11 @@ const {
} = require("devtools/client/shared/vendor/react");
const { connect } = require("devtools/client/shared/vendor/react-redux");
const { HTMLTooltip } = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
const { Task } = require("devtools/shared/task");
const Actions = require("../actions/index");
const {
setTooltipImageContent,
setTooltipStackTraceContent,
} = require("./request-list-tooltip");
} = require("../request-list-tooltip");
const {
getDisplayedRequests,
getWaterfallScale,
@ -28,7 +25,7 @@ const {
// Components
const RequestListItem = createFactory(require("./request-list-item"));
const RequestListContextMenu = require("./request-list-context-menu");
const RequestListContextMenu = require("../request-list-context-menu");
const { div } = DOM;
@ -42,7 +39,6 @@ const RequestListContent = createClass({
displayName: "RequestListContent",
propTypes: {
contextMenu: PropTypes.object.isRequired,
dispatch: PropTypes.func.isRequired,
displayedRequests: PropTypes.object.isRequired,
firstRequestStartedMillis: PropTypes.number.isRequired,
@ -51,11 +47,6 @@ const RequestListContent = createClass({
onSelectDelta: PropTypes.func.isRequired,
scale: PropTypes.number,
selectedRequestId: PropTypes.string,
tooltip: PropTypes.shape({
hide: PropTypes.func.isRequired,
startTogglingOnHover: PropTypes.func.isRequired,
stopTogglingOnHover: PropTypes.func.isRequired,
}).isRequired
},
componentWillMount() {
@ -64,7 +55,10 @@ const RequestListContent = createClass({
cloneSelectedRequest: () => dispatch(Actions.cloneSelectedRequest()),
openStatistics: (open) => dispatch(Actions.openStatistics(open)),
});
this.tooltip = new HTMLTooltip(NetMonitorController._toolbox.doc, { type: "arrow" });
this.tooltip = new HTMLTooltip(
window.NetMonitorController._toolbox.doc,
{ type: "arrow" }
);
},
componentDidMount() {
@ -150,7 +144,7 @@ const RequestListContent = createClass({
* The current tooltip instance.
* @return {Promise}
*/
onHover: Task.async(function* (target, tooltip) {
onHover(target, tooltip) {
let itemEl = target.closest(".request-list-item");
if (!itemEl) {
return false;
@ -171,7 +165,7 @@ const RequestListContent = createClass({
}
return false;
}),
},
/**
* Scroll listener for the requests menu view.
@ -231,15 +225,6 @@ const RequestListContent = createClass({
this.shouldScrollBottom = false;
},
/**
* If a focused item was unmounted, transfer the focus to the container element.
*/
onFocusedNodeUnmount() {
if (this.refs.contentEl) {
this.refs.contentEl.focus();
}
},
render() {
const {
displayedRequests,
@ -257,15 +242,14 @@ const RequestListContent = createClass({
onKeyDown: this.onKeyDown,
},
displayedRequests.map((item, index) => RequestListItem({
key: item.id,
firstRequestStartedMillis,
item,
index,
isSelected: item.id === selectedRequestId,
firstRequestStartedMillis,
onMouseDown: () => onItemMouseDown(item.id),
key: item.id,
onContextMenu: this.onContextMenu,
onFocusedNodeChange: this.onFocusedNodeChange,
onFocusedNodeUnmount: this.onFocusedNodeUnmount,
onMouseDown: () => onItemMouseDown(item.id),
onSecurityIconClick: () => onSecurityIconClick(item.securityState),
}))
)

View File

@ -2,8 +2,6 @@
* 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/. */
/* globals NetMonitorController */
"use strict";
const {
@ -69,6 +67,7 @@ module.exports = connect(
dispatch => ({
onPerfClick: () => dispatch(Actions.openStatistics(true)),
onReloadClick: () =>
NetMonitorController.triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_DEFAULT),
window.NetMonitorController
.triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_DEFAULT),
})
)(RequestListEmptyNotice);

View File

@ -2,14 +2,12 @@
* 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/. */
/* globals document */
"use strict";
const { createClass, PropTypes, DOM } = require("devtools/client/shared/vendor/react");
const { div, button } = DOM;
const { findDOMNode } = require("devtools/client/shared/vendor/react-dom");
const { connect } = require("devtools/client/shared/vendor/react-redux");
const { setNamedTimeout } = require("devtools/client/shared/widgets/view-helpers");
const { L10N } = require("../l10n");
const { getWaterfallScale } = require("../selectors/index");
const Actions = require("../actions/index");
@ -50,19 +48,11 @@ const RequestListHeader = createClass({
},
componentDidMount() {
// This is the first time the waterfall column header is actually rendered.
// Measure its width and update the 'waterfallWidth' property in the store.
// The 'waterfallWidth' will be further updated on every window resize.
const waterfallHeaderEl = findDOMNode(this)
.querySelector("#requests-menu-waterfall-header-box");
if (waterfallHeaderEl) {
const { width } = waterfallHeaderEl.getBoundingClientRect();
this.props.resizeWaterfall(width);
}
// Create the object that takes care of drawing the waterfall canvas background
this.background = new WaterfallBackground(document);
this.background.draw(this.props);
this.resizeWaterfall();
window.addEventListener("resize", this.resizeWaterfall);
},
componentDidUpdate() {
@ -72,6 +62,16 @@ const RequestListHeader = createClass({
componentWillUnmount() {
this.background.destroy();
this.background = null;
window.removeEventListener("resize", this.resizeWaterfall);
},
resizeWaterfall() {
// Measure its width and update the 'waterfallWidth' property in the store.
// The 'waterfallWidth' will be further updated on every window resize.
setNamedTimeout("resize-events", 50, () => {
const { width } = this.refs.header.getBoundingClientRect();
this.props.resizeWaterfall(width);
});
},
render() {
@ -97,8 +97,9 @@ const RequestListHeader = createClass({
return div(
{
id: `requests-menu-${boxName}-header-box`,
key: name,
className: `requests-menu-header requests-menu-${boxName}`,
key: name,
ref: "header",
// Used to style the next column.
"data-active": active,
},

View File

@ -2,8 +2,6 @@
* 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 react/prop-types */
"use strict";
const {
@ -69,7 +67,6 @@ const RequestListItem = createClass({
firstRequestStartedMillis: PropTypes.number.isRequired,
onContextMenu: PropTypes.func.isRequired,
onFocusedNodeChange: PropTypes.func,
onFocusedNodeUnmount: PropTypes.func,
onMouseDown: PropTypes.func.isRequired,
onSecurityIconClick: PropTypes.func.isRequired,
},
@ -94,20 +91,6 @@ const RequestListItem = createClass({
}
},
componentWillUnmount() {
// If this node is being destroyed and has focus, transfer the focus manually
// to the parent tree component. Otherwise, the focus will get lost and keyboard
// navigation in the tree will stop working. This is a workaround for a XUL bug.
// See bugs 1259228 and 1152441 for details.
// DE-XUL: Remove this hack once all usages are only in HTML documents.
if (this.props.isSelected) {
this.refs.el.blur();
if (this.props.onFocusedNodeUnmount) {
this.props.onFocusedNodeUnmount();
}
}
},
render() {
const {
item,
@ -119,7 +102,7 @@ const RequestListItem = createClass({
onSecurityIconClick
} = this.props;
let classList = [ "request-list-item" ];
let classList = ["request-list-item"];
if (isSelected) {
classList.push("selected");
}
@ -158,6 +141,10 @@ const UPDATED_STATUS_PROPS = [
const StatusColumn = createFactory(createClass({
displayName: "StatusColumn",
propTypes: {
item: PropTypes.object.isRequired,
},
shouldComponentUpdate(nextProps) {
return !propertiesEqual(UPDATED_STATUS_PROPS, this.props.item, nextProps.item);
},
@ -199,6 +186,10 @@ const StatusColumn = createFactory(createClass({
const MethodColumn = createFactory(createClass({
displayName: "MethodColumn",
propTypes: {
item: PropTypes.object.isRequired,
},
shouldComponentUpdate(nextProps) {
return this.props.item.method !== nextProps.item.method;
},
@ -221,6 +212,10 @@ const UPDATED_FILE_PROPS = [
const FileColumn = createFactory(createClass({
displayName: "FileColumn",
propTypes: {
item: PropTypes.object.isRequired,
},
shouldComponentUpdate(nextProps) {
return !propertiesEqual(UPDATED_FILE_PROPS, this.props.item, nextProps.item);
},
@ -256,6 +251,11 @@ const UPDATED_DOMAIN_PROPS = [
const DomainColumn = createFactory(createClass({
displayName: "DomainColumn",
propTypes: {
item: PropTypes.object.isRequired,
onSecurityIconClick: PropTypes.func.isRequired,
},
shouldComponentUpdate(nextProps) {
return !propertiesEqual(UPDATED_DOMAIN_PROPS, this.props.item, nextProps.item);
},
@ -264,7 +264,7 @@ const DomainColumn = createFactory(createClass({
const { item, onSecurityIconClick } = this.props;
const { urlDetails, remoteAddress, securityState } = item;
let iconClassList = [ "requests-security-state-icon" ];
let iconClassList = ["requests-security-state-icon"];
let iconTitle;
if (urlDetails.isLocal) {
iconClassList.push("security-state-local");
@ -292,6 +292,10 @@ const DomainColumn = createFactory(createClass({
const CauseColumn = createFactory(createClass({
displayName: "CauseColumn",
propTypes: {
item: PropTypes.object.isRequired,
},
shouldComponentUpdate(nextProps) {
return this.props.item.cause !== nextProps.item.cause;
},
@ -334,6 +338,10 @@ const CONTENT_MIME_TYPE_ABBREVIATIONS = {
const TypeColumn = createFactory(createClass({
displayName: "TypeColumn",
propTypes: {
item: PropTypes.object.isRequired,
},
shouldComponentUpdate(nextProps) {
return this.props.item.mimeType !== nextProps.item.mimeType;
},
@ -366,6 +374,10 @@ const UPDATED_TRANSFERRED_PROPS = [
const TransferredSizeColumn = createFactory(createClass({
displayName: "TransferredSizeColumn",
propTypes: {
item: PropTypes.object.isRequired,
},
shouldComponentUpdate(nextProps) {
return !propertiesEqual(UPDATED_TRANSFERRED_PROPS, this.props.item, nextProps.item);
},
@ -401,6 +413,10 @@ const TransferredSizeColumn = createFactory(createClass({
const ContentSizeColumn = createFactory(createClass({
displayName: "ContentSizeColumn",
propTypes: {
item: PropTypes.object.isRequired,
},
shouldComponentUpdate(nextProps) {
return this.props.item.contentSize !== nextProps.item.contentSize;
},
@ -434,6 +450,11 @@ const UPDATED_WATERFALL_PROPS = [
const WaterfallColumn = createFactory(createClass({
displayName: "WaterfallColumn",
propTypes: {
firstRequestStartedMillis: PropTypes.number.isRequired,
item: PropTypes.object.isRequired,
},
shouldComponentUpdate(nextProps) {
return this.props.firstRequestStartedMillis !== nextProps.firstRequestStartedMillis ||
!propertiesEqual(UPDATED_WATERFALL_PROPS, this.props.item, nextProps.item);
@ -485,12 +506,12 @@ function timingBoxes(item) {
}
}
if (typeof totalTime == "number") {
if (typeof totalTime === "number") {
let text = L10N.getFormatStr("networkMenu.totalMS", totalTime);
boxes.push(div({
key: "total",
className: "requests-menu-timings-total",
title: text
title: text,
}, text));
}
@ -498,5 +519,3 @@ function timingBoxes(item) {
}
module.exports = RequestListItem;
/* eslint-enable react/prop-types */

View File

@ -2,26 +2,13 @@
* 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-env browser */
/* globals gNetwork */
"use strict";
const {
createClass,
createFactory,
DOM,
PropTypes,
} = require("devtools/client/shared/vendor/react");
const { connect } = require("devtools/client/shared/vendor/react-redux");
const { setNamedTimeout } = require("devtools/client/shared/widgets/view-helpers");
const Actions = require("../actions/index");
const { Prefs } = require("../prefs");
const { getFormDataSections } = require("../request-utils");
const {
getActiveFilters,
getSelectedRequest,
} = require("../selectors/index");
// Components
const RequestListContent = createFactory(require("./request-list-content"));
@ -33,94 +20,19 @@ const { div } = DOM;
/**
* Request panel component
*/
const RequestList = createClass({
displayName: "RequestList",
function RequestList({ isEmpty }) {
return (
div({ className: "request-list-container" },
RequestListHeader(),
isEmpty ? RequestListEmptyNotice() : RequestListContent(),
)
);
}
propTypes: {
activeFilters: PropTypes.array,
dispatch: PropTypes.func,
isEmpty: PropTypes.bool.isRequired,
request: PropTypes.object,
networkDetailsOpen: PropTypes.bool,
},
RequestList.displayName = "RequestList";
componentDidMount() {
const { dispatch } = this.props;
RequestList.propTypes = {
isEmpty: PropTypes.bool.isRequired,
};
Prefs.filters.forEach((type) => dispatch(Actions.toggleRequestFilterType(type)));
this.splitter = document.querySelector("#network-inspector-view-splitter");
this.splitter.addEventListener("mouseup", this.resize);
window.addEventListener("resize", this.resize);
},
componentWillReceiveProps(nextProps) {
const { dispatch, request = {}, networkDetailsOpen } = this.props;
if (nextProps.request && nextProps.request !== request) {
dispatch(Actions.openNetworkDetails(true));
}
if (nextProps.networkDetailsOpen !== networkDetailsOpen) {
this.resize();
}
const {
formDataSections,
requestHeaders,
requestHeadersFromUploadStream,
requestPostData,
} = nextProps.request || {};
if (!formDataSections && requestHeaders &&
requestHeadersFromUploadStream && requestPostData) {
getFormDataSections(
requestHeaders,
requestHeadersFromUploadStream,
requestPostData,
gNetwork.getString.bind(gNetwork),
).then((newFormDataSections) => {
dispatch(Actions.updateRequest(
request.id,
{ formDataSections: newFormDataSections },
true,
));
});
}
},
componentWillUnmount() {
this.splitter.removeEventListener("mouseup", this.resize);
window.removeEventListener("resize", this.resize);
},
resize() {
const { dispatch } = this.props;
// Allow requests to settle down first.
setNamedTimeout("resize-events", 50, () => {
const waterfallHeaderEl =
document.querySelector("#requests-menu-waterfall-header-box");
if (waterfallHeaderEl) {
const { width } = waterfallHeaderEl.getBoundingClientRect();
dispatch(Actions.resizeWaterfall(width));
}
});
},
render() {
return (
div({ className: "request-list-container" },
RequestListHeader(),
this.props.isEmpty ? RequestListEmptyNotice() : RequestListContent(),
)
);
}
});
module.exports = connect(
(state) => ({
activeFilters: getActiveFilters(state),
isEmpty: state.requests.requests.isEmpty(),
request: getSelectedRequest(state),
networkDetailsOpen: state.ui.networkDetailsOpen,
})
)(RequestList);
module.exports = RequestList;

View File

@ -2,8 +2,6 @@
* 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/. */
/* globals document */
"use strict";
const {
@ -23,6 +21,7 @@ const {
} = require("../utils/format-utils");
const { button, div } = DOM;
const MediaQueryList = window.matchMedia("(min-width: 700px)");
const NETWORK_ANALYSIS_PIE_CHART_DIAMETER = 200;
const BACK_BUTTON = L10N.getStr("netmonitor.backButton");
@ -43,9 +42,17 @@ const StatisticsPanel = createClass({
requests: PropTypes.object,
},
getInitialState() {
return {
isVerticalSpliter: MediaQueryList.matches,
};
},
componentDidUpdate(prevProps) {
MediaQueryList.addListener(this.onLayoutChange);
const { requests } = this.props;
let ready = requests && requests.every((req) =>
let ready = requests && !requests.isEmpty() && requests.every((req) =>
req.contentSize !== undefined && req.mimeType && req.responseHeaders &&
req.status !== undefined && req.totalTime !== undefined
);
@ -53,16 +60,20 @@ const StatisticsPanel = createClass({
this.createChart({
id: "primedCacheChart",
title: CHARTS_CACHE_ENABLED,
data: ready ? this.sanitizeChartDataSource(requests, false) : null
data: ready ? this.sanitizeChartDataSource(requests, false) : null,
});
this.createChart({
id: "emptyCacheChart",
title: CHARTS_CACHE_DISABLED,
data: ready ? this.sanitizeChartDataSource(requests, true) : null
data: ready ? this.sanitizeChartDataSource(requests, true) : null,
});
},
componentWillUnmount() {
MediaQueryList.removeListener(this.onLayoutChange);
},
createChart({ id, title, data }) {
// Create a new chart.
let chart = Chart.PieTable(document, {
@ -74,7 +85,7 @@ const StatisticsPanel = createClass({
label: L10N.getStr("charts.type"),
size: L10N.getStr("charts.size"),
transferredSize: L10N.getStr("charts.transferred"),
time: L10N.getStr("charts.time")
time: L10N.getStr("charts.time"),
},
data,
strings: {
@ -84,7 +95,7 @@ const StatisticsPanel = createClass({
L10N.getFormatStr("charts.transferredSizeKB",
getSizeWithDecimals(value / 1024)),
time: (value) =>
L10N.getFormatStr("charts.totalS", getTimeWithDecimals(value / 1000))
L10N.getFormatStr("charts.totalS", getTimeWithDecimals(value / 1000)),
},
totals: {
cached: (total) => L10N.getFormatStr("charts.totalCached", total),
@ -129,7 +140,7 @@ const StatisticsPanel = createClass({
label: type,
size: 0,
transferredSize: 0,
time: 0
time: 0,
}));
for (let request of requests) {
@ -219,19 +230,33 @@ const StatisticsPanel = createClass({
return false;
},
onLayoutChange() {
this.setState({
isVerticalSpliter: MediaQueryList.matches,
});
},
render() {
const { closeStatistics } = this.props;
let splitterClassName = ["splitter"];
if (this.state.isVerticalSpliter) {
splitterClassName.push("devtools-side-splitter");
} else {
splitterClassName.push("devtools-horizontal-splitter");
}
return (
div({ className: "statistics-panel" },
button({
className: "back-button devtools-toolbarbutton",
className: "back-button devtools-button",
"data-text-only": "true",
title: BACK_BUTTON,
onClick: closeStatistics,
}, BACK_BUTTON),
div({ className: "charts-container devtools-responsive-container" },
div({ className: "charts-container" },
div({ ref: "primedCacheChart", className: "charts primed-cache-chart" }),
div({ className: "splitter devtools-side-splitter" }),
div({ className: splitterClassName.join(" ") }),
div({ ref: "emptyCacheChart", className: "charts empty-cache-chart" }),
),
)

View File

@ -5,6 +5,7 @@
"use strict";
const {
createClass,
createFactory,
DOM,
PropTypes,
@ -13,8 +14,10 @@ const { connect } = require("devtools/client/shared/vendor/react-redux");
const { PluralForm } = require("devtools/shared/plural-form");
const Actions = require("../actions/index");
const { L10N } = require("../l10n");
const { Prefs } = require("../prefs");
const {
getDisplayedRequestsSummary,
getRequestFilterTypes,
isNetworkDetailsToggleButtonDisabled,
} = require("../selectors/index");
const {
@ -38,123 +41,132 @@ const TOOLBAR_CLEAR = L10N.getStr("netmonitor.toolbar.clear");
* Network monitor toolbar component
* Toolbar contains a set of useful tools to control network requests
*/
function Toolbar({
clearRequests,
openStatistics,
requestFilterTypes,
setRequestFilterText,
networkDetailsToggleDisabled,
networkDetailsOpen,
summary,
toggleNetworkDetails,
toggleRequestFilterType,
}) {
let toggleButtonClassName = [
"network-details-panel-toggle",
"devtools-button",
];
if (!networkDetailsOpen) {
toggleButtonClassName.push("pane-collapsed");
}
const Toolbar = createClass({
displayName: "Toolbar",
let { count, contentSize, transferredSize, millis } = summary;
const text = (count === 0) ? L10N.getStr("networkMenu.empty") :
PluralForm.get(count, L10N.getStr("networkMenu.summary2"))
.replace("#1", count)
.replace("#2", getSizeWithDecimals(contentSize / 1024))
.replace("#3", getSizeWithDecimals(transferredSize / 1024))
.replace("#4", getTimeWithDecimals(millis / 1000));
propTypes: {
clearRequests: PropTypes.func.isRequired,
openStatistics: PropTypes.func.isRequired,
requestFilterTypes: PropTypes.array.isRequired,
setRequestFilterText: PropTypes.func.isRequired,
networkDetailsToggleDisabled: PropTypes.bool.isRequired,
networkDetailsOpen: PropTypes.bool.isRequired,
summary: PropTypes.object.isRequired,
toggleNetworkDetails: PropTypes.func.isRequired,
toggleRequestFilterType: PropTypes.func.isRequired,
},
const buttons = requestFilterTypes.entrySeq().map(([type, checked]) => {
let classList = ["devtools-button"];
checked && classList.push("checked");
componentDidMount() {
Prefs.filters.forEach(this.props.toggleRequestFilterType);
},
toggleRequestFilterType(evt) {
if (evt.type === "keydown" && (evt.key !== "" || evt.key !== "Enter")) {
return;
}
this.props.toggleRequestFilterType(evt.target.dataset.key);
},
render() {
let {
clearRequests,
openStatistics,
requestFilterTypes,
setRequestFilterText,
networkDetailsToggleDisabled,
networkDetailsOpen,
summary,
toggleNetworkDetails,
} = this.props;
let toggleButtonClassName = [
"network-details-panel-toggle",
"devtools-button",
];
if (!networkDetailsOpen) {
toggleButtonClassName.push("pane-collapsed");
}
let { count, contentSize, transferredSize, millis } = summary;
let text = (count === 0) ? L10N.getStr("networkMenu.empty") :
PluralForm.get(count, L10N.getStr("networkMenu.summary2"))
.replace("#1", count)
.replace("#2", getSizeWithDecimals(contentSize / 1024))
.replace("#3", getSizeWithDecimals(transferredSize / 1024))
.replace("#4", getTimeWithDecimals(millis / 1000));
let buttons = requestFilterTypes.map(([type, checked]) => {
let classList = ["devtools-button"];
checked && classList.push("checked");
return (
button({
id: `requests-menu-filter-${type}-button`,
className: classList.join(" "),
key: type,
onClick: this.toggleRequestFilterType,
onKeyDown: this.toggleRequestFilterType,
"aria-pressed": checked,
"data-key": type,
},
L10N.getStr(`netmonitor.toolbar.filter.${type}`)
)
);
});
return (
button({
id: `requests-menu-filter-${type}-button`,
className: classList.join(" "),
key: type,
onClick: toggleRequestFilterType,
onKeyDown: toggleRequestFilterType,
"aria-pressed": checked,
"data-key": type,
},
L10N.getStr(`netmonitor.toolbar.filter.${type}`)
span({ className: "devtools-toolbar devtools-toolbar-container" },
span({ className: "devtools-toolbar-group" },
button({
id: "requests-menu-clear-button",
className: "devtools-button devtools-clear-icon",
title: TOOLBAR_CLEAR,
onClick: clearRequests,
}),
div({ id: "requests-menu-filter-buttons" }, buttons),
),
span({ className: "devtools-toolbar-group" },
button({
id: "requests-menu-network-summary-button",
className: "devtools-button",
title: count ? text : L10N.getStr("netmonitor.toolbar.perf"),
onClick: openStatistics,
},
span({ className: "summary-info-icon" }),
span({ className: "summary-info-text" }, text),
),
SearchBox({
delay: FILTER_SEARCH_DELAY,
keyShortcut: SEARCH_KEY_SHORTCUT,
placeholder: SEARCH_PLACE_HOLDER,
type: "filter",
onChange: setRequestFilterText,
}),
button({
className: toggleButtonClassName.join(" "),
title: networkDetailsOpen ? COLLPASE_DETAILS_PANE : EXPAND_DETAILS_PANE,
disabled: networkDetailsToggleDisabled,
tabIndex: "0",
onClick: toggleNetworkDetails,
}),
)
)
);
}).toArray();
return (
span({ className: "devtools-toolbar devtools-toolbar-container" },
span({ className: "devtools-toolbar-group" },
button({
id: "requests-menu-clear-button",
className: "devtools-button devtools-clear-icon",
title: TOOLBAR_CLEAR,
onClick: clearRequests,
}),
div({ id: "requests-menu-filter-buttons" }, buttons),
),
span({ className: "devtools-toolbar-group" },
button({
id: "requests-menu-network-summary-button",
className: "devtools-button",
title: count ? text : L10N.getStr("netmonitor.toolbar.perf"),
onClick: openStatistics,
},
span({ className: "summary-info-icon" }),
span({ className: "summary-info-text" }, text),
),
SearchBox({
delay: FILTER_SEARCH_DELAY,
keyShortcut: SEARCH_KEY_SHORTCUT,
placeholder: SEARCH_PLACE_HOLDER,
type: "filter",
onChange: setRequestFilterText,
}),
button({
className: toggleButtonClassName.join(" "),
title: networkDetailsOpen ? COLLPASE_DETAILS_PANE : EXPAND_DETAILS_PANE,
disabled: networkDetailsToggleDisabled,
tabIndex: "0",
onClick: toggleNetworkDetails,
}),
)
)
);
}
Toolbar.displayName = "Toolbar";
Toolbar.propTypes = {
clearRequests: PropTypes.func.isRequired,
openStatistics: PropTypes.func.isRequired,
requestFilterTypes: PropTypes.object.isRequired,
setRequestFilterText: PropTypes.func.isRequired,
networkDetailsToggleDisabled: PropTypes.bool.isRequired,
networkDetailsOpen: PropTypes.bool.isRequired,
summary: PropTypes.object.isRequired,
toggleNetworkDetails: PropTypes.func.isRequired,
toggleRequestFilterType: PropTypes.func.isRequired,
};
}
});
module.exports = connect(
(state) => ({
networkDetailsToggleDisabled: isNetworkDetailsToggleButtonDisabled(state),
networkDetailsOpen: state.ui.networkDetailsOpen,
requestFilterTypes: state.filters.requestFilterTypes,
requestFilterTypes: getRequestFilterTypes(state),
summary: getDisplayedRequestsSummary(state),
}),
(dispatch) => ({
clearRequests: () => dispatch(Actions.clearRequests()),
openStatistics: () => dispatch(Actions.openStatistics(true)),
setRequestFilterText: (text) => dispatch(Actions.setRequestFilterText(text)),
toggleRequestFilterType: (evt) => {
if (evt.type === "keydown" && (evt.key !== "" || evt.key !== "Enter")) {
return;
}
dispatch(Actions.toggleRequestFilterType(evt.target.dataset.key));
},
toggleRequestFilterType: (type) => dispatch(Actions.toggleRequestFilterType(type)),
toggleNetworkDetails: () => dispatch(Actions.toggleNetworkDetails()),
})
}),
)(Toolbar);

View File

@ -14,7 +14,7 @@ add_task(function* () {
let { document, gStore, windowRequire } = monitor.panelWin;
let Actions = windowRequire("devtools/client/netmonitor/actions/index");
let RequestListContextMenu = windowRequire(
"devtools/client/netmonitor/components/request-list-context-menu");
"devtools/client/netmonitor/request-list-context-menu");
gStore.dispatch(Actions.batchEnable(false));

View File

@ -15,7 +15,7 @@ add_task(function* () {
let { document, gStore, windowRequire } = monitor.panelWin;
let Actions = windowRequire("devtools/client/netmonitor/actions/index");
let RequestListContextMenu = windowRequire(
"devtools/client/netmonitor/components/request-list-context-menu");
"devtools/client/netmonitor/request-list-context-menu");
gStore.dispatch(Actions.batchEnable(false));

View File

@ -19,7 +19,7 @@ function* throttleUploadTest(actuallyThrottle) {
let { document, gStore, windowRequire, NetMonitorController } = monitor.panelWin;
let Actions = windowRequire("devtools/client/netmonitor/actions/index");
let RequestListContextMenu = windowRequire(
"devtools/client/netmonitor/components/request-list-context-menu");
"devtools/client/netmonitor/request-list-context-menu");
gStore.dispatch(Actions.batchEnable(false));

View File

@ -4,7 +4,7 @@
"use strict";
const {LocalizationHelper} = require("devtools/shared/l10n");
const { LocalizationHelper } = require("devtools/shared/l10n");
const NET_STRINGS_URI = "devtools/client/locales/netmonitor.properties";
const WEBCONSOLE_STRINGS_URI = "devtools/client/locales/webconsole.properties";

View File

@ -9,7 +9,7 @@ const {
TOGGLE_REQUEST_FILTER_TYPE,
} = require("../constants");
const { Prefs } = require("../prefs");
const { getActiveFilters } = require("../selectors/index");
const { getRequestFilterTypes } = require("../selectors/index");
/**
* Whenever the User clicks on a filter in the network monitor, save the new
@ -20,7 +20,9 @@ function prefsMiddleware(store) {
const res = next(action);
if (action.type === ENABLE_REQUEST_FILTER_TYPE_ONLY ||
action.type === TOGGLE_REQUEST_FILTER_TYPE) {
Prefs.filters = getActiveFilters(store.getState());
Prefs.filters = getRequestFilterTypes(store.getState())
.filter(([type, check]) => check)
.map(([type, check]) => type);
}
return res;
};

View File

@ -19,9 +19,10 @@ DevToolsModules(
'filter-predicates.js',
'l10n.js',
'netmonitor-controller.js',
'netmonitor-view.js',
'panel.js',
'prefs.js',
'request-list-context-menu.js',
'request-list-tooltip.js',
'request-utils.js',
'sort-predicates.js',
'store.js',

View File

@ -2,9 +2,6 @@
* 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 mozilla/reject-some-requires */
/* globals window, NetMonitorView, gStore, gNetwork, dumpn */
"use strict";
const promise = require("promise");
@ -12,7 +9,6 @@ const Services = require("Services");
const EventEmitter = require("devtools/shared/event-emitter");
const { TimelineFront } = require("devtools/shared/fronts/timeline");
const { CurlUtils } = require("devtools/client/shared/curl");
const { Task } = require("devtools/shared/task");
const { ACTIVITY_TYPE } = require("./constants");
const { EVENTS } = require("./events");
const { configureStore } = require("./store");
@ -26,8 +22,7 @@ const {
getDisplayedRequestById,
} = require("./selectors/index");
// Initialize the global Redux store
window.gStore = configureStore();
const gStore = window.gStore = configureStore();
/**
* Object defining the network monitor controller components.
@ -39,18 +34,15 @@ var NetMonitorController = {
* @return object
* A promise that is resolved when the monitor finishes startup.
*/
startupNetMonitor: Task.async(function* () {
async startupNetMonitor() {
if (this._startup) {
return this._startup.promise;
}
this._startup = promise.defer();
{
NetMonitorView.initialize();
yield this.connect();
}
await this.connect();
this._startup.resolve();
return undefined;
}),
},
/**
* Destroys the view and disconnects the monitor client from the server.
@ -58,21 +50,18 @@ var NetMonitorController = {
* @return object
* A promise that is resolved when the monitor finishes shutdown.
*/
shutdownNetMonitor: Task.async(function* () {
async shutdownNetMonitor() {
if (this._shutdown) {
return this._shutdown.promise;
}
this._shutdown = promise.defer();
{
gStore.dispatch(Actions.batchReset());
NetMonitorView.destroy();
this.TargetEventsHandler.disconnect();
this.NetworkEventsHandler.disconnect();
yield this.disconnect();
}
gStore.dispatch(Actions.batchReset());
this.TargetEventsHandler.disconnect();
this.NetworkEventsHandler.disconnect();
await this.disconnect();
this._shutdown.resolve();
return undefined;
}),
},
/**
* Initiates remote or chrome network monitoring based on the current target,
@ -83,7 +72,7 @@ var NetMonitorController = {
* @return object
* A promise that is resolved when the monitor finishes connecting.
*/
connect: Task.async(function* () {
async connect() {
if (this._connection) {
return this._connection.promise;
}
@ -107,7 +96,7 @@ var NetMonitorController = {
};
this.webConsoleClient = this._target.activeConsole;
yield connectTimeline();
await connectTimeline();
this.TargetEventsHandler.connect();
this.NetworkEventsHandler.connect();
@ -117,12 +106,12 @@ var NetMonitorController = {
this._connection.resolve();
this._connected = true;
return undefined;
}),
},
/**
* Disconnects the debugger client and removes event handlers as necessary.
*/
disconnect: Task.async(function* () {
async disconnect() {
if (this._disconnection) {
return this._disconnection.promise;
}
@ -130,7 +119,7 @@ var NetMonitorController = {
// Wait for the connection to finish first.
if (!this.isConnected()) {
yield this._connection.promise;
await this._connection.promise;
}
// When debugging local or a remote instance, the connection is closed by
@ -141,14 +130,14 @@ var NetMonitorController = {
// The timeline front wasn't initialized and started if the server wasn't
// recent enough to emit the markers we were interested in.
if (this._target.getTrait("documentLoadingMarkers")) {
yield this.timelineFront.destroy();
await this.timelineFront.destroy();
this.timelineFront = null;
}
this._disconnection.resolve();
this._connected = false;
return undefined;
}),
},
/**
* Checks whether the netmonitor connection is active.
@ -386,7 +375,6 @@ TargetEventsHandler.prototype = {
* Listen for events emitted by the current tab target.
*/
connect: function () {
dumpn("TargetEventsHandler is connecting...");
this.target.on("close", this._onTabDetached);
this.target.on("navigate", this._onTabNavigated);
this.target.on("will-navigate", this._onTabNavigated);
@ -399,7 +387,6 @@ TargetEventsHandler.prototype = {
if (!this.target) {
return;
}
dumpn("TargetEventsHandler is disconnecting...");
this.target.off("close", this._onTabDetached);
this.target.off("navigate", this._onTabNavigated);
this.target.off("will-navigate", this._onTabNavigated);
@ -449,6 +436,7 @@ TargetEventsHandler.prototype = {
function NetworkEventsHandler() {
this.addRequest = this.addRequest.bind(this);
this.updateRequest = this.updateRequest.bind(this);
this.getString = this.getString.bind(this);
this._onNetworkEvent = this._onNetworkEvent.bind(this);
this._onNetworkEventUpdate = this._onNetworkEventUpdate.bind(this);
this._onDocLoadingMarker = this._onDocLoadingMarker.bind(this);
@ -479,7 +467,6 @@ NetworkEventsHandler.prototype = {
* Connect to the current target client.
*/
connect: function () {
dumpn("NetworkEventsHandler is connecting...");
this.webConsoleClient.on("networkEvent", this._onNetworkEvent);
this.webConsoleClient.on("networkEventUpdate", this._onNetworkEventUpdate);
@ -497,7 +484,6 @@ NetworkEventsHandler.prototype = {
if (!this.client) {
return;
}
dumpn("NetworkEventsHandler is disconnecting...");
this.webConsoleClient.off("networkEvent", this._onNetworkEvent);
this.webConsoleClient.off("networkEventUpdate", this._onNetworkEventUpdate);
@ -579,9 +565,9 @@ NetworkEventsHandler.prototype = {
.then(() => window.emit(EVENTS.REQUEST_ADDED, id));
},
updateRequest: Task.async(function* (id, data) {
async updateRequest(id, data) {
const action = Actions.updateRequest(id, data, true);
yield gStore.dispatch(action);
await gStore.dispatch(action);
let {
responseContent,
responseCookies,
@ -593,10 +579,9 @@ NetworkEventsHandler.prototype = {
let request = getRequestById(gStore.getState(), action.id);
if (requestHeaders && requestHeaders.headers && requestHeaders.headers.length) {
let headers = yield fetchHeaders(
requestHeaders, gNetwork.getString.bind(gNetwork));
let headers = await fetchHeaders(requestHeaders, this.getString);
if (headers) {
yield gStore.dispatch(Actions.updateRequest(
await gStore.dispatch(Actions.updateRequest(
action.id,
{ requestHeaders: headers },
true,
@ -605,10 +590,9 @@ NetworkEventsHandler.prototype = {
}
if (responseHeaders && responseHeaders.headers && responseHeaders.headers.length) {
let headers = yield fetchHeaders(
responseHeaders, gNetwork.getString.bind(gNetwork));
let headers = await fetchHeaders(responseHeaders, this.getString);
if (headers) {
yield gStore.dispatch(Actions.updateRequest(
await gStore.dispatch(Actions.updateRequest(
action.id,
{ responseHeaders: headers },
true,
@ -619,7 +603,7 @@ NetworkEventsHandler.prototype = {
if (request && responseContent && responseContent.content) {
let { mimeType } = request;
let { text, encoding } = responseContent.content;
let response = yield gNetwork.getString(text);
let response = await this.getString(text);
let payload = {};
if (mimeType.includes("image/")) {
@ -629,7 +613,7 @@ NetworkEventsHandler.prototype = {
responseContent.content.text = response;
payload.responseContent = responseContent;
yield gStore.dispatch(Actions.updateRequest(action.id, payload, true));
await gStore.dispatch(Actions.updateRequest(action.id, payload, true));
if (mimeType.includes("image/")) {
window.emit(EVENTS.RESPONSE_IMAGE_THUMBNAIL_DISPLAYED);
@ -640,7 +624,7 @@ NetworkEventsHandler.prototype = {
// them as a separate property, different from the classic headers.
if (requestPostData && requestPostData.postData) {
let { text } = requestPostData.postData;
let postData = yield gNetwork.getString(text);
let postData = await this.getString(text);
const headers = CurlUtils.getHeadersFromMultipartText(postData);
const headersSize = headers.reduce((acc, { name, value }) => {
return acc + name.length + value.length + 2;
@ -650,7 +634,7 @@ NetworkEventsHandler.prototype = {
payload.requestPostData = Object.assign({}, requestPostData);
payload.requestHeadersFromUploadStream = { headers, headersSize };
yield gStore.dispatch(Actions.updateRequest(action.id, payload, true));
await gStore.dispatch(Actions.updateRequest(action.id, payload, true));
}
// Fetch request and response cookies long value.
@ -665,11 +649,11 @@ NetworkEventsHandler.prototype = {
if (typeof cookies[Symbol.iterator] === "function") {
for (let cookie of cookies) {
reqCookies.push(Object.assign({}, cookie, {
value: yield gNetwork.getString(cookie.value),
value: await this.getString(cookie.value),
}));
}
if (reqCookies.length) {
yield gStore.dispatch(Actions.updateRequest(
await gStore.dispatch(Actions.updateRequest(
action.id,
{ requestCookies: reqCookies },
true));
@ -686,18 +670,18 @@ NetworkEventsHandler.prototype = {
if (typeof cookies[Symbol.iterator] === "function") {
for (let cookie of cookies) {
resCookies.push(Object.assign({}, cookie, {
value: yield gNetwork.getString(cookie.value),
value: await this.getString(cookie.value),
}));
}
if (resCookies.length) {
yield gStore.dispatch(Actions.updateRequest(
await gStore.dispatch(Actions.updateRequest(
action.id,
{ responseCookies: resCookies },
true));
}
}
}
}),
},
/**
* The "networkEventUpdate" message type handler.
@ -917,17 +901,6 @@ EventEmitter.decorate(window);
*/
NetMonitorController.TargetEventsHandler = new TargetEventsHandler();
NetMonitorController.NetworkEventsHandler = new NetworkEventsHandler();
/**
* Export some properties to the global scope for easier access.
*/
Object.defineProperties(window, {
"gNetwork": {
get: function () {
return NetMonitorController.NetworkEventsHandler;
},
configurable: true
}
});
window.gNetwork = NetMonitorController.NetworkEventsHandler;
exports.NetMonitorController = NetMonitorController;

View File

@ -1,97 +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/. */
/* eslint-env browser */
/* globals gStore, NetMonitorController */
"use strict";
const { ACTIVITY_TYPE } = require("./constants");
const { createFactory } = require("devtools/client/shared/vendor/react");
const ReactDOM = require("devtools/client/shared/vendor/react-dom");
const Provider = createFactory(require("devtools/client/shared/vendor/react-redux").Provider);
// Components
const NetworkDetailsPanel = createFactory(require("./shared/components/network-details-panel"));
const RequestList = createFactory(require("./components/request-list"));
const StatisticsPanel = createFactory(require("./components/statistics-panel"));
const Toolbar = createFactory(require("./components/toolbar"));
/**
* Object defining the network monitor view components.
*/
exports.NetMonitorView = {
/**
* Initializes the network monitor view.
*/
initialize: function () {
this._body = document.querySelector("#body");
this.networkDetailsPanel = document.querySelector(
"#react-network-details-panel-hook");
ReactDOM.render(Provider(
{ store: gStore },
NetworkDetailsPanel({ toolbox: NetMonitorController._toolbox }),
), this.networkDetailsPanel);
this.requestList = document.querySelector("#react-request-list-hook");
ReactDOM.render(Provider(
{ store: gStore },
RequestList({ toolbox: NetMonitorController._toolbox })
), this.requestList);
this.statisticsPanel = document.querySelector("#react-statistics-panel-hook");
ReactDOM.render(Provider(
{ store: gStore },
StatisticsPanel(),
), this.statisticsPanel);
this.toolbar = document.querySelector("#react-toolbar-hook");
ReactDOM.render(Provider(
{ store: gStore },
Toolbar(),
), this.toolbar);
// Store watcher here is for observing the statisticsOpen state change.
// It should be removed once we migrate to react and apply react/redex binding.
this.unsubscribeStore = gStore.subscribe(storeWatcher(
false,
() => gStore.getState().ui.statisticsOpen,
this.toggleFrontendMode.bind(this)
));
},
/**
* Destroys the network monitor view.
*/
destroy: function () {
ReactDOM.unmountComponentAtNode(this.networkDetailsPanel);
ReactDOM.unmountComponentAtNode(this.requestList);
ReactDOM.unmountComponentAtNode(this.statisticsPanel);
ReactDOM.unmountComponentAtNode(this.toolbar);
this.unsubscribeStore();
},
toggleFrontendMode: function () {
if (gStore.getState().ui.statisticsOpen) {
this._body.selectedPanel = document.querySelector("#react-statistics-panel-hook");
NetMonitorController.triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED);
} else {
this._body.selectedPanel = document.querySelector("#inspector-panel");
}
},
};
// A smart store watcher to notify store changes as necessary
function storeWatcher(initialValue, reduceValue, onChange) {
let currentValue = initialValue;
return () => {
const newValue = reduceValue();
if (newValue !== currentValue) {
onChange();
currentValue = newValue;
}
};
}

View File

@ -2,59 +2,40 @@
* 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/. */
/* globals window, document, NetMonitorController, NetMonitorView */
/* exported Netmonitor, NetMonitorController, NetMonitorView, $, $all, dumpn */
"use strict";
const Cu = Components.utils;
const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
const { BrowserLoader } = Cu.import("resource://devtools/client/shared/browser-loader.js", {});
function Netmonitor(toolbox) {
const { require } = BrowserLoader({
baseURI: "resource://devtools/client/netmonitor/",
window,
commonLibRequire: toolbox.browserRequire,
});
window.windowRequire = require;
const { NetMonitorController } = require("./netmonitor-controller.js");
const { NetMonitorView } = require("./netmonitor-view.js");
window.NetMonitorController = NetMonitorController;
window.NetMonitorView = NetMonitorView;
NetMonitorController._toolbox = toolbox;
NetMonitorController._target = toolbox.target;
window.NetMonitorController = require("./netmonitor-controller").NetMonitorController;
window.NetMonitorController._toolbox = toolbox;
window.NetMonitorController._target = toolbox.target;
}
Netmonitor.prototype = {
init() {
const require = window.windowRequire;
const { createFactory } = require("devtools/client/shared/vendor/react");
const { render } = require("devtools/client/shared/vendor/react-dom");
const Provider = createFactory(require("devtools/client/shared/vendor/react-redux").Provider);
// Components
const NetworkMonitor = createFactory(require("./components/network-monitor"));
this.networkMonitor = document.querySelector(".root");
render(Provider(
{ store: window.gStore },
NetworkMonitor({ toolbox: window.NetMonitorController._toolbox }),
), this.networkMonitor);
return window.NetMonitorController.startupNetMonitor();
},
destroy() {
const require = window.windowRequire;
const { unmountComponentAtNode } = require("devtools/client/shared/vendor/react-dom");
unmountComponentAtNode(this.networkMonitor);
return window.NetMonitorController.shutdownNetMonitor();
}
};
/**
* DOM query helper.
* TODO: Move it into "dom-utils.js" module and "require" it when needed.
*/
var $ = (selector, target = document) => target.querySelector(selector);
var $all = (selector, target = document) => target.querySelectorAll(selector);
/**
* Helper method for debugging.
* @param string
*/
function dumpn(str) {
if (wantLogging) {
dump("NET-FRONTEND: " + str + "\n");
}
}
var wantLogging = Services.prefs.getBoolPref("devtools.debugger.log");

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" dir="">
<head>
<link rel="stylesheet" href="chrome://devtools/content/shared/widgets/widgets.css"/>
<link rel="stylesheet" href="chrome://devtools/skin/widgets.css"/>
<link rel="stylesheet" href="chrome://devtools/skin/netmonitor.css"/>
<script src="chrome://devtools/content/shared/theme-switching.js"/>
<script>
"use strict";
const { BrowserLoader } = Components.utils.import(
"resource://devtools/client/shared/browser-loader.js", {});
const { require } = BrowserLoader({
baseURI: "resource://devtools/client/netmonitor/",
window,
});
window.windowRequire = require;
</script>
</head>
<body class="theme-sidebar" role="application">
<div class="root"></div>
<script src="netmonitor.js" defer="true"/>
</body>
</html>

View File

@ -1,51 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/content/shared/widgets/widgets.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/widgets.css" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/netmonitor.css" type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml">
<script type="application/javascript;version=1.8"
src="chrome://devtools/content/shared/theme-switching.js"/>
<script type="text/javascript" src="netmonitor.js"/>
<deck id="body"
class="theme-sidebar"
flex="1"
data-localization-bundle="devtools/client/locales/netmonitor.properties">
<vbox id="inspector-panel" flex="1">
<html:div xmlns="http://www.w3.org/1999/xhtml"
id="react-toolbar-hook"/>
<hbox id="network-table-and-sidebar"
class="devtools-responsive-container"
flex="1">
<html:div xmlns="http://www.w3.org/1999/xhtml"
id="react-request-list-hook"
class="devtools-main-content">
</html:div>
<splitter id="network-inspector-view-splitter"
class="devtools-side-splitter"/>
<box id="splitter-adjustable-box"
hidden="true">
<html:div xmlns="http://www.w3.org/1999/xhtml"
id="react-network-details-panel-hook">
</html:div>
</box>
</hbox>
</vbox>
<html:div xmlns="http://www.w3.org/1999/xhtml"
id="react-statistics-panel-hook">
</html:div>
</deck>
</window>

View File

@ -4,71 +4,29 @@
"use strict";
const promise = require("promise");
const EventEmitter = require("devtools/shared/event-emitter");
const { Task } = require("devtools/shared/task");
const { localizeMarkup } = require("devtools/shared/l10n");
function NetMonitorPanel(iframeWindow, toolbox) {
this.panelWin = iframeWindow;
this.panelDoc = iframeWindow.document;
this._toolbox = toolbox;
this._netmonitor = new iframeWindow.Netmonitor(toolbox);
EventEmitter.decorate(this);
this.toolbox = toolbox;
this.netmonitor = new iframeWindow.Netmonitor(toolbox);
}
exports.NetMonitorPanel = NetMonitorPanel;
NetMonitorPanel.prototype = {
/**
* Open is effectively an asynchronous constructor.
*
* @return object
* A promise that is resolved when the NetMonitor completes opening.
*/
open: Task.async(function* () {
if (this._opening) {
return this._opening;
async open() {
if (!this.toolbox.target.isRemote) {
await this.toolbox.target.makeRemote();
}
// Localize all the nodes containing a data-localization attribute.
localizeMarkup(this.panelDoc);
let deferred = promise.defer();
this._opening = deferred.promise;
// Local monitoring needs to make the target remote.
if (!this.target.isRemote) {
yield this.target.makeRemote();
}
yield this._netmonitor.init();
this.isReady = true;
await this.netmonitor.init();
this.emit("ready");
deferred.resolve(this);
return this._opening;
}),
// DevToolPanel API
get target() {
return this._toolbox.target;
this.isReady = true;
return this;
},
destroy: Task.async(function* () {
if (this._destroying) {
return this._destroying;
}
let deferred = promise.defer();
this._destroying = deferred.promise;
yield this._netmonitor.destroy();
async destroy() {
await this.netmonitor.destroy();
this.emit("destroyed");
deferred.resolve();
return this._destroying;
})
return this;
},
};
exports.NetMonitorPanel = NetMonitorPanel;

View File

@ -4,12 +4,11 @@
"use strict";
const {PrefsHelper} = require("devtools/client/shared/prefs");
const { PrefsHelper } = require("devtools/client/shared/prefs");
/**
* Shortcuts for accessing various network monitor preferences.
*/
exports.Prefs = new PrefsHelper("devtools.netmonitor", {
networkDetailsWidth: ["Int", "panes-network-details-width"],
networkDetailsHeight: ["Int", "panes-network-details-height"],

View File

@ -6,9 +6,13 @@
const I = require("devtools/client/shared/vendor/immutable");
const {
CLEAR_REQUESTS,
OPEN_NETWORK_DETAILS,
OPEN_STATISTICS,
REMOVE_SELECTED_CUSTOM_REQUEST,
SELECT_DETAILS_PANEL_TAB,
SEND_CUSTOM_REQUEST,
SELECT_REQUEST,
WATERFALL_RESIZE,
} = require("../constants");
@ -40,12 +44,19 @@ function setDetailsPanelTab(state, action) {
function ui(state = new UI(), action) {
switch (action.type) {
case CLEAR_REQUESTS:
return openNetworkDetails(state, { open: false });
case OPEN_NETWORK_DETAILS:
return openNetworkDetails(state, action);
case OPEN_STATISTICS:
return openStatistics(state, action);
case REMOVE_SELECTED_CUSTOM_REQUEST:
case SEND_CUSTOM_REQUEST:
return openNetworkDetails(state, { open: false });
case SELECT_DETAILS_PANEL_TAB:
return setDetailsPanelTab(state, action);
case SELECT_REQUEST:
return openNetworkDetails(state, { open: true });
case WATERFALL_RESIZE:
return resizeWaterfall(state, action);
default:

View File

@ -2,27 +2,24 @@
* 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/. */
/* globals NetMonitorController, gNetwork, gStore */
"use strict";
const Services = require("Services");
const { Task } = require("devtools/shared/task");
const { Curl } = require("devtools/client/shared/curl");
const { gDevTools } = require("devtools/client/framework/devtools");
const Menu = require("devtools/client/framework/menu");
const MenuItem = require("devtools/client/framework/menu-item");
const { L10N } = require("../l10n");
const { L10N } = require("./l10n");
const {
formDataURI,
getFormDataSections,
getUrlQuery,
parseQueryString,
} = require("../request-utils");
} = require("./request-utils");
const {
getSelectedRequest,
getSortedRequests,
} = require("../selectors/index");
} = require("./selectors/index");
loader.lazyRequireGetter(this, "HarExporter",
"devtools/client/netmonitor/har/har-exporter", true);
@ -40,11 +37,11 @@ function RequestListContextMenu({
RequestListContextMenu.prototype = {
get selectedRequest() {
return getSelectedRequest(gStore.getState());
return getSelectedRequest(window.gStore.getState());
},
get sortedRequests() {
return getSortedRequests(gStore.getState());
return getSortedRequests(window.gStore.getState());
},
/**
@ -160,7 +157,7 @@ RequestListContextMenu.prototype = {
id: "request-menu-context-resend",
label: L10N.getStr("netmonitor.context.editAndResend"),
accesskey: L10N.getStr("netmonitor.context.editAndResend.accesskey"),
visible: !!(NetMonitorController.supportsCustomRequest &&
visible: !!(window.NetMonitorController.supportsCustomRequest &&
selectedRequest && !selectedRequest.isCustom),
click: this.cloneSelectedRequest,
}));
@ -182,11 +179,11 @@ RequestListContextMenu.prototype = {
id: "request-menu-context-perf",
label: L10N.getStr("netmonitor.context.perfTools"),
accesskey: L10N.getStr("netmonitor.context.perfTools.accesskey"),
visible: !!NetMonitorController.supportsPerfStats,
visible: !!window.NetMonitorController.supportsPerfStats,
click: () => this.openStatistics(true)
}));
menu.popup(screenX, screenY, NetMonitorController._toolbox);
menu.popup(screenX, screenY, window.NetMonitorController._toolbox);
return menu;
},
@ -220,15 +217,15 @@ RequestListContextMenu.prototype = {
* Copy the request form data parameters (or raw payload) from
* the currently selected item.
*/
copyPostData: Task.async(function* () {
async copyPostData() {
let selected = this.selectedRequest;
// Try to extract any form data parameters.
let formDataSections = yield getFormDataSections(
let formDataSections = await getFormDataSections(
selected.requestHeaders,
selected.requestHeadersFromUploadStream,
selected.requestPostData,
gNetwork.getString.bind(gNetwork));
window.gNetwork.getString.bind(window.gNetwork));
let params = [];
formDataSections.forEach(section => {
@ -245,19 +242,19 @@ RequestListContextMenu.prototype = {
// Fall back to raw payload.
if (!string) {
let postData = selected.requestPostData.postData.text;
string = yield gNetwork.getString(postData);
string = await window.gNetwork.getString(postData);
if (Services.appinfo.OS !== "WINNT") {
string = string.replace(/\r/g, "");
}
}
clipboardHelper.copyString(string);
}),
},
/**
* Copy a cURL command from the currently selected item.
*/
copyAsCurl: Task.async(function* () {
async copyAsCurl() {
let selected = this.selectedRequest;
// Create a sanitized object for the Curl command generator.
@ -271,18 +268,18 @@ RequestListContextMenu.prototype = {
// Fetch header values.
for (let { name, value } of selected.requestHeaders.headers) {
let text = yield gNetwork.getString(value);
let text = await window.gNetwork.getString(value);
data.headers.push({ name: name, value: text });
}
// Fetch the request payload.
if (selected.requestPostData) {
let postData = selected.requestPostData.postData.text;
data.postDataText = yield gNetwork.getString(postData);
data.postDataText = await window.gNetwork.getString(postData);
}
clipboardHelper.copyString(Curl.generateCommand(data));
}),
},
/**
* Copy the raw request headers from the currently selected item.
@ -312,7 +309,7 @@ RequestListContextMenu.prototype = {
copyImageAsDataUri() {
const { mimeType, text, encoding } = this.selectedRequest.responseContent.content;
gNetwork.getString(text).then(string => {
window.gNetwork.getString(text).then(string => {
let data = formDataURI(mimeType, encoding, string);
clipboardHelper.copyString(data);
});
@ -324,7 +321,7 @@ RequestListContextMenu.prototype = {
copyResponse() {
const { text } = this.selectedRequest.responseContent.content;
gNetwork.getString(text).then(string => {
window.gNetwork.getString(text).then(string => {
clipboardHelper.copyString(string);
});
},
@ -346,11 +343,11 @@ RequestListContextMenu.prototype = {
},
getDefaultHarOptions() {
let form = NetMonitorController._target.form;
let form = window.NetMonitorController._target.form;
let title = form.title || form.url;
return {
getString: gNetwork.getString.bind(gNetwork),
getString: window.gNetwork.getString.bind(window.gNetwork),
items: this.sortedRequests,
title: title
};

View File

@ -2,17 +2,14 @@
* 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/. */
/* globals gNetwork, NetMonitorController */
"use strict";
const { Task } = require("devtools/shared/task");
const {
setImageTooltip,
getImageDimensions,
} = require("devtools/client/shared/widgets/tooltip/ImageTooltipHelper");
const { WEBCONSOLE_L10N } = require("../l10n");
const { formDataURI } = require("../request-utils");
const { WEBCONSOLE_L10N } = require("./l10n");
const { formDataURI } = require("./request-utils");
// px
const REQUESTS_TOOLTIP_IMAGE_MAX_DIM = 400;
@ -21,24 +18,24 @@ const REQUESTS_TOOLTIP_STACK_TRACE_WIDTH = 600;
const HTML_NS = "http://www.w3.org/1999/xhtml";
const setTooltipImageContent = Task.async(function* (tooltip, itemEl, requestItem) {
async function setTooltipImageContent(tooltip, itemEl, requestItem) {
let { mimeType, text, encoding } = requestItem.responseContent.content;
if (!mimeType || !mimeType.includes("image/")) {
return false;
}
let string = yield gNetwork.getString(text);
let string = await window.gNetwork.getString(text);
let src = formDataURI(mimeType, encoding, string);
let maxDim = REQUESTS_TOOLTIP_IMAGE_MAX_DIM;
let { naturalWidth, naturalHeight } = yield getImageDimensions(tooltip.doc, src);
let { naturalWidth, naturalHeight } = await getImageDimensions(tooltip.doc, src);
let options = { maxDim, naturalWidth, naturalHeight };
setImageTooltip(tooltip, tooltip.doc, src, options);
return itemEl.querySelector(".requests-menu-icon");
});
}
const setTooltipStackTraceContent = Task.async(function* (tooltip, requestItem) {
async function setTooltipStackTraceContent(tooltip, requestItem) {
let {stacktrace} = requestItem.cause;
if (!stacktrace || stacktrace.length == 0) {
@ -92,7 +89,7 @@ const setTooltipStackTraceContent = Task.async(function* (tooltip, requestItem)
frameEl.addEventListener("click", () => {
// hide the tooltip immediately, not after delay
tooltip.hide();
NetMonitorController.viewSourceInDebugger(filename, lineNumber);
window.NetMonitorController.viewSourceInDebugger(filename, lineNumber);
});
el.appendChild(frameEl);
@ -101,7 +98,7 @@ const setTooltipStackTraceContent = Task.async(function* (tooltip, requestItem)
tooltip.setContent(el, {width: REQUESTS_TOOLTIP_STACK_TRACE_WIDTH});
return true;
});
}
module.exports = {
setTooltipImageContent,

View File

@ -6,8 +6,6 @@
"use strict";
const { Task } = require("devtools/shared/task");
/**
* Extracts any urlencoded form data sections (e.g. "?foo=bar&baz=42") from a
* POST request.
@ -18,8 +16,7 @@ const { Task } = require("devtools/shared/task");
* @param {function} getString - callback to retrieve a string from a LongStringGrip.
* @return {array} a promise list that is resolved with the extracted form data.
*/
const getFormDataSections = Task.async(function* (headers, uploadHeaders, postData,
getString) {
async function getFormDataSections(headers, uploadHeaders, postData, getString) {
let formDataSections = [];
let requestHeaders = headers.headers;
@ -32,11 +29,11 @@ const getFormDataSections = Task.async(function* (headers, uploadHeaders, postDa
let contentTypeLongString = contentTypeHeader ? contentTypeHeader.value : "";
let contentType = yield getString(contentTypeLongString);
let contentType = await getString(contentTypeLongString);
if (contentType.includes("x-www-form-urlencoded")) {
let postDataLongString = postData.postData.text;
let text = yield getString(postDataLongString);
let text = await getString(postDataLongString);
for (let section of text.split(/\r\n|\r|\n/)) {
// Before displaying it, make sure this section of the POST data
@ -48,7 +45,7 @@ const getFormDataSections = Task.async(function* (headers, uploadHeaders, postDa
}
return formDataSections;
});
}
/**
* Fetch headers full content from actor server
@ -57,13 +54,12 @@ const getFormDataSections = Task.async(function* (headers, uploadHeaders, postDa
* @param {function} getString - callback to retrieve a string from a LongStringGrip
* @return {object} a headers object with updated content payload
*/
const fetchHeaders = Task.async(function* (headers, getString) {
async function fetchHeaders(headers, getString) {
for (let { value } of headers.headers) {
headers.headers.value = yield getString(value);
headers.headers.value = await getString(value);
}
return headers;
});
}
/**
* Form a data: URI given a mime type, encoding, and some text.

View File

@ -4,11 +4,10 @@
"use strict";
function getActiveFilters(state) {
return state.filters.requestFilterTypes.toSeq()
.filter(checked => checked).keySeq().toArray();
function getRequestFilterTypes(state) {
return state.filters.requestFilterTypes.entrySeq().toArray();
}
module.exports = {
getActiveFilters
getRequestFilterTypes,
};

View File

@ -92,8 +92,8 @@ const getDisplayedRequestsSummary = createSelector(
return {
count: requests.size,
contentSize: totalBytes.contentSize,
transferredSize: totalBytes.transferredSize,
millis: totalMillis,
transferredSize: totalBytes.transferredSize,
};
}
);

View File

@ -2,8 +2,6 @@
* 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/. */
/* globals NetMonitorController */
"use strict";
const {
@ -162,7 +160,7 @@ const HeadersPanel = createClass({
readOnly: true,
value: `${status} ${statusText}`,
}),
NetMonitorController.supportsCustomRequest && button({
window.NetMonitorController.supportsCustomRequest && button({
className: "devtools-button",
onClick: cloneSelectedRequest,
}, EDIT_AND_RESEND),

View File

@ -2,12 +2,9 @@
* 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/. */
/* globals document */
"use strict";
const {
createClass,
createFactory,
DOM,
PropTypes,
@ -15,84 +12,58 @@ const {
const { connect } = require("devtools/client/shared/vendor/react-redux");
const Actions = require("../../actions/index");
const { getSelectedRequest } = require("../../selectors/index");
const { Prefs } = require("../../prefs");
// Components
const CustomRequestPanel = createFactory(require("./custom-request-panel"));
const TabboxPanel = createFactory(require("./tabbox-panel"));
const { div } = DOM;
/*
* Network details panel component
*/
const NetworkDetailsPanel = createClass({
displayName: "NetworkDetailsPanel",
propTypes: {
activeTabId: PropTypes.string,
cloneSelectedRequest: PropTypes.func.isRequired,
open: PropTypes.bool,
request: PropTypes.object,
selectTab: PropTypes.func.isRequired,
toolbox: PropTypes.object.isRequired,
},
componentDidMount() {
// FIXME: Workaround should be removed in bug 1309183
document.getElementById("splitter-adjustable-box")
.setAttribute("width", Prefs.networkDetailsWidth);
document.getElementById("splitter-adjustable-box")
.setAttribute("height", Prefs.networkDetailsHeight);
},
componentWillUnmount() {
// FIXME: Workaround should be removed in bug 1309183
Prefs.networkDetailsWidth =
document.getElementById("splitter-adjustable-box").getAttribute("width");
Prefs.networkDetailsHeight =
document.getElementById("splitter-adjustable-box").getAttribute("height");
},
render() {
let {
activeTabId,
cloneSelectedRequest,
open,
request,
selectTab,
toolbox,
} = this.props;
if (!open || !request) {
// FIXME: Workaround should be removed in bug 1309183
document.getElementById("splitter-adjustable-box").setAttribute("hidden", true);
return null;
}
// FIXME: Workaround should be removed in bug 1309183
document.getElementById("splitter-adjustable-box").removeAttribute("hidden");
return (
div({ className: "network-details-panel" },
!request.isCustom ?
TabboxPanel({
activeTabId,
request,
selectTab,
toolbox,
}) :
CustomRequestPanel({
cloneSelectedRequest,
request,
})
)
);
function NetworkDetailsPanel({
activeTabId,
cloneSelectedRequest,
request,
selectTab,
toolbox,
}) {
if (!request) {
return null;
}
});
return (
div({ className: "network-details-panel" },
!request.isCustom ?
TabboxPanel({
activeTabId,
request,
selectTab,
toolbox,
}) :
CustomRequestPanel({
cloneSelectedRequest,
request,
})
)
);
}
NetworkDetailsPanel.displayName = "NetworkDetailsPanel";
NetworkDetailsPanel.propTypes = {
activeTabId: PropTypes.string,
cloneSelectedRequest: PropTypes.func.isRequired,
open: PropTypes.bool,
request: PropTypes.object,
selectTab: PropTypes.func.isRequired,
toolbox: PropTypes.object.isRequired,
};
module.exports = connect(
(state) => ({
activeTabId: state.ui.detailsPanelSelectedTab,
open: state.ui.networkDetailsOpen,
request: getSelectedRequest(state),
}),
(dispatch) => ({

View File

@ -128,7 +128,6 @@ skip-if = (os == 'linux' && debug && bits == 32) # Bug 1303439
[browser_net_post-data-04.js]
[browser_net_prefs-and-l10n.js]
[browser_net_prefs-reload.js]
skip-if = true # bug 1309183, it should be fixed by SplitBox support
[browser_net_raw_headers.js]
[browser_net_reload-button.js]
[browser_net_reload-markers.js]

View File

@ -25,42 +25,49 @@ add_task(function* () {
yield wait;
wait = waitForDOM(document, "#params-panel .tree-section", 2);
gStore.dispatch(Actions.selectRequestByIndex(0));
EventUtils.sendMouseEvent({ type: "mousedown" },
document.querySelectorAll(".request-list-item")[0]);
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector("#params-tab"));
yield wait;
testParamsTab1("a", '""', '{ "foo": "bar" }', '""');
wait = waitForDOM(document, "#params-panel .tree-section", 2);
gStore.dispatch(Actions.selectRequestByIndex(1));
EventUtils.sendMouseEvent({ type: "mousedown" },
document.querySelectorAll(".request-list-item")[1]);
yield wait;
testParamsTab1("a", '"b"', '{ "foo": "bar" }', '""');
wait = waitForDOM(document, "#params-panel .tree-section", 2);
gStore.dispatch(Actions.selectRequestByIndex(2));
EventUtils.sendMouseEvent({ type: "mousedown" },
document.querySelectorAll(".request-list-item")[2]);
yield wait;
testParamsTab1("a", '"b"', "foo", '"bar"');
wait = waitForDOM(document, "#params-panel tr:not(.tree-section).treeRow", 2);
gStore.dispatch(Actions.selectRequestByIndex(3));
EventUtils.sendMouseEvent({ type: "mousedown" },
document.querySelectorAll(".request-list-item")[3]);
yield wait;
testParamsTab2("a", '""', '{ "foo": "bar" }', "js");
wait = waitForDOM(document, "#params-panel tr:not(.tree-section).treeRow", 2);
gStore.dispatch(Actions.selectRequestByIndex(4));
EventUtils.sendMouseEvent({ type: "mousedown" },
document.querySelectorAll(".request-list-item")[4]);
yield wait;
testParamsTab2("a", '"b"', '{ "foo": "bar" }', "js");
// Wait for all tree sections and editor updated by react
let waitSections = waitForDOM(document, "#params-panel .tree-section", 2);
let waitEditor = waitForDOM(document, "#params-panel .editor-mount iframe");
gStore.dispatch(Actions.selectRequestByIndex(5));
EventUtils.sendMouseEvent({ type: "mousedown" },
document.querySelectorAll(".request-list-item")[5]);
let [, editorFrames] = yield Promise.all([waitSections, waitEditor]);
yield once(editorFrames[0], "DOMContentLoaded");
yield waitForDOM(editorFrames[0].contentDocument, ".CodeMirror-code");
testParamsTab2("a", '"b"', "?foo=bar", "text");
gStore.dispatch(Actions.selectRequestByIndex(6));
EventUtils.sendMouseEvent({ type: "mousedown" },
document.querySelectorAll(".request-list-item")[6]);
testParamsTab3();
yield teardown(monitor);

View File

@ -57,8 +57,8 @@ add_task(function* () {
yield waitForClipboardPromise(function setup() {
// Context menu is appending in XUL document, we must select it from
// _toolbox.doc
monitor._toolbox.doc
// toolbox.doc
monitor.toolbox.doc
.querySelector("#request-menu-context-copy-as-curl").click();
}, function validate(result) {
if (typeof result !== "string") {

View File

@ -42,8 +42,8 @@ add_task(function* () {
yield waitForClipboardPromise(function setup() {
// Context menu is appending in XUL document, we must select it from
// _toolbox.doc
monitor._toolbox.doc
// toolbox.doc
monitor.toolbox.doc
.querySelector("#request-menu-context-copy-request-headers").click();
}, function validate(result) {
// Sometimes, a "Cookie" header is left over from other tests. Remove it:
@ -67,8 +67,8 @@ add_task(function* () {
yield waitForClipboardPromise(function setup() {
// Context menu is appending in XUL document, we must select it from
// _toolbox.doc
monitor._toolbox.doc
// _oolbox.doc
monitor.toolbox.doc
.querySelector("#response-menu-context-copy-response-headers").click();
}, function validate(result) {
// Fake the "Last-Modified" and "Date" headers because they will vary:

View File

@ -26,8 +26,8 @@ add_task(function* () {
yield waitForClipboardPromise(function setup() {
// Context menu is appending in XUL document, we must select it from
// _toolbox.doc
monitor._toolbox.doc
// toolbox.doc
monitor.toolbox.doc
.querySelector("#request-menu-context-copy-image-as-data-uri").click();
}, TEST_IMAGE_DATA_URI);

View File

@ -62,7 +62,7 @@ add_task(function* () {
document.querySelectorAll(".request-list-item")[index]);
EventUtils.sendMouseEvent({ type: "contextmenu" },
document.querySelectorAll(".request-list-item")[index]);
let copyUrlParamsNode = monitor._toolbox.doc
let copyUrlParamsNode = monitor.toolbox.doc
.querySelector("#request-menu-context-copy-url-params");
is(!!copyUrlParamsNode, !hidden,
"The \"Copy URL Parameters\" context menu item should" + (hidden ? " " : " not ") +
@ -75,7 +75,7 @@ add_task(function* () {
EventUtils.sendMouseEvent({ type: "contextmenu" },
document.querySelectorAll(".request-list-item")[index]);
yield waitForClipboardPromise(function setup() {
monitor._toolbox.doc
monitor.toolbox.doc
.querySelector("#request-menu-context-copy-url-params").click();
}, queryString);
ok(true, "The url query string copied from the selected item is correct.");
@ -86,7 +86,7 @@ add_task(function* () {
document.querySelectorAll(".request-list-item")[index]);
EventUtils.sendMouseEvent({ type: "contextmenu" },
document.querySelectorAll(".request-list-item")[index]);
let copyPostDataNode = monitor._toolbox.doc
let copyPostDataNode = monitor.toolbox.doc
.querySelector("#request-menu-context-copy-post-data");
is(!!copyPostDataNode, !hidden,
"The \"Copy POST Data\" context menu item should" + (hidden ? " " : " not ") +
@ -99,7 +99,7 @@ add_task(function* () {
EventUtils.sendMouseEvent({ type: "contextmenu" },
document.querySelectorAll(".request-list-item")[index]);
yield waitForClipboardPromise(function setup() {
monitor._toolbox.doc
monitor.toolbox.doc
.querySelector("#request-menu-context-copy-post-data").click();
}, postData);
ok(true, "The post data string copied from the selected item is correct.");

View File

@ -28,8 +28,8 @@ add_task(function* () {
yield waitForClipboardPromise(function setup() {
// Context menu is appending in XUL document, we must select it from
// _toolbox.doc
monitor._toolbox.doc
// toolbox.doc
monitor.toolbox.doc
.querySelector("#request-menu-context-copy-response").click();
}, EXPECTED_RESULT);

View File

@ -28,8 +28,8 @@ add_task(function* () {
yield waitForClipboardPromise(function setup() {
// Context menu is appending in XUL document, we must select it from
// _toolbox.doc
monitor._toolbox.doc
// toolbox.doc
monitor.toolbox.doc
.querySelector("#request-menu-context-copy-image-as-data-uri").click();
}, function check(text) {
return text.startsWith("data:") && !/undefined/.test(text);

View File

@ -29,8 +29,8 @@ add_task(function* () {
yield waitForClipboardPromise(function setup() {
// Context menu is appending in XUL document, we must select it from
// _toolbox.doc
monitor._toolbox.doc
// toolbox.doc
monitor.toolbox.doc
.querySelector("#request-menu-context-copy-url").click();
}, requestItem.url);

View File

@ -21,7 +21,7 @@ add_task(function* test() {
getDisplayedRequests,
getSortedRequests,
} = windowRequire("devtools/client/netmonitor/selectors/index");
let toolboxDoc = monitor._toolbox.doc;
let toolboxDoc = monitor.toolbox.doc;
gStore.dispatch(Actions.batchEnable(false));
@ -38,7 +38,7 @@ add_task(function* test() {
// Hide tooltip before next test, to avoid the situation that tooltip covers
// the icon for the request of the next test.
info("Checking the image thumbnail gets hidden...");
yield hideTooltipAndVerify(monitor._toolbox.doc,
yield hideTooltipAndVerify(monitor.toolbox.doc,
document.querySelectorAll(".request-list-item")[0]);
// +1 extra document reload

View File

@ -33,8 +33,8 @@ add_task(function* () {
let onTabOpen = once(gBrowser.tabContainer, "TabOpen", false);
// Context menu is appending in XUL document, we must select it from
// _toolbox.doc
monitor._toolbox.doc
// toolbox.doc
monitor.toolbox.doc
.querySelector("#request-menu-context-newtab").click();
yield onTabOpen;

View File

@ -8,11 +8,10 @@
*/
add_task(function* () {
let Actions = require("devtools/client/netmonitor/actions/index");
let { getActiveFilters } = require("devtools/client/netmonitor/selectors/index");
let { monitor } = yield initNetMonitor(SIMPLE_URL);
let { document, gStore, Prefs } = monitor.panelWin;
let { getRequestFilterTypes } = monitor.panelWin
.windowRequire("devtools/client/netmonitor/selectors/index");
let Actions = monitor.panelWin.windowRequire("devtools/client/netmonitor/actions/index");
info("Starting test... ");
// This test reopens the network monitor a bunch of times, for different
@ -22,7 +21,9 @@ add_task(function* () {
// Use these getters instead of caching instances inside the panel win,
// since the tool is reopened a bunch of times during this test
// and the instances will differ.
let getStore = () => gStore;
let getDoc = () => monitor.panelWin.document;
let getPrefs = () => monitor.panelWin.windowRequire("devtools/client/netmonitor/prefs").Prefs;
let getStore = () => monitor.panelWin.gStore;
let getState = () => getStore().getState();
let prefsToCheck = {
@ -31,7 +32,9 @@ add_task(function* () {
newValue: ["html", "css"],
// Getter used to retrieve the current value from the frontend, in order
// to verify that the pref was applied properly.
validateValue: () => getActiveFilters(getState()),
validateValue: () => getRequestFilterTypes(getState())
.filter(([type, check]) => check)
.map(([type, check]) => type),
// Predicate used to modify the frontend when setting the new pref value,
// before trying to validate the changes.
modifyFrontend: (value) => value.forEach(e =>
@ -40,16 +43,16 @@ add_task(function* () {
networkDetailsWidth: {
newValue: ~~(Math.random() * 200 + 100),
validateValue: () =>
~~document.querySelector(".network-details-panel").getAttribute("width"),
getDoc().querySelector(".monitor-panel .split-box .controlled").clientWidth,
modifyFrontend: (value) =>
document.querySelector(".network-details-panel").setAttribute("width", value)
getDoc().querySelector(".monitor-panel .split-box .controlled").style.width = `${value}px`,
},
networkDetailsHeight: {
newValue: ~~(Math.random() * 300 + 100),
validateValue: () =>
~~document.querySelector(".network-details-panel").getAttribute("height"),
getDoc().querySelector(".monitor-panel .split-box .controlled").clientHeight,
modifyFrontend: (value) =>
document.querySelector(".network-details-panel").setAttribute("height", value)
getDoc().querySelector(".monitor-panel .split-box .controlled").style.height = `${value}px`
}
/* add more prefs here... */
};
@ -59,38 +62,48 @@ add_task(function* () {
yield testWindow();
info("Moving toolbox back to the bottom...");
yield monitor._toolbox.switchHost(Toolbox.HostType.BOTTOM);
yield monitor.toolbox.switchHost(Toolbox.HostType.BOTTOM);
return teardown(monitor);
function storeFirstPrefValues() {
info("Caching initial pref values.");
for (let name in prefsToCheck) {
let currentValue = Prefs[name];
let currentValue = getPrefs()[name];
prefsToCheck[name].firstValue = currentValue;
}
}
function validateFirstPrefValues() {
function validateFirstPrefValues(isVerticalSplitter) {
info("Validating current pref values to the UI elements.");
for (let name in prefsToCheck) {
let currentValue = Prefs[name];
if ((isVerticalSplitter && name === "networkDetailsHeight") ||
(!isVerticalSplitter && name === "networkDetailsWidth")) {
continue
}
let currentValue = getPrefs()[name];
let firstValue = prefsToCheck[name].firstValue;
let validateValue = prefsToCheck[name].validateValue;
is(currentValue.toSource(), firstValue.toSource(),
"Pref " + name + " should be equal to first value: " + firstValue);
is(currentValue.toSource(), validateValue().toSource(),
is(firstValue.toSource(), currentValue.toSource(),
"Pref " + name + " should be equal to first value: " + currentValue);
is(validateValue().toSource(), currentValue.toSource(),
"Pref " + name + " should validate: " + currentValue);
}
}
function modifyFrontend() {
function modifyFrontend(isVerticalSplitter) {
info("Modifying UI elements to the specified new values.");
for (let name in prefsToCheck) {
let currentValue = Prefs[name];
if ((isVerticalSplitter && name === "networkDetailsHeight") ||
(!isVerticalSplitter && name === "networkDetailsWidth")) {
continue
}
let currentValue = getPrefs()[name];
let firstValue = prefsToCheck[name].firstValue;
let newValue = prefsToCheck[name].newValue;
let validateValue = prefsToCheck[name].validateValue;
@ -99,38 +112,48 @@ add_task(function* () {
modFrontend(newValue);
info("Modified UI element affecting " + name + " to: " + newValue);
is(currentValue.toSource(), firstValue.toSource(),
"Pref " + name + " should still be equal to first value: " + firstValue);
isnot(currentValue.toSource(), newValue.toSource(),
"Pref " + name + " should't yet be equal to second value: " + newValue);
is(newValue.toSource(), validateValue().toSource(),
is(firstValue.toSource(), currentValue.toSource(),
"Pref " + name + " should still be equal to first value: " + currentValue);
isnot(newValue.toSource(), currentValue.toSource(),
"Pref " + name + " should't yet be equal to second value: " + currentValue);
is(validateValue().toSource(), newValue.toSource(),
"The UI element affecting " + name + " should validate: " + newValue);
}
}
function validateNewPrefValues() {
function validateNewPrefValues(isVerticalSplitter) {
info("Invalidating old pref values to the modified UI elements.");
for (let name in prefsToCheck) {
let currentValue = Prefs[name];
if ((isVerticalSplitter && name === "networkDetailsHeight") ||
(!isVerticalSplitter && name === "networkDetailsWidth")) {
continue
}
let currentValue = getPrefs()[name];
let firstValue = prefsToCheck[name].firstValue;
let newValue = prefsToCheck[name].newValue;
let validateValue = prefsToCheck[name].validateValue;
isnot(currentValue.toSource(), firstValue.toSource(),
"Pref " + name + " should't be equal to first value: " + firstValue);
is(currentValue.toSource(), newValue.toSource(),
"Pref " + name + " should now be equal to second value: " + newValue);
is(newValue.toSource(), validateValue().toSource(),
isnot(firstValue.toSource(), currentValue.toSource(),
"Pref " + name + " should't be equal to first value: " + currentValue);
is(newValue.toSource(), currentValue.toSource(),
"Pref " + name + " should now be equal to second value: " + currentValue);
is(validateValue().toSource(), newValue.toSource(),
"The UI element affecting " + name + " should validate: " + newValue);
}
}
function resetFrontend() {
function resetFrontend(isVerticalSplitter) {
info("Resetting UI elements to the cached initial pref values.");
for (let name in prefsToCheck) {
let currentValue = Prefs[name];
if ((isVerticalSplitter && name === "networkDetailsHeight") ||
(!isVerticalSplitter && name === "networkDetailsWidth")) {
continue
}
let currentValue = getPrefs()[name];
let firstValue = prefsToCheck[name].firstValue;
let newValue = prefsToCheck[name].newValue;
let validateValue = prefsToCheck[name].validateValue;
@ -139,84 +162,98 @@ add_task(function* () {
modFrontend(firstValue);
info("Modified UI element affecting " + name + " to: " + firstValue);
isnot(currentValue.toSource(), firstValue.toSource(),
"Pref " + name + " should't yet be equal to first value: " + firstValue);
is(currentValue.toSource(), newValue.toSource(),
"Pref " + name + " should still be equal to second value: " + newValue);
is(firstValue.toSource(), validateValue().toSource(),
isnot(firstValue.toSource(), currentValue.toSource(),
"Pref " + name + " should't yet be equal to first value: " + currentValue);
is(newValue.toSource(), currentValue.toSource(),
"Pref " + name + " should still be equal to second value: " + currentValue);
is(validateValue().toSource(), firstValue.toSource(),
"The UI element affecting " + name + " should validate: " + firstValue);
}
}
function* restartNetMonitorAndSetupEnv() {
let newMonitor = yield restartNetMonitor(monitor);
monitor = newMonitor.monitor;
let networkEvent = waitForNetworkEvents(monitor, 1);
newMonitor.tab.linkedBrowser.reload();
yield networkEvent;
let wait = waitForDOM(getDoc(), ".network-details-panel");
EventUtils.sendMouseEvent({ type: "click" },
getDoc().querySelector(".network-details-panel-toggle"));
yield wait;
}
function* testBottom() {
yield restartNetMonitorAndSetupEnv();
info("Testing prefs reload for a bottom host.");
storeFirstPrefValues();
// Validate and modify while toolbox is on the bottom.
validateFirstPrefValues();
modifyFrontend();
validateFirstPrefValues(true);
modifyFrontend(true);
let newMonitor = yield restartNetMonitor(monitor);
monitor = newMonitor.monitor;
yield restartNetMonitorAndSetupEnv();
// Revalidate and reset frontend while toolbox is on the bottom.
validateNewPrefValues();
resetFrontend();
validateNewPrefValues(true);
resetFrontend(true);
newMonitor = yield restartNetMonitor(monitor);
monitor = newMonitor.monitor;
yield restartNetMonitorAndSetupEnv();
// Revalidate.
validateFirstPrefValues();
validateFirstPrefValues(true);
}
function* testSide() {
yield restartNetMonitorAndSetupEnv();
info("Moving toolbox to the side...");
yield monitor._toolbox.switchHost(Toolbox.HostType.SIDE);
yield monitor.toolbox.switchHost(Toolbox.HostType.SIDE);
info("Testing prefs reload for a side host.");
storeFirstPrefValues();
// Validate and modify frontend while toolbox is on the side.
validateFirstPrefValues();
modifyFrontend();
validateFirstPrefValues(false);
modifyFrontend(false);
let newMonitor = yield restartNetMonitor(monitor);
monitor = newMonitor.monitor;
yield restartNetMonitorAndSetupEnv();
// Revalidate and reset frontend while toolbox is on the side.
validateNewPrefValues();
resetFrontend();
validateNewPrefValues(false);
resetFrontend(false);
newMonitor = yield restartNetMonitor(monitor);
monitor = newMonitor.monitor;
yield restartNetMonitorAndSetupEnv();
// Revalidate.
validateFirstPrefValues();
validateFirstPrefValues(false);
}
function* testWindow() {
yield restartNetMonitorAndSetupEnv();
info("Moving toolbox into a window...");
yield monitor._toolbox.switchHost(Toolbox.HostType.WINDOW);
yield monitor.toolbox.switchHost(Toolbox.HostType.WINDOW);
info("Testing prefs reload for a window host.");
storeFirstPrefValues();
// Validate and modify frontend while toolbox is in a window.
validateFirstPrefValues();
modifyFrontend();
validateFirstPrefValues(true);
modifyFrontend(true);
let newMonitor = yield restartNetMonitor(monitor);
monitor = newMonitor.monitor;
yield restartNetMonitorAndSetupEnv();
// Revalidate and reset frontend while toolbox is in a window.
validateNewPrefValues();
resetFrontend();
validateNewPrefValues(true);
resetFrontend(true);
newMonitor = yield restartNetMonitor(monitor);
monitor = newMonitor.monitor;
yield restartNetMonitorAndSetupEnv();
// Revalidate.
validateFirstPrefValues();
validateFirstPrefValues(true);
}
});

View File

@ -32,13 +32,13 @@ add_task(function* () {
verifyRequest(0);
// Switch to the webconsole.
let onWebConsole = monitor._toolbox.once("webconsole-selected");
monitor._toolbox.selectTool("webconsole");
let onWebConsole = monitor.toolbox.once("webconsole-selected");
monitor.toolbox.selectTool("webconsole");
yield onWebConsole;
// Switch back to the netmonitor.
let onNetMonitor = monitor._toolbox.once("netmonitor-selected");
monitor._toolbox.selectTool("netmonitor");
let onNetMonitor = monitor.toolbox.once("netmonitor-selected");
monitor.toolbox.selectTool("netmonitor");
yield onNetMonitor;
// Reload debugee.

View File

@ -22,8 +22,6 @@ function test() {
function checkIfInitialized(tag) {
info(`Checking if initialization is ok (${tag}).`);
ok(monitor.panelWin.NetMonitorView,
`The network monitor view object exists (${tag}).`);
ok(monitor.panelWin.NetMonitorController,
`The network monitor controller object exists (${tag}).`);
ok(monitor.panelWin.NetMonitorController._startup,
@ -43,8 +41,6 @@ function test() {
function checkIfDestroyed(tag) {
gInfo("Checking if destruction is ok.");
gOk(monitor.panelWin.NetMonitorView,
`The network monitor view object still exists (${tag}).`);
gOk(monitor.panelWin.NetMonitorController,
`The network monitor controller object still exists (${tag}).`);
gOk(monitor.panelWin.NetMonitorController._shutdown,

View File

@ -4,7 +4,7 @@
"use strict";
/**
* Tests if the statistics view is populated correctly.
* Tests if the statistics panel displays correctly.
*/
add_task(function* () {
@ -15,36 +15,27 @@ add_task(function* () {
let { document, gStore, windowRequire } = panel;
let Actions = windowRequire("devtools/client/netmonitor/actions/index");
let body = document.querySelector("#body");
is(body.selectedPanel.id, "inspector-panel",
ok(document.querySelector(".monitor-panel"),
"The current main panel is correct.");
info("Displaying statistics view");
info("Displaying statistics panel");
gStore.dispatch(Actions.openStatistics(true));
is(body.selectedPanel.id, "react-statistics-panel-hook",
ok(document.querySelector(".statistics-panel"),
"The current main panel is correct.");
info("Waiting for placeholder to display");
is(document.querySelector(".primed-cache-chart").childNodes.length, 1,
"There should be a placeholder primed cache chart created now.");
is(document.querySelector(".empty-cache-chart").childNodes.length, 1,
"There should be a placeholder empty cache chart created now.");
yield waitUntil(
() => document.querySelectorAll(".pie-chart-container[placeholder=true]").length == 2);
ok(true, "Two placeholder pie charts appear to be rendered correctly.");
is(document.querySelectorAll(".pie-chart-container[placeholder=true]").length, 2,
"Two placeholder pie chart appear to be rendered correctly.");
is(document.querySelectorAll(".table-chart-container[placeholder=true]").length, 2,
"Two placeholder table chart appear to be rendered correctly.");
yield waitUntil(
() => document.querySelectorAll(".table-chart-container[placeholder=true]").length == 2);
ok(true, "Two placeholde table charts appear to be rendered correctly.");
info("Waiting for chart to display");
is(document.querySelector(".primed-cache-chart").childNodes.length, 1,
"There should be a real primed cache chart created now.");
is(document.querySelector(".empty-cache-chart").childNodes.length, 1,
"There should be a real empty cache chart created now.");
yield waitUntil(
() => document.querySelectorAll(".pie-chart-container:not([placeholder=true])").length == 2);
ok(true, "Two real pie charts appear to be rendered correctly.");

View File

@ -31,9 +31,7 @@ add_task(function* () {
gStore.dispatch(Actions.openStatistics(true));
let body = document.querySelector("#body");
is(body.selectedPanel.id, "react-statistics-panel-hook",
ok(document.querySelector(".statistics-panel"),
"The main panel is switched to the statistics panel.");
yield waitUntil(
@ -43,8 +41,8 @@ add_task(function* () {
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector(".pie-chart-slice"));
is(body.selectedPanel.id, "inspector-panel",
"The main panel is switched back to the inspector panel.");
ok(document.querySelector(".monitor-panel"),
"The main panel is switched back to the monitor panel.");
testFilterButtons(monitor, "html");
info("The correct filtering predicate is used when exiting perf. analysis mode.");

View File

@ -152,7 +152,7 @@ function restartNetMonitor(monitor, newUrl) {
info("Restarting the specified network monitor.");
return Task.spawn(function* () {
let tab = monitor.target.tab;
let tab = monitor.toolbox.target.tab;
let url = newUrl || tab.linkedBrowser.currentURI.spec;
let onDestroyed = monitor.once("destroyed");
@ -167,7 +167,7 @@ function teardown(monitor) {
info("Destroying the specified network monitor.");
return Task.spawn(function* () {
let tab = monitor.target.tab;
let tab = monitor.toolbox.target.tab;
let onDestroyed = monitor.once("destroyed");
yield removeTab(tab);
@ -249,16 +249,6 @@ function waitForNetworkEvents(aMonitor, aGetRequests, aPostRequests = 0) {
return deferred.promise;
}
/**
* Convert a store record (model) to the rendered element. Tests that need to use
* this should be rewritten - test the rendered markup at unit level, integration
* mochitest should check only the store state.
*/
function getItemTarget(requestList, requestItem) {
const items = requestList.mountPoint.querySelectorAll(".request-list-item");
return [...items].find(el => el.dataset.id == requestItem.id);
}
function verifyRequestItemTarget(document, requestList, requestItem, aMethod,
aUrl, aData = {}) {
info("> Verifying: " + aMethod + " " + aUrl + " " + aData.toSource());

View File

@ -20,23 +20,23 @@ const SplitBox = React.createClass({
// Custom class name. You can use more names separated by a space.
className: PropTypes.string,
// Initial size of controlled panel.
initialSize: PropTypes.number,
initialSize: PropTypes.string,
// Initial width of controlled panel.
initialWidth: PropTypes.number,
initialWidth: PropTypes.string,
// Initial height of controlled panel.
initialHeight: PropTypes.number,
initialHeight: PropTypes.string,
// Left/top panel
startPanel: PropTypes.any,
// Min panel size.
minSize: PropTypes.number,
minSize: PropTypes.string,
// Max panel size.
maxSize: PropTypes.number,
maxSize: PropTypes.string,
// Right/bottom panel
endPanel: PropTypes.any,
// True if the right/bottom panel should be controlled.
endPanelControl: PropTypes.bool,
// Size of the splitter handle bar.
splitterSize: PropTypes.number,
splitterSize: PropTypes.string,
// True if the splitter bar is vertical (default is vertical).
vert: PropTypes.bool,
// Style object.
@ -64,6 +64,14 @@ const SplitBox = React.createClass({
};
},
componentWillReceiveProps(nextProps) {
let { vert } = nextProps;
if (vert !== this.props.vert) {
this.setState({ vert });
}
},
// Dragging Events
/**

View File

@ -22,7 +22,7 @@ let Tabbar = createClass({
displayName: "Tabbar",
propTypes: {
children: PropTypes.object,
children: PropTypes.array,
onSelect: PropTypes.func,
showAllTabsMenu: PropTypes.bool,
activeTabId: PropTypes.string,

View File

@ -9,7 +9,9 @@
// A helper actor for inspector and markupview tests.
const { Cc, Ci, Cu } = require("chrome");
const {getRect, getElementFromPoint, getAdjustedQuads} = require("devtools/shared/layout/utils");
const {
getRect, getElementFromPoint, getAdjustedQuads, getWindowDimensions
} = require("devtools/shared/layout/utils");
const defer = require("devtools/shared/defer");
const {Task} = require("devtools/shared/task");
const {isContentStylesheet} = require("devtools/shared/inspector/css-logic");
@ -289,6 +291,12 @@ var testSpec = protocol.generateActorSpec({
response: {
value: RetVal("json")
}
},
getWindowDimensions: {
request: {},
response: {
value: RetVal("json")
}
}
}
});
@ -782,6 +790,16 @@ var TestActor = exports.TestActor = protocol.ActorClassWithSpec(testSpec, {
}
return sheets;
},
/**
* Returns the window's dimensions for the `window` given.
*
* @return {Object} An object with `width` and `height` properties, representing the
* number of pixels for the document's size.
*/
getWindowDimensions: function () {
return getWindowDimensions(this.content);
}
});

View File

@ -2,6 +2,7 @@
* 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/. */
@import "resource://devtools/client/shared/components/splitter/split-box.css";
@import "resource://devtools/client/shared/components/tree/tree-view.css";
@import "resource://devtools/client/shared/components/tabs/tabs.css";
@import "resource://devtools/client/shared/components/tabs/tabbar.css";
@ -85,6 +86,8 @@
.request-list-container {
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
}
.request-list-empty-notice {
@ -135,12 +138,8 @@
flex-direction: column;
overflow-x: hidden;
overflow-y: auto;
--timings-scale: 1;
--timings-rev-scale: 1;
/* Devtools panel view height - tabbar height - toolbar height */
height: calc(100vh - 48px);
}
.requests-menu-subitem {
@ -149,6 +148,7 @@
box-sizing: border-box;
align-items: center;
padding: 3px;
cursor: default;
}
.subitem-label {
@ -757,10 +757,9 @@
/* Custom request view */
.custom-request-panel {
height: 100%;
overflow: auto;
/* Full view hight - toolbar height */
height: calc(100vh - 24px);
padding: 6px 4px;
padding: 0 4px;
background-color: var(--theme-sidebar-background);
}
@ -858,7 +857,7 @@
border-color: rgba(0,0,0,0.2);
cursor: default;
pointer-events: none;
height: 100vh;
height: 100%;
}
.statistics-panel .charts-container {
@ -872,12 +871,22 @@
height: 100%;
}
.pie-table-chart-container {
display: flex;
justify-content: center;
align-items: center;
}
.statistics-panel .pie-chart-container {
margin-inline-start: 3vw;
margin-inline-end: 1vw;
}
.statistics-panel .table-chart-container {
display: flex;
flex-direction: column;
flex: 1 1 auto;
min-width: 240px;
margin-inline-start: 1vw;
margin-inline-end: 3vw;
}
@ -922,6 +931,10 @@
background: var(--theme-highlight-red);
}
.table-chart-row {
display: flex;
}
.table-chart-row-label[name=cached] {
display: none;
}
@ -933,14 +946,22 @@
.table-chart-row-label[name=label] {
width: 7em;
text-align: start;
}
.table-chart-row-label[name=size] {
width: 7em;
text-align: start;
}
.table-chart-row-label[name=time] {
width: 7em;
text-align: start;
}
.table-chart-totals {
display: flex;
flex-direction: column;
}
/* Firebug theme support for network charts */
@ -1002,15 +1023,6 @@
padding-left: 8px;
}
.custom-request-panel {
height: auto;
}
#details-pane {
margin: 0 !important;
/* To prevent all the margin hacks to hide the sidebar. */
}
.requests-menu-status {
max-width: none;
width: 10vw;
@ -1048,11 +1060,13 @@
.statistics-panel .charts-container {
flex-direction: column;
/* Minus 4em for statistics back button width */
width: calc(100% - 4em);
}
.statistics-panel .splitter {
width: 100vw;
height: 0;
width: 100%;
height: 1px;
}
}
@ -1263,40 +1277,22 @@
}
.network-details-panel {
width: 100%;
height: 100%;
}
/*
* FIXME: normal html block element cannot fill outer XUL element
* This workaround should be removed after netmonitor is migrated to react
*/
#network-table {
display: -moz-box;
-moz-box-orient: vertical;
-moz-box-flex: 1;
overflow: hidden;
}
#react-network-details-panel-hook,
#react-statistics-panel-hook,
#splitter-adjustable-box {
.split-box {
overflow: hidden;
}
html,
body,
.root,
.network-monitor,
.monitor-panel {
display: flex;
flex-direction: column;
min-width: 50px;
height: 100%;
overflow: hidden;
}
#splitter-adjustable-box[hidden=true] {
display: none;
}
#react-request-list-hook {
-moz-box-flex: 1;
}
#primed-cache-chart,
#empty-cache-chart {
display: -moz-box;
-moz-box-flex: 1;
}

View File

@ -35,8 +35,16 @@
--highlighter-bubble-border-color: rgba(255, 255, 255, 0.2);
}
/**
* Highlighters are asbolute positioned in the page by default.
* A single highlighter can have fixed position in its css class if needed (see below the
* eye dropper or rulers highlighter, for example); but if it has to handle the
* document's scrolling (as rulers does), it would lag a bit behind due the APZ (Async
* Pan/Zoom module), that performs asynchronously panning and zooming on the compositor
* thread rather than the main thread.
*/
:-moz-native-anonymous .highlighter-container {
position: fixed;
position: absolute;
width: 100%;
height: 100%;
/* The container for all highlighters doesn't react to pointer-events by
@ -383,6 +391,10 @@
cursor: crosshair;
}
:-moz-native-anonymous .measuring-tool-highlighter-elements {
position: absolute;
}
:-moz-native-anonymous .measuring-tool-highlighter-root path {
shape-rendering: crispEdges;
fill: rgba(135, 206, 235, 0.6);
@ -443,7 +455,7 @@
--label-height: 23px;
--color: #e0e0e0;
position: absolute;
position: fixed;
/* Tool start position. This should match the X/Y defines in JS */
top: 100px;
left: 100px;

View File

@ -7,11 +7,41 @@
const { Cu } = require("chrome");
const EventEmitter = require("devtools/shared/event-emitter");
const { isNodeValid } = require("./utils/markup");
const { getAdjustedQuads } = require("devtools/shared/layout/utils");
const { getAdjustedQuads, getCurrentZoom,
getWindowDimensions } = require("devtools/shared/layout/utils");
// Note that the order of items in this array is important because it is used
// for drawing the BoxModelHighlighter's path elements correctly.
const BOX_MODEL_REGIONS = ["margin", "border", "padding", "content"];
const QUADS_PROPS = ["p1", "p2", "p3", "p4", "bounds"];
function areValuesDifferent(oldValue, newValue, zoom) {
let delta = Math.abs(oldValue.toFixed(4) - newValue.toFixed(4));
return delta / zoom > 1 / zoom;
}
function areQuadsDifferent(oldQuads, newQuads, zoom) {
for (let region of BOX_MODEL_REGIONS) {
if (oldQuads[region].length !== newQuads[region].length) {
return true;
}
for (let i = 0; i < oldQuads[region].length; i++) {
for (let prop of QUADS_PROPS) {
let oldProp = oldQuads[region][i][prop];
let newProp = newQuads[region][i][prop];
for (let key of Object.keys(oldProp)) {
if (areValuesDifferent(oldProp[key], newProp[key], zoom)) {
return true;
}
}
}
}
}
return false;
}
/**
* Base class for auto-refresh-on-change highlighters. Sub classes will have a
@ -41,6 +71,8 @@ function AutoRefreshHighlighter(highlighterEnv) {
this.currentNode = null;
this.currentQuads = {};
this._winDimensions = getWindowDimensions(this.win);
this.update = this.update.bind(this);
}
@ -139,6 +171,8 @@ AutoRefreshHighlighter.prototype = {
* Update the stored box quads by reading the current node's box quads.
*/
_updateAdjustedQuads: function () {
this.currentQuads = {};
for (let region of BOX_MODEL_REGIONS) {
this.currentQuads[region] = getAdjustedQuads(
this.win,
@ -152,17 +186,32 @@ AutoRefreshHighlighter.prototype = {
* @return {Boolean}
*/
_hasMoved: function () {
let oldQuads = JSON.stringify(this.currentQuads);
let oldQuads = this.currentQuads;
this._updateAdjustedQuads();
let newQuads = JSON.stringify(this.currentQuads);
return oldQuads !== newQuads;
return areQuadsDifferent(oldQuads, this.currentQuads, getCurrentZoom(this.win));
},
/**
* Update the knowledge we have of the current window's dimensions and return `true`
* if they have changed since.
* @return {Boolean}
*/
_haveWindowDimensionsChanged: function () {
let { width, height } = getWindowDimensions(this.win);
let haveChanged = (this._winDimensions.width !== width ||
this._winDimensions.height !== height);
this._winDimensions = { width, height };
return haveChanged;
},
/**
* Update the highlighter if the node has moved since the last update.
*/
update: function () {
if (!this._isNodeValid(this.currentNode) || !this._hasMoved()) {
if (!this._isNodeValid(this.currentNode) ||
(!this._hasMoved() && !this._haveWindowDimensionsChanged())) {
return;
}

View File

@ -15,7 +15,8 @@ const {
} = require("./utils/markup");
const {
getCurrentZoom,
setIgnoreLayoutChanges
setIgnoreLayoutChanges,
getWindowDimensions
} = require("devtools/shared/layout/utils");
const { stringifyGridFragments } = require("devtools/server/actors/utils/css-grid-utils");
@ -118,11 +119,20 @@ CssGridHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
}
});
let root = createNode(this.win, {
parent: container,
attributes: {
"id": "root",
"class": "root"
},
prefix: this.ID_CLASS_PREFIX
});
// We use a <canvas> element so that we can draw an arbitrary number of lines
// which wouldn't be possible with HTML or SVG without having to insert and remove
// the whole markup on every update.
createNode(this.win, {
parent: container,
parent: root,
nodeType: "canvas",
attributes: {
"id": "canvas",
@ -135,7 +145,7 @@ CssGridHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
// Build the SVG element
let svg = createSVGNode(this.win, {
nodeType: "svg",
parent: container,
parent: root,
attributes: {
"id": "elements",
"width": "100%",
@ -355,8 +365,16 @@ CssGridHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
_update() {
setIgnoreLayoutChanges(true);
let root = this.getElement("root");
// Hide the root element and force the reflow in order to get the proper window's
// dimensions without increasing them.
root.setAttribute("style", "display: none");
this.currentNode.offsetWidth;
let { width, height } = getWindowDimensions(this.win);
// Clear the canvas the grid area highlights.
this.clearCanvas();
this.clearCanvas(width, height);
this.clearGridAreas();
// Start drawing the grid fragments.
@ -375,6 +393,9 @@ CssGridHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
this._showGrid();
root.setAttribute("style",
`position:absolute; width:${width}px;height:${height}px; overflow:hidden`);
setIgnoreLayoutChanges(false, this.highlighterEnv.window.document.documentElement);
return true;
},
@ -434,15 +455,13 @@ CssGridHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
moveInfobar(container, bounds, this.win);
},
clearCanvas() {
clearCanvas(width, height) {
let ratio = parseFloat((this.win.devicePixelRatio || 1).toFixed(2));
let width = this.win.innerWidth;
let height = this.win.innerHeight;
// Resize the canvas taking the dpr into account so as to have crisp lines.
this.canvas.setAttribute("width", width * ratio);
this.canvas.setAttribute("height", height * ratio);
this.canvas.setAttribute("style", `width:${width}px;height:${height}px`);
this.canvas.setAttribute("style", `width:${width}px;height:${height}px;`);
this.ctx.scale(ratio, ratio);
this.ctx.clearRect(0, 0, width, height);

View File

@ -5,7 +5,7 @@
"use strict";
const events = require("sdk/event/core");
const { getCurrentZoom,
const { getCurrentZoom, getWindowDimensions,
setIgnoreLayoutChanges } = require("devtools/shared/layout/utils");
const {
CanvasFrameAnonymousContentHelper,
@ -65,6 +65,7 @@ MeasuringToolHighlighter.prototype = {
attributes: {
"id": "root",
"class": "root",
"hidden": "true",
},
prefix
});
@ -77,7 +78,6 @@ MeasuringToolHighlighter.prototype = {
"class": "elements",
width: "100%",
height: "100%",
hidden: "true"
},
prefix
});
@ -157,23 +157,7 @@ MeasuringToolHighlighter.prototype = {
let zoom = getCurrentZoom(window);
let { documentElement } = window.document;
let width = Math.max(documentElement.clientWidth,
documentElement.scrollWidth,
documentElement.offsetWidth);
let height = Math.max(documentElement.clientHeight,
documentElement.scrollHeight,
documentElement.offsetHeight);
let { body } = window.document;
// get the size of the content document despite the compatMode
if (body) {
width = Math.max(width, body.scrollWidth, body.offsetWidth);
height = Math.max(height, body.scrollHeight, body.offsetHeight);
}
let { width, height } = getWindowDimensions(window);
let { coords } = this;
@ -198,7 +182,7 @@ MeasuringToolHighlighter.prototype = {
this.updateViewport();
}
setIgnoreLayoutChanges(false, documentElement);
setIgnoreLayoutChanges(false, window.document.documentElement);
this._rafID = window.requestAnimationFrame(() => this._update());
},
@ -232,7 +216,7 @@ MeasuringToolHighlighter.prototype = {
show() {
setIgnoreLayoutChanges(true);
this.getElement("elements").removeAttribute("hidden");
this.getElement("root").removeAttribute("hidden");
this._update();
@ -245,7 +229,7 @@ MeasuringToolHighlighter.prototype = {
this.hideLabel("size");
this.hideLabel("position");
this.getElement("elements").setAttribute("hidden", "true");
this.getElement("root").setAttribute("hidden", "true");
this._cancelUpdate();
@ -390,7 +374,7 @@ MeasuringToolHighlighter.prototype = {
},
updateViewport() {
let { scrollX, scrollY, devicePixelRatio } = this.env.window;
let { devicePixelRatio } = this.env.window;
let { documentWidth, documentHeight, zoom } = this.coords;
// Because `devicePixelRatio` is affected by zoom (see bug 809788),
@ -401,13 +385,12 @@ MeasuringToolHighlighter.prototype = {
// width we can actually assign: on retina, for instance, it would be 0.5,
// where on non high dpi monitor would be 1.
let minWidth = 1 / pixelRatio;
let strokeWidth = Math.min(minWidth, minWidth / zoom);
let strokeWidth = minWidth / zoom;
this.getElement("root").setAttribute("style",
`stroke-width:${strokeWidth};
width:${documentWidth}px;
height:${documentHeight}px;
transform: translate(${-scrollX}px,${-scrollY}px)`);
height:${documentHeight}px;`);
},
updateGuides() {
@ -549,10 +532,7 @@ MeasuringToolHighlighter.prototype = {
}
break;
case "scroll":
setIgnoreLayoutChanges(true);
this.updateViewport();
setIgnoreLayoutChanges(false, this.env.window.document.documentElement);
this.hideLabel("position");
break;
case "pagehide":
this.destroy();

View File

@ -5,7 +5,7 @@
"use strict";
const { Cc, Ci, Cu } = require("chrome");
const { getCurrentZoom,
const { getCurrentZoom, getWindowDimensions,
getRootBindingParent } = require("devtools/shared/layout/utils");
const { on, emit } = require("sdk/event/core");
@ -529,15 +529,24 @@ CanvasFrameAnonymousContentHelper.prototype = {
* @param {String} id The ID of the root element inserted with this API.
*/
scaleRootElement: function (node, id) {
let boundaryWindow = this.highlighterEnv.window;
let zoom = getCurrentZoom(node);
let value = "position:absolute;width:100%;height:100%;";
// Hide the root element and force the reflow in order to get the proper window's
// dimensions without increasing them.
this.setAttributeForElement(id, "style", "display: none");
node.offsetWidth;
let { width, height } = getWindowDimensions(boundaryWindow);
let value = "";
if (zoom !== 1) {
value = "position:absolute;";
value += "transform-origin:top left;transform:scale(" + (1 / zoom) + ");";
value += "width:" + (100 * zoom) + "%;height:" + (100 * zoom) + "%;";
value = `transform-origin:top left; transform:scale(${1 / zoom}); `;
width *= zoom;
height *= zoom;
}
value += `position:absolute; width:${width}px;height:${height}px; overflow:hidden`;
this.setAttributeForElement(id, "style", value);
}
};

View File

@ -2,8 +2,6 @@
* 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/. */
/* globals StopIteration */
"use strict";
const {Cc, Ci, Cu, CC} = require("chrome");
@ -1766,7 +1764,9 @@ StorageActors.createActor({
if (!DebuggerServer.isInChildProcess) {
this.backToChild = (func, rv) => rv;
this.clearDBStore = indexedDBHelpers.clearDBStore;
this.gatherFilesOrFolders = indexedDBHelpers.gatherFilesOrFolders;
this.findIDBPathsForHost = indexedDBHelpers.findIDBPathsForHost;
this.findSqlitePathsForHost = indexedDBHelpers.findSqlitePathsForHost;
this.findStorageTypePaths = indexedDBHelpers.findStorageTypePaths;
this.getDBMetaData = indexedDBHelpers.getDBMetaData;
this.getDBNamesForHost = indexedDBHelpers.getDBNamesForHost;
this.getNameFromDatabaseFile = indexedDBHelpers.getNameFromDatabaseFile;
@ -2043,35 +2043,23 @@ var indexedDBHelpers = {
// idb/1556056096MeysDaabta.sqlite
// - PathToProfileDir/storage/temporary/http+++www.example.com/
// idb/1556056096MeysDaabta.sqlite
//
// The subdirectory inside the storage folder is determined by the storage
// type:
// - default: { storage: "default" } or not specified.
// - permanent: { storage: "persistent" }.
// - temporary: { storage: "temporary" }.
let sqliteFiles = yield this.gatherFilesOrFolders(storagePath, path => {
if (path.endsWith(".sqlite")) {
let { components } = OS.Path.split(path);
let isIDB = components[components.length - 2] === "idb";
return isIDB;
}
return false;
});
let sqliteFiles = yield this.findSqlitePathsForHost(storagePath, sanitizedHost);
for (let file of sqliteFiles) {
let splitPath = OS.Path.split(file).components;
let idbIndex = splitPath.indexOf("idb");
let name = splitPath[idbIndex - 1];
let storage = splitPath[idbIndex - 2];
let relative = file.substr(profileDir.length + 1);
if (name.startsWith(sanitizedHost)) {
files.push({
file: relative,
storage: storage === "permanent" ? "persistent" : storage
});
}
files.push({
file: relative,
storage: storage === "permanent" ? "persistent" : storage
});
}
if (files.length > 0) {
@ -2090,48 +2078,55 @@ var indexedDBHelpers = {
}),
/**
* Gather together all of the files in path and pass each path through a
* validation function.
*
* @param {String}
* Path in which to begin searching.
* @param {Function}
* Validation function, which checks each file path. If this function
* Returns true the file path is kept.
*
* @returns {Array}
* An array of file paths.
* Find all SQLite files that hold IndexedDB data for a host, such as:
* storage/temporary/http+++www.example.com/idb/1556056096MeysDaabta.sqlite
*/
gatherFilesOrFolders: Task.async(function* (path, validationFunc) {
let files = [];
let iterator;
let paths = [path];
while (paths.length > 0) {
try {
iterator = new OS.File.DirectoryIterator(paths.pop());
for (let child in iterator) {
child = yield child;
path = child.path;
if (child.isDir) {
paths.push(path);
} else if (validationFunc(path)) {
files.push(path);
}
}
} catch (ex) {
// Ignore StopIteration to prevent exiting the loop.
if (ex != StopIteration) {
throw ex;
findSqlitePathsForHost: Task.async(function* (storagePath, sanitizedHost) {
let sqlitePaths = [];
let idbPaths = yield this.findIDBPathsForHost(storagePath, sanitizedHost);
for (let idbPath of idbPaths) {
let iterator = new OS.File.DirectoryIterator(idbPath);
yield iterator.forEach(entry => {
if (!entry.isDir && entry.path.endsWith(".sqlite")) {
sqlitePaths.push(entry.path);
}
});
iterator.close();
}
return sqlitePaths;
}),
/**
* Find all paths that hold IndexedDB data for a host, such as:
* storage/temporary/http+++www.example.com/idb
*/
findIDBPathsForHost: Task.async(function* (storagePath, sanitizedHost) {
let idbPaths = [];
let typePaths = yield this.findStorageTypePaths(storagePath);
for (let typePath of typePaths) {
let idbPath = OS.Path.join(typePath, sanitizedHost, "idb");
if (yield OS.File.exists(idbPath)) {
idbPaths.push(idbPath);
}
}
iterator.close();
return idbPaths;
}),
return files;
/**
* Find all the storage types, such as "default", "permanent", or "temporary".
* These names have changed over time, so it seems simpler to look through all types
* that currently exist in the profile.
*/
findStorageTypePaths: Task.async(function* (storagePath) {
let iterator = new OS.File.DirectoryIterator(storagePath);
let typePaths = [];
yield iterator.forEach(entry => {
if (entry.isDir) {
typePaths.push(entry.path);
}
});
iterator.close();
return typePaths;
}),
/**

View File

@ -249,7 +249,7 @@ CssLogic.prototype = {
cssSheet._passId = this._passId;
// Find import and keyframes rules.
for (let aDomRule of domSheet.cssRules) {
for (let aDomRule of cssSheet.getCssRules()) {
if (aDomRule.type == CSSRule.IMPORT_RULE &&
aDomRule.styleSheet &&
this.mediaMatches(aDomRule)) {
@ -819,9 +819,29 @@ CssSheet.prototype = {
* @return {number} the number of nsIDOMCSSRule objects in this stylesheet.
*/
get ruleCount() {
return this._ruleCount > -1 ?
this._ruleCount :
this.domSheet.cssRules.length;
try {
return this._ruleCount > -1 ?
this._ruleCount :
this.getCssRules().length;
} catch (e) {
return 0;
}
},
/**
* Retrieve the array of css rules for this stylesheet.
*
* Accessing cssRules on a stylesheet that is not completely loaded can throw a
* DOMException (Bug 625013). This wrapper will return an empty array instead.
*
* @return {Array} array of css rules.
**/
getCssRules: function () {
try {
return this.domSheet.cssRules;
} catch (e) {
return [];
}
},
/**

View File

@ -4,7 +4,7 @@
"use strict";
/* eslint-env worker */
/* eslint-env mozilla/chrome-worker */
/* global worker, loadSubScript, global */
// This function is used to do remote procedure calls from the worker to the

View File

@ -1506,44 +1506,43 @@ getCoreDumpOutputStream(ErrorResult& rv, TimeStamp& start, nsAString& outFilePat
if (NS_WARN_IF(rv.Failed()))
return nullptr;
return outputStream.forget();
} else {
// Request a file descriptor from the parent process over IPDL.
auto cc = ContentChild::GetSingleton();
if (!cc) {
rv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
UniqueHeapSnapshotTempFileHelperChild helper(
cc->SendPHeapSnapshotTempFileHelperConstructor());
if (NS_WARN_IF(!helper)) {
rv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
OpenHeapSnapshotTempFileResponse response;
if (!helper->SendOpenHeapSnapshotTempFile(&response)) {
rv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
if (response.type() == OpenHeapSnapshotTempFileResponse::Tnsresult) {
rv.Throw(response.get_nsresult());
return nullptr;
}
auto opened = response.get_OpenedFile();
outFilePath = opened.path();
nsCOMPtr<nsIOutputStream> outputStream =
FileDescriptorOutputStream::Create(opened.descriptor());
if (NS_WARN_IF(!outputStream)) {
rv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
return outputStream.forget();
}
// Request a file descriptor from the parent process over IPDL.
auto cc = ContentChild::GetSingleton();
if (!cc) {
rv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
UniqueHeapSnapshotTempFileHelperChild helper(
cc->SendPHeapSnapshotTempFileHelperConstructor());
if (NS_WARN_IF(!helper)) {
rv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
OpenHeapSnapshotTempFileResponse response;
if (!helper->SendOpenHeapSnapshotTempFile(&response)) {
rv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
if (response.type() == OpenHeapSnapshotTempFileResponse::Tnsresult) {
rv.Throw(response.get_nsresult());
return nullptr;
}
auto opened = response.get_OpenedFile();
outFilePath = opened.path();
nsCOMPtr<nsIOutputStream> outputStream =
FileDescriptorOutputStream::Create(opened.descriptor());
if (NS_WARN_IF(!outputStream)) {
rv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
return outputStream.forget();
}
} // namespace devtools

View File

@ -203,6 +203,10 @@ function getAdjustedQuads(boundaryWindow, node, region) {
let [xOffset, yOffset] = getFrameOffsets(boundaryWindow, node);
let scale = getCurrentZoom(node);
let { scrollX, scrollY } = boundaryWindow;
xOffset += scrollX * scale;
yOffset += scrollY * scale;
let adjustedQuads = [];
for (let quad of quads) {
@ -320,7 +324,7 @@ function getNodeBounds(boundaryWindow, node) {
if (!node) {
return null;
}
let { scrollX, scrollY } = boundaryWindow;
let scale = getCurrentZoom(node);
// Find out the offset of the node in its current frame
@ -347,8 +351,8 @@ function getNodeBounds(boundaryWindow, node) {
// And add the potential frame offset if the node is nested
let [xOffset, yOffset] = getFrameOffsets(boundaryWindow, node);
xOffset += offsetLeft;
yOffset += offsetTop;
xOffset += offsetLeft + scrollX;
yOffset += offsetTop + scrollY;
xOffset *= scale;
yOffset *= scale;
@ -628,6 +632,33 @@ function getCurrentZoom(node) {
}
exports.getCurrentZoom = getCurrentZoom;
/**
* Returns the window's dimensions for the `window` given.
*
* @return {Object} An object with `width` and `height` properties, representing the
* number of pixels for the document's size.
*/
function getWindowDimensions(window) {
// First we'll try without flushing layout, because it's way faster.
let windowUtils = utilsFor(window);
let { width, height } = windowUtils.getRootBounds();
if (!width || !height) {
// We need a flush after all :'(
width = window.innerWidth + window.scrollMaxX - window.scrollMinX;
height = window.innerHeight + window.scrollMaxY - window.scrollMinY;
let scrollbarHeight = {};
let scrollbarWidth = {};
windowUtils.getScrollbarSize(false, scrollbarWidth, scrollbarHeight);
width -= scrollbarWidth.value;
height -= scrollbarHeight.value;
}
return { width, height };
}
exports.getWindowDimensions = getWindowDimensions;
/**
* Return the default view for a given node, where node can be:
* - a DOM node

View File

@ -20,7 +20,7 @@ public:
: TimelineMarker("Styles", aTracingType)
{
if (aRestyleHint) {
mRestyleHint.AssignWithConversion(RestyleManager::RestyleHintToString(aRestyleHint));
mRestyleHint.AssignWithConversion(GeckoRestyleManager::RestyleHintToString(aRestyleHint));
}
}

View File

@ -15,8 +15,8 @@
#include "mozilla/AnimationUtils.h"
#include "mozilla/EffectSet.h"
#include "mozilla/LayerAnimationInfo.h"
#include "mozilla/RestyleManagerHandle.h"
#include "mozilla/RestyleManagerHandleInlines.h"
#include "mozilla/RestyleManager.h"
#include "mozilla/RestyleManagerInlines.h"
#include "mozilla/StyleAnimationValue.h"
#include "nsComputedDOMStyle.h" // nsComputedDOMStyle::GetPresShellForContent
#include "nsCSSPropertyIDSet.h"
@ -314,7 +314,7 @@ EffectCompositor::PostRestyleForAnimation(dom::Element* aElement,
// FIXME: stylo only supports Self and Subtree hints now, so we override it
// for stylo if we are not in process of restyling.
if (mPresContext->StyleSet()->IsServo() &&
!mPresContext->RestyleManager()->AsBase()->IsInStyleRefresh()) {
!mPresContext->RestyleManager()->IsInStyleRefresh()) {
hint = eRestyle_Self | eRestyle_Subtree;
}
mPresContext->PresShell()->RestyleForAnimation(element, hint);

Some files were not shown because too many files have changed in this diff Show More