compatibility.js (8504B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 "use strict"; 6 7 const { 8 COMPATIBILITY_APPEND_NODE_FAILURE, 9 COMPATIBILITY_CLEAR_DESTROYED_NODES, 10 COMPATIBILITY_INIT_USER_SETTINGS_SUCCESS, 11 COMPATIBILITY_INIT_USER_SETTINGS_FAILURE, 12 COMPATIBILITY_INTERNAL_APPEND_NODE, 13 COMPATIBILITY_INTERNAL_NODE_UPDATE, 14 COMPATIBILITY_INTERNAL_REMOVE_NODE, 15 COMPATIBILITY_INTERNAL_UPDATE_SELECTED_NODE_ISSUES, 16 COMPATIBILITY_REMOVE_NODE_FAILURE, 17 COMPATIBILITY_UPDATE_NODE_FAILURE, 18 COMPATIBILITY_UPDATE_NODES_FAILURE, 19 COMPATIBILITY_UPDATE_SELECTED_NODE_SUCCESS, 20 COMPATIBILITY_UPDATE_SELECTED_NODE_FAILURE, 21 COMPATIBILITY_UPDATE_SETTINGS_VISIBILITY, 22 COMPATIBILITY_UPDATE_TARGET_BROWSERS_START, 23 COMPATIBILITY_UPDATE_TARGET_BROWSERS_SUCCESS, 24 COMPATIBILITY_UPDATE_TARGET_BROWSERS_FAILURE, 25 COMPATIBILITY_UPDATE_TARGET_BROWSERS_COMPLETE, 26 COMPATIBILITY_UPDATE_TOP_LEVEL_TARGET_START, 27 COMPATIBILITY_UPDATE_TOP_LEVEL_TARGET_SUCCESS, 28 COMPATIBILITY_UPDATE_TOP_LEVEL_TARGET_FAILURE, 29 COMPATIBILITY_UPDATE_TOP_LEVEL_TARGET_COMPLETE, 30 } = require("resource://devtools/client/inspector/compatibility/actions/index.js"); 31 32 const INITIAL_STATE = { 33 defaultTargetBrowsers: [], 34 isSettingsVisible: false, 35 isTopLevelTargetProcessing: false, 36 selectedNode: null, 37 selectedNodeIssues: [], 38 topLevelTarget: null, 39 topLevelTargetIssues: [], 40 targetBrowsers: [], 41 }; 42 43 const reducers = { 44 [COMPATIBILITY_APPEND_NODE_FAILURE](state, { error }) { 45 _showError(COMPATIBILITY_APPEND_NODE_FAILURE, error); 46 return state; 47 }, 48 [COMPATIBILITY_CLEAR_DESTROYED_NODES](state) { 49 const topLevelTargetIssues = _clearDestroyedNodes( 50 state.topLevelTargetIssues 51 ); 52 return Object.assign({}, state, { topLevelTargetIssues }); 53 }, 54 [COMPATIBILITY_INIT_USER_SETTINGS_SUCCESS]( 55 state, 56 { defaultTargetBrowsers, targetBrowsers } 57 ) { 58 return Object.assign({}, state, { defaultTargetBrowsers, targetBrowsers }); 59 }, 60 [COMPATIBILITY_INIT_USER_SETTINGS_FAILURE](state, { error }) { 61 _showError(COMPATIBILITY_INIT_USER_SETTINGS_FAILURE, error); 62 return state; 63 }, 64 [COMPATIBILITY_INTERNAL_APPEND_NODE](state, { node, issues }) { 65 const topLevelTargetIssues = _appendTopLevelTargetIssues( 66 state.topLevelTargetIssues, 67 node, 68 issues 69 ); 70 return Object.assign({}, state, { topLevelTargetIssues }); 71 }, 72 [COMPATIBILITY_INTERNAL_NODE_UPDATE](state, { node, issues }) { 73 const topLevelTargetIssues = _updateTopLevelTargetIssues( 74 state.topLevelTargetIssues, 75 node, 76 issues 77 ); 78 return Object.assign({}, state, { topLevelTargetIssues }); 79 }, 80 [COMPATIBILITY_INTERNAL_REMOVE_NODE](state, { node }) { 81 const topLevelTargetIssues = _removeNodeOrIssues( 82 state.topLevelTargetIssues, 83 node, 84 [] 85 ); 86 return Object.assign({}, state, { topLevelTargetIssues }); 87 }, 88 [COMPATIBILITY_INTERNAL_UPDATE_SELECTED_NODE_ISSUES](state, { issues }) { 89 return Object.assign({}, state, { selectedNodeIssues: issues }); 90 }, 91 [COMPATIBILITY_REMOVE_NODE_FAILURE](state, { error }) { 92 _showError(COMPATIBILITY_UPDATE_NODES_FAILURE, error); 93 return state; 94 }, 95 [COMPATIBILITY_UPDATE_NODE_FAILURE](state, { error }) { 96 _showError(COMPATIBILITY_UPDATE_NODES_FAILURE, error); 97 return state; 98 }, 99 [COMPATIBILITY_UPDATE_NODES_FAILURE](state, { error }) { 100 _showError(COMPATIBILITY_UPDATE_NODES_FAILURE, error); 101 return state; 102 }, 103 [COMPATIBILITY_UPDATE_SELECTED_NODE_SUCCESS](state, { node }) { 104 return Object.assign({}, state, { selectedNode: node }); 105 }, 106 [COMPATIBILITY_UPDATE_SELECTED_NODE_FAILURE](state, { error }) { 107 _showError(COMPATIBILITY_UPDATE_SELECTED_NODE_FAILURE, error); 108 return state; 109 }, 110 [COMPATIBILITY_UPDATE_SETTINGS_VISIBILITY](state, { visibility }) { 111 return Object.assign({}, state, { isSettingsVisible: visibility }); 112 }, 113 [COMPATIBILITY_UPDATE_TARGET_BROWSERS_START](state) { 114 return Object.assign({}, state, { 115 isTopLevelTargetProcessing: true, 116 topLevelTargetIssues: [], 117 }); 118 }, 119 [COMPATIBILITY_UPDATE_TARGET_BROWSERS_SUCCESS](state, { targetBrowsers }) { 120 return Object.assign({}, state, { targetBrowsers }); 121 }, 122 [COMPATIBILITY_UPDATE_TARGET_BROWSERS_FAILURE](state, { error }) { 123 _showError(COMPATIBILITY_UPDATE_TARGET_BROWSERS_FAILURE, error); 124 return state; 125 }, 126 [COMPATIBILITY_UPDATE_TARGET_BROWSERS_COMPLETE](state) { 127 return Object.assign({}, state, { isTopLevelTargetProcessing: false }); 128 }, 129 [COMPATIBILITY_UPDATE_TOP_LEVEL_TARGET_START](state) { 130 return Object.assign({}, state, { 131 isTopLevelTargetProcessing: true, 132 topLevelTargetIssues: [], 133 }); 134 }, 135 [COMPATIBILITY_UPDATE_TOP_LEVEL_TARGET_SUCCESS](state, { target }) { 136 return Object.assign({}, state, { topLevelTarget: target }); 137 }, 138 [COMPATIBILITY_UPDATE_TOP_LEVEL_TARGET_FAILURE](state, { error }) { 139 _showError(COMPATIBILITY_UPDATE_TOP_LEVEL_TARGET_FAILURE, error); 140 return state; 141 }, 142 [COMPATIBILITY_UPDATE_TOP_LEVEL_TARGET_COMPLETE](state) { 143 return Object.assign({}, state, { isTopLevelTargetProcessing: false }); 144 }, 145 }; 146 147 function _appendTopLevelTargetIssues(targetIssues, node, issues) { 148 targetIssues = [...targetIssues]; 149 for (const issue of issues) { 150 const index = _indexOfIssue(targetIssues, issue); 151 if (index < 0) { 152 issue.nodes = [node]; 153 targetIssues.push(issue); 154 continue; 155 } 156 157 const targetIssue = targetIssues[index]; 158 const nodeIndex = targetIssue.nodes.findIndex( 159 issueNode => issueNode.actorID === node.actorID 160 ); 161 162 if (nodeIndex < 0) { 163 targetIssue.nodes = [...targetIssue.nodes, node]; 164 } 165 } 166 return targetIssues; 167 } 168 169 function _clearDestroyedNodes(targetIssues) { 170 return targetIssues.reduce((newIssues, targetIssue) => { 171 const retainedNodes = targetIssue.nodes.filter( 172 n => n.targetFront && !n.targetFront.isDestroyed() 173 ); 174 175 // All the nodes for a given issue are destroyed 176 if (retainedNodes.length === 0) { 177 // Remove issue. 178 return newIssues; 179 } 180 181 targetIssue.nodes = retainedNodes; 182 return [...newIssues, targetIssue]; 183 }, []); 184 } 185 186 function _indexOfIssue(issues, issue) { 187 return issues.findIndex( 188 i => i.type === issue.type && i.property === issue.property 189 ); 190 } 191 192 function _indexOfNode(issue, node) { 193 return issue.nodes.findIndex(n => n.actorID === node.actorID); 194 } 195 196 function _removeNodeOrIssues(targetIssues, node, issues) { 197 return targetIssues.reduce((newIssues, targetIssue) => { 198 if (issues.length && _indexOfIssue(issues, targetIssue) >= 0) { 199 // The targetIssue is still in the node. 200 return [...newIssues, targetIssue]; 201 } 202 203 const indexOfNodeInTarget = _indexOfNode(targetIssue, node); 204 if (indexOfNodeInTarget < 0) { 205 // The targetIssue does not have the node to remove. 206 return [...newIssues, targetIssue]; 207 } 208 209 // This issue on the updated node is gone. 210 if (targetIssue.nodes.length === 1) { 211 // Remove issue. 212 return newIssues; 213 } 214 215 // Remove node from the nodes. 216 targetIssue.nodes = [ 217 ...targetIssue.nodes.slice(0, indexOfNodeInTarget), 218 ...targetIssue.nodes.slice(indexOfNodeInTarget + 1), 219 ]; 220 return [...newIssues, targetIssue]; 221 }, []); 222 } 223 224 function _updateTopLevelTargetIssues(targetIssues, node, issues) { 225 // Remove issues or node. 226 targetIssues = _removeNodeOrIssues(targetIssues, node, issues); 227 228 // Append issues or node. 229 const appendables = issues.filter(issue => { 230 const indexOfIssue = _indexOfIssue(targetIssues, issue); 231 return ( 232 indexOfIssue < 0 || _indexOfNode(targetIssues[indexOfIssue], node) < 0 233 ); 234 }); 235 targetIssues = _appendTopLevelTargetIssues(targetIssues, node, appendables); 236 237 // Update fields. 238 for (const issue of issues) { 239 const indexOfIssue = _indexOfIssue(targetIssues, issue); 240 241 if (indexOfIssue < 0) { 242 continue; 243 } 244 245 const targetIssue = targetIssues[indexOfIssue]; 246 targetIssues[indexOfIssue] = Object.assign(issue, { 247 nodes: targetIssue.nodes, 248 }); 249 } 250 251 return targetIssues; 252 } 253 254 function _showError(action, error) { 255 console.error(`[${action}] ${error.message}`); 256 console.error(error.stack); 257 } 258 259 module.exports = function (state = INITIAL_STATE, action) { 260 const reducer = reducers[action.type]; 261 return reducer ? reducer(state, action) : state; 262 };