Skip to content

Commit

Permalink
Add configurable initialization for origin matching
Browse files Browse the repository at this point in the history
  • Loading branch information
calinoracation committed Feb 21, 2024
1 parent 398b1f6 commit 48765d0
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 62 deletions.
58 changes: 2 additions & 56 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,60 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import {
ScrollTimeline,
ViewTimeline,
} from "./scroll-timeline-base";
import {
animate,
elementGetAnimations,
documentGetAnimations,
ProxyAnimation
} from "./proxy-animation.js";
import { initPolyfill } from './initialize';

import { initCSSPolyfill } from "./scroll-timeline-css"

function initPolyfill() {
// initCSSPolyfill returns true iff the host browser supports SDA
if (initCSSPolyfill()) {
return;
}

if (
!Reflect.defineProperty(window, 'ScrollTimeline', { value: ScrollTimeline })
) {
throw Error(
'Error installing ScrollTimeline polyfill: could not attach ScrollTimeline to window'
);
}
if (
!Reflect.defineProperty(window, 'ViewTimeline', { value: ViewTimeline })
) {
throw Error(
'Error installing ViewTimeline polyfill: could not attach ViewTimeline to window'
);
}

if (
!Reflect.defineProperty(Element.prototype, 'animate', { value: animate })
) {
throw Error(
"Error installing ScrollTimeline polyfill: could not attach WAAPI's animate to DOM Element"
);
}
if (!Reflect.defineProperty(window, 'Animation', { value: ProxyAnimation })) {
throw Error('Error installing Animation constructor.');
}
if (!Reflect.defineProperty(Element.prototype, "getAnimations", { value: elementGetAnimations })) {
throw Error(
"Error installing ScrollTimeline polyfill: could not attach WAAPI's getAnimations to DOM Element"
);
}
if (!Reflect.defineProperty(document, "getAnimations", { value: documentGetAnimations })) {
throw Error(
"Error installing ScrollTimeline polyfill: could not attach WAAPI's getAnimations to document"
);
}
}

initPolyfill();
initPolyfill('*');
76 changes: 76 additions & 0 deletions src/initialize.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import {
ScrollTimeline,
ViewTimeline,
} from "./scroll-timeline-base";
import {
animate,
elementGetAnimations,
documentGetAnimations,
ProxyAnimation
} from "./proxy-animation.js";

import { initCSSPolyfill } from "./scroll-timeline-css"

/**
* Initializes the polyfill and by default will parse all stylesheets on the
* page.
* @param {'*' | string | RegExp | Array<string | RegExp>} allowedOrigins
* Defaults to *, which attempts to parse all linked stylesheets. It also
* accepts a string, a RegExp or an array of either.
*/
export function initPolyfill(allowedOrigins) {
// initCSSPolyfill returns true iff the host browser supports SDA
if (initCSSPolyfill(allowedOrigins)) {
return;
}

if (
!Reflect.defineProperty(window, 'ScrollTimeline', { value: ScrollTimeline })
) {
throw Error(
'Error installing ScrollTimeline polyfill: could not attach ScrollTimeline to window'
);
}
if (
!Reflect.defineProperty(window, 'ViewTimeline', { value: ViewTimeline })
) {
throw Error(
'Error installing ViewTimeline polyfill: could not attach ViewTimeline to window'
);
}

if (
!Reflect.defineProperty(Element.prototype, 'animate', { value: animate })
) {
throw Error(
"Error installing ScrollTimeline polyfill: could not attach WAAPI's animate to DOM Element"
);
}
if (!Reflect.defineProperty(window, 'Animation', { value: ProxyAnimation })) {
throw Error('Error installing Animation constructor.');
}
if (!Reflect.defineProperty(Element.prototype, "getAnimations", { value: elementGetAnimations })) {
throw Error(
"Error installing ScrollTimeline polyfill: could not attach WAAPI's getAnimations to DOM Element"
);
}
if (!Reflect.defineProperty(document, "getAnimations", { value: documentGetAnimations })) {
throw Error(
"Error installing ScrollTimeline polyfill: could not attach WAAPI's getAnimations to document"
);
}
}
32 changes: 26 additions & 6 deletions src/scroll-timeline-css.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import { ScrollTimeline, ViewTimeline, getScrollParent, calculateRange,

const parser = new StyleParser();

function initMutationObserver() {
/**
* @param {'*' | string | RegExp | Array<string | RegExp>} allowedOrigins
*/
function initMutationObserver(allowedOrigins) {
const sheetObserver = new MutationObserver((entries) => {
for (const entry of entries) {
for (const addedNode of entry.addedNodes) {
Expand Down Expand Up @@ -41,14 +44,31 @@ function initMutationObserver() {
el.innerHTML = newSrc;
}

function isAllowedOrigin(href) {
if (allowedOrigins == '*') return true;
const url = new URL(href, document.baseURI);
// If specified as false or anything not truthy, default to same origin.
if (!allowedOrigins) {
return url.origin == location.origin;
}
if (typeof allowedOrigins == 'string') {
return allowedOrigins == url.origin;
}
return allowedOrigins.some((matcher) => {
if (matcher instanceof RegExp) {
return matcher.test(url.origin);
}
return matcher == url.origin;
});
}

function handleLinkedStylesheet(linkElement) {
// Filter only css links to external stylesheets.
if (linkElement.type != 'text/css' && linkElement.rel != 'stylesheet' || !linkElement.href) {
return;
}
const url = new URL(linkElement.href, document.baseURI);
if (url.origin != location.origin) {
// Most likely we won't be able to fetch resources from other origins.
if (!isAllowedOrigin(linkElement.href)) {
// Don't attempt to fetch resources from regions not in the allowlist.
return;
}
fetch(linkElement.getAttribute('href')).then(async (response) => {
Expand Down Expand Up @@ -150,13 +170,13 @@ function updateKeyframesIfNecessary(anim, options) {
}
}

export function initCSSPolyfill() {
export function initCSSPolyfill(allowedOrigins) {
// Don't load if browser claims support
if (CSS.supports("animation-timeline: --works")) {
return true;
}

initMutationObserver();
initMutationObserver(allowedOrigins);

// Override CSS.supports() to claim support for the CSS properties from now on
const oldSupports = CSS.supports;
Expand Down

0 comments on commit 48765d0

Please sign in to comment.