Skip to content

Commit

Permalink
Bug 1777203 - [devtools] Migrate SourceTree to be based on source act…
Browse files Browse the repository at this point in the history
…ors. r=bomsy

This doesn't change anything. It actually complexify things a bit.
But this will be an helpful change we can do right away in order to help
create a unique Source object per URL (instead of per URL and target).

Differential Revision: https://phabricator.services.mozilla.com/D151553
  • Loading branch information
ochameau committed Jul 26, 2022
1 parent a7a60ed commit 546c9e4
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 19 deletions.
7 changes: 6 additions & 1 deletion devtools/client/debugger/src/actions/sources/newSources.js
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,12 @@ export function newGeneratedSources(sourceResources) {
// We are sometimes notified about a new source multiple times if we
// request a new source list and also get a source event from the server.
if (!hasSourceActor(getState(), actorId)) {
newSourceActors.push(createSourceActor(sourceResource));
newSourceActors.push(
createSourceActor(
sourceResource,
getSource(getState(), id) || newSourcesObj[id]
)
);
}

resultIds.push(id);
Expand Down
5 changes: 4 additions & 1 deletion devtools/client/debugger/src/client/firefox/create.js
Original file line number Diff line number Diff line change
Expand Up @@ -311,8 +311,10 @@ export function createPrettyPrintOriginalSource(id, url, thread) {
* @param {SOURCE} sourceResource
* SOURCE resource coming from the ResourceCommand API.
* This represents the `SourceActor` from the server codebase.
* @param {Object} sourceObject
* Source object stored in redux, i.e. created via createSourceObject.
*/
export function createSourceActor(sourceResource) {
export function createSourceActor(sourceResource, sourceObject) {
const actorId = sourceResource.actor;

// As sourceResource is only SourceActor's form and not the SourceFront,
Expand All @@ -326,6 +328,7 @@ export function createSourceActor(sourceResource) {
thread: threadActorID,
// `source` is the reducer source ID
source: makeSourceId(sourceResource),
sourceObject,
sourceMapBaseURL: sourceResource.sourceMapBaseURL,
sourceMapURL: sourceResource.sourceMapURL,
url: sourceResource.url,
Expand Down
90 changes: 73 additions & 17 deletions devtools/client/debugger/src/reducers/sources-tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,26 +67,71 @@ export function initialSourcesTreeState() {

export default function update(state = initialSourcesTreeState(), action) {
switch (action.type) {
case "ADD_SOURCES":
const newSources = action.sources.filter(source =>
case "ADD_SOURCES": {
// With this action, we only process the original sources.
// That's because original sources don't have any direct source actor.
const newSources = action.sources.filter(
source =>
source.isOriginal &&
isSourceVisibleInSourceTree(
source,
state.chromeAndExtensionsEnabled,
state.isWebExtension
)
);
if (newSources.length == 0) {
return state;
}
let changed = false;
// Fork the array only once for all the sources
const threadItems = [...state.threadItems];
for (const source of newSources) {
changed |= addSource(threadItems, source.thread, source);
}
if (changed) {
return {
...state,
threadItems,
};
}
return state;
}
case "INSERT_SOURCE_ACTORS": {
// With this action, we only cover generated sources.
// (i.e. we need something else for sourcemapped/original sources)
// But we do want to process source actors in order to be able to display
// distinct Source Tree Items for sources with the same URL loaded in distinct thread.
// (And may be also later be able to highlight the many sources with the same URL loaded in a given thread)
const newSourceActors = action.items.filter(sourceActor =>
isSourceVisibleInSourceTree(
source,
sourceActor.sourceObject,
state.chromeAndExtensionsEnabled,
state.isWebExtension
)
);
if (newSources.length == 0) {
if (newSourceActors.length == 0) {
return state;
}
// We know that state will change in any case,
// so fork `state` right away.
const newState = { ...state };
for (const source of newSources) {
addSource(newState.threadItems, source);
let changed = false;
// Fork the array only once for all the sources
const threadItems = [...state.threadItems];
for (const sourceActor of newSourceActors) {
// We mostly wanted to read the thread of the SourceActor,
// most of the interesting attributes are on the Source Object.
changed |= addSource(
threadItems,
sourceActor.thread,
sourceActor.sourceObject
);
}
if (changed) {
return {
...state,
threadItems,
};
}
// Copy the array once for all the sources in order to force updating react
newState.threadItems = [...newState.threadItems];
return newState;
return state;
}

case "NAVIGATE":
return initialSourcesTreeState();
Expand Down Expand Up @@ -191,21 +236,20 @@ function isSourceVisibleInSourceTree(
);
}

function addSource(threadItems, source) {
function addSource(threadItems, thread, source) {
// Ensure creating or fetching the related Thread Item
const { thread } = source;

let threadItem = threadItems.find(item => {
return item.threadActorID == thread;
});
if (!threadItem) {
threadItem = createThreadTreeItem(thread);
// Note that threadItems will be clone once to force a state update
// by the callsite of addSource
// Note that threadItems will be cloned once to force a state update
// by the callsite of `addSourceActor`
threadItems.push(threadItem);
}

// Then ensure creating or fetching the related Group Item
// About `source` versus `sourceActor`:
const { displayURL } = source;
const { group } = displayURL;

Expand All @@ -226,13 +270,25 @@ function addSource(threadItems, source) {
const parentPath = path.substring(0, path.lastIndexOf("/"));
const directoryItem = addOrGetParentDirectory(thread, groupItem, parentPath);

// Check if a previous source actor registered this source.
// It happens if we load the same url multiple times, or,
// for inline sources (=HTML pages with inline scripts).
const existing = directoryItem.children.find(item => {
return item.type == "source" && item.source == source;
});
if (existing) {
return false;
}

// Finaly, create the Source Item and register it in its parent Directory Item
const sourceItem = createSourceTreeItem(source, thread, group, directoryItem);
// Copy children in order to force updating react in case we picked
// this directory as a project root
directoryItem.children = [...directoryItem.children, sourceItem];
// Re-sort the items in this directory
directoryItem.children.sort(sortItems);

return true;
}

function sortItems(a, b) {
Expand Down

0 comments on commit 546c9e4

Please sign in to comment.