@readium/navigator 2.2.9 → 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/dist/index.js +2786 -1791
  2. package/dist/index.umd.cjs +79 -86
  3. package/package.json +2 -3
  4. package/src/Navigator.ts +76 -0
  5. package/src/epub/EpubNavigator.ts +93 -10
  6. package/src/epub/css/Properties.ts +8 -8
  7. package/src/epub/css/ReadiumCSS.ts +7 -5
  8. package/src/epub/frame/FrameManager.ts +38 -2
  9. package/src/epub/frame/FramePoolManager.ts +9 -2
  10. package/src/epub/fxl/FXLFrameManager.ts +31 -2
  11. package/src/epub/fxl/FXLFramePoolManager.ts +9 -3
  12. package/src/epub/preferences/EpubDefaults.ts +6 -6
  13. package/src/epub/preferences/EpubPreferences.ts +6 -6
  14. package/src/epub/preferences/EpubPreferencesEditor.ts +1 -3
  15. package/src/epub/preferences/EpubSettings.ts +5 -7
  16. package/src/helpers/lineLength.ts +4 -6
  17. package/src/injection/epubInjectables.ts +11 -3
  18. package/src/injection/webpubInjectables.ts +12 -2
  19. package/src/peripherals/KeyboardPeripherals.ts +53 -0
  20. package/src/protection/AutomationDetector.ts +66 -0
  21. package/src/protection/ContextMenuProtector.ts +46 -0
  22. package/src/protection/DevToolsDetector.ts +291 -0
  23. package/src/protection/IframeEmbeddingDetector.ts +73 -0
  24. package/src/protection/NavigatorProtector.ts +95 -0
  25. package/src/protection/PrintProtector.ts +58 -0
  26. package/src/protection/utils/WorkerConsole.ts +84 -0
  27. package/src/protection/utils/console.ts +16 -0
  28. package/src/protection/utils/match.ts +18 -0
  29. package/src/protection/utils/platform.ts +22 -0
  30. package/src/webpub/WebPubFrameManager.ts +38 -5
  31. package/src/webpub/WebPubFramePoolManager.ts +9 -2
  32. package/src/webpub/WebPubNavigator.ts +91 -7
  33. package/types/src/Navigator.d.ts +14 -0
  34. package/types/src/epub/EpubNavigator.d.ts +14 -2
  35. package/types/src/epub/css/Properties.d.ts +4 -0
  36. package/types/src/epub/frame/FrameManager.d.ts +5 -1
  37. package/types/src/epub/frame/FramePoolManager.d.ts +4 -1
  38. package/types/src/epub/fxl/FXLFrameManager.d.ts +5 -1
  39. package/types/src/epub/fxl/FXLFramePoolManager.d.ts +4 -2
  40. package/types/src/epub/preferences/EpubDefaults.d.ts +4 -0
  41. package/types/src/epub/preferences/EpubPreferences.d.ts +4 -0
  42. package/types/src/epub/preferences/EpubPreferencesEditor.d.ts +2 -0
  43. package/types/src/epub/preferences/EpubSettings.d.ts +4 -0
  44. package/types/src/helpers/lineLength.d.ts +2 -3
  45. package/types/src/injection/epubInjectables.d.ts +2 -2
  46. package/types/src/injection/webpubInjectables.d.ts +2 -1
  47. package/types/src/peripherals/KeyboardPeripherals.d.ts +13 -0
  48. package/types/src/protection/AutomationDetector.d.ts +14 -0
  49. package/types/src/protection/ContextMenuProtector.d.ts +11 -0
  50. package/types/src/protection/DevToolsDetector.d.ts +75 -0
  51. package/types/src/protection/IframeEmbeddingDetector.d.ts +14 -0
  52. package/types/src/protection/NavigatorProtector.d.ts +12 -0
  53. package/types/src/protection/PrintProtector.d.ts +13 -0
  54. package/types/src/protection/utils/WorkerConsole.d.ts +15 -0
  55. package/types/src/protection/utils/console.d.ts +6 -0
  56. package/types/src/protection/utils/match.d.ts +8 -0
  57. package/types/src/protection/utils/platform.d.ts +4 -0
  58. package/types/src/webpub/WebPubFrameManager.d.ts +5 -1
  59. package/types/src/webpub/WebPubFramePoolManager.d.ts +4 -1
  60. package/types/src/webpub/WebPubNavigator.d.ts +13 -1
  61. package/dist/assets/AccessibleDfA-Bold.woff2 +0 -0
  62. package/dist/assets/AccessibleDfA-Italic.woff2 +0 -0
  63. package/dist/assets/AccessibleDfA-Regular.woff +0 -0
  64. package/dist/assets/AccessibleDfA-Regular.woff2 +0 -0
  65. package/dist/assets/iAWriterDuospace-Regular.ttf +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@readium/navigator",
3
- "version": "2.2.9",
3
+ "version": "2.3.1",
4
4
  "type": "module",
5
5
  "description": "Next generation SDK for publications in Web Apps",
6
6
  "author": "readium",
@@ -50,8 +50,7 @@
50
50
  "generate:css-selector": "node scripts/generate-css-selector.js"
51
51
  },
