/* 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 actionTypes = {
  ADD_REQUEST: "ADD_REQUEST",
  SET_EVENT_STREAM_FLAG: "SET_EVENT_STREAM_FLAG",
  ADD_TIMING_MARKER: "ADD_TIMING_MARKER",
  ADD_BLOCKED_URL: "ADD_BLOCKED_URL",
  BATCH_ACTIONS: "BATCH_ACTIONS",
  BATCH_ENABLE: "BATCH_ENABLE",
  BATCH_FLUSH: "BATCH_FLUSH",
  BLOCK_SELECTED_REQUEST_DONE: "BLOCK_SELECTED_REQUEST_DONE",
  CLEAR_REQUESTS: "CLEAR_REQUESTS",
  CLEAR_TIMING_MARKERS: "CLEAR_TIMING_MARKERS",
  CLONE_REQUEST: "CLONE_REQUEST",
  CLONE_SELECTED_REQUEST: "CLONE_SELECTED_REQUEST",
  ENABLE_REQUEST_FILTER_TYPE_ONLY: "ENABLE_REQUEST_FILTER_TYPE_ONLY",
  OPEN_NETWORK_DETAILS: "OPEN_NETWORK_DETAILS",
  OPEN_ACTION_BAR: "OPEN_ACTION_BAR",
  RESIZE_NETWORK_DETAILS: "RESIZE_NETWORK_DETAILS",
  RIGHT_CLICK_REQUEST: "RIGHT_CLICK_REQUEST",
  ENABLE_PERSISTENT_LOGS: "ENABLE_PERSISTENT_LOGS",
  DISABLE_BROWSER_CACHE: "DISABLE_BROWSER_CACHE",
  OPEN_STATISTICS: "OPEN_STATISTICS",
  PERSIST_CHANGED: "PERSIST_CHANGED",
  PRESELECT_REQUEST: "PRESELECT_REQUEST",
  REMOVE_SELECTED_CUSTOM_REQUEST: "REMOVE_SELECTED_CUSTOM_REQUEST",
  RESET_COLUMNS: "RESET_COLUMNS",
  SELECT_REQUEST: "SELECT_REQUEST",
  SELECT_DETAILS_PANEL_TAB: "SELECT_DETAILS_PANEL_TAB",
  SELECT_ACTION_BAR_TAB: "SELECT_ACTION_BAR_TAB",
  SEND_CUSTOM_REQUEST: "SEND_CUSTOM_REQUEST",
  SET_REQUEST_FILTER_TEXT: "SET_REQUEST_FILTER_TEXT",
  SORT_BY: "SORT_BY",
  SYNCED_BLOCKED_URLS: "SYNCED_BLOCKED_URLS",
  TOGGLE_BLOCKING_ENABLED: "TOGGLE_BLOCKING_ENABLED",
  REMOVE_BLOCKED_URL: "REMOVE_BLOCKED_URL",
  REMOVE_ALL_BLOCKED_URLS: "REMOVE_ALL_BLOCKED_URLS",
  ENABLE_ALL_BLOCKED_URLS: "ENABLE_ALL_BLOCKED_URLS",
  DISABLE_ALL_BLOCKED_URLS: "DISABLE_ALL_BLOCKED_URLS",
  TOGGLE_BLOCKED_URL: "TOGGLE_BLOCKED_URL",
  UPDATE_BLOCKED_URL: "UPDATE_BLOCKED_URL",
  DISABLE_MATCHING_URLS: "DISABLE_MATCHING_URLS",
  REQUEST_BLOCKING_UPDATE_COMPLETE: "REQUEST_BLOCKING_UPDATE_COMPLETE",
  TOGGLE_COLUMN: "TOGGLE_COLUMN",
  SET_RECORDING_STATE: "SET_RECORDING_STATE",
  TOGGLE_REQUEST_FILTER_TYPE: "TOGGLE_REQUEST_FILTER_TYPE",
  UNBLOCK_SELECTED_REQUEST_DONE: "UNBLOCK_SELECTED_REQUEST_DONE",
  UPDATE_REQUEST: "UPDATE_REQUEST",
  WATERFALL_RESIZE: "WATERFALL_RESIZE",
  SET_COLUMNS_WIDTH: "SET_COLUMNS_WIDTH",
  MSG_ADD: "MSG_ADD",
  MSG_SELECT: "MSG_SELECT",
  MSG_OPEN_DETAILS: "MSG_OPEN_DETAILS",
  MSG_CLEAR: "MSG_CLEAR",
  MSG_TOGGLE_FILTER_TYPE: "MSG_TOGGLE_FILTER_TYPE",
  MSG_TOGGLE_CONTROL: "MSG_TOGGLE_CONTROL",
  MSG_SET_FILTER_TEXT: "MSG_SET_FILTER_TEXT",
  MSG_TOGGLE_COLUMN: "MSG_TOGGLE_COLUMN",
  MSG_RESET_COLUMNS: "MSG_RESET_COLUMNS",
  MSG_CLOSE_CONNECTION: "MSG_CLOSE_CONNECTION",
  SET_HEADERS_URL_PREVIEW_EXPANDED: "SET_HEADERS_URL_PREVIEW_EXPANDED",
  SET_DEFAULT_RAW_RESPONSE: "SET_DEFAULT_RAW_RESPONSE",

  // Search
  ADD_SEARCH_QUERY: "ADD_SEARCH_QUERY",
  ADD_SEARCH_RESULT: "ADD_SEARCH_RESULT",
  CLEAR_SEARCH_RESULTS: "CLEAR_SEARCH_RESULTS",
  ADD_ONGOING_SEARCH: "ADD_ONGOING_SEARCH",
  TOGGLE_SEARCH_CASE_SENSITIVE_SEARCH: "TOGGLE_SEARCH_CASE_SENSITIVE_SEARCH",
  UPDATE_SEARCH_STATUS: "UPDATE_SEARCH_STATUS",
  SET_TARGET_SEARCH_RESULT: "SET_TARGET_SEARCH_RESULT",
};

// Search status types
const SEARCH_STATUS = {
  INITIAL: "INITIAL",
  FETCHING: "FETCHING",
  CANCELED: "CANCELED",
  DONE: "DONE",
  ERROR: "ERROR",
};

const CHANNEL_TYPE = {
  WEB_SOCKET: "WEB_SOCKET",
  EVENT_STREAM: "EVENT_STREAM",
};

// Descriptions for what this frontend is currently doing.
const ACTIVITY_TYPE = {
  // Standing by and handling requests normally.
  NONE: 0,

  // Forcing the target to reload with cache enabled or disabled.
  RELOAD: {
    WITH_CACHE_ENABLED: 1,
    WITH_CACHE_DISABLED: 2,
    WITH_CACHE_DEFAULT: 3,
  },

  // Enabling or disabling the cache without triggering a reload.
  ENABLE_CACHE: 3,
  DISABLE_CACHE: 4,
};

// The panel's window global is an EventEmitter firing the following events:
const EVENTS = {
  // When a network event is added to the view
  REQUEST_ADDED: "NetMonitor:RequestAdded",

  // When request headers begin receiving.
  UPDATING_REQUEST_HEADERS: "NetMonitor:NetworkEventUpdating:RequestHeaders",

  // When request cookies begin receiving.
  UPDATING_REQUEST_COOKIES: "NetMonitor:NetworkEventUpdating:RequestCookies",

  // When request post data begins receiving.
  UPDATING_REQUEST_POST_DATA: "NetMonitor:NetworkEventUpdating:RequestPostData",

  // When security information begins receiving.
  UPDATING_SECURITY_INFO: "NetMonitor:NetworkEventUpdating:SecurityInfo",

  // When response headers begin receiving.
  UPDATING_RESPONSE_HEADERS: "NetMonitor:NetworkEventUpdating:ResponseHeaders",

  // When response cookies begin receiving.
  UPDATING_RESPONSE_COOKIES: "NetMonitor:NetworkEventUpdating:ResponseCookies",

  // When event timings begin and finish receiving.
  UPDATING_EVENT_TIMINGS: "NetMonitor:NetworkEventUpdating:EventTimings",
  RECEIVED_EVENT_TIMINGS: "NetMonitor:NetworkEventUpdated:EventTimings",

  // When response content updates receiving.
  UPDATING_RESPONSE_CONTENT: "NetMonitor:NetworkEventUpdating:ResponseContent",

  UPDATING_RESPONSE_CACHE: "NetMonitor:NetworkEventUpdating:ResponseCache",

  // Fired once the connection is established
  CONNECTED: "connected",

  // When request payload (HTTP details data) are fetched from the backend.
  PAYLOAD_READY: "NetMonitor:PayloadReady",
};

const TEST_EVENTS = {
  // When a network or timeline event is received.
  // See https://firefox-source-docs.mozilla.org/devtools-user/web_console/remoting/ for
  // more information about what each packet is supposed to deliver.
  NETWORK_EVENT: "NetMonitor:NetworkEvent",
  NETWORK_EVENT_UPDATED: "NetMonitor:NetworkEventUpdated",
  TIMELINE_EVENT: "NetMonitor:TimelineEvent",

  // When response content begins receiving.
  STARTED_RECEIVING_RESPONSE: "NetMonitor:NetworkEventUpdating:ResponseStart",

  // When request headers finish receiving.
  RECEIVED_REQUEST_HEADERS: "NetMonitor:NetworkEventUpdated:RequestHeaders",

  // When early hints response headers finish receiving.
  RECEIVED_EARLY_HINTS_RESPONSE_HEADERS:
    "NetMonitor:NetworkEventUpdated:EarlyHintsResponseHeaders",

  // When response headers finish receiving.
  RECEIVED_RESPONSE_HEADERS: "NetMonitor:NetworkEventUpdated:ResponseHeaders",

  // When request cookies finish receiving.
  RECEIVED_REQUEST_COOKIES: "NetMonitor:NetworkEventUpdated:RequestCookies",

  // When request post data finishes receiving.
  RECEIVED_REQUEST_POST_DATA: "NetMonitor:NetworkEventUpdated:RequestPostData",

  // When security information finishes receiving.
  RECEIVED_SECURITY_INFO: "NetMonitor:NetworkEventUpdated:SecurityInfo",

  // When response cookies finish receiving.
  RECEIVED_RESPONSE_COOKIES: "NetMonitor:NetworkEventUpdated:ResponseCookies",

  RECEIVED_RESPONSE_CACHE: "NetMonitor:NetworkEventUpdated:ResponseCache",

  // When response content finishes receiving.
  RECEIVED_RESPONSE_CONTENT: "NetMonitor:NetworkEventUpdated:ResponseContent",

  // When stack-trace finishes receiving.
  RECEIVED_EVENT_STACKTRACE: "NetMonitor:NetworkEventUpdated:StackTrace",

  // When throttling is set on the backend.
  THROTTLING_CHANGED: "NetMonitor:ThrottlingChanged",

  // When a long string is resolved
  LONGSTRING_RESOLVED: "NetMonitor:LongStringResolved",
};

const UPDATE_PROPS = [
  "method",
  "url",
  "earlyHintsStatus",
  "remotePort",
  "remoteAddress",
  "status",
  "statusText",
  "httpVersion",
  // @backward-compat { version 151 } isRacing can be removed once 151 is on
  // release. It was part of RCWN which was removed in Bug 2020946.
  "isRacing",
  "securityState",
  "securityInfo",
  "securityInfoAvailable",
  "mimeType",
  "contentSize",
  "transferredSize",
  "totalTime",
  "eventTimings",
  "eventTimingsAvailable",
  "earlyHintsResponseHeaders",
  "earlyHintsResponseHeadersAvailable",
  "headersSize",
  "customQueryValue",
  "requestHeaders",
  "requestHeadersAvailable",
  "requestHeadersFromUploadStream",
  "requestCookies",
  "requestCookiesAvailable",
  "requestPostData",
  "requestPostDataAvailable",
  "responseHeaders",
  "responseHeadersAvailable",
  "responseCookies",
  "responseCookiesAvailable",
  "responseContent",
  "responseContentAvailable",
  "responseCache",
  "responseCacheAvailable",
  "formDataSections",
  "stacktrace",
  "isThirdPartyTrackingResource",
  "isResolvedByTRR",
  "isRedirect",
  "referrerPolicy",
  "priority",
  "blockedReason",
  "extension",
  "channelId",
  "waitingTime",
  "proxyHttpVersion",
  "proxyStatus",
  "proxyStatusText",
  "fromCache",
  "fromServiceWorker",
  "securityFlags",
];

const PANELS = {
  COOKIES: "cookies",
  HEADERS: "headers",
  MESSAGES: "messages",
  REQUEST: "request",
  RESPONSE: "response",
  CACHE: "cache",
  SECURITY: "security",
  STACK_TRACE: "stack-trace",
  TIMINGS: "timings",
  HTTP_CUSTOM_REQUEST: "network-action-bar-HTTP-custom-request",
  SEARCH: "network-action-bar-search",
  BLOCKING: "network-action-bar-blocked",
};

const RESPONSE_HEADERS = [
  "Cache-Control",
  "Connection",
  "Content-Encoding",
  "Content-Length",
  "ETag",
  "Keep-Alive",
  "Last-Modified",
  "Server",
  "Vary",
];

const HEADERS = [
  {
    name: "override",
    canFilter: false,
  },
  {
    name: "status",
    label: "status3",
    canFilter: true,
    filterKey: "status-code",
  },
  {
    name: "method",
    canFilter: true,
  },
  {
    name: "domain",
    canFilter: true,
  },
  {
    name: "file",
    canFilter: false,
  },
  {
    name: "path",
    canFilter: false,
  },
  {
    name: "url",
    canFilter: true,
  },
  {
    name: "protocol",
    canFilter: true,
  },
  {
    name: "scheme",
    canFilter: true,
  },
  {
    name: "remoteip",
    canFilter: true,
    filterKey: "remote-ip",
  },
  {
    name: "cause",
    canFilter: true,
  },
  {
    name: "initiator",
    canFilter: true,
  },
  {
    name: "type",
    canFilter: false,
  },
  {
    name: "cookies",
    canFilter: false,
  },
  {
    name: "setCookies",
    boxName: "set-cookies",
    canFilter: false,
  },
  {
    name: "transferred",
    canFilter: true,
  },
  {
    name: "contentSize",
    boxName: "size",
    filterKey: "size",
    canFilter: true,
  },
  {
    name: "priority",
    boxName: "priority",
    canFilter: true,
  },
  {
    name: "startTime",
    boxName: "start-time",
    canFilter: false,
    subMenu: "timings",
  },
  {
    name: "endTime",
    boxName: "end-time",
    canFilter: false,
    subMenu: "timings",
  },
  {
    name: "responseTime",
    boxName: "response-time",
    canFilter: false,
    subMenu: "timings",
  },
  {
    name: "duration",
    canFilter: false,
    subMenu: "timings",
  },
  {
    name: "latency",
    canFilter: false,
    subMenu: "timings",
  },
  ...RESPONSE_HEADERS.map(header => ({
    name: header,
    boxName: "response-header",
    canFilter: false,
    subMenu: "responseHeaders",
    noLocalization: true,
  })),
  {
    name: "waterfall",
    canFilter: false,
  },
];

const HEADER_FILTERS = HEADERS.filter(h => h.canFilter).map(
  h => h.filterKey || h.name
);

const FILTER_FLAGS = [
  ...HEADER_FILTERS,
  "set-cookie-domain",
  "set-cookie-name",
  "set-cookie-value",
  "mime-type",
  "larger-than",
  "transferred-larger-than",
  "is",
  "has-response-header",
  "regexp",
];

const FILTER_TAGS = [
  "html",
  "css",
  "js",
  "xhr",
  "fonts",
  "images",
  "media",
  "ws",
  "other",
];

const MESSAGE_HEADERS = [
  {
    name: "data",
    width: "40%",
  },
  {
    name: "size",
    width: "12%",
  },
  {
    name: "opCode",
    width: "9%",
  },
  {
    name: "maskBit",
    width: "9%",
  },
  {
    name: "finBit",
    width: "9%",
  },
  {
    name: "time",
    width: "20%",
  },
  {
    name: "eventName",
    width: "9%",
  },
  {
    name: "lastEventId",
    width: "9%",
  },
  {
    name: "retry",
    width: "9%",
  },
];

const REQUESTS_WATERFALL = {
  BACKGROUND_TICKS_MULTIPLE: 5, // ms
  BACKGROUND_TICKS_SCALES: 3,
  BACKGROUND_TICKS_SPACING_MIN: 10, // px
  BACKGROUND_TICKS_COLOR_RGB: [128, 136, 144],
  // 8-bit value of the alpha component of the tick color
  BACKGROUND_TICKS_OPACITY_MIN: 32,
  BACKGROUND_TICKS_OPACITY_ADD: 32,
  // Colors for timing markers (theme colors, see variables.css)
  DOMCONTENTLOADED_TICKS_COLOR: "--timing-marker-dom-content-loaded-color",
  LOAD_TICKS_COLOR: "--timing-marker-load-color",

  HEADER_TICKS_MULTIPLE: 5, // ms
  HEADER_TICKS_SPACING_MIN: 60, // px
  // Reserve extra space for rendering waterfall time label
  LABEL_WIDTH: 50, // px
};

const TIMING_KEYS = [
  "blocked",
  "dns",
  "connect",
  "ssl",
  "send",
  "wait",
  "receive",
];

// Minimal width of Network Monitor column is 30px, for Waterfall 150px
// Default width of columns (which are not defined in DEFAULT_COLUMNS_DATA) is 8%
const MIN_COLUMN_WIDTH = 30; // in px
const DEFAULT_COLUMN_WIDTH = 8; // in %
/**
 * A mapping of HTTP status codes.
 */
