summaryrefslogtreecommitdiffstats
path: root/devtools/client/netmonitor/src/components/request-blocking/RequestBlockingPanel.js
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--devtools/client/netmonitor/src/components/request-blocking/RequestBlockingPanel.js350
1 files changed, 350 insertions, 0 deletions
diff --git a/devtools/client/netmonitor/src/components/request-blocking/RequestBlockingPanel.js b/devtools/client/netmonitor/src/components/request-blocking/RequestBlockingPanel.js
new file mode 100644
index 0000000000..4d209e788d
--- /dev/null
+++ b/devtools/client/netmonitor/src/components/request-blocking/RequestBlockingPanel.js
@@ -0,0 +1,350 @@
+/* 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,
+} = require("resource://devtools/client/shared/vendor/react.js");
+const {
+ button,
+ div,
+ form,
+ input,
+ label,
+ li,
+ span,
+ ul,
+} = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
+const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
+const {
+ connect,
+} = require("resource://devtools/client/shared/redux/visibility-handler-connect.js");
+const Actions = require("resource://devtools/client/netmonitor/src/actions/index.js");
+const {
+ L10N,
+} = require("resource://devtools/client/netmonitor/src/utils/l10n.js");
+const {
+ PANELS,
+} = require("resource://devtools/client/netmonitor/src/constants.js");
+
+const RequestBlockingContextMenu = require("resource://devtools/client/netmonitor/src/widgets/RequestBlockingContextMenu.js");
+
+const ENABLE_BLOCKING_LABEL = L10N.getStr(
+ "netmonitor.actionbar.enableBlocking"
+);
+const ADD_URL_PLACEHOLDER = L10N.getStr(
+ "netmonitor.actionbar.blockSearchPlaceholder"
+);
+const REQUEST_BLOCKING_USAGE_NOTICE = L10N.getStr(
+ "netmonitor.actionbar.requestBlockingUsageNotice"
+);
+const REQUEST_BLOCKING_ADD_NOTICE = L10N.getStr(
+ "netmonitor.actionbar.requestBlockingAddNotice"
+);
+const REMOVE_URL_TOOLTIP = L10N.getStr("netmonitor.actionbar.removeBlockedUrl");
+
+class RequestBlockingPanel extends Component {
+ static get propTypes() {
+ return {
+ blockedUrls: PropTypes.array.isRequired,
+ addBlockedUrl: PropTypes.func.isRequired,
+ isDisplaying: PropTypes.bool.isRequired,
+ removeBlockedUrl: PropTypes.func.isRequired,
+ toggleBlockingEnabled: PropTypes.func.isRequired,
+ toggleBlockedUrl: PropTypes.func.isRequired,
+ updateBlockedUrl: PropTypes.func.isRequired,
+ removeAllBlockedUrls: PropTypes.func.isRequired,
+ disableAllBlockedUrls: PropTypes.func.isRequired,
+ enableAllBlockedUrls: PropTypes.func.isRequired,
+ blockingEnabled: PropTypes.bool.isRequired,
+ };
+ }
+
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ editingUrl: null,
+ };
+ }
+
+ componentDidMount() {
+ this.refs.addInput.focus();
+ }
+
+ componentDidUpdate(prevProps) {
+ if (this.state.editingUrl) {
+ this.refs.editInput.focus();
+ this.refs.editInput.select();
+ } else if (this.props.isDisplaying && !prevProps.isDisplaying) {
+ this.refs.addInput.focus();
+ }
+ }
+
+ componentWillUnmount() {
+ if (this.scrollToBottomTimeout) {
+ clearTimeout(this.scrollToBottomTimeout);
+ }
+ }
+
+ scrollToBottom() {
+ if (this.scrollToBottomTimeout) {
+ clearTimeout(this.scrollToBottomTimeout);
+ }
+ this.scrollToBottomTimeout = setTimeout(() => {
+ const { contents } = this.refs;
+ if (contents.scrollHeight > contents.offsetHeight) {
+ contents.scrollTo({ top: contents.scrollHeight });
+ }
+ }, 40);
+ }
+
+ renderEnableBar() {
+ return div(
+ { className: "request-blocking-enable-bar" },
+ div(
+ { className: "request-blocking-enable-form" },
+ label(
+ { className: "devtools-checkbox-label" },
+ input({
+ type: "checkbox",
+ className: "devtools-checkbox",
+ checked: this.props.blockingEnabled,
+ ref: "enabledCheckbox",
+ onChange: () =>
+ this.props.toggleBlockingEnabled(
+ this.refs.enabledCheckbox.checked
+ ),
+ }),
+ span({ className: "request-blocking-label" }, ENABLE_BLOCKING_LABEL)
+ )
+ )
+ );
+ }
+
+ renderItemContent({ url, enabled }) {
+ const { toggleBlockedUrl, removeBlockedUrl } = this.props;
+
+ return li(
+ { key: url },
+ label(
+ {
+ className: "devtools-checkbox-label",
+ onDoubleClick: () => this.setState({ editingUrl: url }),
+ },
+ input({
+ type: "checkbox",
+ className: "devtools-checkbox",
+ checked: enabled,
+ onChange: () => toggleBlockedUrl(url),
+ }),
+ span(
+ {
+ className: "request-blocking-label request-blocking-editable-label",
+ title: url,
+ },
+ url
+ )
+ ),
+ button({
+ className: "request-blocking-remove-button",
+ title: REMOVE_URL_TOOLTIP,
+ "aria-label": REMOVE_URL_TOOLTIP,
+ onClick: () => removeBlockedUrl(url),
+ })
+ );
+ }
+
+ renderEditForm(url) {
+ const { updateBlockedUrl, removeBlockedUrl } = this.props;
+ return li(
+ { key: url, className: "request-blocking-edit-item" },
+ form(
+ {
+ onSubmit: e => {
+ const { editInput } = this.refs;
+ const newValue = editInput.value;
+ e.preventDefault();
+
+ if (url != newValue) {
+ if (editInput.value.trim() === "") {
+ removeBlockedUrl(url, newValue);
+ } else {
+ updateBlockedUrl(url, newValue);
+ }
+ }
+ this.setState({ editingUrl: null });
+ },
+ },
+ input({
+ type: "text",
+ defaultValue: url,
+ ref: "editInput",
+ className: "devtools-searchinput",
+ placeholder: ADD_URL_PLACEHOLDER,
+ onBlur: () => this.setState({ editingUrl: null }),
+ onKeyDown: e => {
+ if (e.key === "Escape") {
+ e.stopPropagation();
+ e.preventDefault();
+ this.setState({ editingUrl: null });
+ }
+ },
+ }),
+
+ input({ type: "submit", style: { display: "none" } })
+ )
+ );
+ }
+
+ renderBlockedList() {
+ const {
+ blockedUrls,
+ blockingEnabled,
+ removeAllBlockedUrls,
+ disableAllBlockedUrls,
+ enableAllBlockedUrls,
+ } = this.props;
+
+ if (blockedUrls.length === 0) {
+ return null;
+ }
+
+ const listItems = blockedUrls.map(item =>
+ this.state.editingUrl === item.url
+ ? this.renderEditForm(item.url)
+ : this.renderItemContent(item)
+ );
+
+ return div(
+ {
+ className: "request-blocking-contents",
+ ref: "contents",
+ onContextMenu: event => {
+ if (!this.contextMenu) {
+ this.contextMenu = new RequestBlockingContextMenu({
+ removeAllBlockedUrls,
+ disableAllBlockedUrls,
+ enableAllBlockedUrls,
+ });
+ }
+
+ const contextMenuOptions = {
+ disableDisableAllBlockedUrls: blockedUrls.every(
+ ({ enabled }) => enabled === false
+ ),
+ disableEnableAllBlockedUrls: blockedUrls.every(
+ ({ enabled }) => enabled === true
+ ),
+ };
+
+ this.contextMenu.open(event, contextMenuOptions);
+ },
+ },
+ ul(
+ {
+ className: `request-blocking-list ${
+ blockingEnabled ? "" : "disabled"
+ }`,
+ },
+ ...listItems
+ )
+ );
+ }
+
+ renderAddForm() {
+ const { addBlockedUrl } = this.props;
+ return div(
+ { className: "request-blocking-footer" },
+ form(
+ {
+ className: "request-blocking-add-form",
+ onSubmit: e => {
+ const { addInput } = this.refs;
+ e.preventDefault();
+ addBlockedUrl(addInput.value);
+ addInput.value = "";
+ addInput.focus();
+ this.scrollToBottom();
+ },
+ },
+ input({
+ type: "text",
+ ref: "addInput",
+ className: "devtools-searchinput",
+ placeholder: ADD_URL_PLACEHOLDER,
+ onKeyDown: e => {
+ if (e.key === "Escape") {
+ e.stopPropagation();
+ e.preventDefault();
+
+ const { addInput } = this.refs;
+ addInput.value = "";
+ addInput.focus();
+ }
+ },
+ }),
+ input({ type: "submit", style: { display: "none" } })
+ )
+ );
+ }
+
+ renderEmptyListNotice() {
+ return div(
+ { className: "request-blocking-list-empty-notice" },
+ div(
+ { className: "request-blocking-notice-element" },
+ REQUEST_BLOCKING_USAGE_NOTICE
+ ),
+ div(
+ { className: "request-blocking-notice-element" },
+ REQUEST_BLOCKING_ADD_NOTICE
+ )
+ );
+ }
+
+ render() {
+ const { blockedUrls, addBlockedUrl } = this.props;
+
+ return div(
+ {
+ className: "request-blocking-panel",
+ onDragOver: e => {
+ e.preventDefault();
+ },
+ onDrop: e => {
+ e.preventDefault();
+ const url = e.dataTransfer.getData("text/plain");
+ addBlockedUrl(url);
+ this.scrollToBottom();
+ },
+ },
+ this.renderEnableBar(),
+ this.renderBlockedList(),
+ this.renderAddForm(),
+ !blockedUrls.length && this.renderEmptyListNotice()
+ );
+ }
+}
+
+module.exports = connect(
+ state => ({
+ blockedUrls: state.requestBlocking.blockedUrls,
+ blockingEnabled: state.requestBlocking.blockingEnabled,
+ isDisplaying: state.ui.selectedActionBarTabId === PANELS.BLOCKING,
+ }),
+ dispatch => ({
+ toggleBlockingEnabled: checked =>
+ dispatch(Actions.toggleBlockingEnabled(checked)),
+ addBlockedUrl: url => dispatch(Actions.addBlockedUrl(url)),
+ removeBlockedUrl: url => dispatch(Actions.removeBlockedUrl(url)),
+ toggleBlockedUrl: url => dispatch(Actions.toggleBlockedUrl(url)),
+ removeAllBlockedUrls: () => dispatch(Actions.removeAllBlockedUrls()),
+ enableAllBlockedUrls: () => dispatch(Actions.enableAllBlockedUrls()),
+ disableAllBlockedUrls: () => dispatch(Actions.disableAllBlockedUrls()),
+ updateBlockedUrl: (oldUrl, newUrl) =>
+ dispatch(Actions.updateBlockedUrl(oldUrl, newUrl)),
+ })
+)(RequestBlockingPanel);