52
52
  "devDependencies": {
53
- "@laynezh/vite-plugin-lib-assets": "^2.1.3",
54
- "@readium/css": "2.0.0-beta.24",
53
+ "@readium/css": "^2.0.0",
55
54
  "@readium/navigator-html-injectables": "workspace:*",
56
55
  "@readium/shared": "workspace:*",
57
56
  "@types/path-browserify": "^1.0.3",
package/src/Navigator.ts CHANGED
@@ -1,7 +1,21 @@
1
1
  import { Link, Locator, Publication, ReadingProgression } from "@readium/shared";
2
+ import {
3
+ ContentProtectionConfig,
4
+ PrintProtectionConfig,
5
+ KeyboardPeripheral,
6
+ KeyCombo,
7
+ DEV_TOOLS,
8
+ SELECT_ALL,
9
+ PRINT,
10
+ SAVE
11
+ } from "@readium/navigator-html-injectables";
2
12
 
3
13
  type cbb = (ok: boolean) => void;
4
14
 
15
+ export type IKeyboardPeripheralsConfig = Array<Omit<KeyboardPeripheral, 'type'> & {
16
+ type: Exclude<string, 'developer_tools' | 'select_all' | 'print' | 'save'>;
17
+ }>;
18
+
5
19
  export interface ProgressionRange {
6
20
  start: number;
7
21
  end: number;
@@ -13,6 +27,12 @@ export interface VisualNavigatorViewport {
13
27
  positions: number[] | null; // Range of visible positions
14
28
  }
15
29
 
30
+ export interface IContentProtectionConfig extends ContentProtectionConfig {
31
+ protectPrinting?: PrintProtectionConfig;
32
+ checkAutomation?: boolean;
33
+ checkIFrameEmbedding?: boolean;
34
+ }
35
+
16
36
  export abstract class Navigator {
17
37
  abstract get publication(): Publication; // Publication rendered by this navigator.
18
38
  abstract get currentLocator(): Locator; // Current position (detailed) in the publication. Can be used to save a bookmark to the current position.
@@ -43,6 +63,62 @@ export abstract class Navigator {
43
63
  * Destroy all resources associated with this navigator. Synonymous with "unmount"
44
64
  */
45
65
  abstract destroy(): void;
66
+
67
+ /**
68
+ * Merges keyboard peripherals from content protection config with user-provided peripherals
69
+ * Content protection peripherals are added first for priority, then user peripherals are added only if they don't conflict
70
+ */
71
+ protected mergeKeyboardPeripherals(
72
+ config: IContentProtectionConfig,
73
+ keyboardPeripherals: IKeyboardPeripheralsConfig = []
74
+ ): IKeyboardPeripheralsConfig {
75
+ const peripherals: IKeyboardPeripheralsConfig = [];
76
+
77
+ // Filter out any peripherals with reserved content protection types
78
+ const filteredUserPeripherals = keyboardPeripherals.filter(
79
+ peripheral => !['developer_tools', 'select_all', 'print', 'save'].includes(peripheral.type)
80
+ );
81
+
82
+ // Add content protection peripherals first for priority
83
+ if (config.disableSelectAll) {
84
+ peripherals.push(SELECT_ALL);
85
+ }
86
+ if (config.disableSave) {
87
+ peripherals.push(SAVE);
88
+ }
89
+ if (config.monitorDevTools) {
90
+ peripherals.push(DEV_TOOLS);
91
+ }
92
+ if (config.protectPrinting?.disable) {
93
+ peripherals.push(PRINT);
94
+ }
95
+
96
+ // Add user peripherals with conflicting combos removed
97
+ for (const userPeripheral of filteredUserPeripherals) {
98
+ // Filter out combos that conflict with existing peripherals
99
+ const filteredCombos = userPeripheral.keyCombos.filter((userCombo: KeyCombo) =>
100
+ !peripherals.some(existingPeripheral =>
101
+ existingPeripheral.keyCombos.some((existingCombo: KeyCombo) =>
102
+ userCombo.keyCode === existingCombo.keyCode &&
103
+ userCombo.ctrl === existingCombo.ctrl &&
104
+ userCombo.shift === existingCombo.shift &&
105
+ userCombo.alt === existingCombo.alt &&
106
+ userCombo.meta === existingCombo.meta
107
+ )
108
+ )
109
+ );
110
+
111
+ // Add the peripheral with filtered combos if any remain
112
+ if (filteredCombos.length > 0) {
113
+ peripherals.push({
114
+ ...userPeripheral,
115
+ keyCombos: filteredCombos
116
+ });
117
+ }
118
+ }
119
+
120
+ return peripherals;
121
+ }
46
122
  }
47
123
 
48
124
  export abstract class VisualNavigator extends Navigator {
@@ -2,8 +2,8 @@ import { Layout, Link, Locator, Profile, Publication, ReadingProgression } from
2
2
  import { Configurable, ConfigurablePreferences, ConfigurableSettings, LineLengths, ProgressionRange, VisualNavigator, VisualNavigatorViewport } from "../";
3
3
  import { FramePoolManager } from "./frame/FramePoolManager";
4
4
  import { FXLFramePoolManager } from "./fxl/FXLFramePoolManager";
5
- import { CommsEventKey, FXLModules, ModuleLibrary, ModuleName, ReflowableModules } from "@readium/navigator-html-injectables";
6
- import { BasicTextSelection, FrameClickEvent } from "@readium/navigator-html-injectables";
5
+ import { CommsEventKey, ContextMenuEvent, FXLModules, KeyboardEventData, ModuleLibrary, ModuleName, ReflowableModules } from "@readium/navigator-html-injectables";
6
+ import { BasicTextSelection, FrameClickEvent, SuspiciousActivityEvent } from "@readium/navigator-html-injectables";
7
7
  import * as path from "path-browserify";
8
8
  import { FXLFrameManager } from "./fxl/FXLFrameManager";
9
9
  import { FrameManager } from "./frame/FrameManager";
@@ -17,6 +17,9 @@ import { getContentWidth } from "../helpers/dimensions";
17
17
  import { Injector } from "../injection/Injector";
18
18
  import { createReadiumEpubRules } from "../injection/epubInjectables";
19
19
  import { IInjectablesConfig } from "../injection/Injectable";
20
+ import { IContentProtectionConfig, IKeyboardPeripheralsConfig } from "../Navigator";
21
+ import { NavigatorProtector, NAVIGATOR_SUSPICIOUS_ACTIVITY_EVENT } from "../protection/NavigatorProtector";
22
+ import { KeyboardPeripherals, NAVIGATOR_KEYBOARD_PERIPHERAL_EVENT } from "../peripherals/KeyboardPeripherals";
20
23
 
21
24
  export type ManagerEventKey = "zoom";
22
25
 
@@ -24,6 +27,8 @@ export interface EpubNavigatorConfiguration {
24
27
  preferences: IEpubPreferences;
25
28
  defaults: IEpubDefaults;
26
29
  injectables?: IInjectablesConfig;
30
+ contentProtection?: IContentProtectionConfig;
31
+ keyboardPeripherals?: IKeyboardPeripheralsConfig;
27
32
  }
28
33
 
29
34
  export interface EpubNavigatorListeners {
@@ -35,8 +40,11 @@ export interface EpubNavigatorListeners {
35
40
  miscPointer: (amount: number) => void;
36
41
  scroll: (delta: number) => void;
37
42
  customEvent: (key: string, data: unknown) => void;
38
- handleLocator: (locator: Locator) => boolean; // Retrun true to prevent handling here
43
+ handleLocator: (locator: Locator) => boolean; // Return true to prevent handling here
39
44
  textSelected: (selection: BasicTextSelection) => void;
45
+ contentProtection: (type: string, data: SuspiciousActivityEvent) => void;
46
+ contextMenu: (data: ContextMenuEvent) => void;
47
+ peripheral: (data: KeyboardEventData) => void;
40
48
  // showToc: () => void;
41
49
  }
42
50
 
@@ -50,7 +58,10 @@ const defaultListeners = (listeners: EpubNavigatorListeners): EpubNavigatorListe
50
58
  scroll: listeners.scroll || (() => {}),
51
59
  customEvent: listeners.customEvent || (() => {}),
52
60
  handleLocator: listeners.handleLocator || (() => false),
53
- textSelected: listeners.textSelected || (() => {})
61
+ textSelected: listeners.textSelected || (() => {}),
62
+ contentProtection: listeners.contentProtection || (() => {}),
63
+ contextMenu: listeners.contextMenu || (() => {}),
64
+ peripheral: listeners.peripheral || (() => {}),
54
65
  })
55
66
 
56
67
  export class EpubNavigator extends VisualNavigator implements Configurable<ConfigurableSettings, ConfigurablePreferences> {
@@ -70,6 +81,12 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
70
81
  private _css: ReadiumCSS;
71
82
  private _preferencesEditor: EpubPreferencesEditor | null = null;
72
83
  private readonly _injector: Injector | null = null;
84
+ private readonly _contentProtection: IContentProtectionConfig;
85
+ private readonly _keyboardPeripherals: IKeyboardPeripheralsConfig;
86
+ private readonly _navigatorProtector: NavigatorProtector | null = null;
87
+ private readonly _keyboardPeripheralsManager: KeyboardPeripherals | null = null;
88
+ private readonly _suspiciousActivityListener: ((event: Event) => void) | null = null;
89
+ private readonly _keyboardPeripheralListener: ((event: Event) => void) | null = null;
73
90
 
74
91
  private resizeObserver: ResizeObserver;
75
92
 
@@ -98,7 +115,9 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
98
115
  optimalChars: this._settings.optimalLineLength,
99
116
  minChars: this._settings.minimalLineLength,
100
117
  maxChars: this._settings.maximalLineLength,
101
- pageGutter: this._settings.pageGutter,
118
+ padding: this._settings.scroll
119
+ ? (this._settings.scrollPaddingLeft || 0) + (this._settings.scrollPaddingRight || 0)
120
+ : (this._settings.pageGutter || 0) * 2,
102
121
  fontFace: this._settings.fontFamily,
103
122
  letterSpacing: this._settings.letterSpacing,
104
123
  wordSpacing: this._settings.wordSpacing,
@@ -112,13 +131,55 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
112
131
  this.currentProgression = pub.metadata.effectiveReadingProgression;
113
132
 
114
133
  // Combine Readium rules with user-provided injectables
115
- const readiumRules = createReadiumEpubRules(pub.metadata);
134
+ const readiumRules = createReadiumEpubRules(pub.metadata, pub.readingOrder.items);
116
135
  const userConfig = configuration.injectables || { rules: [], allowedDomains: [] };
117
136
 
118
137
  this._injector = new Injector({
119
138
  rules: [...readiumRules, ...userConfig.rules],
120
139
  allowedDomains: userConfig.allowedDomains
121
140
  });
141
+
142
+ this._contentProtection = configuration.contentProtection || {};
143
+
144
+ // Merge keyboard peripherals
145
+ this._keyboardPeripherals = this.mergeKeyboardPeripherals(
146
+ this._contentProtection,
147
+ configuration.keyboardPeripherals || []
148
+ );
149
+
150
+ // Initialize navigator protection if any protection is configured
151
+ if (this._contentProtection.disableContextMenu ||
152
+ this._contentProtection.checkAutomation ||
153
+ this._contentProtection.checkIFrameEmbedding ||
154
+ this._contentProtection.monitorDevTools ||
155
+ this._contentProtection.protectPrinting?.disable) {
156
+ this._navigatorProtector = new NavigatorProtector(this._contentProtection);
157
+
158
+ // Listen for custom events from NavigatorProtector
159
+ this._suspiciousActivityListener = (event: Event) => {
160
+ const { type, ...activity } = (event as CustomEvent).detail;
161
+ if (type === "context_menu") {
162
+ this.listeners.contextMenu(activity as ContextMenuEvent);
163
+ } else {
164
+ this.listeners.contentProtection(type, activity);
165
+ }
166
+ };
167
+ window.addEventListener(NAVIGATOR_SUSPICIOUS_ACTIVITY_EVENT, this._suspiciousActivityListener);
168
+ }
169
+
170
+ // Initialize keyboard peripherals separately (works independently of protection)
171
+ if (this._keyboardPeripherals.length > 0) {
172
+ this._keyboardPeripheralsManager = new KeyboardPeripherals({
173
+ keyboardPeripherals: this._keyboardPeripherals
174
+ });
175
+
176
+ // Listen for keyboard peripheral events from main window
177
+ this._keyboardPeripheralListener = (event: Event) => {
178
+ const activity = (event as CustomEvent).detail;
179
+ this.listeners.peripheral(activity);
180
+ };
181
+ window.addEventListener(NAVIGATOR_KEYBOARD_PERIPHERAL_EVENT, this._keyboardPeripheralListener);
182
+ }
122
183
 
123
184
  // We use a resizeObserver cos’ the container parent may not be the width of
124
185
  // the document/window e.g. app using a docking system with left and right panels.
@@ -154,7 +215,9 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
154
215
  this.container,
155
216
  this.positions,
156
217
  this.pub,
157
- this._injector
218
+ this._injector,
219
+ this._contentProtection,
220
+ this._keyboardPeripherals
158
221
  );
159
222
  this.framePool.listener = (key: CommsEventKey | ManagerEventKey, data: unknown) => {
160
223
  this.eventListener(key, data);
@@ -166,7 +229,9 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
166
229
  this.container,
167
230
  this.positions,
168
231
  cssProperties,
169
- this._injector
232
+ this._injector,
233
+ this._contentProtection,
234
+ this._keyboardPeripherals
170
235
  );
171
236
  }
172
237
 
@@ -434,6 +499,16 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
434
499
  case "progress":
435
500
  this.syncLocation(data as ProgressionRange);
436
501
  break;
502
+ case "content_protection":
503
+ const activity = data as SuspiciousActivityEvent;
504
+ this.listeners.contentProtection(activity.type, activity);
505
+ break;
506
+ case "context_menu":
507
+ this.listeners.contextMenu(data as ContextMenuEvent);
508
+ break;
509
+ case "keyboard_peripherals":
510
+ this.listeners.peripheral(data as KeyboardEventData);
511
+ break;
437
512
  case "log":
438
513
  console.log(this._cframes[0]?.source?.split("/")[3], ...(data as any[]));
439
514
  break;
@@ -482,6 +557,14 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
482
557
  }
483
558
 
484
559
  public async destroy() {
560
+ if (this._suspiciousActivityListener) {
561
+ window.removeEventListener(NAVIGATOR_SUSPICIOUS_ACTIVITY_EVENT, this._suspiciousActivityListener);
562
+ }
563
+ if (this._keyboardPeripheralListener) {
564
+ window.removeEventListener(NAVIGATOR_KEYBOARD_PERIPHERAL_EVENT, this._keyboardPeripheralListener);
565
+ }
566
+ this._navigatorProtector?.destroy();
567
+ this._keyboardPeripheralsManager?.destroy();
485
568
  await this.framePool?.destroy();
486
569
  }
487
570
 
@@ -711,7 +794,7 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
711
794
 
712
795
  private async loadLocator(locator: Locator, cb: (ok: boolean) => void) {
713
796
  let done = false;
714
- let cssSelector = (typeof locator.locations.getCssSelector === 'function') && locator.locations.getCssSelector();
797
+ let cssSelector = (typeof locator.locations.getCssSelector === "function") && locator.locations.getCssSelector();
715
798
  if(locator.text?.highlight) {
716
799
  done = await new Promise<boolean>((res, _) => {
717
800
  // Attempt to go to a highlighted piece of text in the resource
@@ -743,7 +826,7 @@ export class EpubNavigator extends VisualNavigator implements Configurable<Confi
743
826
  // This sanity check has to be performed because we're still passing non-locator class
744
827
  // locator objects to this function. This is not good and should eventually be forbidden
745
828
  // or the locator should be deserialized sometime before this function.
746
- const hid = (typeof locator.locations.htmlId === 'function') && locator.locations.htmlId();
829
+ const hid = (typeof locator.locations.htmlId === "function") && locator.locations.htmlId();
747
830
  if(hid)
748
831
  done = await new Promise<boolean>((res, _) => {
749
832
  // Attempt to go to an HTML ID in the resource
@@ -203,8 +203,8 @@ export interface IRSProperties {
203
203
  sansSerifJaV?: string | null;
204
204
  sansTf?: string | null;
205
205
  scrollPaddingBottom?: number | null;
206
- // scrollPaddingLeft?: number | null;
207
- // scrollPaddingRight?: number | null;
206
+ scrollPaddingLeft?: number | null;
207
+ scrollPaddingRight?: number | null;
208
208
  scrollPaddingTop?: number | null;
209
209
  secondaryColor?: string | null;
210
210
  selectionBackgroundColor?: string | null;
@@ -248,8 +248,8 @@ export class RSProperties extends Properties {
248
248
  sansSerifJaV: string | null;
249
249
  sansTf: string | null;
250
250
  scrollPaddingBottom: number | null;
251
- // scrollPaddingLeft: number | null;
252
- // scrollPaddingRight: number | null;
251
+ scrollPaddingLeft: number | null;
252
+ scrollPaddingRight: number | null;
253
253
  scrollPaddingTop: number | null;
254
254
  secondaryColor: string | null;
255
255
  selectionBackgroundColor: string | null;
@@ -290,8 +290,8 @@ export class RSProperties extends Properties {
290
290
  this.paraSpacing = props.paraSpacing ?? null;
291
291
  this.primaryColor = props.primaryColor ?? null;
292
292
  this.scrollPaddingBottom = props.scrollPaddingBottom ?? null;
293
- // this.scrollPaddingLeft = props.scrollPaddingLeft ?? null;
294
- // this.scrollPaddingRight = props.scrollPaddingRight ?? null;
293
+ this.scrollPaddingLeft = props.scrollPaddingLeft ?? null;
294
+ this.scrollPaddingRight = props.scrollPaddingRight ?? null;
295
295
  this.scrollPaddingTop = props.scrollPaddingTop ?? null;
296
296
  this.sansSerifJa = props.sansSerifJa ?? null;
297
297
  this.sansSerifJaV = props.sansSerifJaV ?? null;
@@ -340,8 +340,8 @@ export class RSProperties extends Properties {
340
340
  if (this.sansSerifJaV) cssProperties["--RS__sans-serif-ja-v"] = this.sansSerifJaV;
341
341
  if (this.sansTf) cssProperties["--RS__sansTf"] = this.sansTf;
342
342
  if (this.scrollPaddingBottom != null) cssProperties["--RS__scrollPaddingBottom"] = this.toPx(this.scrollPaddingBottom);
343
- // if (this.scrollPaddingLeft != null) cssProperties["--RS__scrollPaddingLeft"] = this.toPx(this.scrollPaddingLeft);
344
- // if (this.scrollPaddingRight != null) cssProperties["--RS__scrollPaddingRight"] = this.toPx(this.scrollPaddingRight);
343
+ if (this.scrollPaddingLeft != null) cssProperties["--RS__scrollPaddingLeft"] = this.toPx(this.scrollPaddingLeft);
344
+ if (this.scrollPaddingRight != null) cssProperties["--RS__scrollPaddingRight"] = this.toPx(this.scrollPaddingRight);
345
345
  if (this.scrollPaddingTop != null) cssProperties["--RS__scrollPaddingTop"] = this.toPx(this.scrollPaddingTop);
346
346
  if (this.secondaryColor) cssProperties["--RS__secondaryColor"] = this.secondaryColor;
347
347
  if (this.selectionBackgroundColor) cssProperties["--RS__selectionBackgroundColor"] = this.selectionBackgroundColor;
@@ -45,11 +45,11 @@ export class ReadiumCSS {
45
45
  if (settings.scrollPaddingBottom !== this.rsProperties.scrollPaddingBottom)
46
46
  this.rsProperties.scrollPaddingBottom = settings.scrollPaddingBottom;
47
47
 
48
- // if (settings.scrollPaddingLeft !== this.rsProperties.scrollPaddingLeft)
49
- // this.rsProperties.scrollPaddingLeft = settings.scrollPaddingLeft;
48
+ if (settings.scrollPaddingLeft !== this.rsProperties.scrollPaddingLeft)
49
+ this.rsProperties.scrollPaddingLeft = settings.scrollPaddingLeft;
50
50
 
51
- // if (settings.scrollPaddingRight !== this.rsProperties.scrollPaddingRight)
52
- // this.rsProperties.scrollPaddingRight = settings.scrollPaddingRight;
51
+ if (settings.scrollPaddingRight !== this.rsProperties.scrollPaddingRight)
52
+ this.rsProperties.scrollPaddingRight = settings.scrollPaddingRight;
53
53
 
54
54
  if (settings.scrollPaddingTop !== this.rsProperties.scrollPaddingTop)
55
55
  this.rsProperties.scrollPaddingTop = settings.scrollPaddingTop;
@@ -62,7 +62,9 @@ export class ReadiumCSS {
62
62
  this.lineLengths.update({
63
63
  fontFace: settings.fontFamily,
64
64
  letterSpacing: settings.letterSpacing,
65
- pageGutter: settings.pageGutter,
65
+ padding: settings.scroll
66
+ ? (settings.scrollPaddingLeft || 0) + (settings.scrollPaddingRight || 0)
67
+ : (settings.pageGutter || 0) * 2,
66
68
  wordSpacing: settings.wordSpacing,
67
69
  optimalChars: settings.optimalLineLength,
68
70
  minChars: settings.minimalLineLength,
@@ -2,6 +2,7 @@ import { Loader, ModuleName } from "@readium/navigator-html-injectables";
2
2
  import { FrameComms } from "./FrameComms";
3
3
  import { ReadiumWindow } from "../../../../navigator-html-injectables/types/src/helpers/dom";
4
4
  import { sML } from "../../helpers";
5
+ import type { IContentProtectionConfig, IKeyboardPeripheralsConfig } from "../../Navigator";
5
6
 
6
7
 
7
8
  export class FrameManager {
@@ -11,10 +12,15 @@ export class FrameManager {
11
12
  private comms: FrameComms | undefined;
12
13
  private hidden: boolean = true;
13
14
  private destroyed: boolean = false;
14
-
15
+ private readonly contentProtectionConfig: IContentProtectionConfig;
16
+ private readonly keyboardPeripheralsConfig: IKeyboardPeripheralsConfig;
15
17
  private currModules: ModuleName[] = [];
16
18
 
17
- constructor(source: string) {
19
+ constructor(
20
+ source: string,
21
+ contentProtectionConfig: IContentProtectionConfig = {},
22
+ keyboardPeripheralsConfig: IKeyboardPeripheralsConfig = []
23
+ ) {
18
24
  this.frame = document.createElement("iframe");
19
25
  this.frame.sandbox.value = "allow-same-origin allow-scripts";
20
26
  this.frame.classList.add("readium-navigator-iframe");
@@ -25,6 +31,11 @@ export class FrameManager {
25
31
  this.frame.style.pointerEvents = "none";
26
32
  this.frame.style.transition = "visibility 0s, opacity 0.1s linear";
27
33
  this.source = source;
34
+
35
+ // Use the provided content protection config directly without overriding defaults
36
+ this.contentProtectionConfig = { ...contentProtectionConfig };
37
+ this.keyboardPeripheralsConfig = [...keyboardPeripheralsConfig];
38
+
28
39
  }
29
40
 
30
41
  async load(modules: ModuleName[]): Promise<Window> {
@@ -57,6 +68,28 @@ export class FrameManager {
57
68
  });
58
69
  }
59
70
 
71
+ private applyContentProtection() {
72
+ if (!this.comms) this.comms!.resume();
73
+
74
+ // Send content protection config
75
+ this.comms!.send("peripherals_protection", this.contentProtectionConfig);
76
+
77
+ // Send keyboard peripherals separately
78
+ if (this.keyboardPeripheralsConfig && this.keyboardPeripheralsConfig.length > 0) {
79
+ this.comms!.send("keyboard_peripherals", this.keyboardPeripheralsConfig);
80
+ }
81
+
82
+ // Apply scroll protection
83
+ if (this.contentProtectionConfig.monitorScrollingExperimental) {
84
+ this.comms!.send("scroll_protection", {});
85
+ }
86
+
87
+ // Apply print protection if configured
88
+ if (this.contentProtectionConfig.protectPrinting) {
89
+ this.comms!.send("print_protection", this.contentProtectionConfig.protectPrinting);
90
+ }
91
+ }
92
+
60
93
  async destroy() {
61
94
  await this.hide();
62
95
  this.loader?.destroy();
@@ -91,6 +124,9 @@ export class FrameManager {
91
124
  return new Promise((res, _) => {
92
125
  this.comms?.send("activate", undefined, () => {
93
126
  this.comms?.send("focus", undefined, () => {
127
+ // Apply content protection synchronously
128
+ this.applyContentProtection();
129
+
94
130
  const remove = () => {
95
131
  this.frame.style.removeProperty("visibility");
96
132
  this.frame.style.removeProperty("aria-hidden");
@@ -3,6 +3,7 @@ import { Locator, Publication } from "@readium/shared";
3
3
  import FrameBlobBuider from "./FrameBlobBuilder";
4
4
  import { FrameManager } from "./FrameManager";
5
5
  import { Injector } from "../../injection/Injector";
6
+ import { IContentProtectionConfig, IKeyboardPeripheralsConfig } from "../../Navigator";
6
7
 
7
8
  const UPPER_BOUNDARY = 5;
8
9
  const LOWER_BOUNDARY = 3;
@@ -18,17 +19,23 @@ export class FramePoolManager {
18
19
  private pendingUpdates: Map<string, { inPool: boolean }> = new Map();
19
20
  private currentBaseURL: string | undefined;
20
21
  private readonly injector: Injector | null = null;
22
+ private readonly contentProtectionConfig: IContentProtectionConfig;
23
+ private readonly keyboardPeripheralsConfig: IKeyboardPeripheralsConfig;
21
24
 
22
25
  constructor(
23
26
  container: HTMLElement,
24
27
  positions: Locator[],
25
28
  cssProperties?: { [key: string]: string },
26
- injector?: Injector | null
29
+ injector?: Injector | null,
30
+ contentProtectionConfig?: IContentProtectionConfig,
31
+ keyboardPeripheralsConfig?: IKeyboardPeripheralsConfig
27
32
  ) {
28
33
  this.container = container;
29
34
  this.positions = positions;
30
35
  this.currentCssProperties = cssProperties;
31
36
  this.injector = injector ?? null;
37
+ this.contentProtectionConfig = contentProtectionConfig || {};
38
+ this.keyboardPeripheralsConfig = keyboardPeripheralsConfig || [];
32
39
  }
33
40
 
34
41
  async destroy() {
@@ -164,7 +171,7 @@ export class FramePoolManager {
164
171
  }
165
172
 
166
173
  // Create <iframe>
167
- const fm = new FrameManager(this.blobs.get(href)!);
174
+ const fm = new FrameManager(this.blobs.get(href)!, this.contentProtectionConfig, this.keyboardPeripheralsConfig);
168
175
  if(href !== newHref) await fm.hide(); // Avoid unecessary hide
169
176
  this.container.appendChild(fm.iframe);
170
177
  await fm.load(modules);
@@ -3,6 +3,7 @@ import { Page, ReadingProgression } from "@readium/shared";
3
3
  import { FrameComms } from "../frame/FrameComms";
4
4
  import { FXLPeripherals } from "./FXLPeripherals";
5
5
  import { ReadiumWindow } from "../../../../navigator-html-injectables/types/src/helpers/dom";
6
+ import { IContentProtectionConfig, IKeyboardPeripheralsConfig } from "../../Navigator";
6
7
 
7
8
  export class FXLFrameManager {
8
9
  private frame: HTMLIFrameElement;
@@ -10,7 +11,8 @@ export class FXLFrameManager {
10
11
  public source: string;
11
12
  private comms: FrameComms | undefined;
12
13
  private readonly peripherals: FXLPeripherals;
13
-
14
+ private readonly contentProtectionConfig: IContentProtectionConfig;
15
+ private readonly keyboardPeripheralsConfig: IKeyboardPeripheralsConfig;
14
16
  private currModules: ModuleName[] = [];
15
17
 
16
18
  // NEW
@@ -19,9 +21,18 @@ export class FXLFrameManager {
19
21
  private loadPromise: Promise<Window> | undefined;
20
22
  private showPromise: Promise<void> | undefined;
21
23
 
22
- constructor(peripherals: FXLPeripherals, direction: ReadingProgression, debugHref: string) {
24
+ constructor(
25
+ peripherals: FXLPeripherals,
26
+ direction: ReadingProgression,
27
+ debugHref: string,
28
+ contentProtectionConfig: IContentProtectionConfig = {},
29
+ keyboardPeripheralsConfig: IKeyboardPeripheralsConfig = []
30
+ ) {
23
31
  this.peripherals = peripherals;
24
32
  this.debugHref = debugHref;
33
+ // Use the provided content protection config directly without overriding defaults
34
+ this.contentProtectionConfig = { ...contentProtectionConfig };
35
+ this.keyboardPeripheralsConfig = [...keyboardPeripheralsConfig];
25
36
  this.frame = document.createElement("iframe");
26
37
  this.frame.sandbox.value = "allow-same-origin allow-scripts";
27
38
  this.frame.classList.add("readium-navigator-iframe");
@@ -196,6 +207,23 @@ export class FXLFrameManager {
196
207
  this.comms?.halt();
197
208
  }
198
209
 
210
+ private applyContentProtection() {
211
+ if (!this.comms) this.comms!.resume();
212
+
213
+ // Send content protection config
214
+ this.comms!.send("peripherals_protection", this.contentProtectionConfig);
215
+
216
+ // Send keyboard peripherals separately
217
+ if (this.keyboardPeripheralsConfig && this.keyboardPeripheralsConfig.length > 0) {
218
+ this.comms!.send("keyboard_peripherals", this.keyboardPeripheralsConfig);
219
+ }
220
+
221
+ // Apply print protection if configured
222
+ if (this.contentProtectionConfig.protectPrinting) {
223
+ this.comms!.send("print_protection", this.contentProtectionConfig.protectPrinting);
224
+ }
225
+ }
226
+
199
227
  private cachedPage: Page | undefined = undefined;
200
228
  async show(page: Page): Promise<void> {
201
229
  if(!this.frame.parentElement) {
@@ -221,6 +249,7 @@ export class FXLFrameManager {
221
249
  this.comms!.send("focus", undefined, (_: boolean) => {
222
250
  // this.showPromise = undefined; Don't do this
223
251
  this.update(this.cachedPage);
252
+ this.applyContentProtection();
224
253
  res();
225
254
  });
226
255
  });
@@ -5,7 +5,7 @@ import FrameBlobBuider from "../frame/FrameBlobBuilder";
5
5
  import { FXLFrameManager } from "./FXLFrameManager";
6
6
  import { FXLPeripherals } from "./FXLPeripherals";
7
7
  import { FXLSpreader, Orientation, Spread } from "./FXLSpreader";
8
- import { VisualNavigatorViewport } from "../../Navigator";
8
+ import { VisualNavigatorViewport, IContentProtectionConfig, IKeyboardPeripheralsConfig } from "../../Navigator";
9
9
  import { Injector } from "../../injection/Injector";
10
10
 
11
11
  const UPPER_BOUNDARY = 8;
@@ -28,6 +28,8 @@ export class FXLFramePoolManager {
28
28
  private currentBaseURL: string | undefined;
29
29
  private previousFrames: FXLFrameManager[] = [];
30
30
  private readonly injector: Injector | null = null;
31
+ private readonly contentProtectionConfig: IContentProtectionConfig;
32
+ private readonly keyboardPeripheralsConfig: IKeyboardPeripheralsConfig;
31
33
 
32
34
  // NEW
33
35
  private readonly bookElement: HTMLDivElement;
@@ -50,12 +52,16 @@ export class FXLFramePoolManager {
50
52
  container: HTMLElement,
51
53
  positions: Locator[],
52
54
  pub: Publication,
53
- injector?: Injector | null
55
+ injector?: Injector | null,
56
+ contentProtectionConfig?: IContentProtectionConfig,
57
+ keyboardPeripheralsConfig?: IKeyboardPeripheralsConfig,
54
58
  ) {
55
59
  this.container = container;
56
60
  this.positions = positions;
57
61
  this.pub = pub;
58
62
  this.injector = injector ?? null;
63
+ this.contentProtectionConfig = contentProtectionConfig || {};
64
+ this.keyboardPeripheralsConfig = keyboardPeripheralsConfig || [];
59
65
  this.spreadPresentation = pub.metadata.otherMetadata?.spread || Spread.auto;
60
66
 
61
67
  if(this.pub.metadata.effectiveReadingProgression !== ReadingProgression.rtl && this.pub.metadata.effectiveReadingProgression !== ReadingProgression.ltr)
@@ -82,7 +88,7 @@ export class FXLFramePoolManager {
82
88
 
83
89
  this.pub.readingOrder.items.forEach((link) => {
84
90
  // Create <iframe>
85
- const fm = new FXLFrameManager(this.peripherals, this.pub.metadata.effectiveReadingProgression, link.href);
91
+ const fm = new FXLFrameManager(this.peripherals, this.pub.metadata.effectiveReadingProgression, link.href, this.contentProtectionConfig, this.keyboardPeripheralsConfig);
86
92
  this.spineElement.appendChild(fm.element);
87
93
 
88
94
  // this.pages.push(fm);
@@ -53,8 +53,8 @@ export interface IEpubDefaults {
53
53
  scroll?: boolean | null,
54
54
  scrollPaddingTop?: number | null,
55
55
  scrollPaddingBottom?: number | null,
56
- // scrollPaddingLeft?: number | null,
57
- // scrollPaddingRight?: number | null,
56
+ scrollPaddingLeft?: number | null,
57
+ scrollPaddingRight?: number | null,
58
58
  selectionBackgroundColor?: string | null,
59
59
  selectionTextColor?: string | null,
60
60
  textAlign?: TextAlignment | null,
@@ -97,8 +97,8 @@ export class EpubDefaults {
97
97
  scroll: boolean | null;
98
98
  scrollPaddingTop: number | null;
99
99
  scrollPaddingBottom: number | null;
100
- // scrollPaddingLeft: number | null;
101
- // scrollPaddingRight: number | null;
100
+ scrollPaddingLeft: number | null;
101
+ scrollPaddingRight: number | null;
102
102
  selectionBackgroundColor: string | null;
103
103
  selectionTextColor: string | null;
104
104
  textAlign: TextAlignment | null;
@@ -144,8 +144,8 @@ export class EpubDefaults {
144
144
  this.scroll = ensureBoolean(defaults.scroll) ?? false;
145
145
  this.scrollPaddingTop = ensureNonNegative(defaults.scrollPaddingTop) ?? null;
146
146
  this.scrollPaddingBottom = ensureNonNegative(defaults.scrollPaddingBottom) ?? null;
147
- // this.scrollPaddingLeft = ensureNonNegative(defaults.scrollPaddingLeft) ?? null;
148
- // this.scrollPaddingRight = ensureNonNegative(defaults.scrollPaddingRight) ?? null;
147
+ this.scrollPaddingLeft = ensureNonNegative(defaults.scrollPaddingLeft) ?? null;
148
+ this.scrollPaddingRight = ensureNonNegative(defaults.scrollPaddingRight) ?? null;
149
149
  this.selectionBackgroundColor = ensureString(defaults.selectionBackgroundColor) || null;
150
150
  this.selectionTextColor = ensureString(defaults.selectionTextColor) || null;
151
151
  this.textAlign = ensureEnumValue<TextAlignment>(defaults.textAlign, TextAlignment) || null;