-
-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Closes #2
- Loading branch information
Showing
6 changed files
with
309 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,249 @@ | ||
/** | ||
* @license | ||
* Copyright (C) 2023 WofWca <[email protected]> | ||
* Copyright (C) 2023 Jonas Herzig <[email protected]> | ||
* | ||
* This file is part of Jump Cutter Browser Extension. | ||
* | ||
* Jump Cutter Browser Extension is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU Affero General Public License as published by | ||
* the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* Jump Cutter Browser Extension is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU Affero General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU Affero General Public License | ||
* along with Jump Cutter Browser Extension. If not, see <https://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
|
||
|
||
// function startIntercepting | ||
// function createInterceptor( | ||
// functionName: string, | ||
// object: object, | ||
// callback: (args: unknown[]) | ||
// ) | ||
/** | ||
* @returns stop intercepting | ||
*/ | ||
function startInterceptingCreateObjectUrlCalls( | ||
callback: | ||
( | ||
args: Parameters<typeof URL.createObjectURL>, | ||
retVal: ReturnType<typeof URL.createObjectURL>, | ||
) => void, | ||
): () => void { | ||
type MutatedURL = typeof URL & { | ||
_jumpCutterExtensionOriginalCreateObjectURL: typeof URL.createObjectURL | ||
}; | ||
(URL as MutatedURL)._jumpCutterExtensionOriginalCreateObjectURL = URL.createObjectURL; | ||
URL.createObjectURL = function(...args) { | ||
const originalRetVal = | ||
(URL as MutatedURL)._jumpCutterExtensionOriginalCreateObjectURL(...args); | ||
callback(args, originalRetVal); | ||
|
||
console.log('createObjectURL', args, originalRetVal); | ||
|
||
return originalRetVal; | ||
} | ||
|
||
return () => { | ||
URL.createObjectURL = (URL as MutatedURL)._jumpCutterExtensionOriginalCreateObjectURL; | ||
//@ts-expect-error 2790 | ||
delete (URL as MutatedURL)._jumpCutterExtensionOriginalCreateObjectURL; | ||
} | ||
} | ||
|
||
function startInterceptingMediaSourceConstructorCalls( | ||
callback: | ||
( | ||
constructorArgs: ConstructorParameters<typeof MediaSource>, | ||
newMediaSource: MediaSource, | ||
) => void, | ||
): () => void { | ||
globalThis._jumpCutterExtensionOriginalMediaSource = globalThis.MediaSource; | ||
// TODO big yikes, you forgot the static methods (`isTypeSupported`). | ||
// globalThis.MediaSource = function(...args) { | ||
const proxy = function(...args) { | ||
const originalRetVal = | ||
new globalThis._jumpCutterExtensionOriginalMediaSource(...args); | ||
|
||
console.log('gottem', args, originalRetVal); | ||
|
||
callback(args, originalRetVal); | ||
// Yes, `new currFunc()` will evalate to return value, and not a freshly created empty object. | ||
// Source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new#description | ||
// TODO fix: fuck me, this means that `(new MediaSource()) instanceof MediaSource` will | ||
// always be false... | ||
return originalRetVal; | ||
} | ||
proxy.isTypeSupported = globalThis.MediaSource.isTypeSupported; | ||
globalThis.MediaSource = proxy; | ||
|
||
return () => { | ||
globalThis.MediaSource = globalThis._jumpCutterExtensionOriginalMediaSource; | ||
delete globalThis._jumpCutterExtensionOriginalMediaSource | ||
} | ||
} | ||
|
||
|
||
type ObjectUrlToItsMediaSource = Record< | ||
ReturnType<typeof URL.createObjectURL>, | ||
MediaSource | ||
>; | ||
/** | ||
* Creates and maintains a map of all URLs created with `URL.createObjectURL` | ||
* to the `MediaSource` that the URL was created for. | ||
* When `URL.revokeObjectURL` is called for a URL, it's also automatically | ||
* removed from the map. | ||
* | ||
* Keep in mind that `el.src` can also be made out of `MediaSourceHandle`. | ||
*/ | ||
function createMaintainedObjectUrlToMediaSourceMap(): [ | ||
map: ObjectUrlToItsMediaSource, | ||
stopWatching: () => void, | ||
] { | ||
const map: ObjectUrlToItsMediaSource = {}; | ||
|
||
// // Override the original functions. This can probably make things go wrong, | ||
// // but I don't know a better way. | ||
// type MutatedURL = typeof URL & { | ||
// _jumpCutterExtensionOriginalCreateObjectURL: typeof URL.createObjectURL | ||
// }; | ||
// (URL as MutatedURL)._jumpCutterExtensionOriginalCreateObjectURL = URL.createObjectURL; | ||
// URL.createObjectURL = function(obj, ...rest) { | ||
// const originalRetVal = | ||
// (URL as MutatedURL)._jumpCutterExtensionOriginalCreateObjectURL(obj, ...rest); | ||
|
||
// if (obj instanceof MediaSource) { | ||
// const url = originalRetVal; | ||
// map[url] = obj; | ||
// } | ||
// } | ||
|
||
const stopInterceptingCreateObjectUrlCalls = startInterceptingCreateObjectUrlCalls(([obj], url) => { | ||
// TODO see comment about `instanceof MediaSource` above. | ||
// if (obj instanceof MediaSource) { | ||
if (true) { | ||
map[url] = obj; | ||
} | ||
}); | ||
// TODO perf: also watch `revokeObjectURL` and remove them from the map so there is no memory | ||
// leak. | ||
|
||
return [ | ||
map, | ||
() => { | ||
stopInterceptingCreateObjectUrlCalls(); | ||
} | ||
]; | ||
} | ||
|
||
function makeMaintainedMediaSourceClone( | ||
mediaSourceConstructorArgs, | ||
_originalMediaSource, | ||
// onCloneReady: (cloneMediaSource: MediaSource) => void, | ||
): MediaSource { | ||
const cloneMediaSource = | ||
new _jumpCutterExtensionOriginalMediaSource(...mediaSourceConstructorArgs) as MediaSource; | ||
|
||
// TODO refactor: DRY interception code. | ||
type MutatedMediaSource = MediaSource & { | ||
originalAddSourceBuffer: MediaSource['addSourceBuffer'] | ||
}; | ||
// TODO perf: a way to revert all the objects to the original state and stop | ||
// maintaining the clone? | ||
const originalMediaSource = _originalMediaSource as MutatedMediaSource; | ||
originalMediaSource.originalAddSourceBuffer = originalMediaSource.addSourceBuffer; | ||
originalMediaSource.addSourceBuffer = function(type, ...rest) { | ||
const originalRetVal = originalMediaSource.originalAddSourceBuffer(type, ...rest); | ||
// TODO I believe we need to check for `readyState`. | ||
|
||
|
||
// cloneMediaSource.addEventListener('sourceopen') | ||
|
||
|
||
const cloneSourceBuffer = cloneMediaSource.addSourceBuffer(type, ...rest); | ||
|
||
console.log('addSourceBuffer', [type, ...rest] , originalRetVal); | ||
|
||
const _originalSourceBuffer = originalRetVal; | ||
type MutatedSourceBuffer = SourceBuffer & { | ||
originalAppendBuffer: SourceBuffer['appendBuffer'] | ||
}; | ||
const originalSourceBuffer = _originalSourceBuffer as MutatedSourceBuffer; | ||
|
||
// TODO refactor: DRY these interceptors already, dude. | ||
// And un-nest them. | ||
originalSourceBuffer.originalAppendBuffer = originalSourceBuffer.appendBuffer; | ||
originalSourceBuffer.appendBuffer = function(data, ...rest) { | ||
const originalRetVal = originalSourceBuffer.originalAppendBuffer(data, ...rest); | ||
// debugger; | ||
console.log('appendBuffer', [data, ...rest] , originalRetVal); | ||
cloneSourceBuffer.appendBuffer(data, ...rest); | ||
return originalRetVal; | ||
} | ||
|
||
return originalRetVal; | ||
} | ||
|
||
return cloneMediaSource | ||
// TODO intercept `removeSourceBuffer` as well. And other functions maybe. | ||
} | ||
|
||
function createMaintainedMediaSourceToMediaSourceCloneMap(): [ | ||
map: WeakMap<MediaSource, MediaSource>, | ||
stopMaintaining: () => void, | ||
] { | ||
const map = new WeakMap<MediaSource, MediaSource>(); | ||
const stopInterceptingMediaSourceConstructorCalls = startInterceptingMediaSourceConstructorCalls((constructorArgs, originalMediaSource) => { | ||
// TODO handle `stopMaintainingMediaSourceClone`. | ||
const maintainedClone = makeMaintainedMediaSourceClone(constructorArgs, originalMediaSource); | ||
map.set(originalMediaSource, maintainedClone); | ||
}) | ||
return [ | ||
map, | ||
() => stopInterceptingMediaSourceConstructorCalls(), | ||
]; | ||
} | ||
|
||
// export function watchMediaSources(): [ | ||
// export function startCloningMediaSources(): [ | ||
// objectUrlToMediaSource: ObjectUrlToItsMediaSource, | ||
// ] { | ||
// } | ||
|
||
/* export */ function startCloningMediaSources(): [ | ||
// objectUrlToMediaSource: | ||
getMediaSourceFromObjectUrl: | ||
(url: ReturnType<typeof URL.createObjectURL>) => MediaSource | undefined, | ||
getMediaSourceClone: (originalMediaSource: MediaSource) => MediaSource, | ||
stopCloningMediaSources: () => void, | ||
] { | ||
const [objectUrlToMediaSourceMap, stopMaintainingUrlMap] | ||
= createMaintainedObjectUrlToMediaSourceMap(); | ||
const [mediaSourceToMediaSourceCloneMap, stopMaintainingCloneMap] | ||
= createMaintainedMediaSourceToMediaSourceCloneMap(); | ||
|
||
return [ | ||
(url) => objectUrlToMediaSourceMap[url], | ||
(originalMediaSource) => mediaSourceToMediaSourceCloneMap.get(originalMediaSource)!, | ||
() => { | ||
stopMaintainingUrlMap(); | ||
stopMaintainingCloneMap(); | ||
}, | ||
] | ||
} | ||
|
||
// TODO export just `startCloningMediaSources`, don't call it at the top level. | ||
// export const [ | ||
// getMediaSourceFromObjectUrl, | ||
// getMediaSourceClone, | ||
// stopCloningMediaSources, | ||
// ] = startCloningMediaSources(); | ||
|
||
window.testCloneMediaSources = startCloningMediaSources(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
/** | ||
* @license | ||
* Copyright (C) 2020, 2021, 2022 WofWca <[email protected]> | ||
* Copyright (C) 2020, 2021, 2022, 2023 WofWca <[email protected]> | ||
* Copyright (C) 2023 Jonas Herzig <[email protected]> | ||
* | ||
* This file is part of Jump Cutter Browser Extension. | ||
* | ||
|
@@ -21,6 +22,39 @@ | |
import { enabledSettingDefaultValue, MyStorageChanges, Settings } from '@/settings'; | ||
import { mainStorageAreaName } from '@/settings/mainStorageAreaName'; | ||
import { browserOrChrome } from '@/webextensions-api-browser-or-chrome'; | ||
// Import so we start tracking right after initialization. | ||
// TODO refactor: don't import for side effects | ||
|
||
// import * as asdasd from './cloneMediaSources'; | ||
// console.log(asdasd); | ||
|
||
|
||
|
||
// window.testtt = 1; | ||
// globalThis.testttt = 2; | ||
// console.log('testttt'); | ||
|
||
|
||
// console.log(import('./test')); | ||
|
||
|
||
|
||
// const actualCode = `window.testScriptTag = 'isNice'`; | ||
|
||
// const script = document.createElement('script'); | ||
// script.textContent = actualCode; | ||
// (document.head||document.documentElement).appendChild(script); | ||
// script.remove(); | ||
|
||
|
||
|
||
|
||
const s = document.createElement('script'); | ||
s.src = browser.runtime.getURL('content/cloneMediaSources.js'); | ||
s.onload = function() { this.remove(); }; | ||
(document.head || document.documentElement).appendChild(s); | ||
|
||
|
||
|
||
(async function () { // Just for top-level `await` | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
window.testttImport = 1; | ||
console.log('imported'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters