diff options
Diffstat (limited to 'devtools/client/netmonitor/src/components/request-details/CookiesPanel.js')
-rw-r--r-- | devtools/client/netmonitor/src/components/request-details/CookiesPanel.js | 225 |
1 files changed, 225 insertions, 0 deletions
diff --git a/devtools/client/netmonitor/src/components/request-details/CookiesPanel.js b/devtools/client/netmonitor/src/components/request-details/CookiesPanel.js new file mode 100644 index 0000000000..466c0379cd --- /dev/null +++ b/devtools/client/netmonitor/src/components/request-details/CookiesPanel.js @@ -0,0 +1,225 @@ +/* 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 { + Component, + createFactory, +} = require("resource://devtools/client/shared/vendor/react.js"); +const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js"); +const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js"); +const { + L10N, +} = require("resource://devtools/client/netmonitor/src/utils/l10n.js"); +const { + fetchNetworkUpdatePacket, +} = require("resource://devtools/client/netmonitor/src/utils/request-utils.js"); +const { + sortObjectKeys, +} = require("resource://devtools/client/netmonitor/src/utils/sort-utils.js"); +const { + FILTER_SEARCH_DELAY, +} = require("resource://devtools/client/netmonitor/src/constants.js"); + +// Component +const PropertiesView = createFactory( + require("resource://devtools/client/netmonitor/src/components/request-details/PropertiesView.js") +); +const SearchBox = createFactory( + require("resource://devtools/client/shared/components/SearchBox.js") +); +const Accordion = createFactory( + require("resource://devtools/client/shared/components/Accordion.js") +); + +loader.lazyGetter(this, "TreeRow", function () { + return createFactory( + require("resource://devtools/client/shared/components/tree/TreeRow.js") + ); +}); + +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"); + +/* + * Cookies panel component + * This tab lists full details of any cookies sent with the request or response + */ +class CookiesPanel extends Component { + static get propTypes() { + return { + connector: PropTypes.object.isRequired, + openLink: PropTypes.func, + request: PropTypes.object.isRequired, + targetSearchResult: PropTypes.object, + }; + } + + constructor(props) { + super(props); + this.state = { + filterText: "", + }; + } + + componentDidMount() { + const { connector, request } = this.props; + fetchNetworkUpdatePacket(connector.requestData, request, [ + "requestCookies", + "responseCookies", + ]); + } + + // FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=1774507 + UNSAFE_componentWillReceiveProps(nextProps) { + const { connector, request } = nextProps; + fetchNetworkUpdatePacket(connector.requestData, request, [ + "requestCookies", + "responseCookies", + ]); + } + + /** + * 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} + */ + getProperties(arr, title) { + const cookies = 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; + }, Object.create(null)); + + // To have different roots for Request and Response cookies + return { [title]: cookies }; + } + + /** + * Custom rendering method passed to PropertiesView. It's + * responsible to filter out level 0 node in the tree + * + * @param {Object} props + */ + renderRow(props) { + const { level } = props.member; + + if (level === 0) { + return null; + } + + return TreeRow(props); + } + + /** + * Get the selected cookies path + * @param {Object} searchResult + * @returns {string} + */ + getTargetCookiePath(searchResult) { + if (!searchResult) { + return null; + } + + switch (searchResult.type) { + case "requestCookies": { + return `/${REQUEST_COOKIES}/${searchResult.label}`; + } + case "responseCookies": + return `/${RESPONSE_COOKIES}/${searchResult.label}`; + } + + return null; + } + + render() { + let { + request: { + requestCookies = { cookies: [] }, + responseCookies = { cookies: [] }, + }, + targetSearchResult, + } = this.props; + + const { filterText } = this.state; + + requestCookies = requestCookies.cookies || requestCookies; + responseCookies = responseCookies.cookies || responseCookies; + + if (!requestCookies.length && !responseCookies.length) { + return div({ className: "empty-notice" }, COOKIES_EMPTY_TEXT); + } + + const items = []; + + if (responseCookies.length) { + items.push({ + component: PropertiesView, + componentProps: { + object: sortObjectKeys( + this.getProperties(responseCookies, RESPONSE_COOKIES) + ), + filterText, + targetSearchResult, + defaultSelectFirstNode: false, + selectPath: this.getTargetCookiePath, + renderRow: this.renderRow, + }, + header: RESPONSE_COOKIES, + id: "responseCookies", + opened: true, + }); + } + + if (requestCookies.length) { + items.push({ + component: PropertiesView, + componentProps: { + object: sortObjectKeys( + this.getProperties(requestCookies, REQUEST_COOKIES) + ), + filterText, + targetSearchResult, + defaultSelectFirstNode: false, + selectPath: this.getTargetCookiePath, + renderRow: this.renderRow, + }, + header: REQUEST_COOKIES, + id: "requestCookies", + opened: true, + }); + } + + return div( + { className: "panel-container cookies-panel-container" }, + div( + { className: "devtools-toolbar devtools-input-toolbar" }, + SearchBox({ + delay: FILTER_SEARCH_DELAY, + type: "filter", + onChange: text => this.setState({ filterText: text }), + placeholder: COOKIES_FILTER_TEXT, + }) + ), + Accordion({ items }) + ); + } +} + +module.exports = CookiesPanel; |