const SUPPORTED_HTTP_CODES = [
  "100",
  "101",
  "103",
  "200",
  "201",
  "202",
  "203",
  "204",
  "205",
  "206",
  "300",
  "301",
  "302",
  "303",
  "304",
  "307",
  "308",
  "400",
  "401",
  "403",
  "404",
  "405",
  "406",
  "407",
  "408",
  "409",
  "410",
  "411",
  "412",
  "413",
  "414",
  "415",
  "416",
  "417",
  "418",
  "422",
  "425",
  "426",
  "428",
  "429",
  "431",
  "451",
  "500",
  "501",
  "502",
  "503",
  "504",
  "505",
  "511",
];

// Keys are the codes provided by server, values are localization messages
// prefixed by "netmonitor.blocked."
const BLOCKED_REASON_MESSAGES = {
  devtools: "Blocked by DevTools",
  1001: "CORS disabled",
  1002: "CORS Failed",
  1003: "CORS Not HTTP",
  1004: "CORS Multiple Origin Not Allowed",
  1005: "CORS Missing Allow Origin",
  1006: "CORS No Allow Credentials",
  1007: "CORS Allow Origin Not Matching Origin",
  1008: "CORS Missing Allow Credentials",
  1009: "CORS Origin Header Missing",
  1010: "CORS External Redirect Not Allowed",
  1011: "CORS Preflight Did Not Succeed",
  1012: "CORS Invalid Allow Method",
  1013: "CORS Method Not Found",
  1014: "CORS Invalid Allow Header",
  1015: "CORS Missing Allow Header",
  2001: "Malware",
  2002: "Phishing",
  2003: "Unwanted",
  2004: "Tracking",
  2005: "Blocked",
  2006: "Harmful",
  2007: "Cryptomining",
  2008: "Fingerprinting",
  2009: "Socialtracking",
  2011: "Harmful Add-on Blocked",
  3001: "Mixed Block",
  4000: "CSP",
  4001: "CSP No Data Protocol",
  4002: "CSP Web Extension",
  4003: "CSP Content Blocked",
  4004: "CSP Data Document",
  4005: "CSP Web Browser",
  4006: "CSP Preload",
  5000: "Not same-origin",
  6000: "Blocked By Extension",
};

