syncBreakpoint.js (4627B)
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 import { setBreakpointPositions } from "./breakpointPositions"; 6 import { 7 findPosition, 8 makeBreakpointServerLocation, 9 } from "../../utils/breakpoint/index"; 10 11 import { comparePosition, createLocation } from "../../utils/location"; 12 13 import { addBreakpoint, removeBreakpointAtGeneratedLocation } from "./modify"; 14 15 async function findBreakpointPosition({ dispatch }, location) { 16 const positions = await dispatch(setBreakpointPositions(location)); 17 18 const position = findPosition(positions, location); 19 return position; 20 } 21 22 // Breakpoint syncing occurs when a source is found that matches either the 23 // original or generated URL of a pending breakpoint. A new breakpoint is 24 // constructed that might have a different original and/or generated location, 25 // if the original source has changed since the pending breakpoint was created. 26 // There are a couple subtle aspects to syncing: 27 // 28 // - We handle both the original and generated source because there is no 29 // guarantee that seeing the generated source means we will also see the 30 // original source. When connecting, a breakpoint will be installed in the 31 // client for the generated location in the pending breakpoint, and we need 32 // to make sure that either a breakpoint is added to the reducer or that this 33 // client breakpoint is deleted. 34 // 35 // - If we see both the original and generated sources and the source mapping 36 // has changed, we need to make sure that only a single breakpoint is added 37 // to the reducer for the new location corresponding to the original location 38 // in the pending breakpoint. 39 export function syncPendingBreakpoint(source, pendingBreakpoint) { 40 return async thunkArgs => { 41 const { getState, client, dispatch } = thunkArgs; 42 43 const generatedSource = source.isOriginal ? source.generatedSource : source; 44 45 if (!source || !generatedSource) { 46 return null; 47 } 48 49 // /!\ Pending breakpoint locations come only with sourceUrl, line and column attributes. 50 // We have to map it to a specific source object and avoid trying to query its non-existent 'source' attribute. 51 const { location, generatedLocation } = pendingBreakpoint; 52 const isPendingBreakpointWithSourceMap = 53 location.sourceUrl != generatedLocation.sourceUrl; 54 const sourceGeneratedLocation = createLocation({ 55 ...generatedLocation, 56 source: generatedSource, 57 }); 58 59 if (source == generatedSource && isPendingBreakpointWithSourceMap) { 60 // We are handling the generated source and the pending breakpoint has a 61 // source mapping. Supply a cancellation callback that will abort the 62 // breakpoint if the original source was synced to a different location, 63 // in which case the client breakpoint has been removed. 64 const breakpointServerLocation = makeBreakpointServerLocation( 65 getState(), 66 sourceGeneratedLocation 67 ); 68 return dispatch( 69 addBreakpoint( 70 sourceGeneratedLocation, 71 pendingBreakpoint.options, 72 pendingBreakpoint.disabled, 73 () => !client.hasBreakpoint(breakpointServerLocation) 74 ) 75 ); 76 } 77 78 const originalLocation = createLocation({ 79 ...location, 80 source, 81 }); 82 83 const newPosition = await findBreakpointPosition( 84 thunkArgs, 85 originalLocation 86 ); 87 88 const newGeneratedLocation = newPosition?.generatedLocation; 89 if (!newGeneratedLocation) { 90 // We couldn't find a new mapping for the breakpoint. If there is a source 91 // mapping, remove any breakpoints for the generated location, as if the 92 // breakpoint moved. If the old generated location still maps to an 93 // original location then we don't want to add a breakpoint for it. 94 if (isPendingBreakpointWithSourceMap) { 95 dispatch(removeBreakpointAtGeneratedLocation(sourceGeneratedLocation)); 96 } 97 return null; 98 } 99 100 const isSameLocation = comparePosition( 101 generatedLocation, 102 newGeneratedLocation 103 ); 104 105 // If the new generated location has changed from that in the pending 106 // breakpoint, remove any breakpoint associated with the old generated 107 // location. 108 if (!isSameLocation) { 109 dispatch(removeBreakpointAtGeneratedLocation(sourceGeneratedLocation)); 110 } 111 112 return dispatch( 113 addBreakpoint( 114 newGeneratedLocation, 115 pendingBreakpoint.options, 116 pendingBreakpoint.disabled 117 ) 118 ); 119 }; 120 }