Compare commits

..

5 Commits

Author SHA1 Message Date
d322c0909a Merge branch 'hotfix/iframe-fix' 2025-01-25 21:19:55 +01:00
0bfe84d73d fix saving settings 2025-01-25 21:18:04 +01:00
244e8227e7 Settings: make setProp() functional 2025-01-25 21:17:20 +01:00
5da8073235 fix conflict 2025-01-25 21:15:26 +01:00
5479b7ee95 Fix the iframe problem 2025-01-25 20:51:22 +01:00
16 changed files with 81 additions and 708 deletions

View File

@ -16,7 +16,6 @@
"decycle",
"dinked",
"dinks",
"Discardable",
"disneyplus",
"endregion",
"equalish",

43
package-lock.json generated
View File

@ -3243,7 +3243,8 @@
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"dev": true
},
"at-least-node": {
"version": "1.0.0",
@ -3374,28 +3375,6 @@
"integrity": "sha512-3AungXC4I8kKsS9PuS4JH2nc+0bVY/mjgrephHTIi8fpEeGsTHBUJeosp0Wc1myYMElmD0B3Oc4XL/HVJ4PV2g==",
"dev": true
},
"axios": {
"version": "1.7.9",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz",
"integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==",
"requires": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
},
"dependencies": {
"form-data": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz",
"integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
}
}
}
},
"babel-code-frame": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
@ -4870,6 +4849,7 @@
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dev": true,
"requires": {
"delayed-stream": "~1.0.0"
}
@ -5977,7 +5957,8 @@
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"dev": true
},
"delegates": {
"version": "1.0.0",
@ -7016,11 +6997,6 @@
"readable-stream": "^2.3.6"
}
},
"follow-redirects": {
"version": "1.15.9",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ=="
},
"for-each": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
@ -9399,12 +9375,14 @@
"mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"dev": true
},
"mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dev": true,
"requires": {
"mime-db": "1.52.0"
}
@ -12832,11 +12810,6 @@
"ipaddr.js": "1.9.1"
}
},
"proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
},
"prr": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",

View File

@ -29,7 +29,6 @@
"@mdi/font": "^6.5.95",
"@mdi/js": "^6.4.95",
"@types/resize-observer-browser": "^0.1.6",
"axios": "^1.7.9",
"concurrently": "^5.3.0",
"fs-extra": "^7.0.1",
"gl-matrix": "^3.4.3",

View File