/** @see {@link https://searchfox.org/mozilla-central/rev/d7a8eadc28298c31381119cbf25c8ba14b8712b3/netwerk/protocol/websocket/nsIWebSocketEventService.idl#30-38} */
const WEB_SOCKET_OPCODE = {
  CONTINUATION: 0,
  TEXT: 1,
  BINARY: 2,
  CLOSE: 8,
  PING: 9,
  PONG: 10,
};

const general = {
  ACTIVITY_TYPE,
  EVENTS,
  TEST_EVENTS,
  FILTER_SEARCH_DELAY: 200,
  UPDATE_PROPS,
  HEADERS,
  MESSAGE_HEADERS,
  RESPONSE_HEADERS,
  FILTER_FLAGS,
  FILTER_TAGS,
  REQUESTS_WATERFALL,
  PANELS,
  TIMING_KEYS,
  MIN_COLUMN_WIDTH,
  DEFAULT_COLUMN_WIDTH,
  SUPPORTED_HTTP_CODES,
  BLOCKED_REASON_MESSAGES,
  SEARCH_STATUS,
  AUTO_EXPAND_MAX_LEVEL: 7,
  AUTO_EXPAND_MAX_NODES: 50,
  CHANNEL_TYPE,
  WEB_SOCKET_OPCODE,
  // Arbitrary limit for rendering strings in the netmonitor table.
  // Useful for instance for very big data URIs.
  MAX_UI_STRING_LENGTH: 10000,
};

// flatten constants
module.exports = Object.assign({}, general, actionTypes);
