From 61790522a36fd84ca5648bc02dab8e706c70e0bc Mon Sep 17 00:00:00 2001 From: Fred Lin Date: Thu, 24 Nov 2016 17:05:31 +0800 Subject: [PATCH] Bug 1317649 - Implement Cookies Panel;r=Honza,jsnajdr,rickychien use properties-view show other Cookie properties MozReview-Commit-ID: 7526Hm2ORbL --HG-- extra : rebase_source : 2e2850fd8daf6646648cc3e63c2a53cd381e90f3 --- devtools/client/netmonitor/actions/filters.js | 6 +- .../netmonitor/components/search-box.js | 8 +- .../client/netmonitor/components/toolbar.js | 4 +- devtools/client/netmonitor/details-view.js | 119 ++---------------- devtools/client/netmonitor/netmonitor.xul | 3 +- .../client/netmonitor/reducers/filters.js | 8 +- .../client/netmonitor/requests-menu-view.js | 86 +++++++++---- .../client/netmonitor/selectors/requests.js | 28 +++++ .../shared/components/cookies-panel.js | 99 +++++++++++++++ .../netmonitor/shared/components/moz.build | 1 + devtools/client/themes/netmonitor.css | 2 + 11 files changed, 220 insertions(+), 144 deletions(-) create mode 100644 devtools/client/netmonitor/shared/components/cookies-panel.js diff --git a/devtools/client/netmonitor/actions/filters.js b/devtools/client/netmonitor/actions/filters.js index a8e67282788c..ba6173372c34 100644 --- a/devtools/client/netmonitor/actions/filters.js +++ b/devtools/client/netmonitor/actions/filters.js @@ -5,8 +5,8 @@ "use strict"; const { - TOGGLE_REQUEST_FILTER_TYPE, ENABLE_REQUEST_FILTER_TYPE_ONLY, + TOGGLE_REQUEST_FILTER_TYPE, SET_REQUEST_FILTER_TEXT, } = require("../constants"); @@ -40,7 +40,7 @@ function enableRequestFilterTypeOnly(filter) { } /** - * Set filter text. + * Set filter text in toolbar. * * @param {string} text - A filter text is going to be set */ @@ -52,7 +52,7 @@ function setRequestFilterText(text) { } module.exports = { - toggleRequestFilterType, enableRequestFilterTypeOnly, + toggleRequestFilterType, setRequestFilterText, }; diff --git a/devtools/client/netmonitor/components/search-box.js b/devtools/client/netmonitor/components/search-box.js index 160cf1c98174..bb67222656cd 100644 --- a/devtools/client/netmonitor/components/search-box.js +++ b/devtools/client/netmonitor/components/search-box.js @@ -8,18 +8,16 @@ const { connect } = require("devtools/client/shared/vendor/react-redux"); const SearchBox = require("devtools/client/shared/components/search-box"); const { L10N } = require("../l10n"); const Actions = require("../actions/index"); -const { FREETEXT_FILTER_SEARCH_DELAY } = require("../constants"); +const { FILTER_SEARCH_DELAY } = require("../constants"); module.exports = connect( (state) => ({ - delay: FREETEXT_FILTER_SEARCH_DELAY, + delay: FILTER_SEARCH_DELAY, keyShortcut: L10N.getStr("netmonitor.toolbar.filterFreetext.key"), placeholder: L10N.getStr("netmonitor.toolbar.filterFreetext.label"), type: "filter", }), (dispatch) => ({ - onChange: (url) => { - dispatch(Actions.setRequestFilterText(url)); - }, + onChange: (text) => dispatch(Actions.setRequestFilterText(text)), }) )(SearchBox); diff --git a/devtools/client/netmonitor/components/toolbar.js b/devtools/client/netmonitor/components/toolbar.js index 14ebf52a1dc2..c2f481a52eef 100644 --- a/devtools/client/netmonitor/components/toolbar.js +++ b/devtools/client/netmonitor/components/toolbar.js @@ -10,7 +10,7 @@ const { } = require("devtools/client/shared/vendor/react"); const ClearButton = createFactory(require("./clear-button")); const FilterButtons = createFactory(require("./filter-buttons")); -const SearchBox = createFactory(require("./search-box")); +const ToolbarSearchBox = createFactory(require("./search-box")); const SummaryButton = createFactory(require("./summary-button")); const ToggleButton = createFactory(require("./toggle-button")); @@ -28,7 +28,7 @@ function Toolbar() { ), span({ className: "devtools-toolbar-group" }, SummaryButton(), - SearchBox(), + ToolbarSearchBox(), ToggleButton() ) ); diff --git a/devtools/client/netmonitor/details-view.js b/devtools/client/netmonitor/details-view.js index 2a6d2db947bb..6515c23ed4d0 100644 --- a/devtools/client/netmonitor/details-view.js +++ b/devtools/client/netmonitor/details-view.js @@ -3,22 +3,20 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* eslint-disable mozilla/reject-some-requires */ -/* globals window, dumpn, $, gNetwork */ +/* globals window, dumpn, $ */ "use strict"; const promise = require("promise"); const EventEmitter = require("devtools/shared/event-emitter"); -const { Heritage } = require("devtools/client/shared/widgets/view-helpers"); const { Task } = require("devtools/shared/task"); const { ToolSidebar } = require("devtools/client/framework/sidebar"); -const { VariablesView } = require("resource://devtools/client/shared/widgets/VariablesView.jsm"); const { EVENTS } = require("./events"); -const { L10N } = require("./l10n"); const { Filters } = require("./filter-predicates"); 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); +const CookiesPanel = createFactory(require("./shared/components/cookies-panel")); const HeadersPanel = createFactory(require("./shared/components/headers-panel")); const ParamsPanel = createFactory(require("./shared/components/params-panel")); const PreviewPanel = createFactory(require("./shared/components/preview-panel")); @@ -26,18 +24,6 @@ const ResponsePanel = createFactory(require("./shared/components/response-panel" const SecurityPanel = createFactory(require("./shared/components/security-panel")); const TimingsPanel = createFactory(require("./shared/components/timings-panel")); -const GENERIC_VARIABLES_VIEW_SETTINGS = { - lazyEmpty: true, - // ms - lazyEmptyDelay: 10, - searchEnabled: true, - editableValueTooltip: "", - editableNameTooltip: "", - preventDisableOnChange: true, - preventDescriptorModifiers: true, - eval: () => {} -}; - /** * Functions handling the requests details view. */ @@ -70,6 +56,13 @@ DetailsView.prototype = { initialize: function (store) { dumpn("Initializing the DetailsView"); + this._cookiesPanelNode = $("#react-cookies-tabpanel-hook"); + + ReactDOM.render(Provider( + { store }, + CookiesPanel() + ), this._cookiesPanelNode); + this._headersPanelNode = $("#react-headers-tabpanel-hook"); ReactDOM.render(Provider( @@ -117,16 +110,6 @@ DetailsView.prototype = { disableTelemetry: true, showAllTabsMenu: true }); - - this._cookies = new VariablesView($("#all-cookies"), - Heritage.extend(GENERIC_VARIABLES_VIEW_SETTINGS, { - emptyText: L10N.getStr("cookiesEmptyText"), - searchPlaceholder: L10N.getStr("cookiesFilterText") - })); - - this._requestCookies = L10N.getStr("requestCookies"); - this._responseCookies = L10N.getStr("responseCookies"); - $("tabpanels", this.widget).addEventListener("select", this._onTabSelect); }, @@ -135,8 +118,9 @@ DetailsView.prototype = { */ destroy: function () { dumpn("Destroying the DetailsView"); - ReactDOM.unmountComponentAtNode(this._paramsPanelNode); + ReactDOM.unmountComponentAtNode(this._cookiesPanelNode); ReactDOM.unmountComponentAtNode(this._headersPanelNode); + ReactDOM.unmountComponentAtNode(this._paramsPanelNode); ReactDOM.unmountComponentAtNode(this._previewPanelNode); ReactDOM.unmountComponentAtNode(this._responsePanelNode); ReactDOM.unmountComponentAtNode(this._securityPanelNode); @@ -177,8 +161,6 @@ DetailsView.prototype = { this.widget.selectedIndex = 0; } - this._cookies.empty(); - this._dataSrc = { src: data, populated: [] }; this._onTabSelect(); window.emit(EVENTS.NETWORKDETAILSVIEW_POPULATED); @@ -211,14 +193,6 @@ DetailsView.prototype = { } Task.spawn(function* () { - viewState.updating[tab] = true; - switch (tab) { - // "Cookies" - case 1: - yield view._setResponseCookies(src.responseCookies); - yield view._setRequestCookies(src.requestCookies); - break; - } viewState.updating[tab] = false; }).then(() => { if (tab == this.widget.selectedIndex) { @@ -239,78 +213,7 @@ DetailsView.prototype = { }, e => console.error(e)); }, - /** - * Sets the network request cookies shown in this view. - * - * @param object response - * The message received from the server. - * @return object - * A promise that is resolved when the request cookies are set. - */ - _setRequestCookies: Task.async(function* (response) { - if (response && response.cookies.length) { - response.cookies.sort((a, b) => a.name > b.name); - yield this._addCookies(this._requestCookies, response); - } - }), - - /** - * Sets the network response cookies shown in this view. - * - * @param object response - * The message received from the server. - * @return object - * A promise that is resolved when the response cookies are set. - */ - _setResponseCookies: Task.async(function* (response) { - if (response && response.cookies.length) { - yield this._addCookies(this._responseCookies, response); - } - }), - - /** - * Populates the cookies container in this view with the specified data. - * - * @param string name - * The type of cookies to populate (request or response). - * @param object response - * The message received from the server. - * @return object - * Returns a promise that resolves upon the adding of cookies. - */ - _addCookies: Task.async(function* (name, response) { - let cookiesScope = this._cookies.addScope(name); - cookiesScope.expanded = true; - - for (let cookie of response.cookies) { - let cookieVar = cookiesScope.addItem(cookie.name, {}, {relaxed: true}); - let cookieValue = yield gNetwork.getString(cookie.value); - cookieVar.setGrip(cookieValue); - - // By default the cookie name and value are shown. If this is the only - // information available, then nothing else is to be displayed. - let cookieProps = Object.keys(cookie); - if (cookieProps.length == 2) { - continue; - } - - // Display any other information other than the cookie name and value - // which may be available. - let rawObject = Object.create(null); - let otherProps = cookieProps.filter(e => e != "name" && e != "value"); - for (let prop of otherProps) { - rawObject[prop] = cookie[prop]; - } - cookieVar.populate(rawObject); - cookieVar.twisty = true; - cookieVar.expanded = true; - } - }), - _dataSrc: null, - _cookies: null, - _requestCookies: "", - _responseCookies: "" }; exports.DetailsView = DetailsView; diff --git a/devtools/client/netmonitor/netmonitor.xul b/devtools/client/netmonitor/netmonitor.xul index 84f7f7341f32..6d6043f17d07 100644 --- a/devtools/client/netmonitor/netmonitor.xul +++ b/devtools/client/netmonitor/netmonitor.xul @@ -129,7 +129,8 @@ - + selectedId ? requests.get(selectedId) : null ); +const getSelectedRequestCookies = createSelector( + getSelectedRequest, + selectedRequest => { + // request store cookies in requestCookies or requestCookies.cookies + if (selectedRequest && selectedRequest.requestCookies) { + return selectedRequest.requestCookies.cookies ? + selectedRequest.requestCookies.cookies : selectedRequest.requestCookies; + } + + return []; + } +); + +const getSelectedResponseCookies = createSelector( + getSelectedRequest, + selectedRequest => { + // response store cookies in responseCookies or responseCookies.cookies + if (selectedRequest && selectedRequest.responseCookies) { + return selectedRequest.responseCookies.cookies ? + selectedRequest.responseCookies.cookies : selectedRequest.responseCookies; + } + + return []; + } +); + function getRequestById(state, id) { return state.requests.requests.get(id); } @@ -111,5 +137,7 @@ module.exports = { getDisplayedRequestsSummary, getRequestById, getSelectedRequest, + getSelectedRequestCookies, + getSelectedResponseCookies, getSortedRequests, }; diff --git a/devtools/client/netmonitor/shared/components/cookies-panel.js b/devtools/client/netmonitor/shared/components/cookies-panel.js new file mode 100644 index 000000000000..2b4960ff61ef --- /dev/null +++ b/devtools/client/netmonitor/shared/components/cookies-panel.js @@ -0,0 +1,99 @@ +/* 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"); +const { L10N } = require("../../l10n"); +const { + getSelectedRequestCookies, + getSelectedResponseCookies, +} = require("../../selectors/index"); + +// Component +const PropertiesView = createFactory(require("./properties-view")); + +const { div } = DOM; + +const COOKIES_EMPTY_TEXT = L10N.getStr("cookiesEmptyText"); +const COOKIES_FILTER_TEXT = L10N.getStr("cookiesFilterText"); +const REQUEST_COOKIES = L10N.getStr("requestCookies"); +const RESPONSE_COOKIES = L10N.getStr("responseCookies"); +const SECTION_NAMES = [ + RESPONSE_COOKIES, + REQUEST_COOKIES, +]; + +/* + * Cookies panel component + * This tab lists full details of any cookies sent with the request or response + */ +function CookiesPanel({ + request, + response, +}) { + if (!response.length && !request.length) { + return div({ className: "empty-notice" }, + COOKIES_EMPTY_TEXT + ); + } + + let object = {}; + if (response.length) { + object[RESPONSE_COOKIES] = getProperties(response); + } + if (request.length) { + object[REQUEST_COOKIES] = getProperties(request); + } + + return ( + PropertiesView({ + object, + filterPlaceHolder: COOKIES_FILTER_TEXT, + sectionNames: SECTION_NAMES, + }) + ); +} + +CookiesPanel.displayName = "CookiesPanel"; + +CookiesPanel.propTypes = { + request: PropTypes.array.isRequired, + response: PropTypes.array.isRequired, +}; + +/** + * Mapping array to dict for TreeView usage. + * Since TreeView only support Object(dict) format. + * + * @param {Object[]} arr - key-value pair array like cookies or params + * @returns {Object} + */ +function getProperties(arr) { + return arr.reduce((map, obj) => { + // Generally cookies object contains only name and value properties and can + // be rendered as name: value pair. + // When there are more properties in cookies object such as extra or path, + // We will pass the object to display these extra information + if (Object.keys(obj).length > 2) { + map[obj.name] = Object.assign({}, obj); + delete map[obj.name].name; + } else { + map[obj.name] = obj.value; + } + return map; + }, {}); +} + +module.exports = connect( + state => ({ + request: getSelectedRequestCookies(state), + response: getSelectedResponseCookies(state), + }) +)(CookiesPanel); diff --git a/devtools/client/netmonitor/shared/components/moz.build b/devtools/client/netmonitor/shared/components/moz.build index 081cd469d5d8..3a4a73ef7264 100644 --- a/devtools/client/netmonitor/shared/components/moz.build +++ b/devtools/client/netmonitor/shared/components/moz.build @@ -3,6 +3,7 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. DevToolsModules( + 'cookies-panel.js', 'editor.js', 'headers-panel.js', 'params-panel.js', diff --git a/devtools/client/themes/netmonitor.css b/devtools/client/themes/netmonitor.css index 1c81018cd6d8..f58bb00ba822 100644 --- a/devtools/client/themes/netmonitor.css +++ b/devtools/client/themes/netmonitor.css @@ -1272,6 +1272,7 @@ * FIXME: normal html block element cannot fill outer XUL element * This workaround should be removed after netmonitor is migrated to react */ +#react-cookies-tabpanel-hook, #react-headers-tabpanel-hook, #react-params-tabpanel-hook, #react-preview-tabpanel-hook, @@ -1286,6 +1287,7 @@ } /* For vbox */ +#react-cookies-tabpanel-hook, #react-headers-tabpanel-hook, #react-params-tabpanel-hook, #react-preview-tabpanel-hook,