@ -164,7 +164,20 @@ interface SettingsInterface {
arDetect: AardSettings,
ui: {
inPlayer: InPlayerUISettingsInterface
inPlayer: {
enabled: boolean,
enabledFullscreenOnly: boolean,
popupAlignment: 'left' | 'right',
minEnabledWidth: number, // don't show UI if player is narrower than % of screen width
minEnabledHeight: number, // don't show UI if player is narrower than % of screen height
activation: 'trigger-zone' | 'player', // what needs to be hovered in order for UI to be visible
triggerZoneDimensions: { // how large the trigger zone is (relative to player size)
width: number
height: number,
offsetX: number, // fed to translateX(offsetX + '%'). Valid range [-100, 0]
offsetY: number // fed to translateY(offsetY + '%'). Valid range [-100, 100]
},
}
}
restrictions?: RestrictionsSettings;
@ -292,16 +305,13 @@ interface SettingsInterface {
//
sites: {
[x: string]: SiteSettingsInterface,
},
telemetry?: UwTelemetryInterface
}
}
export interface SiteSettingsInterface {
enable: ExtensionEnvironmentSettingsInterface;
enableAard: ExtensionEnvironmentSettingsInterface;
enableKeyboard: ExtensionEnvironmentSettingsInterface;
ui?: InPlayerUISettingsInterface,
type?: 'official' | 'community' | 'user-defined' | 'testing' | 'officially-disabled' | 'unknown' | 'modified';
defaultType: 'official' | 'community' | 'user-defined' | 'testing' | 'officially-disabled' | 'unknown' | 'modified';
@ -330,13 +340,6 @@ export interface SiteSettingsInterface {
// the following fields are for use with extension update script
override?: boolean; // whether settings for this site will be overwritten by extension upgrade script
workarounds?: {
iframeTransparencyWarningDismissed?: boolean;
disableColorSchemeAwareness?: boolean;
forceColorScheme?: 'normal' | 'light' | 'dark',
lastColorSchemeAwarenessCheck?: Date;
}
}
export interface PlayerAutoConfigInterface {
@ -369,28 +372,4 @@ export interface SiteDOMElementSettingsInterface {
nodeCss?: {[x: string]: string};
}
export interface InPlayerUISettingsInterface {
enabled: boolean,
enabledFullscreenOnly: boolean,
popupAlignment: 'left' | 'right',
minEnabledWidth: number, // don't show UI if player is narrower than % of screen width
minEnabledHeight: number, // don't show UI if player is narrower than % of screen height
activation: 'trigger-zone' | 'player', // what needs to be hovered in order for UI to be visible
triggerZoneDimensions: { // how large the trigger zone is (relative to player size)
width: number
height: number,
offsetX: number, // fed to translateX(offsetX + '%'). Valid range [-100, 0]
offsetY: number // fed to translateY(offsetY + '%'). Valid range [-100, 100]
},
}
export interface UwTelemetryInterface {
iframeTransparency?: {
[siteName: string]: {
reportedWithColorSchemeAllowed?: boolean;
reportedWithColorSchemeDisabled?: boolean;
}
}
}
export default SettingsInterface;

View File

@ -36,17 +36,6 @@
</div>
Ultrawidify
<div
v-if="!siteSettings?.data.workarounds?.iframeTransparencyWarningDismissed"
class="absolute ui-warning small uw-clickable">
If all you see is white or black screen,<br/>disable UI and report your GPU to github.<br/>Open.<br/>
<br />
<br /> You can also <a href="" @click="iframeAutofix()">click here to attempt a fix</a>.
<br />
<br /> If you experience no issues, <a href="" @click="iframeDismiss()">click here to hide this warning.</a>
</div>
</div>
</template>
@ -463,7 +452,6 @@ export default {
this.sendToParentLowLevel('uw-bus-tunnel', {
action: 'get-player-dimensions'
});
this.sendToParentLowLevel('uw-get-page-stats', null);
},
destroyed() {
@ -597,22 +585,6 @@ export default {
handleBusTunnelIn(payload) {
this.eventBus.send(payload.action, payload.config, payload.routingData);
},
async iframeAutofix() {
console.log('site:', this.site, this.siteSettings);
if (this.siteSettings?.data.workarounds?.disableColorSchemeAwareness === false) {
await this.settings.setProp(['sites', this.site, 'workarounds', 'disableColorSchemeAwareness'], true);
} else {
await this.settings.setProp(['sites', this.site, 'workarounds', 'disableColorSchemeAwareness'], false);
}
await this.settings.saveWithoutReload();
this.eventBus.sendToTunnel('uw-reload-window');
},
async iframeDismiss() {
await this.settings.setProp(['sites', this.site, 'workarounds', 'iframeTransparencyWarningDismissed'], true);
await this.settings.save();
this.eventBus.sendToTunnel('uw-reload-window');
}
}
}
@ -684,14 +656,10 @@ export default {
.ui-warning {
position: absolute;
top: 0;
left: 0;
transform: translateY(-100%);
max-width: 20rem;
width: 20rem;
padding: 1rem;
box-sizing: border-box;
max-width: 16rem;
width: 16rem;
overflow: hidden;
overflow-wrap: break-word;
@ -699,17 +667,6 @@ export default {
white-space: normal;
word-break: break-word;
word-wrap: break-word;
&.small {
font-size: 0.85rem;
color: #888;
background-color: rgba(0,0,0,0.69);
a {
color: #fa6;
opacity: 0.69;
}
}
}

View File

@ -16,27 +16,6 @@
<li> <a target="_blank" href="https://github.com/tamius-han/ultrawidify/issues"><b>Github (preferred)</b></a><br/></li>
<li>Email: <a target="_blank" :href="mailtoLink">tamius.han@gmail.com</a></li>
</ul>
<hr />
<b>Is your screen entirely white or entirely black?</b>
<p>This appears to be a rare issue that happens to some people. If you're experiencing this issue, please consider contacting me and sharing the following data:</p>
<ul>
<li>Which sites this problem appears on and whether it happens on youtube. If you use youtube premium, please try signing out of youtube (or use a new profile in Google Chrome) in order to see whether youtube premium is required.</li>
<li>your browser (get full version from <code>chrome://settings/help</code>). if using browsers other than Chrome, please try to reproduce this issue in Chrome</li>
<li>your operating system</li>
<li>your graphics card</li>
<li>whether you're using dark theme in your OS, and whether you're using website's built-in dark/light theme.</li>
</ul>
<p>Please post this info to <a href="https://github.com/tamius-han/ultrawidify/issues/262" target="_blank">this thread</a>, or message me via e-mail.</p>
<p>Then, disable the in-player UI.</p>
<hr />
<p>
When reporting bugs, please include the following information:
</p>
@ -88,7 +67,6 @@ export default {
mailtoLink: '',
redditLink: '',
showEasterEgg: false,
pageData: {}
}
},
async created() {

View File

@ -32,7 +32,7 @@
<li>Half-fixed in-player UI on sites where player is not detected correctly</li>
</ul>
<h3>6.2.3 (current)</h3>
<h3>6.2.3</h3>
<ul>
<li>Fixed default persistent mode</li>
</ul>

View File

@ -62,9 +62,7 @@ export default {
'uw-page-stats': {
function: (data) => {
console.log('got page statss:', data);
this.pageData = JSON.parse(JSON.stringify(data));
this.$nextTick( () => this.$forceUpdate());
this.pageData = data;
}
}
},

View File

@ -111,7 +111,7 @@ export default class UWContent {
this.logger.log('info', 'debug', "[uw.js::setup] KeyboardHandler initiated.");
this.globalUi = new UI('ultrawidify-global-ui', {eventBus: this.eventBus, isGlobal: true, siteSettings: this.siteSettings});
this.globalUi = new UI('ultrawidify-global-ui', {eventBus: this.eventBus, isGlobal: true});
this.globalUi.enable();
this.globalUi.setUiVisibility(false);

View File

@ -5,9 +5,6 @@ import Settings from './lib/Settings';
import Logger, { baseLoggingOptions } from './lib/Logger';
import { sleep } from '../common/js/utils';
import EventBus, { EventBusCommand } from './lib/EventBus';
import { IframeTransparencyVerifier, IframeVerificationPlayerData } from './lib/IframeTransparencyVerifier';
import { MessageSender } from './lib/comms/ChromeTabInterfaces';
import axios from 'axios';
export default class UWServer {
settings: Settings;
@ -21,8 +18,6 @@ export default class UWServer {
videoTabs: any = {};
currentTabId: number = 0;
IframeTransparencyVerifier: IframeTransparencyVerifier = new IframeTransparencyVerifier()
selectedSubitem: any = {
'siteSettings': undefined,
'videoSettings': undefined,
@ -49,11 +44,6 @@ export default class UWServer {
},
'get-current-site': {
function: (message, context) => this.getCurrentSite()
},
'verify-iframe-transparency': {
function: (message, context) => {
this.verifyUiTransparency(message.playerData, context.comms.sender, message.telemetryData);
}
}
};
@ -111,7 +101,7 @@ export default class UWServer {
});
}
async injectCss(css, sender: MessageSender) {
async injectCss(css, sender) {
if (!css) {
return;
}
@ -143,7 +133,7 @@ export default class UWServer {
this.logger.log('error','debug', '[UwServer::injectCss] Error while injecting css:', {error: e, css, sender});
}
}
async removeCss(css, sender: MessageSender) {
async removeCss(css, sender) {
try {
if (BrowserDetect.firefox) {
chrome.scripting.removeCSS({
@ -377,39 +367,4 @@ export default class UWServer {
return hostname;
}
async verifyUiTransparency(verificationData: IframeVerificationPlayerData, sender: MessageSender, telemetryData?: any) {
const transparencyVerificationResult = await this.IframeTransparencyVerifier.verifyUiTransparency(
sender.tab.windowId,
{
player: verificationData,
tab: {
width: sender.tab.width,
height: sender.tab.height,
}
}
);
console.log('Transparency confirmed.');
this.eventBus.send(
'iframe-transparency-results',
{
transparencyVerificationResult
},
{
comms: {
forwardTo: 'active'
}
}
);
// axios.post(
// 'https://uw-telemetry.tamius.net/iframe-transparency',
// {
// ...telemetryData,
// transparencyCheckResult: hasTransparency,
// }
// );
}
}

View File

@ -1842,9 +1842,6 @@ const ExtensionConf: SettingsInterface = {
}
}
},
},
telemetry: {
}
}

View File

@ -1,233 +0,0 @@
export enum TransparencyVerificationResult {
Ok = 0,
Fail = 1,
NoVisibleElements = 2,
Error = 3
}
export interface IframeVerificationData {
tab: {
width: number,
height: number,
},
player: IframeVerificationPlayerData
}
export interface IframeVerificationPlayerData {
x: number,
y: number,
width: number,
height: number,
}
interface IframeCheckItem {
position: 'top' | 'center' | 'bottom' | 'left' | 'right';
offset: number;
}
interface IframeCheckPosition {
positionX: 'top' | 'center' | 'bottom';
positionY: 'left' | 'center' | 'right';
offsetX: number;
offsetY: number;
}
export class IframeTransparencyVerifier {
async verifyUiTransparency(windowId: number, tabDimensions: IframeVerificationData): Promise<{result: TransparencyVerificationResult, dataUrl?: string}> { console.info('Verifying UI transparency:', tabDimensions);
// const {visibleX, visibleY} = this.getVisibleMarkers(tabDimensions);
// if (!visibleX.length || !visibleY.length) {
// console.warn('[transparency check] No visible elements.');
return {result: TransparencyVerificationResult.NoVisibleElements};
// }
// const checkPositions = this.processMarkers(visibleX, visibleY);
// const dataUrl = await chrome.tabs.captureVisibleTab(
// undefined, // windowId,
// {
// format: "png"
// }
// );
// try {
// const canvas = new OffscreenCanvas(tabDimensions.tab.width, tabDimensions.tab.height);
// const ctx = canvas.getContext('2d')!;
// const res = await fetch(dataUrl);
// const blob = await res.blob();
// const bitmap = createImageBitmap(blob);
// (ctx as any).drawImage(bitmap, 0, 0);
// const imageData = (ctx as any).getImageData(0, 0, tabDimensions.tab.width, tabDimensions.tab.height).data;
// if (this.detectMarkers(checkPositions, tabDimensions.tab.width, imageData)) {
// console.info('Verified transparency');
// return {
// result: TransparencyVerificationResult.Ok,
// dataUrl: dataUrl
// };
// } else {
// console.info('Transparency checks came back negative');
// return {
// result: TransparencyVerificationResult.Fail,
// dataUrl: dataUrl
// };
// }
// } catch (e) {
// console.error('[transparency check] Error while checking for transparency:', e);
// return {result: TransparencyVerificationResult.Error};
// }
}
private getVisibleMarkers({tab, player}: IframeVerificationData) {
const visibleX: IframeCheckItem[] = [];
const visibleY: IframeCheckItem[] = [];
// Determine which markers should be visible.
// Visibility: TOP ROW
console.log('player:', player.y, tab.height);
if (player.y >= 0 && player.y < tab.height) {
visibleY.push({
position: 'top',
offset: player.y,
});
}
// Visibility: CENTER
const yHalfPosition = Math.floor(player.y + player.height / 2);
if (player.y + yHalfPosition - 2 > 0 && player.y + yHalfPosition + 2 < tab.height) {
visibleY.push({
position: 'center',
offset: player.y + yHalfPosition,
});
}
// Visibility: BOTTOM ROW
if (player.y + player.height - 1 > 0 && player.y + player.height + 1 < tab.height) {
visibleY.push({
position: 'bottom',
offset: player.y + player.height - 1,
});
}
// Visibility: LEFT SIDE
if (player.x >= 0 && player.x < tab.width) {
visibleX.push({
position: 'left',
offset: player.x,
});
}
// Visibility: X CENTER
const xHalfPosition = Math.floor(player.x + player.width / 2);
if (player.x + xHalfPosition - 2 > 0 && player.x + xHalfPosition + 2 < tab.width) {
visibleX.push({
position: 'center',
offset: player.x + xHalfPosition,
});
}
// Visibility: RIGHT SIDE
if (player.x + player.width - 1 > 0 && player.x + player.width + 1 < tab.width) {
visibleX.push({
position: 'right',
offset: player.x + player.width - 1,
});
}
console.log('visible candidates', visibleX, visibleY);
return {
visibleX,
visibleY,
};
}
private processMarkers(candidatesX: IframeCheckItem[], candidatesY: IframeCheckItem[]): IframeCheckPosition[] {
if (!candidatesX.length || !candidatesY.length) {
return [] as IframeCheckPosition[];
}
const checkPositions: IframeCheckPosition[] = [];
for (const row of candidatesY) {
for (const col of candidatesX) {
// 'center center' is not valid position.
if (row.position !== col.position) {
checkPositions.push({
positionX: row.position as 'top' | 'center' | 'bottom',
positionY: col.position as 'left' | 'center' | 'right',
offsetX: col.offset,
offsetY: row.offset,
});
}
}
}
return checkPositions;
}
private detectMarkers(checkPositions: IframeCheckPosition[], rowLength: number, imageData: Uint8ClampedArray): boolean {
for (const position of checkPositions) {
if (position.positionY === 'center') {
if (this.detectColumnMarker(position.offsetX, position.offsetY, rowLength, imageData)) {
return true;
}
} else {
if (this.detectRowMarker(position.offsetX, position.offsetY, rowLength, imageData)) {
return true;
}
}
}
return false;
}
// Checks if our magic sequence is present in a row configuration
private detectRowMarker(x, y, rowLength, imageData): boolean {
const start = (y * rowLength + x) * 4;
return imageData[start] === 0
&& imageData[start + 1] === 1
&& imageData[start + 2] === 2
&& imageData[start + 4] === 3
&& imageData[start + 5] === 4
&& imageData[start + 6] === 5
&& imageData[start + 8] === 5
&& imageData[start + 9] === 4
&& imageData[start + 10] === 3
&& imageData[start + 12] === 2
&& imageData[start + 13] === 1
&& imageData[start + 15] === 0;
}
// Checks if our magic sequence is present in a column configuration
private detectColumnMarker(x, y, rowLength, imageData) {
const rowOffset = rowLength * 4;
const r1 = (y * rowLength + x) * 4;
const r2 = r1 + rowOffset;
const r3 = r2 + rowOffset;
const r4 = r3 + rowOffset;
return imageData[r1] === 0
&& imageData[r1 + 1] === 1
&& imageData[r1 + 2] === 2
&& imageData[r2 ] === 3
&& imageData[r2 + 1] === 4
&& imageData[r2 + 2] === 5
&& imageData[r3 ] === 5
&& imageData[r3 + 1] === 4
&& imageData[r3 + 2] === 3
&& imageData[r4 ] === 2
&& imageData[r4 + 1] === 1
&& imageData[r4 + 2] === 0;
}
}

View File

@ -1,31 +0,0 @@
export interface MessageSender {
documentId?: string,
frameId?: number,
id: string,
origin: string,
tab: Tab,
url: string,
}
export interface Tab {
active: boolean,
audible?: boolean,
autoDiscardable: boolean,
discarded: boolean,
favIconUrl?: string,
frozen?: boolean,
groupId: number,
height: number,
highlighted: boolean,
id?: number,
incognito: boolean,
index: number,
lastAccessed: number,
openerTabId?: number,
pendingUrl?: string,
pinned: boolean,
sessionId: string,
url?: string;
width: number;
windowId?: number;
}

View File

@ -117,29 +117,6 @@ class CommsClient {
this.commsId = (Math.random() * 20).toFixed(0);
this.eventBus.subscribeMulti(
{
'uw-get-page-stats': {
function: (config, routingData) => {
this.eventBus.send(
'uw-page-stats',
{
pcsDark: window.matchMedia('(prefers-color-scheme: dark)').matches,
pcsLight: window.matchMedia('(prefers-color-scheme: light)').matches,
colorScheme: window.getComputedStyle( document.body ,null).getPropertyValue('color-scheme')
},
{
comms: {
forwardTo: 'popup'
}
}
);
}
},
},
this
);
} catch (e) {
console.error("CONSTRUCOTR FAILED:", e)
}

View File

@ -4,15 +4,6 @@ if (process.env.CHANNEL !== 'stable'){
console.info("Loading: UI");
}
// When this was first coded in summer of 2024, websites using color-scheme other than 'normal'
// displayed a black square over the video instead of a transparent iframe that we expect.
// StackOverflow said that this was a workaround for the issue, and it worked just fine. However,
// 6 months later, this fix is no longer working. Using csui-overlay-normal even on dark mode websites
// appears to give us a transparent iframe, as we require.
// Twitter is an example of a site using this color-scheme=dark property, so any changes to this code
// should be tested on this video:
// https://x.com/TheKhelIndia/status/1874019989357027511?mx=2
// As of 1. 1. 2025, 'light' and 'dark' are commented out in order to force 'csui-overlay-normal' everywhere.
const csuiVersions = {
'normal': 'csui', // csui-overlay-normal.html, maps to csui.html
'light': 'csui-light', // csui-overlay-light.html, maps to csui-light.html
@ -29,8 +20,6 @@ class UI {
this.interfaceId = interfaceId;
this.uiConfig = uiConfig;
this.lastProbeResponseTs = null;
this.isVisible = false;
this.isOpaque = false;
this.isGlobal = uiConfig.isGlobal ?? false;
@ -40,43 +29,44 @@ class UI {
this.saveState = undefined;
this.playerData = uiConfig.playerData;
this.uiSettings = uiConfig.uiSettings;
this.siteSettings = uiConfig.siteSettings;
this.settings = uiConfig.settings;
this.iframeErrorCount = 0;
this.iframeConfirmed = false;
this.iframeRejected = false;
this.preventHiding = false;
this.wantsToHide = false;
this.wantsToTransparent = false;
this.performedTransparencyCheck = false;
// TODO: at some point, UI should be different for global popup and in-player UI
const csuiVersion = this.getCsuiVersion();
this.csuiScheme = this.getCsuiScheme();
const csuiVersion = this.getCsuiVersion(this.csuiScheme.contentScheme);
this.uiURI = chrome.runtime.getURL(`/csui/${csuiVersion}.html`);
this.extensionBase = chrome.runtime.getURL('').replace(/\/$/, "");
}
async init() {
try {
this.initIframes();
this.initMessaging();
} catch (e) {
console.error('failed to init ui:', e)
}
}
getCsuiVersion() {
if (this.siteSettings?.workarounds?.forceColorScheme) {
return csuiVersions[this.siteSettings.workarounds.forceColorScheme];
}
if (this.siteSettings?.data?.workarounds?.disableColorSchemeAwareness !== false) {
return csuiVersions.normal;
/**
* Returns color scheme we need to use.
*
* contentScheme is used to select the correct HTML template.
* iframeScheme gets applied to the iframe as style
* @returns {contentScheme: string, iframeScheme: string}
*/
getCsuiScheme() {
return {
contentScheme: window.getComputedStyle( document.body ,null).getPropertyValue('color-scheme'),
iframeScheme: document.documentElement.style.colorScheme || document.body.style.colorScheme || undefined
};
}
const preferredScheme = window.getComputedStyle( document.body ,null).getPropertyValue('color-scheme');
/**
* Returns correct template for given preferredScheme parameter
* @param {*} preferredScheme
* @returns
*/
getCsuiVersion(preferredScheme) {
return csuiVersions[preferredScheme] ?? csuiVersions.normal;
}
@ -119,10 +109,16 @@ class UI {
iframe.style.position = "absolute";
iframe.style.zIndex = this.isGlobal ? '90009' : '90000';
iframe.style.border = 0;
iframe.style.opacity = 0;
iframe.style.pointerEvents = 'none';
iframe.style.opacity = 0;
iframe.style.backgroundColor = 'transparent !important';
// If colorScheme is defined via CSS on the HTML or BODY elements, then we need to also
// put a matching style to the iframe itself. Using the correct UI template is not enough.
if (this.csuiScheme.iframeScheme) {
iframe.style.colorScheme = this.csuiScheme.iframeScheme;
}
/* so we have a problem: we want iframe to be clickthrough everywhere except
* on our actual overlay. There's no nice way of doing that, so we need some
* extra javascript to deal with this.
@ -132,7 +128,6 @@ class UI {
* that iframe also needs to run its own instance of onMouseMove.
*/
// set uiIframe for handleMessage
this.uiIframe = iframe;
@ -183,18 +178,6 @@ class UI {
document.addEventListener('mousemove', fn, true);
}
// Add some squares to the page.
// Sets up checks for conditions that cause these two mutually exclusive issues:
// * https://github.com/tamius-han/ultrawidify/issues/262
// * https://github.com/tamius-han/ultrawidify/issues/259
for (const x of ['left', 'center', 'right']) {
for (const y of ['top', 'center', 'bottom']) {
if (x !== y) {
rootDiv.appendChild(this.generateDebugMarker(x, y));
}
}
}
rootDiv.appendChild(iframe);
}
@ -210,11 +193,6 @@ class UI {
this.eventBus.subscribeMulti(
{
'uw-reload-window': {
function: () => {
window.location.reload();
}
},
'uw-config-broadcast': {
function: (config, routingData) => {
this.sendToIframe('uw-config-broadcast', config, routingData);
@ -232,6 +210,24 @@ class UI {
this.sendToIframe('uw-set-ui-state', {...config, isGlobal: this.isGlobal}, routingData);
}
},
'uw-get-page-stats': {
function: (config, routingData) => {
console.log('got get page stats!');
this.eventBus.send(
'uw-page-stats',
{
pcsDark: window.matchMedia('(prefers-color-scheme: dark)').matches,
pcsLight: window.matchMedia('(prefers-color-scheme: light)').matches,
colorScheme: window.getComputedStyle( document.body ,null).getPropertyValue('color-scheme')
},
{
comms: {
forwardTo: 'popup'
}
}
);
}
},
'uw-restore-ui-state': {
function: (config, routingData) => {
if (!this.isGlobal) {
@ -239,26 +235,8 @@ class UI {
this.sendToIframe('uw-restore-ui-state', config, routingData);
}
}
},
'iframe-transparency-results': {
function: (data, routingData) => {
console.log('——————————— iframe transparency results are back!', data);
}
},
'uw-get-page-stats': {
function: (config, routingData) => {
console.log('uw:Č Got page stats request')
this.sendToIframeLowLevel(
'uw-page-stats',
{
pcsDark: window.matchMedia('(prefers-color-scheme: dark)').matches,
pcsLight: window.matchMedia('(prefers-color-scheme: light)').matches,
colorScheme: window.getComputedStyle( document.body ,null).getPropertyValue('color-scheme')
},
);
}
},
},
this
);
}
@ -271,151 +249,7 @@ class UI {
this.handleMessage(message);
}
verifyIframeTransparency() {
if (this.isGlobal) {
return;
}
if (!this.siteSettings || !this.settings) {
console.warn('settings not provided, not verifying transparency');
return;
}
if (this.performedTransparencyCheck || !this.isOpaque || !this.uiConfig?.parentElement) {
// console.warn('transparency was already checked, opacity is zero, or parent element isnt a thing:', this.performedTransparencyCheck, 'is opaque:', this.isOpaque, this.uiConfig?.parentElement);
return;
}
let reportTelemetry = true;
// if (this.siteSettings.data.workarounds?.disableSchemeAwareness) {
// if (this.settings.active.telemetry?.iframeTransparency?.[window.location.hostname]?.reportedWithColorSchemeDisabled) {
// reportTelemetry = false;
// } else {
// this.settings.setProp(['telemetry', window.location.hostname, 'reportedWithColorSchemaDisabled'], true)
// }
// } else {
// if (this.settings.active.telemetry?.iframeTransparency?.[window.location.hostname]?.reportedWithColorSchemeAllowed) {
// reportTelemetry = false;
// } else {
// this.settings.setProp(['telemetry', window.location.hostname, 'reportedWithColorSchemeAllowed'], true)
// }
// }
const rect = this.uiConfig.parentElement.getBoundingClientRect();
this.preventHiding = true;
setTimeout( () => {
this.eventBus.send(
'verify-iframe-transparency',
{
playerData: {
y: rect.top,
x: rect.left,
width: rect.width,
height: rect.height,
},
telemetryData: {
reportTelemetry,
}
}
);
}, 50);
this.performedTransparencyCheck = true;
setTimeout(() => {
this.preventHiding = false;
if (this.wantsToHide) {
this.setUiVisibility(false);
}
if (this.wantsToTransparent) {
this.setUiOpacity(false);
}
this.wantsToHide = false;
this.wantsToTransparent = false;
});
}
/**
* Generates marker positions for bug mitigations
*/
generateDebugMarker(x, y) {
const [parentMainDimension, parentCrossDimension] = y === 'center' ? ['height', 'width'] : ['width', 'height'];
let anchorStyle;
if (x === 'center' && x === y) {
anchorStyle = 'left: 50%; top: 50%; transform: translate(-50%, -50%);';
} else {
switch (x) {
case 'left':
anchorStyle = 'left: 0px;';
break;
case 'center':
anchorStyle = 'left: 50%; transform: translateX(-50%);';
break;
case 'right':
anchorStyle = 'right: 0px;';
break;
}
switch (y) {
case 'top':
anchorStyle = `${anchorStyle} top: 0px;`;
break;
case 'center':
anchorStyle = `${anchorStyle} top: 50%; transform: translateY(-50%);`;
break;
case 'bottom':
anchorStyle = `${anchorStyle} bottom: 0px;`;
break;
}
}
let [mainAxis, crossAxis] = y === 'center' ? ['left', 'top'] : ['top', 'left'];
const template = document.createElement('template');
template.innerHTML = `
<div style="position: absolute; ${anchorStyle} ${parentMainDimension}: 4px; ${parentCrossDimension}: 1px; pointer-events: none;">
<div style="position: relative; width: 100%; height: 100%">
<div style="position: absolute; ${mainAxis}: 0px; ${crossAxis}: 0px; width: 1px; height: 1px; background-color: #000102"></div>
<div style="position: absolute; ${mainAxis}: 1px; ${crossAxis}: 0px; width: 1px; height: 1px; background-color: #030405"></div>
<div style="position: absolute; ${mainAxis}: 2px; ${crossAxis}: 0px; width: 1px; height: 1px; background-color: #050403"></div>
<div style="position: absolute; ${mainAxis}: 3px; ${crossAxis}: 0px; width: 1px; height: 1px; background-color: #020100"></div>
</div>
<div style="top: 5px; left: 5px; opacity: 0">
This marker is Chrome Shitiness Mitigation mechanism for Ultrawidify. It turns out that as of 2025-01, Chrome does not correctly respect
allowTransparency property on certain iframes, and will force white or black background across the entire element. It is unclear what's
causing the issue so far, it seems to appear randomly.
</div>
</div>
`.replace(/\s+/g, ' ').trim();
return template.content.firstChild;
}
setUiOpacity(visible) {
if (!visible && this.isVisible && this.preventHiding) {
this.wantsToTransparent = true;
return;
}
this.uiIframe.style.opacity = visible ? '100' : '0';
this.isOpaque = visible;
if (visible) {
this.verifyIframeTransparency();
}
}
setUiVisibility(visible) {
if (!visible && this.isVisible && this.preventHiding) {
this.wantsToHide = true;
return;
}
this.isVisible = visible;
if (visible) {
this.element.style.width = '100%';
this.element.style.height = '100%';
@ -427,7 +261,6 @@ class UI {
this.uiIframe.style.width = '0px';
this.uiIframe.style.height = '0px';
}
}
async enable() {
@ -529,8 +362,7 @@ class UI {
}
this.uiIframe.style.pointerEvents = event.data.clickable ? 'auto' : 'none';
this.setUiOpacity(event.data.opacity || this.isGlobal);
// this.setUiVisibility( event.data.opacity || this.isGlobal );
this.uiIframe.style.opacity = event.data.opacity || this.isGlobal ? '100' : '0';
break;
case 'uw-bus-tunnel':
const busCommand = event.data.payload;
@ -543,8 +375,7 @@ class UI {
this.setUiVisibility(!this.isGlobal);
break;
case 'uwui-hidden':
this.setUiOpacity(event.data.opacity || this.isGlobal);
// this.setUiVisibility(event.data.opacity || this.isGlobal);
this.uiIframe.style.opacity = event.data.opacity || this.isGlobal ? '100' : '0';
break;
case 'uwui-global-window-hidden':
if (!this.isGlobal) {

View File

@ -248,11 +248,7 @@ class PlayerData {
//#endregion
deferredUiInitialization(playerDimensions) {
if (
this.ui
|| ! this.videoData.settings.active.ui?.inPlayer?.enabled
|| (this.siteSettings.data.ui && !this.siteSettings.data.ui.enabled)
) {
if (this.ui || ! this.videoData.settings.active.ui?.inPlayer?.enabled) {
return;
}
@ -269,9 +265,7 @@ class PlayerData {
parentElement: this.element,
eventBus: this.eventBus,
playerData: this,
uiSettings: this.videoData.settings.active.ui,
settings: this.videoData.settings,
siteSettings: this.siteSettings
uiSettings: this.videoData.settings.active.ui
}
);