Skip to content

Commit

Permalink
feat: video/audio/canvas support
Browse files Browse the repository at this point in the history
  • Loading branch information
adamdbradley committed Jan 15, 2022
1 parent a805cfa commit cd5a15c
Show file tree
Hide file tree
Showing 34 changed files with 1,265 additions and 381 deletions.
44 changes: 44 additions & 0 deletions scripts/build-media-implementations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import type { OutputOptions, RollupOptions } from 'rollup';
import {
BuildOptions,
copyOutputToTests,
fileSize,
jsBannerPlugin,
versionPlugin,
watchDir,
} from './utils';
import { join } from 'path';
import { minifyPlugin } from './minify';

export function buildMediaImplementation(opts: BuildOptions): RollupOptions {
const debugOutput: OutputOptions = {
file: join(opts.distLibDebugDir, `partytown-media.js`),
format: 'es',
exports: 'none',
intro: `((self) => {`,
outro: `})(self);`,
plugins: [...minifyPlugin(opts, true), versionPlugin(opts), fileSize()],
};

const output: OutputOptions[] = [debugOutput];
if (!opts.isDev) {
output.push({
file: join(opts.distLibDir, `partytown-media.js`),
format: 'es',
exports: 'none',
intro: `((self) => {`,
outro: `})(self);`,
plugins: [...minifyPlugin(opts, false), versionPlugin(opts), fileSize()],
});
}

return {
input: join(opts.tscLibDir, 'web-worker', 'media', 'index.js'),
output,
plugins: [
watchDir(opts, join(opts.tscLibDir, 'web-worker', 'media')),
jsBannerPlugin(opts),
copyOutputToTests(opts),
],
};
}
4 changes: 3 additions & 1 deletion scripts/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import type { BuildOptions } from './utils';
import { buildApi } from './build-api';
import { buildAtomics } from './build-atomics';
import { buildMainSnippet } from './build-main-snippet';
import { buildIntegration } from './build-integration';
import { buildMediaImplementation } from './build-media-implementations';
import { buildMainSnippet } from './build-main-snippet';
import { buildReact } from './build-react';
import { buildServiceWorker } from './build-service-worker';
import { buildUtils } from './build-utils';
Expand All @@ -22,6 +23,7 @@ export async function runBuild(rootDir: string, isDev: boolean, generateApi: boo
buildMainSnippet(opts),
buildServiceWorker(opts),
...buildAtomics(opts),
buildMediaImplementation(opts),
buildIntegration(opts),
buildReact(opts),
buildUtils(opts),
Expand Down
113 changes: 49 additions & 64 deletions src/lib/sandbox/main-serialization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,72 +21,53 @@ export const serializeForWorker = (
type?: string,
cstrName?: string
): SerializedTransfer | undefined => {
if (value !== undefined) {
type = typeof value;

if (value !== undefined && (type = typeof value)) {
if (type === 'string' || type === 'number' || type === 'boolean' || value == null) {
return [SerializedType.Primitive, value];
}

if (type === 'function') {
} else if (type === 'function') {
return [SerializedType.Function];
}

added = added || new Set();
if (Array.isArray(value)) {
if (!added.has(value)) {
added.add(value);
return [SerializedType.Array, value.map((v) => serializeForWorker($winId$, v, added))];
} else if ((added = added || new Set()) && Array.isArray(value)) {
if (added.has(value)) {
return [SerializedType.Array, []];
} else {
return (
added.add(value) && [
SerializedType.Array,
value.map((v) => serializeForWorker($winId$, v, added)),
]
);
}
return [SerializedType.Array, []];
}

if (type === 'object') {
cstrName = getConstructorName(value);

if (cstrName === '') {
} else if (type === 'object') {
if ((cstrName = getConstructorName(value)) === '') {
// error reading this object, probably "DOMException: Blocked from accessing a cross-origin frame."
return [SerializedType.Object, {}];
}

if (cstrName === 'Window') {
} else if (cstrName === 'Window') {
return [
SerializedType.Instance,
{
$winId$,
$instanceId$: PlatformInstanceId.window,
},
];
}

if (cstrName === 'HTMLCollection' || cstrName === 'NodeList') {
} else if (cstrName === 'HTMLCollection' || cstrName === 'NodeList') {
return [
SerializedType.NodeList,
Array.from(value).map((v) => serializeForWorker($winId$, v, added)![1]) as any,
];
}

if (cstrName === 'Event') {
} else if (cstrName === 'Event') {
return [SerializedType.Event, serializeObjectForWorker($winId$, value, added)];
}

if (cstrName === 'CSSRuleList') {
} else if (cstrName === 'CSSRuleList') {
return [SerializedType.CSSRuleList, Array.from(value).map(serializeCssRuleForWorker)];
}

if (startsWith(cstrName, 'CSS') && cstrName.endsWith('Rule')) {
} else if (startsWith(cstrName, 'CSS') && cstrName.endsWith('Rule')) {
return [SerializedType.CSSRule, serializeCssRuleForWorker(value)];
}

if (cstrName === 'CSSStyleDeclaration') {
return [SerializedType.Object, serializeObjectForWorker($winId$, value, added)];
}

if (cstrName === 'Attr') {
} else if (cstrName === 'CSSStyleDeclaration') {
return [
SerializedType.CSSStyleDeclaration,
serializeObjectForWorker($winId$, value, added),
];
} else if (cstrName === 'Attr') {
return [SerializedType.Attr, [(value as Attr).name, (value as Attr).value]];
}

if (value.nodeType) {
} else if (value.nodeType) {
return [
SerializedType.Instance,
{
Expand All @@ -95,10 +76,14 @@ export const serializeForWorker = (
$nodeName$: value.nodeName,
},
];
} else {
return [SerializedType.Object, serializeObjectForWorker($winId$, value, added, true, true)];
}

return [SerializedType.Object, serializeObjectForWorker($winId$, value, added, true, true)];
} else {
return;
}
} else {
return value;
}
};

Expand Down Expand Up @@ -152,38 +137,38 @@ export const deserializeFromWorker = (

if (serializedType === SerializedType.Primitive) {
return serializedValue;
}

if (serializedType === SerializedType.Ref) {
} else if (serializedType === SerializedType.Ref) {
return deserializeRefFromWorker(worker, serializedValue);
}

if (serializedType === SerializedType.Array) {
} else if (serializedType === SerializedType.Array) {
return (serializedValue as SerializedTransfer[]).map((v) => deserializeFromWorker(worker, v));
}

if (serializedType === SerializedType.Instance) {
} else if (serializedType === SerializedType.Instance) {
return getInstance(
(serializedValue as SerializedInstance).$winId$,
(serializedValue as SerializedInstance).$instanceId$!
);
}

if (serializedType === SerializedType.Event) {
} else if (serializedType === SerializedType.Event) {
return constructEvent(deserializeObjectFromWorker(worker, serializedValue));
}

if (serializedType === SerializedType.Object) {
} else if (serializedType === SerializedType.Object) {
return deserializeObjectFromWorker(worker, serializedValue);
} else if (serializedType === SerializedType.ArrayBuffer) {
return serializedValue;
} else if (serializedType === SerializedType.ArrayBufferView) {
return new (window as any)[serializedTransfer[2]!](serializedValue);
} else {
return;
}
} else {
// improve minification
return;
}
};

const deserializeRefFromWorker = (
worker: PartytownWebWorker,
{ $winId$, $instanceId$, $refId$ }: SerializedRefTransferData
{ $winId$, $instanceId$, $refId$ }: SerializedRefTransferData,
ref?: any
) => {
let ref = mainRefs.get($refId$);
ref = mainRefs.get($refId$);

if (!ref) {
ref = function (this: any, ...args: any[]) {
Expand Down
33 changes: 15 additions & 18 deletions src/lib/sandbox/read-main-platform.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { debug, getConstructorName, isValidMemberName, len, logMain, noop } from '../utils';
import { debug, getConstructorName, isValidMemberName, len, noop } from '../utils';
import {
InterfaceType,
InterfaceInfo,
Expand All @@ -7,6 +7,7 @@ import {
PartytownConfig,
StorageItem,
} from '../types';
import { logMain } from '../log';

export const readMainPlatform = (win: any) => {
const startTime = debug ? performance.now() : 0;
Expand All @@ -16,10 +17,7 @@ export const readMainPlatform = (win: any) => {
const textNode = docImpl.createTextNode('');
const comment = docImpl.createComment('');
const frag = docImpl.createDocumentFragment();
const elm = docImpl.createElement('i');
const svg = docImpl.createElementNS('http://www.w3.org/2000/svg', 'svg');
const canvas = docImpl.createElement('canvas');
const canvasRenderingContext2D = canvas.getContext('2d');
const mutationObserver = new MutationObserver(noop);
const resizeObserver = new ResizeObserver(noop);
const perf = win.performance;
Expand All @@ -29,10 +27,10 @@ export const readMainPlatform = (win: any) => {
// and create each element to get their implementation
const elms = Object.getOwnPropertyNames(win)
.filter((c) => /^HTML.+Element$/.test(c))
.map((htmlCstrName) => {
const htmlTagName = getHtmlTagNameFromConstructor(htmlCstrName);
return [docImpl.createElement(htmlTagName)];
});
.map((htmlCstrName) => [docImpl.createElement(getHtmlTagNameFromConstructor(htmlCstrName))]);

// get the first HTMLElement to read its properties
const elm = elms[0][0];

const impls: any[] = [
// window implementations
Expand All @@ -59,14 +57,13 @@ export const readMainPlatform = (win: any) => {
[svg],
[docImpl],
[docImpl.doctype!],
[canvasRenderingContext2D!],
...elms,
]
.filter((implData) => implData[0])
.map((implData) => {
const impl = implData[0];
const interfaceType: InterfaceType = implData[1];
const cstrName: string = impl.constructor.name;
const cstrName = getConstructorName(impl);
const CstrPrototype = win[cstrName].prototype;
return [cstrName, CstrPrototype, impl, interfaceType];
});
Expand Down Expand Up @@ -110,10 +107,10 @@ export const readMainPlatform = (win: any) => {
return initWebWorkerData;
};

const readImplementation = (cstrName: string, impl: any) => {
const interfaceMembers: InterfaceMember[] = [];
const interfaceInfo: InterfaceInfo = [cstrName, 'Object', interfaceMembers];
for (const memberName in impl) {
const readImplementation = (cstrName: string, impl: any, memberName?: string) => {
let interfaceMembers: InterfaceMember[] = [];
let interfaceInfo: InterfaceInfo = [cstrName, 'Object', interfaceMembers];
for (memberName in impl) {
readImplementationMember(interfaceMembers, impl, memberName);
}
return interfaceInfo;
Expand All @@ -128,7 +125,7 @@ const readOwnImplementation = (
) => {
if (cstrName !== 'Object' && !interfaces.some((i) => i[0] === cstrName)) {
const SuperCstr = Object.getPrototypeOf(CstrPrototype);
const superCstrName = SuperCstr.constructor.name;
const superCstrName = getConstructorName(SuperCstr);
const interfaceMembers: InterfaceMember[] = [];

readOwnImplementation(interfaces, superCstrName, SuperCstr, impl, interfaceType);
Expand Down Expand Up @@ -156,7 +153,7 @@ const readImplementationMember = (
cstrName?: string
) => {
try {
if (isValidMemberName(memberName) && isNaN((memberName as any)[0])) {
if (isValidMemberName(memberName) && isNaN((memberName as any)[0]) && memberName !== 'all') {
value = implementation[memberName];
memberType = typeof value;

Expand All @@ -169,7 +166,7 @@ const readImplementationMember = (
}
} else if (memberType === 'object' && value != null) {
cstrName = getConstructorName(value);
if (cstrName !== 'Object' && (window as any)[cstrName]) {
if (cstrName !== 'Object' && (self as any)[cstrName]) {
interfaceMembers.push([memberName, value.nodeType || cstrName]);
}
} else if (memberType !== 'symbol') {
Expand Down Expand Up @@ -202,7 +199,7 @@ const htmlConstructorToTagMap: { [key: string]: string } = {
};

const getHtmlTagNameFromConstructor = (t: string) => {
t = t.substr(4).replace('Element', '');
t = t.slice(4).replace('Element', '');
return htmlConstructorToTagMap[t] || t;
};

Expand Down
Loading

0 comments on commit cd5a15c

Please sign in to comment.