Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
52d285d
fix delete highlight crash when search term matches highlight
jivey Feb 25, 2026
6c56ae6
fix type
jivey Feb 26, 2026
21ff105
fix highlighter errors when highlighting inside search highlight
jivey Mar 31, 2026
8fad329
Revert "fix delete highlight crash when search term matches highlight"
jivey Mar 31, 2026
1c854da
recreate the highlighter if it crashes while erasing
jivey Apr 7, 2026
78c60c0
add coverage
jivey Apr 7, 2026
8a7d7d4
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
jivey Apr 7, 2026
8f3f842
just use services
jivey Apr 8, 2026
30c3f9e
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 8, 2026
35d2399
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 8, 2026
38429a3
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 9, 2026
4f2ff15
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 9, 2026
4ccfa0a
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 9, 2026
81adc67
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 9, 2026
55147c8
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 9, 2026
10bd892
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 9, 2026
99d5f18
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 9, 2026
6dbe622
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 9, 2026
9b92467
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 9, 2026
14c6327
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 9, 2026
0d1e468
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 9, 2026
69e651e
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 9, 2026
885f7a4
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 9, 2026
491079f
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 9, 2026
2939b3e
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 9, 2026
707261d
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 9, 2026
ea3fcdd
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 9, 2026
fd16dd1
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 9, 2026
dc6b1e4
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 9, 2026
6838489
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 9, 2026
ffe9a1a
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 9, 2026
3a97f3a
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 9, 2026
49a7304
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 9, 2026
2357ae8
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 9, 2026
1fc9dde
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 9, 2026
a184594
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 9, 2026
16aedaa
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 9, 2026
e73c55f
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 9, 2026
5d03cc4
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 9, 2026
28b841e
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 9, 2026
03f97fb
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 9, 2026
1166e04
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 9, 2026
d9ea838
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 9, 2026
134f3aa
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 9, 2026
4a08d79
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 9, 2026
d7d8e34
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 9, 2026
a915f7b
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 9, 2026
78bcc63
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 9, 2026
38b8b0a
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 9, 2026
1ba6abc
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 10, 2026
4af6ac5
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 13, 2026
5460f14
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 13, 2026
9d2d3c5
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 13, 2026
7ed13db
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 13, 2026
945b45f
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 13, 2026
38afda7
Merge branch 'main' into CORE-1688-fix-delete-highlight-after-search
staxly[bot] Apr 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions src/app/content/components/Page/searchHighlightManager.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,29 @@ describe('searchHighlightManager', () => {
options.formatMessage({ id: 'asdfg' });
expect(intl.formatMessage).toHaveBeenCalledWith({ id: 'asdfg:search' });
});

it('recreates highlighter when eraseAll throws', () => {
const firstHighlighterInstance = Highlighter.mock.instances[0];
firstHighlighterInstance.eraseAll.mockImplementation(() => { throw new Error('orphaned span'); });
firstHighlighterInstance.unmount = jest.fn();

const newSearchResults = [
makeSearchResultHit({book, page, highlights: ['highlight <strong>number</strong> 4']}),
];

attachedManager.update(
{searchResults, selectedResult: null},
{searchResults: newSearchResults, selectedResult: null},
{forceRedraw: false, onSelect: onHighlightSelect}
);

expect(firstHighlighterInstance.unmount).toHaveBeenCalled();
// A new Highlighter instance should have been created to replace the broken one
expect(Highlighter).toHaveBeenCalledTimes(2);
// The replacement highlighter should be used for subsequent highlightResults calls
expect(utils.highlightResults).toHaveBeenCalledWith(
Highlighter.mock.instances[1],
newSearchResults
);
});
});
32 changes: 25 additions & 7 deletions src/app/content/components/Page/searchHighlightManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import allImagesLoaded from '../utils/allImagesLoaded';
interface Services {
highlighter: Highlighter;
container: HTMLElement;
intl: IntlShape;
searchResultMap: ReturnType<typeof highlightResults>;
}

Expand All @@ -33,6 +34,19 @@ export interface UpdateOptions {
onSelect: (selectedHighlight?: Highlight) => void;
}

const safeEraseAll = (services: Services) => {
try {
services.highlighter.eraseAll();
} catch {
// User highlight DOM mutations can orphan search highlight spans (parentNode
// becomes null), making eraseAll crash on insertBefore. When that happens,
// discard the broken instance and create a fresh one — the orphaned spans
// are already detached from the DOM so there is nothing left to unwrap.
services.highlighter.unmount();
services.highlighter = createHighlighter(services.container, services.intl);
}
};

const updateResults = (
services: Services,
previous: HighlightProp | null,
Expand All @@ -42,7 +56,7 @@ const updateResults = (
return;
}

services.highlighter.eraseAll();
safeEraseAll(services);
services.searchResultMap = highlightResults(services.highlighter, current.searchResults);
};

Expand Down Expand Up @@ -99,14 +113,18 @@ const handleUpdate = (services: Services) => (
selectResult(services, previous, current, options);
};

const createHighlighter = (container: HTMLElement, intl: IntlShape) =>
new Highlighter(container, {
className: 'search-highlight',
formatMessage: ({ id }) => intl.formatMessage({ id: `${id}:search` }),
tabbable: false,
});

const searchHighlightManager = (container: HTMLElement, intl: IntlShape) => {
const services = {
const services: Services = {
container,
highlighter: new Highlighter(container, {
className: 'search-highlight',
formatMessage: ({ id }) => intl.formatMessage({ id: `${id}:search` }),
tabbable: false,
}),
highlighter: createHighlighter(container, intl),
intl,
searchResultMap: [],
};

Expand Down
Loading