Full Code of dralletje/Windowed for AI

master 23a8de5fdbd2 cached
28 files
103.7 KB
26.6k tokens
7 symbols
1 requests
Download .txt
Repository: dralletje/Windowed
Branch: master
Commit: 23a8de5fdbd2
Files: 28
Total size: 103.7 KB

Directory structure:
gitextract_b72y24m9/

├── .gitignore
├── Icons.sketch
├── PrivacyPolicyForEdgeWebstore.md
├── README.md
├── Testing.md
├── extension/
│   ├── Background/
│   │   ├── BackgroundModule.js
│   │   ├── theme-color/
│   │   │   ├── chrome-offscreen.html
│   │   │   ├── chrome-offscreen.js
│   │   │   ├── chrome.js
│   │   │   └── firefox.js
│   │   └── tint_image.js
│   ├── Content.js
│   ├── Popup/
│   │   ├── Popup.html
│   │   ├── Popup.js
│   │   └── Theme-Colors.js
│   ├── Vendor/
│   │   ├── Browser.d.ts
│   │   └── Browser.js
│   ├── Windowed-inject-into-page.js
│   ├── manifest-firefox.json
│   ├── manifest.json
│   ├── tsconfig.json
│   └── utilities/
│       └── themecolor-to-string.js
├── package.json
└── testing/
    ├── iframe/
    │   └── iframe.html
    ├── page.html
    ├── stadia-like.html
    ├── test.js
    └── ustream/
        └── ustream.html

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
extension.zip
.DS_Store
node_modules


================================================
FILE: PrivacyPolicyForEdgeWebstore.md
================================================
# Privacy Policy

I don't collect any data.

There are no network requests done by the extension nor any data read from websites.
You can look at the source, there is no way I could get your data 🤷‍♀️


================================================
FILE: README.md
================================================
# ![Windowed Logo](extension/Icons/Icon_32.png) Windowed

