diff --git a/navigator-html-injectables/src/helpers/css.ts b/navigator-html-injectables/src/helpers/css.ts index 845d4a61..6d2d1785 100644 --- a/navigator-html-injectables/src/helpers/css.ts +++ b/navigator-html-injectables/src/helpers/css.ts @@ -1,5 +1,7 @@ import { ReadiumWindow } from "./dom.ts"; +export const isTypedOMSupported = () => typeof window.CSSTransformValue !== 'undefined'; + const COSMETIC_PROPERTIES = new Set([ "backgroundColor", "textColor", "linkColor", "visitedColor", diff --git a/navigator-html-injectables/src/modules/snapper/ColumnSnapper.ts b/navigator-html-injectables/src/modules/snapper/ColumnSnapper.ts index 78adc946..18c5e9c9 100644 --- a/navigator-html-injectables/src/modules/snapper/ColumnSnapper.ts +++ b/navigator-html-injectables/src/modules/snapper/ColumnSnapper.ts @@ -9,6 +9,7 @@ import { rangeFromLocator } from "../../helpers/locator.ts"; import { ReadiumWindow, deselect, findFirstVisibleLocator } from "../../helpers/dom.ts"; import { PatternAnalyzer } from "../../protection/PatternAnalyzer.ts"; import { BaseSuspiciousActivityEvent } from "../Peripherals.ts"; +import { isTypedOMSupported } from "../../helpers/css.ts"; const COLUMN_SNAPPER_STYLE_ID = "readium-column-snapper-style"; @@ -167,7 +168,13 @@ export class ColumnSnapper extends Snapper { const spos = position(currentScrollLeft, so, elapsed, period); doc.scrollLeft = spos; if(this.overscroll !== 0) - doc.style.transform = `translate3d(${-lpos}px, 0px, 0px)`; + if(isTypedOMSupported()) { + doc.attributeStyleMap.set("transform", new CSSTransformValue([ + new CSSTranslate(CSS.px(-lpos), CSS.px(0), CSS.px(0)) + ])); + } else { + doc.style.transform = `translate3d(${-lpos}px, 0px, 0px)`; + } if (elapsed < period) this.wnd.requestAnimationFrame(step); @@ -283,10 +290,22 @@ export class ColumnSnapper extends Snapper { if(newpos < minScrollLeft) { this.overscroll = newpos; - this.doc().style.transform = `translate3d(${-this.overscroll}px, 0px, 0px)`; + if(isTypedOMSupported()) { + this.doc().attributeStyleMap.set("transform", new CSSTransformValue([ + new CSSTranslate(CSS.px(-newpos), CSS.px(0), CSS.px(0)) + ])); + } else { + this.doc().style.transform = `translate3d(${-newpos}px, 0px, 0px)`; + } } else if(newpos > maxScrollLeft) { this.overscroll = newpos; - this.doc().style.transform = `translate3d(${-newpos}px, 0px, 0px)`; + if(isTypedOMSupported()) { + this.doc().attributeStyleMap.set("transform", new CSSTransformValue([ + new CSSTranslate(CSS.px(-newpos), CSS.px(0), CSS.px(0)) + ])); + } else { + this.doc().style.transform = `translate3d(${-newpos}px, 0px, 0px)`; + } } else { this.overscroll = 0; this.doc().style.removeProperty("transform"); diff --git a/navigator/src/audio/AudioNavigator.ts b/navigator/src/audio/AudioNavigator.ts index ba26083d..ae3612c0 100644 --- a/navigator/src/audio/AudioNavigator.ts +++ b/navigator/src/audio/AudioNavigator.ts @@ -1,7 +1,7 @@ import { Link, Locator, LocatorLocations, Publication, Timeline, TimelineItem } from "@readium/shared"; import { MediaNavigator, IContentProtectionConfig, IKeyboardPeripheralsConfig, KeyboardPeripheralEventData } from "../Navigator.ts"; import { Configurable } from "../preferences/Configurable.ts"; -import { WebAudioEngine, PlaybackState } from "./engine/index.ts"; +import { WebAudioEngine, PlaybackState, AudioMseLoaderFactory } from "./engine/index.ts"; import { AudioPreferences, AudioDefaults, @@ -69,6 +69,26 @@ export interface AudioNavigatorConfiguration { defaults: IAudioDefaults; contentProtection?: IAudioContentProtectionConfig; keyboardPeripherals?: IKeyboardPeripheralsConfig; + + /** + * Called with the persistent playback element before + * the first src is assigned, so the host can prepare it. Use for MSE/EME + * setups. When it returns a promise, loading of + * the initial track (and prefetching of adjacent ones) is deferred until + * the promise settles; a rejection is forwarded to the error listener and + * loading proceeds anyway. + */ + mediaElementSetup?: (element: HTMLMediaElement) => void | Promise; + + /** + * When provided, media bytes reach the playback element through Media + * Source Extensions instead of direct `src` assignment: the engine + * creates one loader per track via this factory and the loader owns all + * fetching. Supply one for e.g. EME encrypted audio, since browsers have + * poor support for encrypted audio directly through `src`. The container + * handling (WebM, fMP4, …) is the loader implementation's concern. + */ + mseLoaderFactory?: AudioMseLoaderFactory; } export class AudioNavigator extends MediaNavigator implements Configurable { @@ -94,6 +114,8 @@ export class AudioNavigator extends MediaNavigator implements Configurable