From 8cf865298f71ec6b4701bd691d3f2433a620432a Mon Sep 17 00:00:00 2001 From: niutech Date: Mon, 12 Jul 2021 21:54:20 +0200 Subject: [PATCH 1/2] Add document.location --- .../commands/event-subscription.ts | 8 +- src/main-thread/commands/location.ts | 80 ++++++ src/main-thread/commands/object-creation.ts | 1 + src/main-thread/commands/object-mutation.ts | 1 + src/main-thread/commands/offscreen-canvas.ts | 1 + src/main-thread/configuration.ts | 1 + src/main-thread/debugging.ts | 1 + src/main-thread/index.amp.ts | 1 + src/main-thread/main-thread.d.ts | 15 ++ src/main-thread/mutator.ts | 1 + src/main-thread/nodes.ts | 1 + src/main-thread/serialize.ts | 1 + src/main-thread/worker.ts | 1 + src/test/DocumentCreation.ts | 2 + src/transfer/Messages.ts | 10 +- src/transfer/TransferrableKeys.ts | 3 +- src/transfer/TransferrableMutation.ts | 17 ++ src/worker-thread/LocationPropagation.ts | 35 +++ src/worker-thread/WorkerDOMGlobalScope.ts | 2 + src/worker-thread/dom/Document.ts | 4 +- src/worker-thread/dom/Location.ts | 242 ++++++++++++++++++ src/worker-thread/index.amp.ts | 2 + src/worker-thread/index.ts | 2 + web_compat_table.md | 3 +- 24 files changed, 426 insertions(+), 9 deletions(-) create mode 100644 src/main-thread/commands/location.ts create mode 100644 src/worker-thread/LocationPropagation.ts create mode 100644 src/worker-thread/dom/Location.ts diff --git a/src/main-thread/commands/event-subscription.ts b/src/main-thread/commands/event-subscription.ts index 1aedd4a34..56d98f9cf 100644 --- a/src/main-thread/commands/event-subscription.ts +++ b/src/main-thread/commands/event-subscription.ts @@ -27,6 +27,7 @@ import { WorkerContext } from '../worker'; import { CommandExecutorInterface } from './interface'; import { TransferrableMutationType } from '../../transfer/TransferrableMutation'; import { BASE_ELEMENT_INDEX } from '../nodes'; +import { RenderableElement } from '../main-thread'; /** * Monitoring Nodes attribute changes requires a Mutation Observer. @@ -46,7 +47,7 @@ const shouldTrackChanges = (node: HTMLElement): boolean => node && 'value' in no /** * When a node that has a value needing synced doesn't already have an event listener * listening for input values, ensure the value is synced with a default listener. - * @param worker whom to dispatch value toward. + * @param workerContext worker whom to dispatch value toward. * @param node node to listen to value changes on. */ export const applyDefaultInputListener = (workerContext: WorkerContext, node: RenderableElement): void => { @@ -57,7 +58,7 @@ export const applyDefaultInputListener = (workerContext: WorkerContext, node: Re /** * Use a MutationObserver to capture value changes based on Attribute modification (frequently used by frameworks). - * @param worker whom to dispatch value toward. + * @param workerContext worker whom to dispatch value toward. * @param node node to listen to value changes on. */ export const sendValueChangeOnAttributeMutation = (workerContext: WorkerContext, node: RenderableElement): void => { @@ -71,7 +72,7 @@ export const sendValueChangeOnAttributeMutation = (workerContext: WorkerContext, /** * Tell WorkerDOM what the value is for a Node. - * @param worker whom to dispatch value toward. + * @param workerContext worker whom to dispatch value toward. * @param node where to get the value from. */ const fireValueChange = (workerContext: WorkerContext, node: RenderableElement): void => @@ -117,7 +118,6 @@ export const EventSubscriptionProcessor: CommandExecutorInterface = (strings, no /** * Register an event handler for dispatching events to worker thread - * @param worker whom to dispatch events toward * @param index node index the event comes from (used to dispatchEvent in worker thread). * @return eventHandler function consuming event and dispatching to worker thread */ diff --git a/src/main-thread/commands/location.ts b/src/main-thread/commands/location.ts new file mode 100644 index 000000000..106acea9a --- /dev/null +++ b/src/main-thread/commands/location.ts @@ -0,0 +1,80 @@ +/** + * Copyright 2019 The AMP HTML Authors. All Rights Reserved. + * + * 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 + * + * http://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 { CommandExecutorInterface } from './interface'; +import { + TransferrableMutationType, + LocationMutationIndex +} from '../../transfer/TransferrableMutation'; +import { TransferrableKeys } from '../../transfer/TransferrableKeys'; +import { MessageType, LocationToWorker, GetOrSet } from '../../transfer/Messages'; +import { Location } from '../../worker-thread/dom/Location'; + +export const LocationProcessor: CommandExecutorInterface = (strings, nodeContext, workerContext, objectContext, config) => { + const allowedExecution = config.executorsAllowed.includes(TransferrableMutationType.LOCATION); + + const get = (): void => { + if (config.sanitizer) { + config.sanitizer.getLocation().then((value: Location) => { + const message: LocationToWorker = { + [TransferrableKeys.type]: MessageType.LOCATION, + [TransferrableKeys.location]: value, + }; + workerContext.messageToWorker(message); + }); + } else { + console.error(`LOCATION: Sanitizer not found`); + } + }; + + const set = (location: string): void => { + if (config.sanitizer) { + config.sanitizer.setLocation(location); + } else { + window.location.href = location; + } + }; + + return { + execute(mutations: Uint16Array, startPosition: number, allowedMutation: boolean): number { + if (allowedExecution && allowedMutation) { + const operation = mutations[startPosition + LocationMutationIndex.Operation]; + const locationIndex = mutations[startPosition + LocationMutationIndex.Location]; + + const location = strings.get(locationIndex); + + if (operation === GetOrSet.GET) { + get(); + } else if (operation === GetOrSet.SET) { + set(location); + } + } + + return startPosition + LocationMutationIndex.End; + }, + print(mutations: Uint16Array, startPosition: number): {} { + const operation = mutations[startPosition + LocationMutationIndex.Operation]; + const location = mutations[startPosition + LocationMutationIndex.Location]; + + return { + type: 'LOCATION', + operation, + location, + allowedExecution, + }; + }, + }; +}; diff --git a/src/main-thread/commands/object-creation.ts b/src/main-thread/commands/object-creation.ts index 3534e1cd7..54b55398c 100644 --- a/src/main-thread/commands/object-creation.ts +++ b/src/main-thread/commands/object-creation.ts @@ -17,6 +17,7 @@ import { TransferrableMutationType, ObjectCreationIndex } from '../../transfer/TransferrableMutation'; import { CommandExecutorInterface } from './interface'; import { deserializeTransferrableObject } from '../deserializeTransferrableObject'; +import { RenderableElement } from '../main-thread'; export const ObjectCreationProcessor: CommandExecutorInterface = (strings, nodeContext, workerContext, objectContext, config) => { const allowedExecution = config.executorsAllowed.includes(TransferrableMutationType.OBJECT_CREATION); diff --git a/src/main-thread/commands/object-mutation.ts b/src/main-thread/commands/object-mutation.ts index cd9bba885..ae343dc3f 100644 --- a/src/main-thread/commands/object-mutation.ts +++ b/src/main-thread/commands/object-mutation.ts @@ -17,6 +17,7 @@ import { TransferrableMutationType, ObjectMutationIndex } from '../../transfer/TransferrableMutation'; import { CommandExecutorInterface } from './interface'; import { deserializeTransferrableObject } from '../deserializeTransferrableObject'; +import { RenderableElement } from '../main-thread'; export const ObjectMutationProcessor: CommandExecutorInterface = (strings, nodeContext, workerContext, objectContext, config) => { const allowedExecution = config.executorsAllowed.includes(TransferrableMutationType.OBJECT_MUTATION); diff --git a/src/main-thread/commands/offscreen-canvas.ts b/src/main-thread/commands/offscreen-canvas.ts index 9080c977e..a0460187f 100644 --- a/src/main-thread/commands/offscreen-canvas.ts +++ b/src/main-thread/commands/offscreen-canvas.ts @@ -18,6 +18,7 @@ import { TransferrableKeys } from '../../transfer/TransferrableKeys'; import { MessageType } from '../../transfer/Messages'; import { CommandExecutorInterface } from './interface'; import { OffscreenCanvasMutationIndex, TransferrableMutationType } from '../../transfer/TransferrableMutation'; +import { RenderableElement } from '../main-thread'; export const OffscreenCanvasProcessor: CommandExecutorInterface = (strings, nodeContext, workerContext, objectContext, config) => { const allowedExecution = config.executorsAllowed.includes(TransferrableMutationType.OFFSCREEN_CANVAS_INSTANCE); diff --git a/src/main-thread/configuration.ts b/src/main-thread/configuration.ts index ba7b18e38..1b4c8c594 100644 --- a/src/main-thread/configuration.ts +++ b/src/main-thread/configuration.ts @@ -18,6 +18,7 @@ import { MessageFromWorker, MessageToWorker } from '../transfer/Messages'; import { Phase } from '../transfer/Phase'; import { HydrateableNode } from '../transfer/TransferrableNodes'; import { DefaultAllowedMutations } from '../transfer/TransferrableMutation'; +import { RenderableElement, Sanitizer } from './main-thread'; /** * The callback for `mutationPump`. If specified, this callback will be called diff --git a/src/main-thread/debugging.ts b/src/main-thread/debugging.ts index 66bc8adf4..736bd843a 100644 --- a/src/main-thread/debugging.ts +++ b/src/main-thread/debugging.ts @@ -38,6 +38,7 @@ import { TransferrableSyncValue } from '../transfer/TransferrableSyncValue'; import { createReadableHydrateableRootNode } from './serialize'; import { WorkerDOMConfiguration } from './configuration'; import { WorkerContext } from './worker'; +import { RenderableElement } from './main-thread'; /** * @param element diff --git a/src/main-thread/index.amp.ts b/src/main-thread/index.amp.ts index 571ec111d..32ecdf679 100644 --- a/src/main-thread/index.amp.ts +++ b/src/main-thread/index.amp.ts @@ -22,6 +22,7 @@ import { fetchAndInstall, install } from './install'; import { WorkerDOMConfiguration, LongTaskFunction } from './configuration'; import { toLower } from '../utils'; import { ExportedWorker } from './exported-worker'; +import { RenderableElement, Sanitizer } from './main-thread'; /** * AMP Element Children need to be filtered from Hydration, to avoid Author Code from manipulating it. diff --git a/src/main-thread/main-thread.d.ts b/src/main-thread/main-thread.d.ts index c40c84b90..10407fc8d 100644 --- a/src/main-thread/main-thread.d.ts +++ b/src/main-thread/main-thread.d.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +import { Location } from '../worker-thread/dom/Location'; + /** * Allows clients to apply restrictions on DOM and storage changes. */ @@ -58,6 +60,19 @@ declare interface Sanitizer { * @return True if storage change was applied. */ setStorage(location: number, key: string | null, value: string | null): boolean; + + /** + * Retrieves the current location. + * @return current location + */ + getLocation(): Promise; + + /** + * Requests a change in location. + * @param location href + * @return True if location change was applied. + */ + setLocation(location: string): boolean; } type StorageValue = { [key: string]: string }; diff --git a/src/main-thread/mutator.ts b/src/main-thread/mutator.ts index 5d4a02d85..b4519fc21 100644 --- a/src/main-thread/mutator.ts +++ b/src/main-thread/mutator.ts @@ -36,6 +36,7 @@ import { ImageBitmapProcessor } from './commands/image-bitmap'; import { StorageProcessor } from './commands/storage'; import { FunctionProcessor } from './commands/function'; import { ScrollIntoViewProcessor } from './commands/scroll-into-view'; +import { Sanitizer, WORKER_DOM_DEBUG } from './main-thread'; export class MutatorProcessor { private stringContext: StringContext; diff --git a/src/main-thread/nodes.ts b/src/main-thread/nodes.ts index 657fddf37..5ae8fabc8 100644 --- a/src/main-thread/nodes.ts +++ b/src/main-thread/nodes.ts @@ -16,6 +16,7 @@ import { NodeType, TransferrableNodeIndex } from '../transfer/TransferrableNodes'; import { StringContext } from './strings'; +import { RenderableElement, Sanitizer } from './main-thread'; /** * IE11 doesn't support NodeList.prototype.forEach diff --git a/src/main-thread/serialize.ts b/src/main-thread/serialize.ts index 9ae4fdb55..5e8ea0027 100644 --- a/src/main-thread/serialize.ts +++ b/src/main-thread/serialize.ts @@ -20,6 +20,7 @@ import { NumericBoolean } from '../utils'; import { WorkerDOMConfiguration, HydrationFilterPredicate } from './configuration'; import { applyDefaultInputListener, sendValueChangeOnAttributeMutation } from './commands/event-subscription'; import { WorkerContext } from './worker'; +import { RenderableElement } from './main-thread'; const NODES_ALLOWED_TO_TRANSMIT_TEXT_CONTENT = [NodeType.COMMENT_NODE, NodeType.TEXT_NODE]; diff --git a/src/main-thread/worker.ts b/src/main-thread/worker.ts index b4b409066..02671fbc0 100644 --- a/src/main-thread/worker.ts +++ b/src/main-thread/worker.ts @@ -22,6 +22,7 @@ import { NodeContext } from './nodes'; import { TransferrableKeys } from '../transfer/TransferrableKeys'; import { StorageLocation } from '../transfer/TransferrableStorage'; import { IframeWorker } from './iframe-worker'; +import { IS_AMP, Sanitizer, StorageValue, WORKER_DOM_DEBUG } from './main-thread'; // TODO: Sanitizer storage init is likely broken, since the code currently // attempts to stringify a Promise. diff --git a/src/test/DocumentCreation.ts b/src/test/DocumentCreation.ts index b77578ad8..3268137f8 100644 --- a/src/test/DocumentCreation.ts +++ b/src/test/DocumentCreation.ts @@ -60,6 +60,7 @@ import { DOMTokenList } from '../worker-thread/dom/DOMTokenList'; import { HTMLDataListElement } from '../worker-thread/dom/HTMLDataListElement'; import { Element } from '../worker-thread/dom/Element'; import { rafPolyfill, cafPolyfill } from '../worker-thread/AnimationFrame'; +import { Location } from '../worker-thread/dom/Location'; Object.defineProperty(global, 'ServiceWorkerContainer', { configurable: true, @@ -130,6 +131,7 @@ const GlobalScope: GlobalScope = { MutationObserver, requestAnimationFrame: rafPolyfill, cancelAnimationFrame: cafPolyfill, + location: Location, }; /** diff --git a/src/transfer/Messages.ts b/src/transfer/Messages.ts index f0c757f99..d4d028eb3 100644 --- a/src/transfer/Messages.ts +++ b/src/transfer/Messages.ts @@ -21,6 +21,7 @@ import { HydrateableNode, TransferredNode } from './TransferrableNodes'; import { TransferrableBoundingClientRect } from './TransferrableBoundClientRect'; import { Phase } from './Phase'; import { StorageLocation } from './TransferrableStorage'; +import { Location } from '../worker-thread/dom/Location'; export const enum MessageType { // INIT = 0, @@ -36,6 +37,7 @@ export const enum MessageType { IMAGE_BITMAP_INSTANCE = 10, GET_STORAGE = 11, FUNCTION = 12, + LOCATION = 13, // NAVIGATION_PUSH_STATE = 8, // NAVIGATION_REPLACE_STATE = 9, // NAVIGATION_POP_STATE = 10, @@ -90,7 +92,10 @@ export interface StorageValueToWorker { [TransferrableKeys.storageLocation]: StorageLocation; [TransferrableKeys.value]: { [key: string]: string }; } - +export interface LocationToWorker { + [TransferrableKeys.type]: MessageType.LOCATION; + [TransferrableKeys.location]: Location; +} export interface FunctionCallToWorker { [TransferrableKeys.type]: MessageType.FUNCTION; [TransferrableKeys.index]: number; @@ -106,7 +111,8 @@ export type MessageToWorker = | OffscreenCanvasToWorker | ImageBitmapToWorker | StorageValueToWorker - | FunctionCallToWorker; + | FunctionCallToWorker + | LocationToWorker; /** * Can parameterize a method invocation message as a getter or setter. diff --git a/src/transfer/TransferrableKeys.ts b/src/transfer/TransferrableKeys.ts index ad8c4e914..49396043c 100644 --- a/src/transfer/TransferrableKeys.ts +++ b/src/transfer/TransferrableKeys.ts @@ -94,6 +94,7 @@ export const enum TransferrableKeys { propertyEventHandlers = 76, functionIdentifier = 77, functionArguments = 78, + location = 79, // This must always be the last numerically ordered Key, for testing purposes. - END = 78, + END = 79, } diff --git a/src/transfer/TransferrableMutation.ts b/src/transfer/TransferrableMutation.ts index 9833dbed8..3090dea2c 100644 --- a/src/transfer/TransferrableMutation.ts +++ b/src/transfer/TransferrableMutation.ts @@ -30,6 +30,7 @@ export const enum TransferrableMutationType { STORAGE = 12, FUNCTION_CALL = 13, SCROLL_INTO_VIEW = 14, + LOCATION = 15, } /** @@ -67,6 +68,7 @@ export const DefaultAllowedMutations = [ TransferrableMutationType.STORAGE, TransferrableMutationType.FUNCTION_CALL, TransferrableMutationType.SCROLL_INTO_VIEW, + TransferrableMutationType.LOCATION, ]; export const ReadableMutationType: { [key: number]: string } = { @@ -85,6 +87,7 @@ export const ReadableMutationType: { [key: number]: string } = { 12: 'STORAGE', 13: 'FUNCTION_INVOCATION', 14: 'SCROLL_INTO_VIEW', + 15: 'LOCATION', }; /** @@ -286,3 +289,17 @@ export const enum ScrollIntoViewMutationIndex { Target = 1, End = 2, } + +/** + * Location Mutation + * [ + * TransferrableMutationType.LOCATION, + * GetOrSet, + * Location + * ] + */ +export const enum LocationMutationIndex { + Operation = 1, + Location = 2, + End = 3, +} diff --git a/src/worker-thread/LocationPropagation.ts b/src/worker-thread/LocationPropagation.ts new file mode 100644 index 000000000..74506c63c --- /dev/null +++ b/src/worker-thread/LocationPropagation.ts @@ -0,0 +1,35 @@ +/** + * Copyright 2018 The AMP HTML Authors. All Rights Reserved. + * + * 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 + * + * http://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 { MessageToWorker, MessageType, LocationToWorker } from '../transfer/Messages'; +import { TransferrableKeys } from '../transfer/TransferrableKeys'; +import { WorkerDOMGlobalScope } from './WorkerDOMGlobalScope'; + +export function propagate(global: WorkerDOMGlobalScope): void { + const document = global.document; + if (!document.addGlobalEventListener) { + return; + } + document.addGlobalEventListener('message', ({ data }: { data: MessageToWorker }) => { + if (data[TransferrableKeys.type] !== MessageType.LOCATION) { + return; + } + const location = (data as LocationToWorker)[TransferrableKeys.location]; + if (location) { + document.location = location; + } + }); +} diff --git a/src/worker-thread/WorkerDOMGlobalScope.ts b/src/worker-thread/WorkerDOMGlobalScope.ts index 430efcfa1..4f4bdf486 100644 --- a/src/worker-thread/WorkerDOMGlobalScope.ts +++ b/src/worker-thread/WorkerDOMGlobalScope.ts @@ -57,6 +57,7 @@ import { DocumentFragment } from './dom/DocumentFragment'; import { DOMTokenList } from './dom/DOMTokenList'; import { Element } from './dom/Element'; import { DocumentStub } from './dom/DocumentLite'; +import { Location } from './dom/Location'; /** * Should only contain properties that exist on Window. @@ -114,6 +115,7 @@ export interface GlobalScope { ImageBitmap?: typeof ImageBitmap; requestAnimationFrame: typeof requestAnimationFrame; cancelAnimationFrame: typeof cancelAnimationFrame; + location: typeof Location; } export interface WorkerDOMGlobalScope extends GlobalScope { diff --git a/src/worker-thread/dom/Document.ts b/src/worker-thread/dom/Document.ts index b647af889..55579a0aa 100644 --- a/src/worker-thread/dom/Document.ts +++ b/src/worker-thread/dom/Document.ts @@ -52,6 +52,7 @@ import { Text } from './Text'; import { Comment } from './Comment'; import { toLower } from '../../utils'; import { DocumentFragment } from './DocumentFragment'; +import { Location } from "./Location"; import { PostMessage } from '../worker-thread'; import { NodeType, HTML_NAMESPACE, HydrateableNode } from '../../transfer/TransferrableNodes'; import { Phase } from '../../transfer/Phase'; @@ -68,6 +69,7 @@ export class Document extends Element { public defaultView: WorkerDOMGlobalScope; public documentElement: Document; public body: Element; + public location: Location; // Internal variables. public postMessage: PostMessage; @@ -80,11 +82,11 @@ export class Document extends Element { // Element uppercases its nodeName, but Document doesn't. this.nodeName = DOCUMENT_NAME; this.documentElement = this; // TODO(choumx): Should be the element. - this.defaultView = Object.assign(global, { document: this, addEventListener: this.addEventListener.bind(this), removeEventListener: this.removeEventListener.bind(this), + location: this.location, }); } diff --git a/src/worker-thread/dom/Location.ts b/src/worker-thread/dom/Location.ts new file mode 100644 index 000000000..4f29bfda5 --- /dev/null +++ b/src/worker-thread/dom/Location.ts @@ -0,0 +1,242 @@ +/** + * Copyright 2018 The AMP HTML Authors. All Rights Reserved. + * + * 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 + * + * http://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 { Document} from './Document'; +import { GetOrSet } from '../../transfer/Messages'; +import { TransferrableMutationType } from '../../transfer/TransferrableMutation'; +import { store } from '../strings'; +import { transfer } from '../MutationTransfer'; + +// @see https://developer.mozilla.org/en-US/docs/Web/API/Location +export class Location { + private _href: string; + private _origin: string; + private _protocol: string; + private _host: string; + private _hostname: string; + private _port: string; + private _pathname: string; + private _search: string; + private _hash: string; + private _document: Document; + + /** + * Getter for href + * @see https://developer.mozilla.org/en-US/docs/Web/API/Location/href + * @return string href + */ + get href(): string { + return this._href; + } + + /** + * Setter for href + * @see https://developer.mozilla.org/en-US/docs/Web/API/Location/href + * @param href + */ + set href(href: string) { + this._href = href; + transfer(this._document, [TransferrableMutationType.LOCATION, GetOrSet.SET, store(this.toString())]); + } + + /** + * Getter for origin + * @see https://developer.mozilla.org/en-US/docs/Web/API/Location/origin + * @return string origin + */ + get origin(): string { + return this._origin; + } + + /** + * Setter for origin + * @see https://developer.mozilla.org/en-US/docs/Web/API/Location/origin + * @param origin + */ + set origin(origin: string) { + this._origin = origin; + transfer(this._document, [TransferrableMutationType.LOCATION, GetOrSet.SET, store(this.toString())]); + } + + /** + * Getter for protocol + * @see https://developer.mozilla.org/en-US/docs/Web/API/Location/protocol + * @return string protocol + */ + get protocol(): string { + return this._protocol; + } + + /** + * Setter for protocol + * @see https://developer.mozilla.org/en-US/docs/Web/API/Location/protocol + * @param protocol + */ + set protocol(protocol: string) { + this._protocol = protocol; + transfer(this._document, [TransferrableMutationType.LOCATION, GetOrSet.SET, store(this.toString())]); + } + + /** + * Getter for host + * @see https://developer.mozilla.org/en-US/docs/Web/API/Location/host + * @return string host + */ + get host(): string { + return this._host; + } + + /** + * Setter for host + * @see https://developer.mozilla.org/en-US/docs/Web/API/Location/host + * @param host + */ + set host(host: string) { + this._host = host; + transfer(this._document, [TransferrableMutationType.LOCATION, GetOrSet.SET, store(this.toString())]); + } + + /** + * Getter for hostname + * @see https://developer.mozilla.org/en-US/docs/Web/API/Location/hostname + * @return string hostname + */ + get hostname(): string { + return this._hostname; + } + + /** + * Setter for hostname + * @see https://developer.mozilla.org/en-US/docs/Web/API/Location/hostname + * @param hostname + */ + set hostname(hostname: string) { + this._hostname = hostname; + transfer(this._document, [TransferrableMutationType.LOCATION, GetOrSet.SET, store(this.toString())]); + } + + /** + * Getter for port + * @see https://developer.mozilla.org/en-US/docs/Web/API/Location/port + * @return string port + */ + get port(): string { + return this._port; + } + + /** + * Setter for port + * @see https://developer.mozilla.org/en-US/docs/Web/API/Location/port + * @param port + */ + set port(port: string) { + this._port = port; + transfer(this._document, [TransferrableMutationType.LOCATION, GetOrSet.SET, store(this.toString())]); + } + + /** + * Getter for pathname + * @see https://developer.mozilla.org/en-US/docs/Web/API/Location/pathname + * @return string pathname + */ + get pathname(): string { + return this._pathname; + } + + /** + * Setter for pathname + * @see https://developer.mozilla.org/en-US/docs/Web/API/Location/pathname + * @param pathname + */ + set pathname(pathname: string) { + this._pathname = pathname; + transfer(this._document, [TransferrableMutationType.LOCATION, GetOrSet.SET, store(this.toString())]); + } + + /** + * Getter for search + * @see https://developer.mozilla.org/en-US/docs/Web/API/Location/search + * @return string search + */ + get search(): string { + return this._search; + } + + /** + * Setter for search + * @see https://developer.mozilla.org/en-US/docs/Web/API/Location/search + * @param search + */ + set search(search: string) { + this._search = search; + transfer(this._document, [TransferrableMutationType.LOCATION, GetOrSet.SET, store(this.toString())]); + } + + /** + * Getter for hash + * @see https://developer.mozilla.org/en-US/docs/Web/API/Location/hash + * @return string hash + */ + get hash(): string { + return this._hash; + } + + /** + * Setter for hash + * @see https://developer.mozilla.org/en-US/docs/Web/API/Location/hash + * @param hash + */ + set hash(hash: string) { + this._hash = hash; + transfer(this._document, [TransferrableMutationType.LOCATION, GetOrSet.SET, store(this.toString())]); + } + + /** + * Loads the resource at the URL provided in parameter. + * @see https://developer.mozilla.org/en-US/docs/Web/API/Location/assign + * @param url URL + */ + public assign(url: string): void { + + } + + + /** + * Redirects to the provided URL. + * @see https://developer.mozilla.org/en-US/docs/Web/API/Location/replace + * @param url URL + */ + public replace(url: string): void { + + } + + /** + * Reloads the current URL, like the Refresh button. + * @see https://developer.mozilla.org/en-US/docs/Web/API/Location/reload + */ + public reload(): void { + + } + + /** + * Returns a string containing the whole URL. + * @see https://developer.mozilla.org/en-US/docs/Web/API/Location/toString + * @return string URL + */ + public toString(): string { + return this.href; + } +} diff --git a/src/worker-thread/index.amp.ts b/src/worker-thread/index.amp.ts index e7eab7257..4f0e08798 100644 --- a/src/worker-thread/index.amp.ts +++ b/src/worker-thread/index.amp.ts @@ -63,6 +63,7 @@ import { SVGElement } from './dom/SVGElement'; import { Text } from './dom/Text'; import { wrap as longTaskWrap } from './long-task'; import { HydrateFunction } from './hydrate'; +import { Location } from './dom/Location'; declare const WORKER_DOM_DEBUG: boolean; @@ -112,6 +113,7 @@ const globalScope: GlobalScope = { MutationObserver, requestAnimationFrame: self.requestAnimationFrame || rafPolyfill, cancelAnimationFrame: self.cancelAnimationFrame || cafPolyfill, + location: Location, }; const noop = () => void 0; diff --git a/src/worker-thread/index.ts b/src/worker-thread/index.ts index 9a653bcad..def5e3d33 100644 --- a/src/worker-thread/index.ts +++ b/src/worker-thread/index.ts @@ -59,6 +59,7 @@ import { DocumentFragment } from './dom/DocumentFragment'; import { Element } from './dom/Element'; import { rafPolyfill, cafPolyfill } from './AnimationFrame'; import { HydrateFunction } from './hydrate'; +import { Location } from './dom/Location'; const globalScope: GlobalScope = { innerWidth: 0, @@ -106,6 +107,7 @@ const globalScope: GlobalScope = { MutationObserver, requestAnimationFrame: self.requestAnimationFrame || rafPolyfill, cancelAnimationFrame: self.cancelAnimationFrame || cafPolyfill, + location: Location }; const noop = () => void 0; diff --git a/web_compat_table.md b/web_compat_table.md index aed29248c..36d705c70 100644 --- a/web_compat_table.md +++ b/web_compat_table.md @@ -95,7 +95,7 @@ This section highlights the DOM APIs that are implemented in WorkerDOM currently | Document.lastModified | ✖️ | | | Document.lastStyleSheetSet | ✖️ | | | Document.links | ✖️ | | -| Document.location | ✖️ | | +| Document.location | ✔️ | | | Document.mozSetImageElement() | ✖️ | | | Document.mozSyntheticDocument | ✖️ | | | Document.normalizeDocument() | ✖️ | | @@ -900,6 +900,7 @@ This section highlights the DOM APIs that are implemented in WorkerDOM currently | Window.isSecureContext | ✖️ | | | Window.length | ✖️ | | | Window.localStorage | ✔️ | | +| Window.location | ✔️ | | | Window.locationbar | ✖️ | | | Window.matchMedia() | ✖️ | | | Window.maximize() | ✖️ | | From 8d5e40b023b4f30593d14f7b16edba8239d502f0 Mon Sep 17 00:00:00 2001 From: niutech Date: Mon, 12 Jul 2021 22:13:34 +0200 Subject: [PATCH 2/2] Restore unnecessary imports --- src/main-thread/commands/event-subscription.ts | 8 ++++---- src/main-thread/commands/object-creation.ts | 1 - src/main-thread/commands/object-mutation.ts | 1 - src/main-thread/commands/offscreen-canvas.ts | 1 - src/main-thread/configuration.ts | 1 - src/main-thread/debugging.ts | 1 - src/main-thread/index.amp.ts | 1 - src/main-thread/mutator.ts | 1 - src/main-thread/nodes.ts | 1 - src/main-thread/serialize.ts | 1 - src/main-thread/worker.ts | 1 - 11 files changed, 4 insertions(+), 14 deletions(-) diff --git a/src/main-thread/commands/event-subscription.ts b/src/main-thread/commands/event-subscription.ts index 56d98f9cf..1aedd4a34 100644 --- a/src/main-thread/commands/event-subscription.ts +++ b/src/main-thread/commands/event-subscription.ts @@ -27,7 +27,6 @@ import { WorkerContext } from '../worker'; import { CommandExecutorInterface } from './interface'; import { TransferrableMutationType } from '../../transfer/TransferrableMutation'; import { BASE_ELEMENT_INDEX } from '../nodes'; -import { RenderableElement } from '../main-thread'; /** * Monitoring Nodes attribute changes requires a Mutation Observer. @@ -47,7 +46,7 @@ const shouldTrackChanges = (node: HTMLElement): boolean => node && 'value' in no /** * When a node that has a value needing synced doesn't already have an event listener * listening for input values, ensure the value is synced with a default listener. - * @param workerContext worker whom to dispatch value toward. + * @param worker whom to dispatch value toward. * @param node node to listen to value changes on. */ export const applyDefaultInputListener = (workerContext: WorkerContext, node: RenderableElement): void => { @@ -58,7 +57,7 @@ export const applyDefaultInputListener = (workerContext: WorkerContext, node: Re /** * Use a MutationObserver to capture value changes based on Attribute modification (frequently used by frameworks). - * @param workerContext worker whom to dispatch value toward. + * @param worker whom to dispatch value toward. * @param node node to listen to value changes on. */ export const sendValueChangeOnAttributeMutation = (workerContext: WorkerContext, node: RenderableElement): void => { @@ -72,7 +71,7 @@ export const sendValueChangeOnAttributeMutation = (workerContext: WorkerContext, /** * Tell WorkerDOM what the value is for a Node. - * @param workerContext worker whom to dispatch value toward. + * @param worker whom to dispatch value toward. * @param node where to get the value from. */ const fireValueChange = (workerContext: WorkerContext, node: RenderableElement): void => @@ -118,6 +117,7 @@ export const EventSubscriptionProcessor: CommandExecutorInterface = (strings, no /** * Register an event handler for dispatching events to worker thread + * @param worker whom to dispatch events toward * @param index node index the event comes from (used to dispatchEvent in worker thread). * @return eventHandler function consuming event and dispatching to worker thread */ diff --git a/src/main-thread/commands/object-creation.ts b/src/main-thread/commands/object-creation.ts index 54b55398c..3534e1cd7 100644 --- a/src/main-thread/commands/object-creation.ts +++ b/src/main-thread/commands/object-creation.ts @@ -17,7 +17,6 @@ import { TransferrableMutationType, ObjectCreationIndex } from '../../transfer/TransferrableMutation'; import { CommandExecutorInterface } from './interface'; import { deserializeTransferrableObject } from '../deserializeTransferrableObject'; -import { RenderableElement } from '../main-thread'; export const ObjectCreationProcessor: CommandExecutorInterface = (strings, nodeContext, workerContext, objectContext, config) => { const allowedExecution = config.executorsAllowed.includes(TransferrableMutationType.OBJECT_CREATION); diff --git a/src/main-thread/commands/object-mutation.ts b/src/main-thread/commands/object-mutation.ts index ae343dc3f..cd9bba885 100644 --- a/src/main-thread/commands/object-mutation.ts +++ b/src/main-thread/commands/object-mutation.ts @@ -17,7 +17,6 @@ import { TransferrableMutationType, ObjectMutationIndex } from '../../transfer/TransferrableMutation'; import { CommandExecutorInterface } from './interface'; import { deserializeTransferrableObject } from '../deserializeTransferrableObject'; -import { RenderableElement } from '../main-thread'; export const ObjectMutationProcessor: CommandExecutorInterface = (strings, nodeContext, workerContext, objectContext, config) => { const allowedExecution = config.executorsAllowed.includes(TransferrableMutationType.OBJECT_MUTATION); diff --git a/src/main-thread/commands/offscreen-canvas.ts b/src/main-thread/commands/offscreen-canvas.ts index a0460187f..9080c977e 100644 --- a/src/main-thread/commands/offscreen-canvas.ts +++ b/src/main-thread/commands/offscreen-canvas.ts @@ -18,7 +18,6 @@ import { TransferrableKeys } from '../../transfer/TransferrableKeys'; import { MessageType } from '../../transfer/Messages'; import { CommandExecutorInterface } from './interface'; import { OffscreenCanvasMutationIndex, TransferrableMutationType } from '../../transfer/TransferrableMutation'; -import { RenderableElement } from '../main-thread'; export const OffscreenCanvasProcessor: CommandExecutorInterface = (strings, nodeContext, workerContext, objectContext, config) => { const allowedExecution = config.executorsAllowed.includes(TransferrableMutationType.OFFSCREEN_CANVAS_INSTANCE); diff --git a/src/main-thread/configuration.ts b/src/main-thread/configuration.ts index 1b4c8c594..ba7b18e38 100644 --- a/src/main-thread/configuration.ts +++ b/src/main-thread/configuration.ts @@ -18,7 +18,6 @@ import { MessageFromWorker, MessageToWorker } from '../transfer/Messages'; import { Phase } from '../transfer/Phase'; import { HydrateableNode } from '../transfer/TransferrableNodes'; import { DefaultAllowedMutations } from '../transfer/TransferrableMutation'; -import { RenderableElement, Sanitizer } from './main-thread'; /** * The callback for `mutationPump`. If specified, this callback will be called diff --git a/src/main-thread/debugging.ts b/src/main-thread/debugging.ts index 736bd843a..66bc8adf4 100644 --- a/src/main-thread/debugging.ts +++ b/src/main-thread/debugging.ts @@ -38,7 +38,6 @@ import { TransferrableSyncValue } from '../transfer/TransferrableSyncValue'; import { createReadableHydrateableRootNode } from './serialize'; import { WorkerDOMConfiguration } from './configuration'; import { WorkerContext } from './worker'; -import { RenderableElement } from './main-thread'; /** * @param element diff --git a/src/main-thread/index.amp.ts b/src/main-thread/index.amp.ts index 32ecdf679..571ec111d 100644 --- a/src/main-thread/index.amp.ts +++ b/src/main-thread/index.amp.ts @@ -22,7 +22,6 @@ import { fetchAndInstall, install } from './install'; import { WorkerDOMConfiguration, LongTaskFunction } from './configuration'; import { toLower } from '../utils'; import { ExportedWorker } from './exported-worker'; -import { RenderableElement, Sanitizer } from './main-thread'; /** * AMP Element Children need to be filtered from Hydration, to avoid Author Code from manipulating it. diff --git a/src/main-thread/mutator.ts b/src/main-thread/mutator.ts index b4519fc21..5d4a02d85 100644 --- a/src/main-thread/mutator.ts +++ b/src/main-thread/mutator.ts @@ -36,7 +36,6 @@ import { ImageBitmapProcessor } from './commands/image-bitmap'; import { StorageProcessor } from './commands/storage'; import { FunctionProcessor } from './commands/function'; import { ScrollIntoViewProcessor } from './commands/scroll-into-view'; -import { Sanitizer, WORKER_DOM_DEBUG } from './main-thread'; export class MutatorProcessor { private stringContext: StringContext; diff --git a/src/main-thread/nodes.ts b/src/main-thread/nodes.ts index 5ae8fabc8..657fddf37 100644 --- a/src/main-thread/nodes.ts +++ b/src/main-thread/nodes.ts @@ -16,7 +16,6 @@ import { NodeType, TransferrableNodeIndex } from '../transfer/TransferrableNodes'; import { StringContext } from './strings'; -import { RenderableElement, Sanitizer } from './main-thread'; /** * IE11 doesn't support NodeList.prototype.forEach diff --git a/src/main-thread/serialize.ts b/src/main-thread/serialize.ts index 5e8ea0027..9ae4fdb55 100644 --- a/src/main-thread/serialize.ts +++ b/src/main-thread/serialize.ts @@ -20,7 +20,6 @@ import { NumericBoolean } from '../utils'; import { WorkerDOMConfiguration, HydrationFilterPredicate } from './configuration'; import { applyDefaultInputListener, sendValueChangeOnAttributeMutation } from './commands/event-subscription'; import { WorkerContext } from './worker'; -import { RenderableElement } from './main-thread'; const NODES_ALLOWED_TO_TRANSMIT_TEXT_CONTENT = [NodeType.COMMENT_NODE, NodeType.TEXT_NODE]; diff --git a/src/main-thread/worker.ts b/src/main-thread/worker.ts index 02671fbc0..b4b409066 100644 --- a/src/main-thread/worker.ts +++ b/src/main-thread/worker.ts @@ -22,7 +22,6 @@ import { NodeContext } from './nodes'; import { TransferrableKeys } from '../transfer/TransferrableKeys'; import { StorageLocation } from '../transfer/TransferrableStorage'; import { IframeWorker } from './iframe-worker'; -import { IS_AMP, Sanitizer, StorageValue, WORKER_DOM_DEBUG } from './main-thread'; // TODO: Sanitizer storage init is likely broken, since the code currently // attempts to stringify a Promise.