[Install in Chrome Webstore](https://chrome.google.com/webstore/detail/windowed-floating-youtube/gibipneadnbflmkebnmcbgjdkngkbklb)  
[Install in Firefox](https://addons.mozilla.org/firefox/addon/windowed/)  
[Install in Edge](https://microsoftedge.microsoft.com/addons/detail/windowed-floating-youtu/kfaokmgjemianbbeadblgppcedfihdnb)

A small extension for chrome and firefox I made because I don't really like normal fullscreen. It injects itself into every page, and replaces `HTMLElement.prototype.requestFullscreen` (or the browser specific version) with a popup to get into Windowed, In-window or picture in picture mode. Important for me is that the video/page does not reload when switching to Windowed.

![Chromestore screenshot](Chromewebstore%20screenshot%20%231.png)

### Modes

Even though I want to create a very minimalist extension, I still provide some choice every time you want to go into fullscreen.

- **Windowed** <kbd>w</kbd>  
  Obviously my favourite, hence the namesake of this extension. Puts video in a seperate window with title bar. I can't get rid of the title bar because of restrictions in Chrome extensions. Other extensions do put videos in title-bar-less windows, but that requires a reload of the page, and I really really really don't like that. Also it does not float. Floating also, is exclusive to reload
- **In-Window** <kbd>i</kbd>  
  Does the same as Windowed, but doesn't pop the window out. So it will fill the full window that as it is right now.
- **Fullscreen** <kbd>f</kbd>  
  Get out, ya joker
- **Picture in Picture** <kbd>p</kbd>  
  This only shows up if a video element is found. Will put the video into native Picture-in-Picture mode, with only browser controls. This is amazing in most cases, butttttt here is one big disadvantage, the only reason I'm still working on this extension even, is that it **only works on videos**, and only **without any website specific controls**. For youtube, that doesn't really stack up against the floating-on-top feature for me. Still, there are many websites that do put something else (not a video) in fullscreen, which picture-in-picture does not support.  
  Either picture-in-picture needs to work for arbitrary elements, which would be really awesome, or chrome extensions should be able to make floating-on-top windows (they used to). Until then, we are stuck with two modes for just slightly different situations

### Why so much code

There are actually a lot of things that the websites need when I'm fullscreen-ing them.

1. There is mostly a lot of code for the popup that allows you choose between Windowed and fullscreen.
2. Some of them remove the component we want to fullscreen as soon as we resize, in which case I copy the element and try to show it anyway.
3. When clicking inside a frame, it should fullscreen that frame inside the parent window.
4. Need to be able to disable domain by clicking the Windowed toolbar button, and I want the icon to update accordingly on every tab.
5. Even just selecting the right window to restore the tab to after going out of windowed mode again takes more lines then you'd expect.
6. Firefox has some restrictions on requesting fullscreen from async functions, so had to work around that by using sync functions first where necessary.

### Hey I got a request or even got something that might be useful

Nice. Please open an issue or a PR. That'd be really cool. 😎

---

Thanks for reading,  
Michiel Dral


================================================
FILE: Testing.md
================================================
# Testing

In both browsers currently supported (Chrome and Firefox)
I should test these things:

### Basic usage
1. Activate the extension
2. click a fullscreen button (eg. on youtube)
3. Try all three options (Windowed, in window and fullscreen)
4. Every option should transform the window into the right "format"
5. Click the "un-fullscreen" button, and it should transform back to a normal tab again.

### Disabling
1. Activate the extension
2. Go to youtube
3. Press the "Browser action" (thus, disabling the extension on youtube)
4. Press the fullscreen button
5. No option menu should appear now, it goes into normal fullscreen
6. Go out of fullscreen again
7. Refresh the page
8. Press the fullscreen button
9. No option menu should appear now, it goes into normal fullscreen
10. Go out of fullscreen again


================================================
FILE: extension/Background/BackgroundModule.js
================================================
// Import is not yet allowed in firefox, so for now I put tint_image in manifest.json
import { icon_theme_color_chrome } from "./theme-color/chrome.js";
import { icon_theme_color_firefox } from "./theme-color/firefox.js";
import { tint_image } from "./tint_image.js";
// import { browser } from "../Vendor/Browser.js";

/**
 * @param {import("webextension-polyfill").Tabs.Tab} tab
 * @returns {Promise<string>}
 */
let icon_theme_color = async (tab) => {
  if (await is_firefox) {
    return await icon_theme_color_firefox(tab);
  } else {
    return await icon_theme_color_chrome(tab);
  }
};

/** @type {import("webextension-polyfill").Browser} */
// @ts-ignore
let browser = chrome;

let BROWSERACTION_ICON = "/Images/Icon_Windowed_Mono@1x.png";

let browser_info_promise = browser.runtime.getBrowserInfo
  ? browser.runtime.getBrowserInfo()
  : Promise.resolve({ name: "Chrome" });
let is_firefox = browser_info_promise.then(
  (browser_info) => browser_info.name === "Firefox",
);

/**
 * @param {import("webextension-polyfill").Windows.Window} window
 */
let is_valid_window = (window) => {
  return (
    window.incognito === false &&
    window.type === "normal" &&
    window.state !== "minimized"
  );
};

/**
 * Firefox can't take the `focused` property to browser.windows.create/update
 * So I just take it out when using firefox 🤷‍♀️
 * @param {import("webextension-polyfill").Windows.CreateCreateDataType} window_properties
 * @returns {Promise<import("webextension-polyfill").Windows.CreateCreateDataType>}
 */
let firefix_window = async (window_properties) => {
  let is_it_firefox = await is_firefox;
  if (is_it_firefox) {
    let { focused, ...good_properties } = window_properties;
    return good_properties;
  } else {
    return window_properties;
  }
};

// Get a window to put our tab on: either the last focussed, a random, or none;
// In case of none being found, null is returned and the caller should make a new window himself (with the tab attached)
/**
 * @param {number} windowId
 * @returns {Promise<import("webextension-polyfill").Windows.Window>}
 */
const get_fallback_window = async (windowId) => {
  const first_fallback_window = await browser.windows.getLastFocused({
    // @ts-ignore
    windowTypes: ["normal"],
  });

  if (
    first_fallback_window.id !== windowId &&
    is_valid_window(first_fallback_window)
  ) {
    return first_fallback_window;
  } else {
    const windows = await browser.windows.getAll({ windowTypes: ["normal"] });
    const right_window = windows
      .filter((x) => is_valid_window(x))
      .filter((x) => x.id !== windowId)
      .sort((a, b) => a.tabs.length - b.tabs.length)[0];

    if (right_window) {
      return right_window;
    } else {
      return null;
    }
  }
};

// TODO Instead of using this static height, I can maybe "ping" the page I'm popup-izing
// after it is done becoming a popup: then it can figure out it's position itself
// (and check the size of it's current header itself)
const Chrome_Popup_Menubar_Height = 22; // Do `window.outerHeight - window.innerHeight` in a popup tab

/**
 * @typedef WindowedMode
 * @type {"fullscreen" | "windowed" | "in-window" | "fullscreen" | "ask"}
 */

/**
 * @param {string} mode
 * @param {boolean} disabled
 * @returns {WindowedMode}
 */
let clean_mode = (mode, disabled) => {
  // Any other mode than the known ones are ignored
  if (mode == "fullscreen" || mode == "windowed" || mode == "in-window") {
    return mode;
  }
  return disabled === true ? "fullscreen" : "ask";
};

let ALL_MODE = "mode(*)";
let ALL_PIP = "pip(*)";

/** @param {import("webextension-polyfill").Tabs.Tab} tab */
let get_host_config = async (tab) => {
  let host = new URL(tab.url).host;
  let host_mode = `mode(${host})`;
  let host_pip = `pip(${host})`;
  let {
    [host_mode]: mode,
    [host]: disabled,
    [host_pip]: pip,
    [ALL_MODE]: all_mode,
    [ALL_PIP]: all_pip,
  } = /** @type {any} */ (
    await browser.storage.sync.get([
      host_mode,
      host,
      host_pip,
      ALL_MODE,
      ALL_PIP,
    ])
  ) ?? {};

  return {
    mode: clean_mode(mode ?? all_mode, disabled),
    pip: (pip ?? all_pip) === true,
    all_mode: clean_mode(all_mode, false),
    all_pip: all_pip === true,
  };
};

/**
 * Wrapper to do some basic routing on extension messaging
 * @param {string} type
 * @param {(message: any, sender: import("webextension-polyfill").Runtime.MessageSender) => Promise<any>} fn
 * @return {void}
 */
let onMessage = (type, fn) => {
  browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
    // @ts-ignore
    if (message?.type === type) {
      fn(message, sender)
        .then((result) => {
          return { type: "resolve", value: result };
        })
        .catch((err) => {
          return {
            type: "reject",
            value: { message: err.message, stack: err.stack },
          };
        })
        .then((x) => sendResponse(x));
      return true;
    }
  });
};

onMessage("update_windowed_button", async (message, sender) => {
  let tabs = message.id
    ? [await browser.tabs.get(message.id)]
    : await browser.tabs.query(message.query);
  for (let tab of tabs) {
    await update_button_on_tab(tab);
  }
});

onMessage("get_windowed_config", async (message, sender) => {
  return await get_host_config(sender.tab);
});

onMessage("get_theme", async (message, sender) => {
  return await browser.theme.getCurrent();
});

/** Detatch the current tab and put it into a standalone popup window */
onMessage("please_make_me_a_popup", async (message, sender) => {
  // TODO Save windowId and index inside that window,
  // so when you "pop" it back, it will go where you opened it
  let {
    left: screenLeft,
    top: screenTop,
    type: windowType,
  } = await browser.windows.get(sender.tab.windowId);

  await TabOrigins.set(sender.tab.id, {
    windowId: sender.tab.windowId,
    index: sender.tab.index,
    pinned: sender.tab.pinned,
  });

  // TODO Check possible 'panel' support in firefox
  let frame = message.position;
  if (windowType === "popup") {
    // Already a popup, no need to re-create the window
    await browser.windows.update(
      sender.tab.windowId,
      await firefix_window({
        focused: true,
        left: Math.round(screenLeft + frame.left),
        top: Math.round(screenTop + frame.top - Chrome_Popup_Menubar_Height),
        width: Math.round(frame.width),
        height: Math.round(frame.height + Chrome_Popup_Menubar_Height),
      }),
    );
    return;
  }

  const created_window = await browser.windows.create(
    await firefix_window({
      tabId: sender.tab.id,
      type: "popup",
      focused: true,
      left: Math.round(screenLeft + frame.left),
      top: Math.round(screenTop + frame.top - Chrome_Popup_Menubar_Height),
      width: Math.round(frame.width),
      height: Math.round(frame.height + Chrome_Popup_Menubar_Height),
    }),
  );

  if (await is_firefox) {
    await browser.windows.update(created_window.id, {
      titlePreface: "Windowed: ",
      focused: true,
    });
  }
  return;
});

/**
 * @typedef TabOrigin
 * @type {{
 *   windowId: import("webextension-polyfill").Windows.Window["id"]
 *   pinned: import("webextension-polyfill").Tabs.Tab["pinned"]
 *   index: import("webextension-polyfill").Tabs.Tab["index"]
 * }}
 */
let TabOrigins = {
  /**
   * @param {import("webextension-polyfill").Tabs.Tab["id"]} tabId
   * @returns {string}
   */
  key: (tabId) => `tab_origin(${tabId})`,
  /**
   * @param {import("webextension-polyfill").Tabs.Tab["id"]} tabId
   * @returns {Promise<TabOrigin | undefined>}
   */
  get: async (tabId) => {
    let key = TabOrigins.key(tabId);
    let { [key]: data } = await browser.storage.session.get(key);
    return /** @type {any} */ (data);
  },
  /**
   * @param {import("webextension-polyfill").Tabs.Tab["id"]} tabId
   * @param {TabOrigin} tab_origin
   */
  set: async (tabId, tab_origin) => {
    let key = TabOrigins.key(tabId);
    await browser.storage.session.set({ [key]: tab_origin });
  },
};

/**
 * Take the current tab, and put it into a tab-ed window again.
 * 1. Last focussed window
 * 2. Other tab-containing window (not popups without tab bar)
 * 3. New window we create
 */
onMessage("please_make_me_a_tab_again", async (message, sender) => {
  let { type: windowType } = await browser.windows.get(sender.tab.windowId);
  if (windowType === "normal") {
    return;
  }

  let origin = await TabOrigins.get(sender.tab.id);
  if (origin != undefined) {
    let window = await browser.windows.get(origin.windowId).catch(() => null);
    if (window) {
      await browser.tabs.move(sender.tab.id, {
        windowId: origin.windowId,
        index: origin.index,
      });
      await browser.tabs.update(sender.tab.id, {
        active: true,
        pinned: origin.pinned,
      });
      return;
    }
  }

  let fallback_window = await get_fallback_window(sender.tab.windowId);

  if (fallback_window) {
    await browser.tabs.move(sender.tab.id, {
      windowId: fallback_window.id,
      index: -1,
    });
    await browser.tabs.update(sender.tab.id, { active: true });
  } else {
    // No other window open: create a new window with tabs
    let create_window_with_tabs = await browser.windows.create({
      tabId: sender.tab.id,
      type: "normal",
    });
  }
});

/** @type {{ [tabid: number]: Promise<boolean> }} */
let current_port_promises = {};
/**
 * Check if we can connect with the Windowed content script in a tab
 * @param {number} tabId
 * @returns {Promise<boolean>}
 */
let ping_content_script = async (tabId) => {
  try {
    if (current_port_promises[tabId] != null) {
      return await current_port_promises[tabId];
    } else {
      current_port_promises[tabId] = new Promise((resolve, reject) => {
        let port = browser.tabs.connect(tabId);
        port.onMessage.addListener((message) => {
          resolve(true);
          port.disconnect();
        });
        port.onDisconnect.addListener((p) => {
          /// Just need to check for errors so chrome doesn't show a warning
          browser.runtime.lastError;
          resolve(false);
        });
      });
      return await current_port_promises[tabId];
    }
  } finally {
    delete current_port_promises[tabId];
  }
};

/**
 * This looks a bit weird (and honestly it is) but it opens a port to the content script on a page,
 * and then the page knows it should reload it's settings.
 * TODO? Should I close the port?
 * @param {number} tabId
 * @param {any} properties
 */
let notify_tab_state = async (tabId, properties) => {
  let port = browser.tabs.connect(tabId);
  // port.postMessage(JSON.stringify({ method: 'notify', data: properties }))
};

/**
 * Shorthand for setting the browser action icon on a tab
 * @param {number} tabId
 * @param {{ icon: ImageData, title: string }} action
 */
let apply_browser_action = async (tabId, action) => {
  try {
    await browser.action.setIcon({
      tabId: tabId,
      // @ts-ignore
      imageData: action.icon,
    });
    await browser.action.setTitle({
      tabId: tabId,
      title: action.title,
    });
  } catch (error) {
    console.log(`Setting browser action ERROR:`, error);
  }
};

/**
 * @param {import("webextension-polyfill").Tabs.Tab} tab
 */
let update_button_on_tab = async (tab) => {
  let has_contentscript_active =
    tab.status === "complete" && (await ping_content_script(tab.id));

  // A specific exception for about:blank so, on firefox,
  // when you customize your menu bar, windowed is at it's most beautiful.
  if (has_contentscript_active === false && tab.url === "about:blank") {
    await apply_browser_action(tab.id, {
      icon: await tint_image(BROWSERACTION_ICON, await icon_theme_color(tab)),
      title: `Windowed`,
    });
    return;
  }

  // On some domains windowed will never work, because it is blocked by the browser.
  // To avoid user confusion with the "You have to reload" message,
  // I show a descriptive error 💁‍♀️
  if (
    has_contentscript_active === false &&
    (tab.url.match(/^about:/) ||
      tab.url.match(/^chrome:\/\//) ||
      tab.url.match(/^edge:\/\//) ||
      tab.url.match(/^https?:\/\/chrome\.google\.com/) ||
      tab.url.match(/^https?:\/\/support\.mozilla\.org/) ||
      tab.url.match(/^https?:\/\/addons.mozilla.org/) ||
      tab.url === "")
  ) {
    await apply_browser_action(tab.id, {
      icon: await tint_image(BROWSERACTION_ICON, "rgba(208, 2, 27, .22)"),
      title: `For security reasons, windowed is not supported on this domain (${tab.url}).`,
    });
    return;
  }

  // So if the tab is loaded, and it is not an extra secure domain,
  // it means windowed is not loaded for some reason. So I tell that.
  if (tab.status === "complete" && has_contentscript_active === false) {
    await apply_browser_action(tab.id, {
      icon: await tint_image(BROWSERACTION_ICON, "#D0021B"),
      title:
        "This page needs to be reloaded for Windowed to activate. Click here to reload.",
    });
    return;
  }

  // From here I figure out what the user has configured for Windowed on this domain,
  // and show a specific icon and title for each of those.
  let host = new URL(tab.url).host;
  let config = await get_host_config(tab);
  if (config.mode === "fullscreen" && config.pip === false) {
    // ONLY FULLSCREEN - Dimmed icon because it is basically disabled
    await apply_browser_action(tab.id, {
      icon: await tint_image(BROWSERACTION_ICON, "rgba(133, 133, 133, 0.5)"),
      title: `Windowed is disabled on ${host}, click to re-activate`,
    });
    await notify_tab_state(tab.id, { disabled: true });
  } else if (config.mode === config.all_mode && config.pip === config.all_pip) {
    // SAME AS DEFAULT - This used to be config.mode === "ask" && config.pip === false,
    // but now you can change the default... this is the color for default
    await apply_browser_action(tab.id, {
      icon: await tint_image(BROWSERACTION_ICON, await icon_theme_color(tab)),
      title: `Windowed is enabled on ${host}`,
    });
    await notify_tab_state(tab.id, { disabled: false });
  } else {
    // SOMETHING SPECIFIC - Light blue because now the extension is really changing your browsing explicitly
    await apply_browser_action(tab.id, {
      icon: await tint_image(BROWSERACTION_ICON, "#16a8a8"),
      title: `Windowed is enabled on ${host}`,
    });
    await notify_tab_state(tab.id, { disabled: false });
  }
};

// Events where I refresh the browser action button
browser.runtime.onInstalled.addListener(async () => {
  let all_tabs = await browser.tabs.query({});
  for (let tab of all_tabs) {
    await update_button_on_tab(tab);
  }
});
browser.tabs.onUpdated.addListener(async (tabId, changed, tab) => {
  if (changed.url != null || changed.status != null) {
    await update_button_on_tab(tab);
  }
});
// Not sure if I need this one -
// only reason I need it is for when one would toggle Enabled/Disabled
browser.tabs.onActivated.addListener(async ({ tabId }) => {
  let tab = await browser.tabs.get(tabId);
  await update_button_on_tab(tab);
});
// Because I load this as a module now, I am making sure this code is ran as much as possible
(async () => {
  let all_tabs = await browser.tabs.query({});
  for (let tab of all_tabs) {
    await update_button_on_tab(tab);
  }
})();


================================================
FILE: extension/Background/theme-color/chrome-offscreen.html
================================================
<script src="./chrome-offscreen.js"></script>


================================================
FILE: extension/Background/theme-color/chrome-offscreen.js
================================================
/** @type {import("webextension-polyfill").Browser} */
// @ts-ignore
let browser = chrome;

browser.runtime.onMessage.addListener(
  (/** @type {any} */ message, sender, sendResponse) => {
    // Return early if this message isn't meant for the offscreen document.
    if (message.target !== "offscreen") {
      return;
    }

    if (message.type === "matchMedia") {
      const result = window.matchMedia(message.data);
      sendResponse({
        matches: result.matches,
        media: result.media,
      });
      return true;
    }
  },
);


================================================
FILE: extension/Background/theme-color/chrome.js
================================================
/** @type {import("webextension-polyfill").Browser} */
// @ts-ignore
let browser = chrome;

let creating; // A global promise to avoid concurrency issues
/**
 *
 * @param {string} path
 * @returns {Promise}
 */
async function setupOffscreenDocument(path) {
  // Check all windows controlled by the service worker to see if one
  // of them is the offscreen document with the given path
  const offscreenUrl = browser.runtime.getURL(path);
  const existingContexts = await browser.runtime.getContexts({
    // @ts-ignore
    contextTypes: ["OFFSCREEN_DOCUMENT"],
    documentUrls: [offscreenUrl],
  });

  if (existingContexts.length > 0) {
    return;
  }

  // create offscreen document
  if (creating) {
    await creating;
  } else {
    // @ts-ignore
    creating = browser.offscreen.createDocument({
      url: path,
      reasons: ["MATCH_MEDIA"],
      justification: "Use window.matchMedia to determine the theme color.",
    });

    await creating;
    creating = null;
  }
}

/**
 * @param {string} query
 * @returns {Promise<MediaQueryList>}
 */
let matchMedia = async (query) => {
  await setupOffscreenDocument("Background/theme-color/chrome-offscreen.html");

  // Send message to offscreen document
  return await browser.runtime.sendMessage({
    type: "matchMedia",
    target: "offscreen",
    data: query,
  });
};

/**
 * Tries to figure out the default icon color
 * - Tries to use the current theme on firefox
 * - Else defaults to light and dark mode
 * @param {import("webextension-polyfill").Tabs.Tab} tab
 * @returns {Promise<string>}
 */
export let icon_theme_color_chrome = async (tab) => {
  let x = (await matchMedia("(prefers-color-scheme: dark)")).matches
    ? "rgba(255,255,255,0.8)"
    : "#5f6368";
  return x;
};


================================================
FILE: extension/Background/theme-color/firefox.js
================================================
import { themecolor_to_string } from "../../utilities/themecolor-to-string.js";

/** @type {import("webextension-polyfill").Browser} */
// @ts-ignore
let browser = chrome;

/**
 * Tries to figure out the default icon color
 * - Tries to use the current theme on firefox
 * - Else defaults to light and dark mode
 * @param {import("webextension-polyfill").Tabs.Tab} tab
 * @returns {Promise<string>}
 */
export let icon_theme_color_firefox = async (tab) => {
  let theme = await browser.theme.getCurrent(tab.windowId);

  if (theme?.colors?.icons != undefined) {
    return themecolor_to_string(theme.colors.icons);
  }
  if (theme?.colors?.toolbar_field_border_focus != undefined) {
    return themecolor_to_string(theme.colors.toolbar_field_border_focus);
  }
  return window.matchMedia("(prefers-color-scheme: dark)").matches
    ? "rgba(255,255,255,0.8)"
    : "rgb(250, 247, 252)";
};


================================================
FILE: extension/Background/tint_image.js
================================================
/**
 * @param {string} color
 * @returns {{ color: string, alpha: number }}
 */
let find_and_replace_alpha = (color) => {
  let match = null;
  if (
    (match = color
      .replace(/ /g, "")
      .match(/^(rgba?\([^,]+,[^,]+,[^,]+,)([^,]+)(\))$/))
  ) {
    let [fullmatch, before, transparency, after] = match;
    let alpha = Number(transparency);
    if (!Number.isNaN(alpha)) {
      return {
        color: `${before}1${after}`,
        alpha: alpha,
      };
    } else {
      return { color, alpha: 1 };
    }
  } else if (
    (match = color.replace(/ /g, "").match(/^(#\d\d\d\d\d\d)(\d\d)$/))
  ) {
    let [fullmatch, before, transparency] = match;
    let alpha = Number(`0x${transparency}`) / 255;
    if (!Number.isNaN(alpha)) {
      return {
        color: `${before}`,
        alpha: alpha,
      };
    } else {
      return { color, alpha: 1 };
    }
  } else {
    return { color, alpha: 1 };
  }
};

/** @type {Map<string, Promise<ImageData>>} */
let color_icon_cache = new Map();

/**
 * Colorize an image with the use of a canvas
 * @param {string} url
 * @param {string} color
 * @returns {Promise<ImageData>}
 */
export let tint_image = async (url, color) => {
  let identifier = `${url}@${color}`;
  if (color_icon_cache.has(identifier)) {
    return color_icon_cache.get(identifier);
  } else {
    let icon = _color_icon(url, color);
    color_icon_cache.set(identifier, icon);
    return await icon;
  }
};

/**
 * @param {string} url
 * @param {string} _color
 */
let _color_icon = async (url, _color) => {
  let { color, alpha } = find_and_replace_alpha(_color);

  const blob = await fetch(url).then((r) => r.blob());
  const fg = await createImageBitmap(blob);

  let canvas = new OffscreenCanvas(fg.height, fg.width);

  // Initaliase a 2-dimensional drawing context
  let ctx = canvas.getContext("2d");
  let width = ctx.canvas.width;
  let height = ctx.canvas.height;

  // create offscreen buffer,
  let buffer = new OffscreenCanvas(fg.width, fg.height);

  let bx = buffer.getContext("2d");
  // fill offscreen buffer with the tint color
  bx.fillStyle = color;
  bx.fillRect(0, 0, buffer.width, buffer.height);
  // destination atop makes a result with an alpha channel identical to fg, but with all pixels retaining their original color *as far as I can tell*
  bx.globalCompositeOperation = "destination-atop";
  bx.drawImage(fg, 0, 0);

  // to tint the image, draw it first
  ctx.drawImage(fg, 0, 0);
  ctx.drawImage(buffer, 0, 0);

  ctx.globalAlpha = alpha;
  ctx.globalCompositeOperation = "copy";
  ctx.drawImage(canvas, 0, 0, width, height);
  ctx.globalAlpha = 1.0;

  return ctx.getImageData(0, 0, width, height);
};


================================================
FILE: extension/Content.js
================================================
// This file is by far the most important of the whole extension.
// This gets loaded into every single page you open, so I have to keep it as light as possible.
// Sounds a bit a weird, for a file with 1200 lines, but I want it to so light that I need
// more rather than less code. No modules or anything fance like that

/**
 * From https://github.com/fabiospampinato/noop-tag/blob/master/src/index.ts
 *
 * @param {TemplateStringsArray} strings
 * @param {unknown[]} expressions
 * @returns {string}
 */
const noop_template = (strings, ...expressions) => {
  let result = strings[0];
  for (let i = 1, l = strings.length; i < l; i++) {
    result += expressions[i - 1];
    result += strings[i];
  }
  return result;
};
let css = noop_template;
let html = noop_template;

// @ts-ignore
const browser = /** @type {import("webextension-polyfill").Browser} */ (
  globalThis.browser
);

const fullscreen_id_namespace = `windowed_long_id_that_does_not_conflict`;

const fullscreen_select = `${fullscreen_id_namespace}_select`;
const fullscreen_active = `${fullscreen_id_namespace}_active`;
const fullscreen_element_cloned = `${fullscreen_id_namespace}_ugly_hacky_cloned`;
const fullscreen_parent = `${fullscreen_id_namespace}_parent`;
const body_class = `${fullscreen_id_namespace}_body`;
const shadowdom_trail = `${fullscreen_id_namespace}_shadowdom`;

const max_z_index = "2147483647";

/**
 * @typedef PictureInPictureVideoElement
 * @type {HTMLVideoElement & {
 *  requestPictureInPicture(): Promise<void>,
 *  disablePictureInPicture: boolean,
 * }}
 */

// Aliasses for different browsers (rest of aliasses are in the inserted script)
let fullscreenchange_aliasses = [
  "fullscreenchange",
  "webkitfullscreenchange",
  "mozfullscreenchange",
  "MSFullscreenChange",
];
let requestFullscreen_aliasses = [
  "requestFullscreen",
  "mozRequestFullScreen",
  "webkitRequestFullscreen",
  "webkitRequestFullScreen",
  "msRequestFullscreen",
];
let exitFullscreen_aliasses = [
  "exitFullscreen",
  "webkitExitFullscreen",
  "webkitCancelFullScreen",
  "mozCancelFullScreen",
  "msExitFullscreen",
];
let fullscreenelement_aliasses = [
  "fullscreenElement",
  "webkitFullscreenElement",
  "mozFullscreenElement",
  "mozFullScreenElement",
  "msFullscreenElement",
  "webkitCurrentFullScreenElement",
];

let external_functions = {};
let next_id = 1;

let on_webpage = (strings, ...values) => {
  let result = strings[0];

  let value_index = 1;
  for (let value of values) {
    if (typeof value === "string") {
      result = result + value;
    }
    if (typeof value === "object") {
      result = result + JSON.stringify(value);
    }
    if (typeof value === "function") {
      external_functions[next_id] = value;
      result = result + `external_function(${next_id});`;
      next_id = next_id + 1;
    }
    result = result + strings[value_index];
    value_index = value_index + 1;
  }

  return result;
};

let all_communication_id = 0;
let external_function_parent =
  (function_id) =>
  async (...args) => {
    let request_id = `FROM_CONTENT:${all_communication_id}`;
    all_communication_id = all_communication_id + 1;

    if (window.parent === window) {
      return;
    }

    window.parent.postMessage(
      {
        type: "CUSTOM_WINDOWED_FROM_PAGE",
        request_id: request_id,
        function_id: function_id,
        args: args,
      },
      "*",
    );

    return new Promise((resolve, reject) => {
      let listener = (event) => {
        // We only accept messages from ourselves
        if (event.source != window.parent) return;
        if (event.data == null) return;

        if (event.data.type === "CUSTOM_WINDOWED_TO_PAGE") {
          if (event.data.request_id === request_id) {
            window.removeEventListener("message", listener);
            resolve(event.data.result);
          }
        }
      };
      window.addEventListener("message", listener);
    });
  };

let enable_selector = (element, key) => {
  element.dataset[key] = true;
};
let disable_selector = (element, key) => {
  delete element.dataset[key];
};

/**
 * @returns {Promise<{
 *  mode: import("./Background/BackgroundModule").WindowedMode,
 *  pip: boolean,
 * }>}
 */
let get_host_config_local = async () => {
  return await send_chrome_message({
    type: "get_windowed_config",
  });
};

/**
 * @param {ParentNode} root
 * @returns {HTMLElement}
 */
let get_fullscreen_select_element = (root = document) => {
  /** @type {HTMLElement} */
  let found_in_lightdom = root.querySelector(`[data-${fullscreen_select}]`);
  if (found_in_lightdom) return found_in_lightdom;

  // If not found in the lightdom, we follow the trail down the shadows
  let path_with_breadcrumb = root.querySelector(`[data-${shadowdom_trail}]`);
  if (path_with_breadcrumb?.shadowRoot != null) {
    return get_fullscreen_select_element(path_with_breadcrumb.shadowRoot);
  } else {
    throw new Error("Could not find the element that wants to be fullscreened");
  }
};

let Button = ({ icon, title, text, target }) => `
  <button data-target="${target}" title="${title}">
    <div class="icon mask-icon" style="mask-image: url(${icon})"></div>
    <span>${text}</span>
  </button>
`;

/**
 * @param {import("webextension-polyfill").Manifest.ThemeColor} color
 * @returns {string}
 */
let themecolor_to_string = (color) => {
  if (typeof color === "string") {
    return color;
  } else if (Array.isArray(color)) {
    if (color.length === 3) {
      return `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
    } else if (color.length === 4) {
      return `rgba(${color[0]}, ${color[1]}, ${color[2]}, ${color[3]})`;
    } else {
      // prettier-ignore
      throw new Error(`Invalid theme color array: ${JSON.stringify(color)}`);
    }
  } else {
    throw new Error(`Invalid theme color: ${color}`);
  }
};

// Insert requestFullScreen mock
const code_to_insert_in_page = on_webpage`{
  // Alliases for different browsers
  let requestFullscreen_aliasses = ${JSON.stringify(
    requestFullscreen_aliasses,
  )};
  let exitFullscreen_aliasses = ${JSON.stringify(exitFullscreen_aliasses)};
  let fullscreenelement_aliasses = ${JSON.stringify(
    fullscreenelement_aliasses,
  )};
  let fullscreenchange_aliasses = ${JSON.stringify(fullscreenchange_aliasses)};

  const send_event = (element, type) => {
    const event = new Event(type, {
      bubbles: true,
      cancelBubble: false,
      cancelable: false,
    });
    // if (element[\`on\${type}\`]) {
    //   element[\`on\${type}\`](event);
    // }
    element.dispatchEvent(event);
  };

  const send_fullscreen_events = (element) => {
    for (let fullscreenchange of fullscreenchange_aliasses) {
      send_event(document, fullscreenchange);
    }
    send_event(window, 'resize');
  };

  let all_communication_id = 0;
  let external_function = (function_id) => async (...args) => {
    let request_id = all_communication_id;
    all_communication_id = all_communication_id + 1;

    window.postMessage({
      type: 'CUSTOM_WINDOWED_FROM_PAGE',
      request_id: request_id,
      function_id: function_id,
      args: args,
    }, '*');

    return new Promise((resolve, reject) => {
      let listener = (event) => {
        // We only accept messages from ourselves
        if (event.source != window) return;
        if (event.data == null) return;

        if (event.data.type === 'CUSTOM_WINDOWED_TO_PAGE') {
          if (event.data.request_id === request_id) {
            window.removeEventListener('message', listener);
            if (event.data.resultType === 'resolve') {
              resolve(event.data.result);
            } else {
              let err = new Error(event.data.result.message);
              err.stack = event.data.result.stack;
              reject(err);
            }
          }
        }
      }
      window.addEventListener('message', listener);
    });
  }

  let overwrite = (object, property, value) => {
    try {
      if (property in object) {
        Object.defineProperty(object, property, {
          value: value,
          configurable: true,
          writable: true,
        });
      }
    } catch (err) {
      // Nothing
    }
  }

  let set_fullscreen_element = (element = null) => {
    if (element == null) {
      throw new Error('WINDOWED: Got null in set_fullscreen_element');
    }

    overwrite(document, 'webkitIsFullScreen', true); // Old old old
    overwrite(document, 'fullscreen', true); // Old old old
    for (let fullscreenelement_alias of fullscreenelement_aliasses) {
      overwrite(document, fullscreenelement_alias, element);
    }
  }

  let make_tab_go_fullscreen = ${async () => {
    await go_into_fullscreen();
  }}
  let make_tab_go_inwindow = ${async () => {
    await go_in_window();
  }}

  let create_popup = ${async () => {
    clear_popup();

    let is_fullscreen = await external_function_parent("is_fullscreen")();
    if (is_fullscreen) {
      await go_out_of_fullscreen();
      return;
    }

    let element = get_fullscreen_select_element();

    // Find possible picture-in-picture video element
    let video_element = /** @type {PictureInPictureVideoElement} */ (
      element.querySelector("video:not([disablepictureinpicture])")
    );
    video_element =
      video_element != null &&
      video_element.requestPictureInPicture != null &&
      video_element.readyState >= 1 &&
      !video_element.disablePictureInPicture
        ? video_element
        : null;

    let { mode, pip } = is_shift_pressed
      ? /// Force the mode to be "ask" when shift is pressed
        { mode: "ask", pip: false }
      : await get_host_config_local();
    if (pip === true && video_element != null) {
      video_element.requestPictureInPicture();
      onEscapePress(() => {
        // @ts-ignore
        document.exitPictureInPicture();
      });
      return "PICTURE-IN-PICTURE";
    }

    if (mode === "fullscreen" || mode === "windowed" || mode === "in-window") {
      if (mode === "fullscreen") {
        let element = get_fullscreen_select_element();
        disable_selector(element, fullscreen_select);
        element.requestFullscreen();
        return "FULLSCREEN";
      }
      if (mode === "windowed") {
        await go_into_fullscreen();
        return "WINDOWED";
      }
      if (mode === "in-window") {
        await go_in_window();
        return "IN-WINDOW";
      }
    }

    let popup_div = document.createElement("div");
    let shadowRoot = popup_div.attachShadow({ mode: "open" });

    if (theme?.colors != undefined) {
      let root = popup_div;
      for (let [key, value] of Object.entries(theme.colors)) {
        if (value != undefined) {
          root.style.setProperty(`--theme-${key}`, themecolor_to_string(value));
        }
      }
    }

    shadowRoot.appendChild(
      createElementFromHTML(`<style>${popup_css}</style>`),
    );

    let clicked_element_still_exists =
      last_click_y != null && last_click_x != null; // && document.elementsFromPoint(last_click_x, last_click_y).includes(last_click_element)
    if (
      clicked_element_still_exists &&
      Date.now() - last_click_timestamp <
        CLICK_IS_CONSIDERED_FULLSCREEN_CLICK_DELAY
    ) {
      let top_vs_bottom =
        last_click_y < window.innerHeight / 2
          ? "translateY(0px)"
          : "translateY(-100%)";
      let left_vs_right =
        last_click_x < window.innerWidth / 2
          ? "translateX(0px)"
          : "translateX(-100%)";

      /// Popup that shows if there is a recent click
      let popup = createElementFromHTML(`
        <menu class="popup" tabIndex="1" style="
          position: absolute;
          top: ${last_click_y}px;
          left: ${last_click_x}px;
          transform: ${top_vs_bottom} ${left_vs_right};
        ">
          ${
            video_element
              ? `<li>
                ${Button({
                  icon: browser.runtime.getURL("Images/Icon_PiP@scalable.svg"),
                  text: "PiP",
                  title: "Picture-in-picture (p)",
                  target: "picture-in-picture",
                })}
              </li>`
              : ""
          }
          <li>
            ${Button({
              icon: browser.runtime.getURL(
                "Images/Icon_Windowed_Mono@scalable.svg",
              ),
              text: "Windowed",
              title: "Windowed (w)",
              target: "windowed",
            })}
          </li>
          <li>
            ${Button({
              icon: browser.runtime.getURL(
                "Images/Icon_InWindow_Mono@scalable.svg",
              ),
              text: "In-window",
              title: "In-window (i)",
              target: "in-window",
            })}
          </li>
          <li>
            ${Button({
              icon: browser.runtime.getURL(
                "Images/Icon_EnterFullscreen@scalable.svg",
              ),
              text: "Fullscreen",
              title: "Fullscreen (f)",
              target: "fullscreen",
            })}
          </li>
        </menu>
      `);
      shadowRoot.appendChild(popup);
    } else {
      /// Popup that shows if there is no recent click (centered in the screen)
      let popup = createElementFromHTML(`
        <div>
          <div
            style="
              position: fixed;
              top: 0; left: 0;
              right: 0; bottom: 0;
              background-color: rgba(0,0,0,.8);
              pointer-events: none;
              z-index: ${max_z_index};
            "
          ></div>

          <div class="popup" tabIndex="1" style="
            position: fixed;
            top: 25vh;
            left: 50vw;
            transform: translateX(-50%) translateY(-50%);
            font-size: 20px;
          ">
            <div style="padding: 1.25em; padding-bottom: 0.25em; padding-top: 0.25em">Enter fullscreen</div>
            <div style="height: 10px"></div>
            <menu style="display: flex; flex-direction: column; align-items: stretch;">
              ${
                video_element
                  ? `
                    <li>
                      <button data-target="picture-in-picture" title="Picture in picture (p)" class="flex flex-row">
                        <div class="icon mask-icon" style="mask-image: url(${browser.runtime.getURL("Images/Icon_PiP@scalable.svg")})"></div>
                        <span class="flex-1">PiP</span>
                        <kbd style="margin-left: 16px">p</kbd>
                      </button>
                    </li>
                  `
                  : ""
              }
              <li>
                <button data-target="windowed" title="Windowed (w)" class="flex flex-row">
                  <div class="icon mask-icon" style="mask-image: url(${browser.runtime.getURL("Images/Icon_Windowed_Mono@scalable.svg")})"></div>
                  <span class="flex-1">Windowed</span>
                  <kbd style="margin-left: 16px">w</kbd>
                </button>
              </li>
              <li>
                <button data-target="in-window" title="In-window (i)" class="flex flex-row">
                  <div class="icon mask-icon" style="mask-image: url(${browser.runtime.getURL("Images/Icon_InWindow_Mono@scalable.svg")})"></div>
                  <span class="flex-1">In-window</span>
                  <kbd style="margin-left: 16px">i</kbd>
                </button>
              </li>
              <li>
                <button data-target="fullscreen" title="Fullscreen (f)" class="flex flex-row">
                  <div class="icon mask-icon" style="mask-image: url(${browser.runtime.getURL("Images/Icon_EnterFullscreen@scalable.svg")})"></div>
                  <span class="flex-1">Fullscreen</span>
                  <kbd style="margin-left: 16px">f</kbd>
                </button>
              </li>
            </menu>
          </div>
        </div>
      `);
      shadowRoot.appendChild(popup);
    }

    /** @type {HTMLElement} */
    let popup_element = shadowRoot.querySelector(`.popup`);
    setTimeout(() => {
      popup_element.focus();
    }, 0);

    document.body.appendChild(popup_div);
    last_popup = popup_div;

    /** @type {"windowed" | "in-window" | "fullscreen" | "picture-in-picture" | "nothing" | "cool-pip"} */
    let result = await new Promise((resolve) => {
      popup_element.addEventListener("focusout", (event) => {
        // @ts-ignore
        if (!event.currentTarget.contains(event.relatedTarget)) {
          resolve("nothing");
        }
      });

      // For people who like keyboard shortcuts
      popup_element.addEventListener(
        "keydown",
        (/** @type {KeyboardEvent} */ event) => {
          if (event.key === "w" || event.key === "W") {
            event.stopPropagation();
            resolve("windowed");
          }
          if (event.key === "i" || event.key === "I") {
            event.stopPropagation();
            resolve("in-window");
          }
          if (event.key === "f" || event.key === "F") {
            event.stopPropagation();

            // I need this check here, because I can't call the original fullscreen from a
            // 'async' function (or anywhere async (eg. after `resolve()` is called))
            let element = get_fullscreen_select_element();
            disable_selector(element, fullscreen_select);
            element.requestFullscreen();

            resolve("fullscreen");
          }
          if (event.key === "p" || event.key === "P") {
            event.stopPropagation();
            resolve("picture-in-picture");
          }
          if (event.key === "Escape") {
            event.stopPropagation();
            resolve("nothing");
          }
        },
      );

      for (let button of shadowRoot.querySelectorAll(`[data-target]`)) {
        button.addEventListener("click", (e) => {
          // @ts-ignore
          let target_keyword = button.dataset.target;
          if (target_keyword === "fullscreen") {
            // I need this check here, because I can't call the original fullscreen from a
            // 'async' function (or anywhere async (eg. after `resolve()` is called))
            let element = get_fullscreen_select_element();
            disable_selector(element, fullscreen_select);
            element.requestFullscreen();
          }
          resolve(target_keyword);
        });
      }
    });

    clear_popup();

    if (result === "fullscreen") {
      // NOTE This is now all done sync in the popup callback.
      // .... because firefox does not like it when I call it from a promise.
      return "FULLSCREEN";
    }
    if (result === "windowed") {
      await go_into_fullscreen();
      return "WINDOWED";
    }
    if (result === "in-window") {
      await go_in_window();
      return "IN-WINDOW";
    }
    if (result === "picture-in-picture") {
      video_element.requestPictureInPicture();

      onEscapePress(() => {
        // @ts-ignore
        document.exitPictureInPicture();
      });

      return "PICTURE-IN-PICTURE";
    }

    // if (result === "cool-pip") {
    //   await go_in_cool_pip();
    //   return "COOL-PIP";
    // }

    if (result === "nothing") {
      return "NOTHING";
    }
  }}

  let make_tab_exit_fullscreen = ${async () => {
    await go_out_of_fullscreen();
    send_fullscreen_events();
  }}

  let exitFullscreen = async function(original) {
    let windowed_fullscreen = document.querySelector('[data-${fullscreen_active}], [data-${shadowdom_trail}]');

    if (windowed_fullscreen) {
      // If the fullscreen element is a frame, tell it to exit fullscreen too
      if (typeof windowed_fullscreen.postMessage === 'function') {
        document.fullscreenElement.postMessage.sendMessage({ type: "exit_fullscreen_iframe" });
      }

      // Reset all the variables to their browser form
      delete window.screen.width;
      delete window.screen.height;
      delete document['webkitIsFullScreen'];
      delete document['fullscreen'];
      for (let fullscreenelement_alias of fullscreenelement_aliasses) {
        delete document[fullscreenelement_alias];
      }

      await make_tab_exit_fullscreen();
    } else {
      original();
    }
  }

  ${"" /* NOTE requestFullscreen */}
  const requestFullscreen_windowed = async function(original, ...args) {
    const element = this;
    element.dataset['${fullscreen_select}'] = true;

    let shadowroot = element.getRootNode()
    while (shadowroot != null && shadowroot.host != null) {
        shadowroot.host.dataset['${shadowdom_trail}'] = true;
        shadowroot = shadowroot.host.getRootNode()
    }

    // Tell extension code (outside of this block) to go into fullscreen
    // window.postMessage({ type: force ? "enter_fullscreen" : "show_fullscreen_popup" }, "*");
    // send_windowed_event(element, force ? "enter_fullscreen" : "show_fullscreen_popup");
    try {
      await create_popup();
    } catch (err) {
      // Anything gone wrong, we default to normal fullscreen
      console.error(err);
      console.error('[Windowed] Something went wrong, so I default to normal fullscreen:', err.stack);
      original();
    }
  }

  // Because firefox is super cool, it is also super frustrating...
  // So firefox does not allow fullscreen calls from promises, even if it is basically sync.
  // Therefor I need to first define this as sync, and from here call the async version.
  let MUTATE_is_windowed_enabled = true;
  let requestFullscreen = function(original, ...args) {
    if (MUTATE_is_windowed_enabled === false) {
      return original()
    } else {
      requestFullscreen_windowed.call(this, original, ...args);
    }
  }

  let finish_fullscreen = () => {
    // Because youtube actually checks for those sizes?!
    const window_width = Math.max(window.outerWidth, window.innerWidth);
    const window_height = Math.max(window.outerHeight, window.innerHeight);

    overwrite(window.screen, 'width', window_width);
    overwrite(window.screen, 'height', window_height);

    let element = document.querySelector('[data-${fullscreen_select}]');
    if (element == null) {
      console.warn('[WINDOWED] Strange, no fullscreen element shown');
      return;
    }

    document.body.focus();
    element.focus();
    set_fullscreen_element(element || document.body);
    send_fullscreen_events();
  }

  window.addEventListener("message", (message) => {
    // Message from content script or parent content script
    if (message.source === message.target || message.source === window.parent) {
      if (message.data?.type === 'WINDOWED-confirm-fullscreen') {
        finish_fullscreen();
      }

      if (message.data?.type === 'WINDOWED-exit-fullscreen') {
        exitFullscreen.call(document, original_exitFullscreen);
      }

      if (message.data?.type === 'WINDOWED-notify') {
        MUTATE_is_windowed_enabled = !message.data.disabled;
      }
    }

    // Message from frame inside the page (these are tricky not sure if I still know how this works)
    const frame = [...document.querySelectorAll('iframe')].find(x => x.contentWindow === message.source);
    if (frame != null) {
      if (message.data?.type === 'enter_inwindow_iframe') {
        frame.dataset['${fullscreen_select}'] = "true";
        make_tab_go_inwindow();
      }
      if (message.data?.type === 'enter_fullscreen_iframe') {
        frame.dataset['${fullscreen_select}'] = "true";
        make_tab_go_fullscreen();
      }
      if (message.data?.type === 'exit_fullscreen_iframe') {
        // Call my exitFullscreen on the document
        exitFullscreen.call(document, original_exitFullscreen);
      }
    }
  });

  ${
    "" /* NOTE Replace all the `requestFullscreen` aliasses with calls to my own version */
  }
  let original_requestFullscreen = null;
  for (let requestFullscreenAlias of requestFullscreen_aliasses) {
    if (typeof Element.prototype[requestFullscreenAlias] === 'function') {
      let original_function = Element.prototype[requestFullscreenAlias];
      original_requestFullscreen = original_function;
      Element.prototype[requestFullscreenAlias] = function(...args) {
        return requestFullscreen.call(this, original_function.bind(this), ...args);
      };
    }
  }

  ${
    "" /* NOTE Replace all the `exitFullscreen` aliasses with calls to my own version */
  }
  let original_exitFullscreen = null;
  for (let exitFullscreenAlias of exitFullscreen_aliasses) {
    if (typeof Document.prototype[exitFullscreenAlias] === 'function') {
      let original_function = Document.prototype[exitFullscreenAlias];
      original_exitFullscreen = original_function;
      Document.prototype[exitFullscreenAlias] = function(...args) {
        return exitFullscreen.call(this, original_function.bind(this), ...args);
      };
    }
  }
}`;

//// I used to insert the code directly as a <script>...</script> tag,
//// but that doesn't work nicely with some sites CSP.
//// Now I create this separate file with the contents of the script,
//// which is awkward if they aren't in sync... but I need to hack hack hack.
//// This also loads the script asynchronously.. but i think these scripts still execute in order.
//// I still need to do the `on_webpage` call, so the message listeners are set up.
// let elt = document.createElement("script");
// elt.innerHTML = code_to_insert_in_page;
// document.documentElement.appendChild(elt);
// document.documentElement.removeChild(elt);

/// No longer necessary as I insert this via manifest.json
// let elt = document.createElement("script");
// elt.src = browser.runtime.getURL("Windowed-inject-into-page.js");
// document.documentElement.appendChild(elt);
// document.documentElement.removeChild(elt);

//// This is just for myself as debugging, but it will tell me if the script that is inserted,
//// is actually the same as the script I am expecting it to be. (because debugging could get very frustrating)
let async = async (async) => async();
async(async () => {
  let response = await fetch(
    browser.runtime.getURL("Windowed-inject-into-page.js"),
  );
  let result = await response.text();
  if (result !== code_to_insert_in_page) {
    // prettier-ignore
    console.error("[WINDOWED] HEY MICHIEL! The script I am inserting is not the same as the script I expect it to be!");
    console.log("[WINDOWED] Code should actually be:");
    console.log(code_to_insert_in_page);
  }
});

const send_event = (element, type) => {
  const event = new Event(type, {
    bubbles: true,
    cancelable: false,
  });
  element.dispatchEvent(event);
};

const send_fullscreen_events = () => {
  for (let fullscreenchange of fullscreenchange_aliasses) {
    send_event(document, fullscreenchange);
  }
  send_event(window, "resize");
};

// setTimeout(() => {
//   for (let stylesheet of document.styleSheets) {
//     try {
//       for (let rule of stylesheet.cssRules) {
//         // Remove the css rule if the media query doesn't match,
//         // Force match it when it does
//         if (rule.media) {
//           if (window.matchMedia(rule.media.mediaText).matches) {
//             // console.log(`The media (${rule.media.mediaText}) matches!`);
//             rule.media.__WINDOWED_FALLBACK_MEDIATEXT__ = rule.media.mediaText;
//             rule.media.mediaText = "all";
//           } else {
//             // console.log(`The media (${rule.media.mediaText}) does not match!`);
//             rule.media.__WINDOWED_FALLBACK_MEDIATEXT__ = rule.media.mediaText;
//             rule.media.mediaText = "not all";
//           }
//         }
//       }
//     } catch (err) {
//       console.warn(`WINDOWED: Couldn't read stylesheet rules because of CORS...`);
//       console.log(`stylesheet:`, stylesheet)
//     }
//   }
// }, 1000);

// console.log('Runs in proper sandbox:', document.documentElement.constructor === HTMLHtmlElement);
// NOTE On chrome, extensions run in a proper sandbox (above will log true),
// meaning that you can't get access to the actual prototype-s of the Document and Elements-s,
// hence the need for the ugly script insert above.
// On Firefox however, this is not the case, and I might (because firefox screws me with CSP)
// need to use this quirk to work on all pages

let clear_listeners = () => {};

let delay = (ms) => {
  return new Promise((resolve) => {
    setTimeout(() => resolve(), ms);
  });
};

let popup_css = css`
  /* Poor mans tailwind */
  .flex {
    display: flex;
  }
  .flex-col {
    flex-direction: column;
  }
  .flex-row {
    flex-direction: row;
  }
  .flex-1 {
    flex: 1;
  }

  :host {
    --theme-popup: #111;
    --theme-popup_border: #555;
    --theme-popup_text: white;
    --theme-popup_highlight: rgba(255, 255, 255, 0.1);
    --theme-popup_highlight_text: white;
  }

  .popup {
    background-color: var(--theme-popup, white);
    border-radius: 3px;
    border: solid #eee 1px;
    border-color: var(--theme-popup_border, #eee);
    /*box-shadow: 0px 2px 4px #00000026;*/
    padding-top: 5px;
    padding-bottom: 5px;
    font-size: 16px;
    color: var(--theme-popup_text, black);
    min-width: 150px;

    display: flex;
    flex-direction: column;
    align-items: stretch;

    font-family: sans-serif;

    z-index: ${max_z_index};
  }

  .popup:focus {
    outline: none;
  }

  /*@media (prefers-color-scheme: dark) {
    .popup {
      filter: invert(0.9);
    }
  }*/

  .popup [data-target] {
    text-align: inherit;
    cursor: pointer;
    padding: 1.25em;
    padding-top: 0.25em;
    padding-bottom: 0.25em;
    /*background-color: white;*/
    /*background-color: var(--theme-popup, white);*/
    background-color: transparent;
    color: var(--theme-popup_text, #eee);
    /* Force black for if the page has color: white */

    display: flex;
    flex-direction: row;
    align-items: center;

    font-size: inherit;
    border: none;
    box-shadow: none;

    white-space: nowrap;

    &:focus {
      /*filter: brightness(0.95);*/
      background-color: var(--theme-popup_highlight, white);
      color: var(--theme-popup_highlight_text, black);
    }
    &:hover {
      /*filter: brightness(0.9);*/
      background-color: var(--theme-popup_highlight, white);
      color: var(--theme-popup_highlight_text, black);
    }
    &:focus:not(:focus-visible) {
      outline: none;
    }

    & > img {
      height: 1.2em;
      width: 1.2em;
      margin-right: 1em;
    }
  }

  .icon {
    height: 1.2em;
    width: 1.2em;
    margin-right: 1em;
  }
  .mask-icon {
    background-color: currentColor;
    mask-size: contain;
  }

  [data-target]::-moz-focus-inner,
  .popup::-moz-focus-inner {
    border: 0;
    outline: 0;
  }

  li {
    display: contents;
  }
  menu {
    margin: 0;
    padding: 0;
    list-style-type: none;
  }

  kbd {
    font-size: 0.9em;
    background-color: #c0c0c0;
    padding-inline: 4px;
    border-radius: 4px;
    border: solid 1px #979797;
  }
`;

/**
 * @param {ParentNode} root
 */
let create_style_rule = (root = document) => {
  let css_text = css`
    [data-${fullscreen_parent}] {
      /* This thing is css black magic */
      all: initial !important;
      z-index: ${max_z_index} !important;

      /* Debugging */
      /* background-color: rgba(0,0,0,.1) !important; */
    }

    /* Not sure if this is necessary, but putting it here just in case */
    [data-${fullscreen_parent}]::before,
    [data-${fullscreen_parent}]::after {
      display: none;
    }

    [data-${body_class}] {
      /* Prevent scrolling */
      overflow: hidden !important;

      /* For debugging, leaving this just in here so I see when something goes wrong */
      /* background-color: rgb(113, 0, 180); */
    }

    /* I know I want it to be generic, but still this is a youtube specific fix */
    #player-theater-container {
      min-height: 0 !important;
    }

    [data-${fullscreen_active}],
    [data-${shadowdom_trail}] {
      position: fixed !important;
      top: 0 !important;
      bottom: 0 !important;
      right: 0 !important;
      left: 0 !important;
      width: 100% !important;
      height: 100% !important;
      max-width: initial !important;
      max-height: initial !important;
      z-index: ${max_z_index} !important;

      background-color: black;
    }
  `;

  let styleEl = document.createElement("style");
  if (root instanceof ShadowRoot) {
    root.appendChild(styleEl);
  } else if (root instanceof Document) {
    root.head.appendChild(styleEl);
  } else {
    throw new Error("[WINDOWED] Could not find a place to put the style");
  }
  styleEl.appendChild(document.createTextNode(css_text));

  let shadowroot_maybe = root.querySelector(`[data-${shadowdom_trail}`);
  if (shadowroot_maybe != null) {
    console.log(`element:`, shadowroot_maybe);
    create_style_rule(shadowroot_maybe.shadowRoot);
  }
};

/**
 * @param {HTMLElement} element
 */
const parent_elements = function* (element) {
  let el = element;
  while (el) {
    let root_node = el.getRootNode();
    if (el.parentElement) {
      el = el.parentElement;
    } else if (root_node instanceof ShadowRoot) {
      el = /** @type {HTMLElement} */ (root_node.host);
    } else {
      break;
    }

    yield el;
  }
};

/**
 * @param {{ type: string, [key: string]: any }} message
 */
let send_chrome_message = async (message) => {
  // @ts-ignore
  let { type, value } = await browser.runtime.sendMessage(message);
  if (type === "resolve") {
    return value;
  } else {
    let err = new Error(value.message);
    err.stack = value.stack;
    // err.stack = [
    //   ...x.value.stack.split('\n'),
    //   'From postMessage to background page',
    //   ...stack,
    // ].join('\n');
    throw err;
  }
};

let CLICK_IS_CONSIDERED_FULLSCREEN_CLICK_DELAY = 1 * 1000;

let last_click_x = null;
let last_click_y = null;
let last_click_timestamp = 0;
let last_click_element = null;

let is_shift_pressed = false;

let last_popup = null;
let is_in_fullscreen = false;

let clear_popup = () => {
  if (last_popup != null) {
    try {
      document.body.removeChild(last_popup);
    } catch (err) {}
    last_popup = null;
    return true;
  }
  return false;
};

document.addEventListener("keydown", (event) => {
  is_shift_pressed = event.shiftKey;
  let { mode, pip } = current_mode;
  window.postMessage(
    {
      type: "WINDOWED-notify",
      disabled: is_shift_pressed
        ? false
        : mode === "fullscreen" && pip === false,
    },
    "*",
  );
});
document.addEventListener("keyup", (event) => {
  is_shift_pressed = event.shiftKey;
  let { mode, pip } = current_mode;
  window.postMessage(
    {
      type: "WINDOWED-notify",
      disabled: is_shift_pressed
        ? false
        : mode === "fullscreen" && pip === false,
    },
    "*",
  );
});

document.addEventListener(
  "click",
  (event) => {
    last_click_x = event.pageX;
    last_click_y = event.pageY;
    last_click_timestamp = Date.now();
    // last_click_element = event.target;

    if (
      last_popup != null &&
      (event.target === last_popup || last_popup.contains(event.target))
    ) {
      // Clicked inside popup
    } else {
      if (clear_popup()) {
        send_fullscreen_events();
      }
    }
  },
  { capture: true },
);

let exit_fullscreen_on_page = () => {
  window.postMessage(
    {
      type: "WINDOWED-exit-fullscreen",
    },
    "*",
  );
};

let createElementFromHTML = (htmlString) => {
  let div = document.createElement("div");
  div.innerHTML = htmlString.trim();

  return div.firstChild;
};

let go_in_window = async () => {
  create_style_rule();

  clear_listeners();
  let element = get_fullscreen_select_element();

  let unlisten_to_escape = onEscapePress(() => {
    exit_fullscreen_on_page();
  });

  let beforeunload_listener = (e) => {
    exit_fullscreen_on_page();
  };
  window.addEventListener("beforeunload", beforeunload_listener);

  clear_listeners = () => {
    unlisten_to_escape();
    window.removeEventListener("beforeunload", beforeunload_listener);
  };

  enable_selector(element, fullscreen_active);
  // Add fullscreen class to every parent of our fullscreen element
  for (let parent_element of parent_elements(element)) {
    enable_selector(parent_element, fullscreen_parent);
  }

  if (window.parent !== window) {
    // Ask parent-windowed code to become fullscreen too
    window.parent.postMessage({ type: "enter_inwindow_iframe" }, "*");
  }

  // Post back to in-page javascript
  window.postMessage({ type: "WINDOWED-confirm-fullscreen" }, "*");

  // Add no scroll to the body and let everything kick in
  enable_selector(document.body, body_class);
};

let go_into_fullscreen = async () => {
  create_style_rule();
  let element = get_fullscreen_select_element();
  let cloned = element.cloneNode(true);

  clear_listeners();
  var mutationObserver = new MutationObserver(async (mutations) => {
    for (let mutation of mutations) {
      for (let removed of mutation.removedNodes) {
        if (removed === element) {
          clear_listeners();

          enable_selector(cloned, fullscreen_element_cloned);
          enable_selector(cloned, fullscreen_select);
          document.body.appendChild(cloned);
          go_into_fullscreen();

          await delay(500);
          if (
            (cloned instanceof HTMLIFrameElement ||
              cloned instanceof HTMLObjectElement) &&
            cloned.contentWindow?.postMessage
          ) {
            cloned.contentWindow.postMessage(
              { type: "WINDOWED-confirm-fullscreen" },
              "*",
            );
          }
        }
      }
    }
  });

  if (element.parentElement) {
    mutationObserver.observe(element.parentElement, {
      childList: true,
    });
  }

  let unlisten_to_escape = onEscapePress(() => {
    exit_fullscreen_on_page();
  });

  let beforeunload_listener = (e) => {
    exit_fullscreen_on_page();
  };
  window.addEventListener("beforeunload", beforeunload_listener);

  clear_listeners = () => {
    mutationObserver.disconnect();
    unlisten_to_escape();
    window.removeEventListener("beforeunload", beforeunload_listener);
  };

  enable_selector(element, fullscreen_active);
  // Add fullscreen class to every parent of our fullscreen element
  for (let parent_element of parent_elements(element)) {
    enable_selector(parent_element, fullscreen_parent);
  }

  if (window.parent !== window) {
    // Ask parent-windowed code to become fullscreen too
    window.parent.postMessage({ type: "enter_fullscreen_iframe" }, "*");
  } else {
    // Send popup command to extension
    let menubar_size = window.outerHeight - window.innerHeight; // Asume there is just header, no browser footer

    let rect = element.getBoundingClientRect();
    let height = Math.max((rect.width * 9) / 16, rect.height);
    let ratio_width = Math.min((height / 9) * 16, rect.width); // 16:9
    let width_diff = rect.width - ratio_width;

    await send_chrome_message({
      type: "please_make_me_a_popup",
      position: {
        height: height,
        width: ratio_width,
        top: rect.top + menubar_size,
        left: rect.left + width_diff / 2,
      },
    });
  }

  // Post back to the javascript we put inside the page
  window.postMessage({ type: "WINDOWED-confirm-fullscreen" }, "*");

  // Add no scroll to the body and let everything kick in
  enable_selector(document.body, body_class);
  window.focus(); // idk
};

let go_out_of_fullscreen = async () => {
  // Remove no scroll from body (and remove all styles)
  disable_selector(document.body, body_class);

  clear_listeners();

  send_fullscreen_events();

  const fullscreen_element = get_fullscreen_select_element();
  disable_selector(fullscreen_element, fullscreen_select);
  disable_selector(fullscreen_element, fullscreen_active);

  // Remove fullscreen class... from everything
  for (let parent of parent_elements(fullscreen_element)) {
    disable_selector(parent, fullscreen_parent);
    disable_selector(parent, shadowdom_trail);
  }

  // If we are a frame, tell the parent frame to exit fullscreen
  // If we aren't (we are a popup), tell the background page to make me tab again
  if (window.parent !== window) {
    window.parent.postMessage({ type: "exit_fullscreen_iframe" }, "*");
  } else {
    await delay(10);
    await send_chrome_message({ type: "please_make_me_a_tab_again" });
    await delay(500);
  }

  let cloned = document.querySelector(`[data-${fullscreen_element_cloned}]`);
  if (cloned) {
    document.body.removeChild(cloned);
  }
};

external_functions.is_fullscreen = () => {
  const fullscreen_element = document.querySelector(
    `[data-${fullscreen_active}]`,
  );
  return fullscreen_element != null;
};

window.addEventListener("message", async (event) => {
  // We only accept messages from ourselves
  if (event.data == null) return;
  if (event.data.type === "CUSTOM_WINDOWED_FROM_PAGE") {
    let fn = external_functions[event.data.function_id];
    try {
      let result = await fn(...event.data.args);
      // @ts-ignore
      event.source.postMessage(
        {
          type: "CUSTOM_WINDOWED_TO_PAGE",
          request_id: event.data.request_id,
          resultType: "resolve",
          result: result,
        },
        // @ts-ignore
        "*",
      );
    } catch (err) {
      event.source.postMessage(
        {
          type: "CUSTOM_WINDOWED_TO_PAGE",
          request_id: event.data.request_id,
          resultType: "reject",
          result: {
            message: err.message,
            stack: err.stack,
          },
        },
        // @ts-ignore
        "*",
      );
    }
  }
});

let is_escape_locked = false;
// TODO Ask feedback from the Stadia community to find out what an helpful solution would be
// https://www.reddit.com/r/Stadia/comments/kibh2r/windowed_stadia_extension_what_could_i_improve/
// WELL one response isn't too bad, is it?
// ...
// const keyboard_lock_injection = on_webpage`{
//   let all_communication_id = 0;
//   let external_function = (function_id) => async (...args) => {
//     let request_id = all_communication_id;
//     all_communication_id = all_communication_id + 1;

//     window.postMessage({
//       type: 'CUSTOM_WINDOWED_FROM_PAGE',
//       request_id: request_id,
//       function_id: function_id,
//       args: args,
//     }, '*');

//     return new Promise((resolve, reject) => {
//       let listener = (event) => {
//         // We only accept messages from ourselves
//         if (event.source != window) return;
//         if (event.data == null) return;

//         if (event.data.type === 'CUSTOM_WINDOWED_TO_PAGE') {
//           if (event.data.request_id === request_id) {
//             window.removeEventListener('message', listener);
//             if (event.data.resultType === 'resolve') {
//               resolve(event.data.result);
//             } else {
//               let err = new Error(event.data.result.message);
//               err.stack = event.data.result.stack;
//               reject(err);
//             }
//           }
//         }
//       }
//       window.addEventListener('message', listener);
//     });
//   }

//   let old_lock = navigator.keyboard.lock.bind(navigator.keyboard);
//   let set_escape_locked = ${(is_locked) => {
//     is_escape_locked = is_locked;
//   }}
//   navigator.keyboard.lock = async (...args) => {
//     let keycodes = args[0]

//     if (keycodes == null || keycodes.includes('Escape')) {
//       try {
//         set_escape_locked(true)
//       } catch (err) {}
//     }
//     return old_lock(...args)
//   }
// }`;
// let keyboard_lock_script_element = document.createElement("script");
// keyboard_lock_script_element.innerHTML = keyboard_lock_injection;
// document.documentElement.appendChild(keyboard_lock_script_element);
// document.documentElement.removeChild(keyboard_lock_script_element);

let onEscapePress = (fn) => {
  let escape_timeout = null;
  let escape_listener = (e) => {
    if (!e.defaultPrevented && e.key === "Escape") {
      if (is_escape_locked) {
        escape_timeout = setTimeout(() => {
          fn();
        }, 1.2 * 1000);
      } else {
        fn();
      }
    }
  };

  let escape_up_listener = (e) => {
    if (!e.defaultPrevented && e.key === "Escape") {
      clearTimeout(escape_timeout);
    }
  };

  window.addEventListener("keydown", escape_listener);
  window.addEventListener("keyup", escape_up_listener);

  return () => {
    window.removeEventListener("keydown", escape_listener);
    window.removeEventListener("keyup", escape_up_listener);
  };
};

let current_mode = { mode: "ask", pip: false };

let check_disabled_state = async () => {
  try {
    let { mode, pip } = await get_host_config_local();
    current_mode = { mode, pip };
    window.postMessage(
      {
        type: "WINDOWED-notify",
        disabled: is_shift_pressed
          ? false
          : mode === "fullscreen" && pip === false,
      },
      "*",
    );
  } catch (err) {
    // prettier-ignore
    console.warn(`[Windowed] Error while checking if windowed is enabled or not`, err)
  }
};

check_disabled_state();

browser.runtime.onConnect.addListener(async (port) => {
  port.postMessage({ type: "I_exists_ping" });
  check_disabled_state();
});

let theme = null;
send_chrome_message({
  type: "get_theme",
}).then((x) => {
  theme = x;
});


================================================
FILE: extension/Popup/Popup.html
================================================
<!doctype html>
<html>
  <head>
    <style>
      :root {
        --theme-popup: transparent;
        --theme-popup_text: currentColor;
        --theme-popup_highlight: rgba(255, 255, 255, 0.1);
        --theme-popup_highlight_text: currentColor;
      }
      body,
      html {
        margin: 0;
        padding: 0;
        background-color: var(--theme-popup, #1a1a1a);
        color-scheme: light dark;
      }

      button {
        font-size: inherit;
        border: none;
        box-shadow: none;
        background-color: transparent;
      }

      kbd {
        font-size: 0.9em;
        padding-inline: 4px;
        border-radius: 4px;

        @media (prefers-color-scheme: dark) {
          border: solid 1px #676767;
          background-color: #404040;
        }
        @media (prefers-color-scheme: light) {
          border: solid 1px #979797;
          background-color: #c0c0c0;
        }
      }

      .popup {
        font-family: system-ui, sans-serif;
        user-select: none;

        /* border-radius: 3px; */
        /* border: solid #eee 1px; */
        /* box-shadow: 0px 2px 4px #00000026; */
        padding-top: 12px;
        padding-bottom: 12px;

        .browser-firefox & {
          padding-inline: 12px;
        }

        font-size: 14px;
        color: var(--theme-popup_text);
        width: min-content;
        min-width: 210px;
        z-index: 2147483647;

        display: flex;
        flex-direction: column;
        align-items: stretch;
      }

      .popup:focus:not(:focus-visible) {
        outline: none;
      }

      .popup [data-target] {
        cursor: pointer;

        display: flex;
        flex-direction: row;
        align-items: center;

        white-space: nowrap;

        .browser-chrome & {
          padding-inline: 1.25em;
          padding-block: 0.5em;
        }
        .browser-firefox & {
          padding-inline: 0.5em;
          padding-block: 0.5em;
          border-radius: 8px;
        }

        &::-moz-focus-inner,
        .popup::-moz-focus-inner {
          border: none;
        }

        &:hover,
        &:focus {
          &:not(:focus-visible) {
            outline: none;
          }

          background-color: var(--theme-popup_highlight);
          color: var(--theme-popup_highlight_text);
          /*.browser-firefox & {
          }
          .browser-chrome & {
            background-color: rgba(255, 255, 255, 0.2);
          }*/
        }

        & > input[type="radio"],
        & > input[type="checkbox"] {
          margin: 0;
          margin-right: 16px;
        }
        & > .icon {
          height: 1.2em;
          width: 1.2em;
          margin-right: 1em;
        }
      }

      .popup::-moz-focus-inner {
        border: none;
      }

      .mask-icon {
        background-color: currentColor;
        mask-size: contain;
        /*mask-image: url(../Images/Icon_InWindow_Mono@scalable.svg);*/
      }

      #set-as-default {
        display: flex;
        flex-direction: column;

        font-size: 14px;
        color: #999;

        .browser-firefox & {
          border-radius: 8px;
        }
        .browser-chrome & {
          padding-block: 8px;
        }

        &:hover,
        &:focus {
          background-color: var(--theme-popup_highlight);
          color: var(--theme-popup_highlight_text);
        }

        &:disabled {
          display: none;
        }
      }
    </style>
    <style>
      hide-if-no-picture-in-picture {
        display: none;
      }
      .picture-in-picture-support hide-if-no-picture-in-picture {
        all: inherit;
      }

      body[data-mode-selected="true"] hide-if-ask {
        display: none;
      }

      p {
        all: unset;
        display: block;
        padding-block: 4px;
        font-size: 14px;

        .browser-firefox & {
          padding-inline: 0px;
        }
        .browser-chrome & {
          padding-inline: 20px;
        }
      }

      button {
        width: 100%;
        min-height: 28px;
        cursor: pointer;
        padding: 1.25em;
        padding-top: 0.25em;
        padding-bottom: 0.25em;
        /*background-color: white;*/

        display: flex;
        flex-direction: row;
        align-items: center;

        font-size: inherit;
        border: none;
        box-shadow: none;

        white-space: nowrap;
      }

      button::-moz-focus-inner {
        border: none;
      }
      button:focus {
        filter: brightness(0.95);
      }
      button:focus:not(:focus-visible) {
        outline: none;
      }
      button:hover {
        filter: brightness(0.9);
      }

      button > input[type="radio"],
      button > input[type="checkbox"] {
        margin: 0;
        margin-right: 16px;
      }

      button > img {
        height: 1.2em;
        width: 1.2em;
        margin-right: 1em;
      }

      .reload {
        &:hover,
        &:focus {
          background-color: var(--theme-popup_highlight);
          color: var(--theme-popup_highlight_text);
        }
      }
    </style>
  </head>
  <body>
    <div class="popup" tabindex="1">
      <div id="something-bad">
        <p>...</p>
      </div>

      <div id="disabled-because-security" style="display: none">
        <p>For security reasons, Windowed is not supported on this domain.</p>
      </div>

      <div id="need-a-refresh" style="display: none">
        <p>You need to refresh this page for Windowed to function properly</p>
        <div style="height: 8px"></div>
        <button class="reload">Reload page</button>
      </div>

      <div id="working" style="display: none">
        <p>
          Fullscreen on <i data-placeholder="domain">DOMAIN.COM</i> should
          always:
        </p>
        <div style="height: 4px"></div>

        <form>
          <label data-target="ask" title="Ask">
            <input type="radio" name="behaviour" value="ask" checked />
            <div class="icon"></div>
            <span>Ask</span>
          </label>
          <label data-target="windowed" title="Windowed">
            <input type="radio" name="behaviour" value="windowed" />
            <div
              class="icon mask-icon"
              data-dynamic-mask-image="../Images/Icon_Windowed_Mono@scalable.svg"
            ></div>
            <span>Windowed</span>
          </label>
          <label data-target="in-window" title="In-window">
            <input type="radio" name="behaviour" value="in-window" />
            <div
              class="icon mask-icon"
              data-dynamic-mask-image="../Images/Icon_InWindow_Mono@scalable.svg"
            ></div>
            <span>In-window</span>
          </label>
          <label data-target="fullscreen" title="Fullscreen">
            <input type="radio" name="behaviour" value="fullscreen" />
            <div
              class="icon mask-icon"
              data-dynamic-mask-image="../Images/Icon_EnterFullscreen@scalable.svg"
            ></div>
            <span>Fullscreen</span>
          </label>

          <hide-if-no-picture-in-picture>
            <div style="height: 8px"></div>
            <label
              data-target="picture-in-picture"
              title="Picture-in-Picture mode, only if supported by the page. This will overwrite the preference above (even ask)"
            >
              <input type="checkbox" name="picture_in_picture" />
              <!--<img src="../Images/Icon_PiP@scalable.svg" />-->
              <div
                style="
                  background-color: currentColor;
                  mask-image: url(&quot;../Images/Icon_PiP@scalable.svg&quot;);
                "
              ></div>
              <span>PiP (if possible)</span>
            </label>
          </hide-if-no-picture-in-picture>

          <button id="set-as-default" disabled style="margin-top: 8px">
            <div>Set as default</div>
            <div>for all websites</div>
          </button>

          <hide-if-ask>
            <p style="margin-top: 8px">
              You can always use <kbd>Shift</kbd> to trigger the ask popup.
            </p>
          </hide-if-ask>
        </form>
      </div>
    </div>
    <script src="../Javascript/browser-polyfill.min.js"></script>
    <script src="./Popup.js" type="module"></script>
    <script src="./Theme-Colors.js" type="module"></script>
  </body>
</html>


================================================
FILE: extension/Popup/Popup.js
================================================
import { browser } from "../Vendor/Browser.js";

/** @param {() => unknown} fn */
let run = (fn) => fn();

/** @param {string} id */
let show_html_for = (id) => {
  /** @type {NodeListOf<HTMLElement>} */
  let popup_divs = document.querySelectorAll(".popup > div");
  for (let div of popup_divs) {
    div.style.display = "none";
  }
  /** @type {HTMLElement} */
  let to_show = document.querySelector(`.popup > ${id}`);
  to_show.style.display = null;
  return to_show;
};

for (let element of document.querySelectorAll("[data-dynamic-mask-image]")) {
  if (element instanceof HTMLElement) {
    let mask_image_path = element.dataset.dynamicMaskImage;
    let string = browser.runtime.getURL(mask_image_path);
    element.style.setProperty("mask-image", `url(${string})`);
  }
}

let browser_info = browser.runtime.getBrowserInfo
  ? await browser.runtime.getBrowserInfo()
  : await Promise.resolve({ name: "Chrome" });
let is_firefox = browser_info.name === "Firefox";

document.documentElement.classList.add(
  is_firefox ? "browser-firefox" : "browser-chrome",
);

/**
 * Function that works with my Extension Messaging Wrapper for nice error handling
 * @param {any} message
 * */
let send_chrome_message = async (message) => {
  let { type, value } = await browser.runtime.sendMessage(message);
  if (type === "resolve") {
    return value;
  } else {
    let err = new Error(value.message);
    err.stack = value.stack;
    // err.stack = [
    //   ...x.value.stack.split('\n'),
    //   'From postMessage to background page',
    //   ...stack,
    // ].join('\n');
    throw err;
  }
};

let ALL_MODE = "mode(*)";
let ALL_PIP = "pip(*)";

/**
 * @param {string} mode
 * @param {boolean} disabled
 * @returns {import("../Background/BackgroundModule.js").WindowedMode}
 */
let clean_mode = (mode, disabled) => {
  // Any other mode than the known ones are ignored
  if (mode == "fullscreen" || mode == "windowed" || mode == "in-window") {
    return mode;
  }
  return disabled === true ? "fullscreen" : "ask";
};
/** @param {import("webextension-polyfill").Tabs.Tab} tab */
let get_host_config = async (tab) => {
  let host = new URL(tab.url).host;
  let host_mode = `mode(${host})`;
  let host_pip = `pip(${host})`;
  let {
    [host_mode]: mode,
    [host]: disabled,
    [host_pip]: pip,
    [ALL_MODE]: all_mode,
    [ALL_PIP]: all_pip,
  } = await browser.storage.sync.get([
    host_mode,
    host,
    host_pip,
    ALL_MODE,
    ALL_PIP,
  ]);

  return {
    mode: clean_mode(mode ?? all_mode, disabled),
    pip: (pip ?? all_pip) === true,
    all_mode: clean_mode(all_mode, false),
    all_pip: all_pip === true,
  };
};

/** @type {{ [tabid: number]: Promise<boolean> }} */
let current_port_promises = {};
/**
 * Check if we can connect with the Windowed content script in a tab
 * @param {number} tabId
 * @returns {Promise<boolean>}
 */
let ping_content_script = async (tabId) => {
  try {
    if (current_port_promises[tabId] != null) {
      return await current_port_promises[tabId];
    } else {
      current_port_promises[tabId] = new Promise((resolve, reject) => {
        let port = browser.tabs.connect(tabId);
        port.onMessage.addListener((message) => {
          resolve(true);
          port.disconnect();
        });
        port.onDisconnect.addListener((p) => {
          resolve(false);
        });
      });
      return await current_port_promises[tabId];
    }
  } finally {
    delete current_port_promises[tabId];
  }
};

let initialize_page = async () => {
  let tabs = await browser.tabs.query({ active: true, currentWindow: true });
  let tab = tabs[0];

  if (tab.status !== "complete") {
    await new Promise((resolve) => {
      setTimeout(resolve, 100);
    });
    await initialize_page();
    return;
  }

  let has_contentscript_active =
    tab.status === "complete" && (await ping_content_script(tab.id));

  if (
    has_contentscript_active === false &&
    (tab.url.match(/^about:/) ||
      tab.url.match(/^chrome:\/\//) ||
      tab.url.match(/^edge:\/\//) ||
      tab.url.match(/^https?:\/\/chrome\.google\.com/) ||
      tab.url.match(/^https?:\/\/support\.mozilla\.org/))
  ) {
    await show_html_for("#disabled-because-security");
    return;
  }

  if (tab.status === "complete" && has_contentscript_active === false) {
    let $root = await show_html_for("#need-a-refresh");
    $root.querySelector(".reload").addEventListener("click", async () => {
      await browser.tabs.reload(tab.id);
      await initialize_page();
    });
    return;
  }

  // @ts-ignore
  if (HTMLVideoElement.prototype.requestPictureInPicture != null) {
    document.body.classList.add("picture-in-picture-support");
  }

  let host = new URL(tab.url).host;
  let host_mode = `mode(${host})`;
  let host_pip = `pip(${host})`;
  for (let element of document.querySelectorAll("[data-placeholder=domain]")) {
    element.textContent = host;
  }

  let $root = await show_html_for("#working");
  let $form = $root.querySelector("form");
  /** @type {HTMLButtonElement} */
  let $set_as_default_button = $root.querySelector("#set-as-default");
  // @ts-ignore
  let behaviour_input = $form.elements.behaviour;
  // @ts-ignore
  let picture_in_picture_input = $form.elements.picture_in_picture;

  let config = await get_host_config(tab);

  document.body.setAttribute("data-mode-selected", config.mode);
  $set_as_default_button.disabled =
    behaviour_input.value === config.all_mode &&
    picture_in_picture_input.checked === config.all_pip;

  $form.addEventListener("input", async (e) => {
    await browser.storage.sync.set({
      [host_mode]: behaviour_input.value,
      [host_pip]: picture_in_picture_input.checked,
    });

    initialize_page();
    send_chrome_message({
      type: "update_windowed_button",
      id: tab.id,
    });
  });

  $set_as_default_button.addEventListener("click", async (e) => {
    await browser.storage.sync.set({
      [ALL_MODE]: behaviour_input.value,
      [ALL_PIP]: picture_in_picture_input.checked,
    });
    config = await get_host_config(tab);
    await send_chrome_message({
      type: "update_windowed_button",
      id: tab.id,
    });
  });

  behaviour_input.value = config.mode;
  picture_in_picture_input.checked = config.pip;
};

run(initialize_page);

export {};


================================================
FILE: extension/Popup/Theme-Colors.js
================================================
import { themecolor_to_string } from "../utilities/themecolor-to-string.js";
import { browser } from "../Vendor/Browser.js";

if (browser.theme) {
  let theme = await browser.theme.getCurrent();

  if (theme.colors != undefined) {
    let root = document.documentElement;
    console.log(`Object.entries(theme.colors):`, Object.entries(theme.colors));

    for (let [key, value] of Object.entries(theme.colors)) {
      console.log(`key:`, key);
      if (value != undefined) {
        root.style.setProperty(`--theme-${key}`, themecolor_to_string(value));
      }
    }
  }
}


================================================
FILE: extension/Vendor/Browser.d.ts
================================================
export declare let browser: import("webextension-polyfill").Browser;


================================================
FILE: extension/Vendor/Browser.js
================================================
import "./browser-polyfill.min.js";

// @ts-ignore
export let browser = globalThis.browser;


================================================
FILE: extension/Windowed-inject-into-page.js
================================================
{
  // Alliases for different browsers
  let requestFullscreen_aliasses = ["requestFullscreen","mozRequestFullScreen","webkitRequestFullscreen","webkitRequestFullScreen","msRequestFullscreen"];
  let exitFullscreen_aliasses = ["exitFullscreen","webkitExitFullscreen","webkitCancelFullScreen","mozCancelFullScreen","msExitFullscreen"];
  let fullscreenelement_aliasses = ["fullscreenElement","webkitFullscreenElement","mozFullscreenElement","mozFullScreenElement","msFullscreenElement","webkitCurrentFullScreenElement"];
  let fullscreenchange_aliasses = ["fullscreenchange","webkitfullscreenchange","mozfullscreenchange","MSFullscreenChange"];

  const send_event = (element, type) => {
    const event = new Event(type, {
      bubbles: true,
      cancelBubble: false,
      cancelable: false,
    });
    // if (element[`on${type}`]) {
    //   element[`on${type}`](event);
    // }
    element.dispatchEvent(event);
  };

  const send_fullscreen_events = (element) => {
    for (let fullscreenchange of fullscreenchange_aliasses) {
      send_event(document, fullscreenchange);
    }
    send_event(window, 'resize');
  };

  let all_communication_id = 0;
  let external_function = (function_id) => async (...args) => {
    let request_id = all_communication_id;
    all_communication_id = all_communication_id + 1;

    window.postMessage({
      type: 'CUSTOM_WINDOWED_FROM_PAGE',
      request_id: request_id,
      function_id: function_id,
      args: args,
    }, '*');

    return new Promise((resolve, reject) => {
      let listener = (event) => {
        // We only accept messages from ourselves
        if (event.source != window) return;
        if (event.data == null) return;

        if (event.data.type === 'CUSTOM_WINDOWED_TO_PAGE') {
          if (event.data.request_id === request_id) {
            window.removeEventListener('message', listener);
            if (event.data.resultType === 'resolve') {
              resolve(event.data.result);
            } else {
              let err = new Error(event.data.result.message);
              err.stack = event.data.result.stack;
              reject(err);
            }
          }
        }
      }
      window.addEventListener('message', listener);
    });
  }

  let overwrite = (object, property, value) => {
    try {
      if (property in object) {
        Object.defineProperty(object, property, {
          value: value,
          configurable: true,
          writable: true,
        });
      }
    } catch (err) {
      // Nothing
    }
  }

  let set_fullscreen_element = (element = null) => {
    if (element == null) {
      throw new Error('WINDOWED: Got null in set_fullscreen_element');
    }

    overwrite(document, 'webkitIsFullScreen', true); // Old old old
    overwrite(document, 'fullscreen', true); // Old old old
    for (let fullscreenelement_alias of fullscreenelement_aliasses) {
      overwrite(document, fullscreenelement_alias, element);
    }
  }

  let make_tab_go_fullscreen = external_function(1);
  let make_tab_go_inwindow = external_function(2);

  let create_popup = external_function(3);

  let make_tab_exit_fullscreen = external_function(4);

  let exitFullscreen = async function(original) {
    let windowed_fullscreen = document.querySelector('[data-windowed_long_id_that_does_not_conflict_active], [data-windowed_long_id_that_does_not_conflict_shadowdom]');

    if (windowed_fullscreen) {
      // If the fullscreen element is a frame, tell it to exit fullscreen too
      if (typeof windowed_fullscreen.postMessage === 'function') {
        document.fullscreenElement.postMessage.sendMessage({ type: "exit_fullscreen_iframe" });
      }

      // Reset all the variables to their browser form
      delete window.screen.width;
      delete window.screen.height;
      delete document['webkitIsFullScreen'];
      delete document['fullscreen'];
      for (let fullscreenelement_alias of fullscreenelement_aliasses) {
        delete document[fullscreenelement_alias];
      }

      await make_tab_exit_fullscreen();
    } else {
      original();
    }
  }

  
  const requestFullscreen_windowed = async function(original, ...args) {
    const element = this;
    element.dataset['windowed_long_id_that_does_not_conflict_select'] = true;

    let shadowroot = element.getRootNode()
    while (shadowroot != null && shadowroot.host != null) {
        shadowroot.host.dataset['windowed_long_id_that_does_not_conflict_shadowdom'] = true;
        shadowroot = shadowroot.host.getRootNode()
    }

    // Tell extension code (outside of this block) to go into fullscreen
    // window.postMessage({ type: force ? "enter_fullscreen" : "show_fullscreen_popup" }, "*");
    // send_windowed_event(element, force ? "enter_fullscreen" : "show_fullscreen_popup");
    try {
      await create_popup();
    } catch (err) {
      // Anything gone wrong, we default to normal fullscreen
      console.error(err);
      console.error('[Windowed] Something went wrong, so I default to normal fullscreen:', err.stack);
      original();
    }
  }

  // Because firefox is super cool, it is also super frustrating...
  // So firefox does not allow fullscreen calls from promises, even if it is basically sync.
  // Therefor I need to first define this as sync, and from here call the async version.
  let MUTATE_is_windowed_enabled = true;
  let requestFullscreen = function(original, ...args) {
    if (MUTATE_is_windowed_enabled === false) {
      return original()
    } else {
      requestFullscreen_windowed.call(this, original, ...args);
    }
  }

  let finish_fullscreen = () => {
    // Because youtube actually checks for those sizes?!
    const window_width = Math.max(window.outerWidth, window.innerWidth);
    const window_height = Math.max(window.outerHeight, window.innerHeight);

    overwrite(window.screen, 'width', window_width);
    overwrite(window.screen, 'height', window_height);

    let element = document.querySelector('[data-windowed_long_id_that_does_not_conflict_select]');
    if (element == null) {
      console.warn('[WINDOWED] Strange, no fullscreen element shown');
      return;
    }

    document.body.focus();
    element.focus();
    set_fullscreen_element(element || document.body);
    send_fullscreen_events();
  }

  window.addEventListener("message", (message) => {
    // Message from content script or parent content script
    if (message.source === message.target || message.source === window.parent) {
      if (message.data?.type === 'WINDOWED-confirm-fullscreen') {
        finish_fullscreen();
      }

      if (message.data?.type === 'WINDOWED-exit-fullscreen') {
        exitFullscreen.call(document, original_exitFullscreen);
      }

      if (message.data?.type === 'WINDOWED-notify') {
        MUTATE_is_windowed_enabled = !message.data.disabled;
      }
    }

    // Message from frame inside the page (these are tricky not sure if I still know how this works)
    const frame = [...document.querySelectorAll('iframe')].find(x => x.contentWindow === message.source);
    if (frame != null) {
      if (message.data?.type === 'enter_inwindow_iframe') {
        frame.dataset['windowed_long_id_that_does_not_conflict_select'] = "true";
        make_tab_go_inwindow();
      }
      if (message.data?.type === 'enter_fullscreen_iframe') {
        frame.dataset['windowed_long_id_that_does_not_conflict_select'] = "true";
        make_tab_go_fullscreen();
      }
      if (message.data?.type === 'exit_fullscreen_iframe') {
        // Call my exitFullscreen on the document
        exitFullscreen.call(document, original_exitFullscreen);
      }
    }
  });

  
  let original_requestFullscreen = null;
  for (let requestFullscreenAlias of requestFullscreen_aliasses) {
    if (typeof Element.prototype[requestFullscreenAlias] === 'function') {
      let original_function = Element.prototype[requestFullscreenAlias];
      original_requestFullscreen = original_function;
      Element.prototype[requestFullscreenAlias] = function(...args) {
        return requestFullscreen.call(this, original_function.bind(this), ...args);
      };
    }
  }

  
  let original_exitFullscreen = null;
  for (let exitFullscreenAlias of exitFullscreen_aliasses) {
    if (typeof Document.prototype[exitFullscreenAlias] === 'function') {
      let original_function = Document.prototype[exitFullscreenAlias];
      original_exitFullscreen = original_function;
      Document.prototype[exitFullscreenAlias] = function(...args) {
        return exitFullscreen.call(this, original_function.bind(this), ...args);
      };
    }
  }
}

================================================
FILE: extension/manifest-firefox.json
================================================
{
  "name": "Windowed - floating Youtube/every website",
  "short_name": "Windowed",
  "description": "Changes fullscreen buttons to go into a popup. Works for every website that uses fullscreen, including Youtube, Vimeo, Netflix",
  "developer": {
    "name": "Michiel Dral",
    "url": "https://dral.eu/"
  },
  "version": "34",
  "manifest_version": 3,
  "browser_specific_settings": {
    "gecko": {
      "id": "{477dbe5e-1742-4641-a2c3-b6113bb5cf6e}"
    }
  },
  "permissions": ["storage", "tabs", "offscreen"],
  "action": {
    "default_popup": "Popup/Popup.html",
    "default_icon": {
      "32": "/Images/Icon_Windowed_Mono@1x.png"
    }
  },
  "content_scripts": [
    {
      "run_at": "document_start",
      "matches": ["<all_urls>"],
      "js": ["Vendor/browser-polyfill.min.js", "Content.js"],
      "all_frames": true
    },
    {
      "run_at": "document_start",
      "matches": ["<all_urls>"],
      "js": ["Windowed-inject-into-page.js"],
      "all_frames": true,
      "world": "MAIN"
    }
  ],
  "background": {
    "scripts": ["Background/BackgroundModule.js"],
    "service_worker": "Background/BackgroundModule.js",
    "type": "module"
  },
  "web_accessible_resources": [
    {
      "resources": ["Images/*"],
      "matches": ["<all_urls>"]
    },
    {
      "resources": [
        "Vendor/browser-polyfill.min.js",
        "Vendor/Browser.js",
        "Windowed-inject-into-page.js"
      ],
      "matches": ["<all_urls>"]
    }
  ],
  "icons": {
    "16": "Icons/Icon_16.png",
    "32": "Icons/Icon_32.png",
    "64": "Icons/Icon_64.png",
    "128": "Icons/Icon_128.png"
  }
}


================================================
FILE: extension/manifest.json
================================================
{
  "name": "Windowed - floating Youtube/every website",
  "short_name": "Windowed",
  "description": "Changes fullscreen buttons to go into a popup. Works for every website that uses fullscreen, including Youtube, Vimeo, Netflix",
  "developer": {
    "name": "Michiel Dral",
    "url": "https://dral.eu/"
  },
  "version": "34",
  "manifest_version": 3,
  "browser_specific_settings": {
    "gecko": {
      "id": "{477dbe5e-1742-4641-a2c3-b6113bb5cf6e}"
    }
  },
  "permissions": ["storage", "tabs", "offscreen"],
  "action": {
    "default_popup": "Popup/Popup.html",
    "default_icon": {
      "32": "/Images/Icon_Windowed_Mono@1x.png"
    }
  },
  "content_scripts": [
    {
      "run_at": "document_start",
      "matches": ["<all_urls>"],
      "js": ["Vendor/browser-polyfill.min.js", "Content.js"],
      "all_frames": true
    },
    {
      "run_at": "document_start",
      "matches": ["<all_urls>"],
      "js": ["Windowed-inject-into-page.js"],
      "all_frames": true,
      "world": "MAIN"
    }
  ],
  "background": {
    "scripts": ["Background/BackgroundModule.js"],
    "service_worker": "Background/BackgroundModule.js",
    "type": "module"
  },
  "web_accessible_resources": [
    {
      "resources": ["Images/*"],
      "matches": ["<all_urls>"]
    },
    {
      "resources": [
        "Vendor/browser-polyfill.min.js",
        "Vendor/Browser.js",
        "Windowed-inject-into-page.js"
      ],
      "matches": ["<all_urls>"]
    }
  ],
  "icons": {
    "16": "Icons/Icon_16.png",
    "32": "Icons/Icon_32.png",
    "64": "Icons/Icon_64.png",
    "128": "Icons/Icon_128.png"
  }
}


================================================
FILE: extension/tsconfig.json
================================================
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "esnext",
    "lib": ["ES2020", "DOM", "dom.iterable"],
    "allowJs": true,
    "checkJs": true,
    "noEmit": true,
    "strict": false,
    "noImplicitThis": true,
    "alwaysStrict": true,
    "esModuleInterop": true,
    "moduleResolution": "node",
    "moduleDetection": "force"
  },
  "exclude": ["./Vendor/browser-polyfill.min.js"]
}


================================================
FILE: extension/utilities/themecolor-to-string.js
================================================
/**
 * @param {import("webextension-polyfill").Manifest.ThemeColor} color
 * @returns {string}
 */
export let themecolor_to_string = (color) => {
  if (typeof color === "string") {
    return color;
  } else if (Array.isArray(color)) {
    if (color.length === 3) {
      return `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
    } else if (color.length === 4) {
      return `rgba(${color[0]}, ${color[1]}, ${color[2]}, ${color[3]})`;
    } else {
      // prettier-ignore
      throw new Error(`Invalid theme color array: ${JSON.stringify(color)}`);
    }
  } else {
    throw new Error(`Invalid theme color: ${color}`);
  }
};


================================================
FILE: package.json
================================================
{
  "name": "windowed",
  "version": "1.0.0",
  "description": "[Install in Chrome Webstore](https://chrome.google.com/webstore/detail/windowed-floating-youtube/gibipneadnbflmkebnmcbgjdkngkbklb)",
  "main": "index.js",
  "scripts": {
    "build": "(cd extension; zip -r ../extension.zip .)",
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "web-ext run -s extension --url \"https://youtu.be/Yocja_N5s1I\""
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/dralletje/Windowed.git"
  },
  "author": "",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/dralletje/Windowed/issues"
  },
  "homepage": "https://github.com/dralletje/Windowed#readme",
  "devDependencies": {
    "@types/webextension-polyfill": "^0.12.4",
    "prettier": "^3.4.2",
    "puppeteer": "^1.18.1",
    "web-ext": "^7.11.0"
  },
  "prettier": {},
  "packageManager": "pnpm@10.16.1"
}


================================================
FILE: testing/iframe/iframe.html
================================================
<html style="background-color: black">
<body>
  <iframe id="thing" src="about:blank" style="border: none; height: 300px"></iframe>
  <script>
    var iframe = document.getElementById('thing');
    iframe.contentWindow.document.open();
    iframe.contentWindow.document.write(`<html><body>
      <div style="background-color: red; width: 200px; height: 200px;" id="supersizeme"></div>
      <button onclick="console.log('hi'); document.querySelector('#supersizeme').requestFullscreen()">Fullscreen</button>
    </body></html>`);
    iframe.contentWindow.document.close();
  </script>
</body>
</html>


================================================
FILE: testing/page.html
================================================
<html>
<body>
  <div style="width: 200px; height: 200px; background-color: blue"></div>
  <div
    class="video"
    style="
      padding: 50px;
      min-height: 300px;
      width: 500px;
      background-color: black;
    "
  >
    <div
      onclick="document.exitFullscreen()"
      class="exit-fullscreen"
      style="
        display: inline-block;
        padding: 5px;
        color: white;
        border: 2px solid white;
      "
    >Exit fullscreen</div>
    <div
      onclick="document.querySelector('.video').requestFullscreen()"
      class="fullscreen"
      style="
        display: inline-block;
        padding: 5px;
        color: white;
        border: 2px solid white;
      "
    >Fullscreen</div>
  </div>

  <div style="width: 200px; height: 200px; background-color: red"></div>
</body>
</html>


================================================
FILE: testing/stadia-like.html
================================================
<html>
<body>
  <div style="width: 200px; height: 200px; background-color: blue"></div>
  <div
    class="video"
    style="
      padding: 50px;
      min-height: 300px;
      width: 500px;
      background-color: black;
    "
  >
    <div
      onclick="document.exitFullscreen()"
      class="exit-fullscreen"
      style="
        display: inline-block;
        padding: 5px;
        color: white;
        border: 2px solid white;
      "
    >Exit fullscreen</div>
    <div
      onclick="document.querySelector('.video').requestFullscreen()"
      class="fullscreen"
      style="
        display: inline-block;
        padding: 5px;
        color: white;
        border: 2px solid white;
      "
    >Fullscreen</div>
  </div>

  <div style="width: 200px; height: 200px; background-color: red"></div>

  <script>
    document.addEventListener('keydown', (e) => {
      if (e.key === 'Escape') {
        e.preventDefault();
      }
    });
  </script>
</body>
</html>


================================================
FILE: testing/test.js
================================================
let puppeteer = require('puppeteer');
let path = require('path');



let extension_path = path.resolve(__dirname, '../extension');
let page_html = path.resolve(__dirname, 'page.html');

console.log(`extension_path:`, extension_path)

let run = async () => {
  try {
    let browser = await puppeteer.launch({
      devtools: false,
      headless: false,
      sloMo: true,
      args: [
        // I need this for CORS stuff
        '--disable-web-security',

        // Some things I saw on the internet idk if they make it better
        "--proxy-server='direct://'",
        '--proxy-bypass-list=*',
        '--no-sandbox',

        `--disable-extensions-except=${extension_path}/`,
        `--load-extension=${extension_path}/`,
      ],
    });

    let page = await browser.newPage();

    page.setDefaultTimeout = 5000;
    await page.setUserAgent(
      'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'
    );

    // let browser_console = make_holy_console({
    //   name: 'From Browser',
    //   map_fn: (line) => chalk.dim(line),
    // });
    page.on('console', async (msg) => {
      let args = await Promise.all(msg.args().map((x) => x.jsonValue()));
      console.log('BROWSER:', ...args);
    });

    let url = `file://${page_html}`;
    page.goto(url).catch((err) => {
      console.log(`Error from page.goto:`, err.message);
    });

    await page._client.send('Emulation.clearDeviceMetricsOverride')

    await page.screenshot({
      path: path.resolve(__dirname, `./screenshots/normal.png`),
    });

    for (let window_target of ['windowed', 'in-window', 'fullscreen']) {
      let fullscreen_button = await page.waitForSelector(`.fullscreen`);
      await fullscreen_button.click();
      let windowed_button = await page.waitForSelector(`[data-target="${window_target}"]`);
      await windowed_button.click();

      // await new Promise((resolve) => {
      //   setTimeout(() => {
      //     resolve();
      //   }, 3000);
      // })

      let viewport = page.viewport();
      // console.log(`viewport:`, viewport)
      await page.screenshot({
        path: path.resolve(__dirname, `./screenshots/${window_target}.png`),
      });

      // let innerheight = await page.evaluate(() => document.querySelector('.video').innerHeight);
      // let innerwidth = await page.evaluate(() => document.querySelector('.video').innerWidth);

      let innerheight = await page.evaluate(() => window.innerHeight);
      let innerwidth = await page.evaluate(() => window.innerWidth);

      let body_innerheight = await page.evaluate(() => document.body.offsetHeight);
      let body_innerwidth = await page.evaluate(() => document.body.offsetWidth);

      console.log(`body_innerheight:`, body_innerheight)
      console.log(`body_innerwidth:`, body_innerwidth)

      console.log(`innerheight:`, innerheight);
      console.log(`innerwidth:`, innerwidth)

      let exit_fullscreen_button = await page.waitForSelector(`.exit-fullscreen`);
      await exit_fullscreen_button.click();
    }

    browser.close();
    console.log('Done');
    process.exit(0);
  } catch (error) {
    console.log(`error.stack:`, error.stack);
    process.exit(1);
  }
}

run();


================================================
FILE: testing/ustream/ustream.html
================================================
<html>
<body>
<h1>Foo Heading</h1>
<p>Foo paragraph.</p>
<iframe width="480" height="270" src="https://ustream.tv/embed/recorded/120712252" scrolling="no" allowfullscreen webkitallowfullscreen frameborder="0" style="border: 0 none transparent;"></iframe>
<p>Bar paragraph.</p>
</body>
</html>
Download .txt
gitextract_b72y24m9/

├── .gitignore
├── Icons.sketch
├── PrivacyPolicyForEdgeWebstore.md
├── README.md
├── Testing.md
├── extension/
│   ├── Background/
│   │   ├── BackgroundModule.js
│   │   ├── theme-color/
│   │   │   ├── chrome-offscreen.html
│   │   │   ├── chrome-offscreen.js
│   │   │   ├── chrome.js
│   │   │   └── firefox.js
│   │   └── tint_image.js
│   ├── Content.js
│   ├── Popup/
│   │   ├── Popup.html
│   │   ├── Popup.js
│   │   └── Theme-Colors.js
│   ├── Vendor/
│   │   ├── Browser.d.ts
│   │   └── Browser.js
│   ├── Windowed-inject-into-page.js
│   ├── manifest-firefox.json
│   ├── manifest.json
│   ├── tsconfig.json
│   └── utilities/
│       └── themecolor-to-string.js
├── package.json
└── testing/
    ├── iframe/
    │   └── iframe.html
    ├── page.html
    ├── stadia-like.html
    ├── test.js
    └── ustream/
        └── ustream.html
Download .txt
SYMBOL INDEX (7 symbols across 4 files)

FILE: extension/Background/BackgroundModule.js
  constant BROWSERACTION_ICON (line 23) | let BROWSERACTION_ICON = "/Images/Icon_Windowed_Mono@1x.png";
  constant ALL_MODE (line 114) | let ALL_MODE = "mode(*)";
  constant ALL_PIP (line 115) | let ALL_PIP = "pip(*)";

FILE: extension/Background/theme-color/chrome.js
  function setupOffscreenDocument (line 11) | async function setupOffscreenDocument(path) {

FILE: extension/Content.js
  constant CLICK_IS_CONSIDERED_FULLSCREEN_CLICK_DELAY (line 1087) | let CLICK_IS_CONSIDERED_FULLSCREEN_CLICK_DELAY = 1 * 1000;

FILE: extension/Popup/Popup.js
  constant ALL_MODE (line 56) | let ALL_MODE = "mode(*)";
  constant ALL_PIP (line 57) | let ALL_PIP = "pip(*)";
Condensed preview — 28 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (113K chars).
[
  {
    "path": ".gitignore",
    "chars": 40,
    "preview": "extension.zip\r\n.DS_Store\r\nnode_modules\r\n"
  },
  {
    "path": "PrivacyPolicyForEdgeWebstore.md",
    "chars": 201,
    "preview": "# Privacy Policy\n\nI don't collect any data.\n\nThere are no network requests done by the extension nor any data read from "
  },
  {
    "path": "README.md",
    "chars": 3516,
    "preview": "# ![Windowed Logo](extension/Icons/Icon_32.png) Windowed\n\n[Install in Chrome Webstore](https://chrome.google.com/webstor"
  },
  {
    "path": "Testing.md",
    "chars": 815,
    "preview": "# Testing\n\nIn both browsers currently supported (Chrome and Firefox)\nI should test these things:\n\n### Basic usage\n1. Act"
  },
  {
    "path": "extension/Background/BackgroundModule.js",
    "chars": 15320,
    "preview": "// Import is not yet allowed in firefox, so for now I put tint_image in manifest.json\nimport { icon_theme_color_chrome }"
  },
  {
    "path": "extension/Background/theme-color/chrome-offscreen.html",
    "chars": 46,
    "preview": "<script src=\"./chrome-offscreen.js\"></script>\n"
  },
  {
    "path": "extension/Background/theme-color/chrome-offscreen.js",
    "chars": 549,
    "preview": "/** @type {import(\"webextension-polyfill\").Browser} */\n// @ts-ignore\nlet browser = chrome;\n\nbrowser.runtime.onMessage.ad"
  },
  {
    "path": "extension/Background/theme-color/chrome.js",
    "chars": 1751,
    "preview": "/** @type {import(\"webextension-polyfill\").Browser} */\n// @ts-ignore\nlet browser = chrome;\n\nlet creating; // A global pr"
  },
  {
    "path": "extension/Background/theme-color/firefox.js",
    "chars": 889,
    "preview": "import { themecolor_to_string } from \"../../utilities/themecolor-to-string.js\";\n\n/** @type {import(\"webextension-polyfil"
  },
  {
    "path": "extension/Background/tint_image.js",
    "chars": 2670,
    "preview": "/**\n * @param {string} color\n * @returns {{ color: string, alpha: number }}\n */\nlet find_and_replace_alpha = (color) => "
  },
  {
    "path": "extension/Content.js",
    "chars": 45281,
    "preview": "// This file is by far the most important of the whole extension.\n// This gets loaded into every single page you open, s"
  },
  {
    "path": "extension/Popup/Popup.html",
    "chars": 8356,
    "preview": "<!doctype html>\n<html>\n  <head>\n    <style>\n      :root {\n        --theme-popup: transparent;\n        --theme-popup_text"
  },
  {
    "path": "extension/Popup/Popup.js",
    "chars": 6277,
    "preview": "import { browser } from \"../Vendor/Browser.js\";\n\n/** @param {() => unknown} fn */\nlet run = (fn) => fn();\n\n/** @param {s"
  },
  {
    "path": "extension/Popup/Theme-Colors.js",
    "chars": 577,
    "preview": "import { themecolor_to_string } from \"../utilities/themecolor-to-string.js\";\nimport { browser } from \"../Vendor/Browser."
  },
  {
    "path": "extension/Vendor/Browser.d.ts",
    "chars": 69,
    "preview": "export declare let browser: import(\"webextension-polyfill\").Browser;\n"
  },
  {
    "path": "extension/Vendor/Browser.js",
    "chars": 92,
    "preview": "import \"./browser-polyfill.min.js\";\n\n// @ts-ignore\nexport let browser = globalThis.browser;\n"
  },
  {
    "path": "extension/Windowed-inject-into-page.js",
    "chars": 8578,
    "preview": "{\n  // Alliases for different browsers\n  let requestFullscreen_aliasses = [\"requestFullscreen\",\"mozRequestFullScreen\",\"w"
  },
  {
    "path": "extension/manifest-firefox.json",
    "chars": 1617,
    "preview": "{\n  \"name\": \"Windowed - floating Youtube/every website\",\n  \"short_name\": \"Windowed\",\n  \"description\": \"Changes fullscree"
  },
  {
    "path": "extension/manifest.json",
    "chars": 1617,
    "preview": "{\n  \"name\": \"Windowed - floating Youtube/every website\",\n  \"short_name\": \"Windowed\",\n  \"description\": \"Changes fullscree"
  },
  {
    "path": "extension/tsconfig.json",
    "chars": 405,
    "preview": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"module\": \"esnext\",\n    \"lib\": [\"ES2020\", \"DOM\", \"dom.iterable\"],\n "
  },
  {
    "path": "extension/utilities/themecolor-to-string.js",
    "chars": 629,
    "preview": "/**\n * @param {import(\"webextension-polyfill\").Manifest.ThemeColor} color\n * @returns {string}\n */\nexport let themecolor"
  },
  {
    "path": "package.json",
    "chars": 916,
    "preview": "{\n  \"name\": \"windowed\",\n  \"version\": \"1.0.0\",\n  \"description\": \"[Install in Chrome Webstore](https://chrome.google.com/w"
  },
  {
    "path": "testing/iframe/iframe.html",
    "chars": 599,
    "preview": "<html style=\"background-color: black\">\n<body>\n  <iframe id=\"thing\" src=\"about:blank\" style=\"border: none; height: 300px\""
  },
  {
    "path": "testing/page.html",
    "chars": 824,
    "preview": "<html>\n<body>\n  <div style=\"width: 200px; height: 200px; background-color: blue\"></div>\n  <div\n    class=\"video\"\n    sty"
  },
  {
    "path": "testing/stadia-like.html",
    "chars": 974,
    "preview": "<html>\n<body>\n  <div style=\"width: 200px; height: 200px; background-color: blue\"></div>\n  <div\n    class=\"video\"\n    sty"
  },
  {
    "path": "testing/test.js",
    "chars": 3258,
    "preview": "let puppeteer = require('puppeteer');\nlet path = require('path');\n\n\n\nlet extension_path = path.resolve(__dirname, '../ex"
  },
  {
    "path": "testing/ustream/ustream.html",
    "chars": 293,
    "preview": "<html>\n<body>\n<h1>Foo Heading</h1>\n<p>Foo paragraph.</p>\n<iframe width=\"480\" height=\"270\" src=\"https://ustream.tv/embed/"
  }
]

// ... and 1 more files (download for full content)

About this extraction

This page contains the full source code of the dralletje/Windowed GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 28 files (103.7 KB), approximately 26.6k tokens, and a symbol index with 7 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!