Skip to content

Commit b51cef2

Browse files
authored
fix(OpenUI5Support): add shared registry for patched functions (#13520)
Store patched OpenUI5 prototype functions in a shared resource (PatchedFunctions) that persists across runtime instances. Each entry records the original (unwrapped) function and the runtime version that installed the patch. On each init, the patch is skipped if an equal or newer version already owns it, and force-applied if no version is recorded (pre-existing patch from older runtimes). This prevents double-wrapping the prototype chain while ensuring the newest loaded runtime always owns the active wrapper. Extract compareVersions from compareRuntimes in Runtimes.ts to enable direct VersionInfo comparisons without requiring registered runtime indices. Fixes: #13406
1 parent 89d1e41 commit b51cef2

2 files changed

Lines changed: 72 additions & 31 deletions

File tree

packages/base/src/Runtimes.ts

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,33 @@ const getCurrentRuntimeIndex = () => {
5555
return currentRuntimeIndex;
5656
};
5757

58+
/**
59+
* Compares two VersionInfo objects and returns 1 if the first is bigger, -1 if the second is bigger, and 0 if equal.
60+
*/
61+
const compareVersions = (v1: VersionInfo, v2: VersionInfo): number => {
62+
if (v1.isNext || v2.isNext) {
63+
return v1.buildTime - v2.buildTime;
64+
}
65+
66+
const majorDiff = v1.major - v2.major;
67+
if (majorDiff) {
68+
return majorDiff;
69+
}
70+
71+
const minorDiff = v1.minor - v2.minor;
72+
if (minorDiff) {
73+
return minorDiff;
74+
}
75+
76+
const patchDiff = v1.patch - v2.patch;
77+
if (patchDiff) {
78+
return patchDiff;
79+
}
80+
81+
const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: "base" });
82+
return collator.compare(v1.suffix, v2.suffix);
83+
};
84+
5885
/**
5986
* Compares two runtimes and returns 1 if the first is of a bigger version, -1 if the second is of a bigger version, and 0 if equal
6087
* @param index1 The index of the first runtime to compare
@@ -74,33 +101,7 @@ const compareRuntimes = (index1: number, index2: number) => {
74101
throw new Error("Invalid runtime index supplied");
75102
}
76103

77-
// If any of the two is a next version, bigger buildTime wins
78-
if (runtime1.isNext || runtime2.isNext) {
79-
return runtime1.buildTime - runtime2.buildTime;
80-
}
81-
82-
// If major versions differ, bigger one wins
83-
const majorDiff = runtime1.major - runtime2.major;
84-
if (majorDiff) {
85-
return majorDiff;
86-
}
87-
88-
// If minor versions differ, bigger one wins
89-
const minorDiff = runtime1.minor - runtime2.minor;
90-
if (minorDiff) {
91-
return minorDiff;
92-
}
93-
94-
// If patch versions differ, bigger one wins
95-
const patchDiff = runtime1.patch - runtime2.patch;
96-
if (patchDiff) {
97-
return patchDiff;
98-
}
99-
100-
// Bigger suffix wins, f.e. rc10 > rc9
101-
// Important: suffix is alphanumeric, must use natural compare
102-
const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: "base" });
103-
const result = collator.compare(runtime1.suffix, runtime2.suffix);
104+
const result = compareVersions(runtime1, runtime2);
104105

105106
compareCache.set(cacheIndex, result);
106107
return result;
@@ -122,6 +123,7 @@ export {
122123
getCurrentRuntimeIndex,
123124
registerCurrentRuntime,
124125
compareRuntimes,
126+
compareVersions,
125127
setRuntimeAlias,
126128
getAllRuntimes,
127129
};

packages/base/src/features/patchPopup.ts

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,29 @@
11
// OpenUI5's Control.js subset
22
import getSharedResource from "../getSharedResource.js";
33
import insertOpenUI5PopupStyles from "./insertOpenUI5PopupStyles.js";
4+
import VersionInfo from "../generated/VersionInfo.js";
5+
import { compareVersions } from "../Runtimes.js";
6+
7+
type PatchedFunctionRecord = {
8+
version: VersionInfo | undefined;
9+
originalFn: (...args: any[]) => any;
10+
};
11+
12+
type PatchedFunctionsRegistry = Record<string, PatchedFunctionRecord>;
13+
14+
const PatchedFunctions = getSharedResource<PatchedFunctionsRegistry>("PatchedFunctions", {});
15+
16+
const shouldRepatch = (key: string): boolean => {
17+
const existing = PatchedFunctions[key];
18+
if (!existing) {
19+
return true;
20+
}
21+
22+
if (!existing.version) {
23+
return true;
24+
}
25+
return compareVersions(VersionInfo, existing.version) > 0;
26+
};
427

528
type Control = {
629
getDomRef: () => HTMLElement | null,
@@ -173,7 +196,11 @@ const isNativePopoverOpen = (root: Document | ShadowRoot = document): boolean =>
173196
};
174197

175198
const patchDialog = (Dialog: OpenUI5DialogClass) => {
176-
const origOnsapescape = Dialog.prototype.onsapescape;
199+
const key = "Dialog.prototype.onsapescape";
200+
if (shouldRepatch(key)) {
201+
PatchedFunctions[key] = { version: VersionInfo, originalFn: PatchedFunctions[key]?.originalFn ?? Dialog.prototype.onsapescape };
202+
}
203+
const origOnsapescape = PatchedFunctions[key].originalFn;
177204
Dialog.prototype.onsapescape = function onsapescape(...args: any[]) {
178205
if (hasWebComponentPopupAbove(this.oPopup)) {
179206
return;
@@ -184,7 +211,11 @@ const patchDialog = (Dialog: OpenUI5DialogClass) => {
184211
};
185212

186213
const patchOpen = (Popup: OpenUI5PopupClass) => {
187-
const origOpen = Popup.prototype.open;
214+
const key = "Popup.prototype.open";
215+
if (shouldRepatch(key)) {
216+
PatchedFunctions[key] = { version: VersionInfo, originalFn: PatchedFunctions[key]?.originalFn ?? Popup.prototype.open };
217+
}
218+
const origOpen = PatchedFunctions[key].originalFn;
188219
Popup.prototype.open = function open(...args: any[]) {
189220
origOpen.apply(this, args); // call open first to initiate opening
190221
openNativePopoverForOpenUI5(this);
@@ -197,7 +228,11 @@ const patchOpen = (Popup: OpenUI5PopupClass) => {
197228
};
198229

199230
const patchClosed = (Popup: OpenUI5PopupClass) => {
200-
const _origClosed = Popup.prototype._closed;
231+
const key = "Popup.prototype._closed";
232+
if (shouldRepatch(key)) {
233+
PatchedFunctions[key] = { version: VersionInfo, originalFn: PatchedFunctions[key]?.originalFn ?? Popup.prototype._closed };
234+
}
235+
const _origClosed = PatchedFunctions[key].originalFn;
201236
Popup.prototype._closed = function _closed(...args: any[]) {
202237
closeNativePopoverForOpenUI5(this);
203238
_origClosed.apply(this, args); // only then call _close
@@ -206,7 +241,11 @@ const patchClosed = (Popup: OpenUI5PopupClass) => {
206241
};
207242

208243
const patchFocusEvent = (Popup: OpenUI5PopupClass) => {
209-
const origFocusEvent = Popup.prototype.onFocusEvent;
244+
const key = "Popup.prototype.onFocusEvent";
245+
if (shouldRepatch(key)) {
246+
PatchedFunctions[key] = { version: VersionInfo, originalFn: PatchedFunctions[key]?.originalFn ?? Popup.prototype.onFocusEvent };
247+
}
248+
const origFocusEvent = PatchedFunctions[key].originalFn;
210249
Popup.prototype.onFocusEvent = function onFocusEvent(...args: any[]) {
211250
if (!hasWebComponentPopupAbove(this)) {
212251
origFocusEvent.apply(this, args);

0 commit comments

Comments
 (0)