diff --git a/src/contentScript/components/App/App.js b/src/contentScript/components/App/App.js index 0a02f0e..3f7ede5 100644 --- a/src/contentScript/components/App/App.js +++ b/src/contentScript/components/App/App.js @@ -120,6 +120,12 @@ export default function App() { tab.signature.originalTitle, tab.signature.originalFaviconUrl )); + if (!newDocumentTitle) { + tab.restoreTitle(); + } + if (!newDocumentFavicon) { + tab.restoreFavicon(); + } setIsVisible(false); } }; diff --git a/src/contentScript/contentScript.js b/src/contentScript/contentScript.js index ce64772..4cf16fb 100644 --- a/src/contentScript/contentScript.js +++ b/src/contentScript/contentScript.js @@ -12,12 +12,15 @@ import log from "../log"; let uiInsertedIntoDOM = false; let root = null; +let tabInitializationPromise = tab.initializeForMainContentScript(); + async function insertUIIntoDOM() { if (uiInsertedIntoDOM === false) { + // await tab.initializeForMainContentScript(); + await tabInitializationPromise; const rootElement = document.createElement(ROOT_TAG_NAME); document.body.appendChild(rootElement); root = createRoot(rootElement); - await tab.initializeForMainContentScript(); root.render( @@ -26,7 +29,7 @@ async function insertUIIntoDOM() { uiInsertedIntoDOM = true; } } - + (function initializeUIListeners() { async function chromeListener(message, _sender, _sendResponse) { if (message.command === COMMAND_OPEN_RENAME_DIALOG) { diff --git a/src/contentScript/initializationContentScript.js b/src/contentScript/initializationContentScript.js index e0d6452..5cd58a9 100644 --- a/src/contentScript/initializationContentScript.js +++ b/src/contentScript/initializationContentScript.js @@ -4,6 +4,7 @@ import bgScriptApi from "./backgroundScriptApi"; import tab from "./tab"; const log = getLogger('InitializationContentScript', 'debug'); +const olog = getLogger('Observer', 'debug'); /** * Update tab signature when the contentScript loads @@ -29,5 +30,57 @@ const log = getLogger('InitializationContentScript', 'debug'); const newSignature = new TabSignature(title, favicon, originalTitle, originalFaviconUrl); log.debug('setting signature to:', newSignature); - await tab.setSignature(newSignature, false, false); + // await tab.setSignature(newSignature, false, false); + + + + + + // Title Observer: + let titleMutationObserver = new MutationObserver((mutations) => { + olog.debug('titleMutationObserver callback called', mutations); + mutations.forEach((mutation) => { + if (mutation.target.nodeName === 'TITLE') { + const newTitle = document.title; + olog.debug('TITLE mutation detected, newTitle:', newTitle); + } + }); + }); + + const titleElement = document.querySelector('head > title'); + // rplog.debug('title element:', titleElement) + if (titleElement) { + titleMutationObserver.observe(titleElement, { subtree: true, characterData: true, childList: true }); + } + + // Favicon Observer: + let faviconMutationObserver = new MutationObserver((mutations) => { + olog.debug('faviconMutationObserver callback called', mutations); + mutations.forEach((mutation) => { + if (mutation.type === 'childList' && mutation.target === document.head) { + olog.debug('Children of have changed'); + ['addedNodes', 'removedNodes'].forEach((nodeType) => { + mutation[nodeType].forEach((node) => { + if (node.nodeName === 'LINK') { + olog.debug(nodeType + ' LINK detected:'); + const newHref = node.href; + if (newHref.length > 100) { + olog.debug('LINK href:', newHref.substring(0, 40) + '...'); + } else { + olog.debug('LINK href:', newHref); + } + } + }); + + }); + } + }); + }); + + const headElement = document.querySelector('head'); + olog.debug('head element:', headElement); + if (headElement) { + faviconMutationObserver.observe(headElement, { subtree: true, childList: true, attributes: true }); + } + })(); \ No newline at end of file diff --git a/src/contentScript/tab.js b/src/contentScript/tab.js index fbdbdeb..53bd396 100644 --- a/src/contentScript/tab.js +++ b/src/contentScript/tab.js @@ -64,6 +64,9 @@ export class Tab { // Check if a favicon link element already exists log.debug('setDocumentFavicon called with faviconUrl:', faviconUrl ? faviconUrl.substring(0, 15) : faviconUrl, preserve); + if (preserve) { + this.preserveFavicon(faviconUrl); + } let faviconLinks = document.querySelectorAll(faviconLinksCSSQuery); faviconLinks.forEach(link => { @@ -75,44 +78,41 @@ export class Tab { link.rel = 'icon'; link.href = faviconUrl; document.getElementsByTagName('head')[0].appendChild(link); - - if (preserve) { - this.preserveFavicon(faviconUrl); - } } - restoreTitle(originalTitle) { - log.debug('restoreDocumentTitle called with originalTitle:', originalTitle); + restoreTitle() { + log.debug('restoreDocumentTitle called with originalTitle:', this.signature.originalTitle); this.disconnectTabTitlePreserver(); - if (originalTitle !== document.title) { - this.setTitle(originalTitle, false); + if (this.signature.originalTitle !== document.title) { + this.setTitle(this.signature.originalTitle, false); } } - async restoreFavicon(originalFaviconUrl) { - log.debug('restoreDocumentFavicon called with originalFaviconUrl:', originalFaviconUrl); + async restoreFavicon() { + log.debug('restoreDocumentFavicon called with originalFaviconUrl:', this.signature.originalFaviconUrl); this.disconnectFaviconPreserver(); - if (originalFaviconUrl !== (await bgScriptApi.getFaviconUrl())) { - this.setFavicon(originalFaviconUrl, false); + if (this.signature.originalFaviconUrl !== (await bgScriptApi.getFaviconUrl())) { + this.setFavicon(this.signature.originalFaviconUrl, false); } } /** * @param {TabSignature} signature - The signature to set. If the title or favicon are not truthy, - * they will be restored to their original form. + * they will be divorced from the values shown in the chrome UI (the preservers will be disconnected). + * This will not restore them to their original values. */ async setSignature(signature, preserve = true, save = true) { log.debug('setDocumentSignature called with signature:', signature); if (signature.title) { this.setTitle(signature.title, preserve); } else { - this.restoreTitle(signature.originalTitle); + this.disconnectTabTitlePreserver(); } if (signature.favicon) { this.setFavicon(Favicon.fromDTO(signature.favicon).getUrl(), preserve); } else { - this.restoreFavicon(signature.originalFaviconUrl); + this.disconnectFaviconPreserver(); } if (save) { @@ -144,7 +144,7 @@ export class Tab { mutations.forEach((mutation) => { if (mutation.target.nodeName === 'TITLE') { const newTitle = document.title; - plog.debug('TITLE mutation detected', newTitle); + plog.debug('TITLE mutation detected', newTitle, 'while desriedTitle is:', desiredTitle); if (newTitle !== desiredTitle) { document.title = desiredTitle; } diff --git a/src/log.js b/src/log.js index 2b089d0..b8722b5 100644 --- a/src/log.js +++ b/src/log.js @@ -27,7 +27,7 @@ export function getLogger(name, level) { const originalMethod = logger[method]; logger[method] = function (message, ...args) { - originalMethod.call(logger, `${name}: ${message}`, ...args); + originalMethod.call(logger, `#${name}: ${message}`, ...args); }; }); diff --git a/tests/e2e/seleniumTests.test.js b/tests/e2e/seleniumTests.test.js index 9b44131..f655aa2 100644 --- a/tests/e2e/seleniumTests.test.js +++ b/tests/e2e/seleniumTests.test.js @@ -5,7 +5,11 @@ const fs = require('fs').promises; const { DriverUtils } = require('./driverUtils.js'); const { ROOT_ELEMENT_ID, INPUT_BOX_ID, SEARCH_BAR_ID, SEARCH_RESULTS_ID } = require('../../src/config.js'); +// eslint-disable-next-line no-unused-vars const SECONDS = 1000; +// eslint-disable-next-line no-unused-vars +const MINUTES = 60 * SECONDS; + jest.setTimeout(10 * SECONDS); describe('Selenium UI Tests', () => { @@ -60,6 +64,7 @@ describe('Selenium UI Tests', () => { if (driver) { const logs = await driver.manage().logs().get(logging.Type.BROWSER); const extensionPrefix = 'chrome-extension://klljeoabgpehcibnkgcfaipponlgpkfc/'; + // eslint-disable-next-line no-unused-vars const extensionLogs = logs.filter(log => ( log.message.startsWith(extensionPrefix) && log.message.includes('#') )).map(log => log.message.replace(extensionPrefix, '')); @@ -213,11 +218,13 @@ describe('Selenium UI Tests', () => { test('Restores original favicon when empty favicon passed', async () => { await driver.get(googleUrl); - await driver.sleep(1 * SECONDS); + // await driver.sleep(1 * SECONDS); await driverUtils.setFavicon('🎂'); await driverUtils.removeFavicon(); + // await driver.sleep(2 * SECONDS); + // await driver.sleep(20 * MINUTES); await driverUtils.assertFaviconUrl(googleFavicon); - }); + }, 30 * MINUTES); test('Title Preserver keeps the title the same', async () => { // This test is mainly written to emulate what Facebook does (revert the title to its original) @@ -229,4 +236,13 @@ describe('Selenium UI Tests', () => { const actualTitle = await driverUtils.getTitle(); expect(actualTitle).toBe('New title'); }); + + + test("Title won't flash to the original, when switching between pages", async () => { + await driver.get(urls[0]); + await driverUtils.renameTab('New title'); + await driver.sleep(5 * SECONDS); + await driver.get(urls[1]); + await driver.sleep(5 * SECONDS); + }, 100 * SECONDS); });