// content.js console.log("Wii Shop Extension Content Script: Running at document_start."); const runtimeAPI = typeof browser !== 'undefined' ? browser : chrome; // Define storageAPI more robustly let storageAPI; try { storageAPI = runtimeAPI.storage.local; } catch (e) { console.error("Content script: Could not access storage API!", e); // Fallback if storage API is unavailable (e.g., in a non-extension context) storageAPI = { get: async () => ({}), set: async () => {} }; } // --- Font Loading Logic --- function loadFont() { console.log("Wii Shop Extension: Attempting to load custom font."); const fontUrl = runtimeAPI.runtime.getURL("fonts/fot_rodin_pro_m.ttf"); const style = document.createElement('style'); style.textContent = ` @font-face { font-family: 'Wii NTLG PGothic JPN Regular'; src: url('${fontUrl}') format('truetype'); } body, #text03-01, #text05-01, #text06-01, .buttonTextBlackM { font-family: 'Wii NTLG PGothic JPN Regular', sans-serif !important; } #text03-01, #text05-01, #text06-01 { display: flex; justify-content: center; align-items: center; height: 100vh; width: 100%; } .buttonTextBlackM { font-size: 24px; color: black; text-align: center; } `; const head = document.head || document.getElementsByTagName('head')[0]; if (head) { head.appendChild(style); console.log("Wii Shop Extension: Font loaded into ."); } else { // Fallback for when head is not yet available at document_start const observer = new MutationObserver((mutations, obs) => { const currentHead = document.head || document.getElementsByTagName('head')[0]; if (currentHead) { currentHead.appendChild(style); obs.disconnect(); console.log("Wii Shop Extension: Font loaded into via MutationObserver fallback."); } }); observer.observe(document.documentElement || document, { childList: true, subtree: true }); } } // Check the font state and load if not disabled async function checkFontState() { try { const result = await storageAPI.get(['fontDisabled']); if (!result.fontDisabled) { //loadFont(); disabled } else { console.log("Wii Shop Extension: Font loading disabled by user preference."); } } catch (error) { console.error("Wii Shop Extension: Error checking font state:", error); } } // --- Sound and BGM File Paths (Ensure these are correctly mapped to web-accessible resources) --- const soundFilePaths = [ '', // Index 0 (unused) 'audio/1.wav', 'audio/2.wav', 'audio/3.wav', 'audio/4.wav', 'audio/5.wav', 'audio/6.wav', 'audio/7.wav', 'audio/8.wav', 'audio/9.wav', 'audio/10.wav', 'audio/11.wav', 'audio/12.wav', 'audio/13.wav', 'audio/14.wav', 'audio/15.wav', 'audio/16.wav' ]; const bgmFilePath = 'audio/bgm.wav'; // Generate the full, web-accessible URLs. These MUST be defined at the top level. const generatedSoundUrls = soundFilePaths.map(path => path ? runtimeAPI.runtime.getURL(path) : ''); const generatedBgmUrl = runtimeAPI.runtime.getURL(bgmFilePath); // --- Function to load class definitions and inject globals --- const loadClassDefinitions = async () => { const classDefinitionsUrl = runtimeAPI.runtime.getURL('classDefinitions.js'); console.log("Wii Shop Extension: Attempting to fetch classDefinitions.js from:", classDefinitionsUrl); try { const response = await fetch(classDefinitionsUrl); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status} fetching ${classDefinitionsUrl}`); } const classDefinitionsText = await response.text(); console.log("Wii Shop Extension: classDefinitions.js fetched successfully. Length:", classDefinitionsText.length); // Get initial BGM and Font state from storage for globals const storageResult = await storageAPI.get(['bgmEnabled', 'fontDisabled']); const bgmInitiallyEnabled = storageResult.bgmEnabled === true; const fontInitiallyDisabled = storageResult.fontDisabled === true; // Construct the script content to prepend to classDefinitions.js const prependedGlobals = ` (function() { // Ensure these are globally available within the page context window._wiiShopSoundUrls_ = ${JSON.stringify(generatedSoundUrls)}; window._wiiShopBgmUrl_ = ${JSON.stringify(generatedBgmUrl)}; window._bgmInitiallyEnabled = ${JSON.stringify(bgmInitiallyEnabled)}; window._fontInitiallyDisabled = ${JSON.stringify(fontInitiallyDisabled)}; // Define the bridge function for the page context to communicate with content script const _sendMessageToContentScript = (action, payload = {}) => { window.postMessage({ type: 'WiiShopExtension_MessageFromPage', // Unique type for messages from page to content script action: action, // The specific action like 'getStorage', 'playBGM', etc. payload: payload }, window.location.origin); }; window.WiiShopExtension_sendMessage = _sendMessageToContentScript; // Expose globally for page's use // Redefine/Expose extensionStorage if it's used by original page code or other injected scripts // This makes sure get/set calls from page go through our background script window.extensionStorage = { get: async function(key) { const response = await new Promise(resolve => { _sendMessageToContentScript('getStorage', { key: key }); // Listen for the response back from the content script const listener = (event) => { if (event.source === window && event.data && event.data.type === 'WiiShopExtension_ResponseToPage' && event.data.action === 'getStorage' && event.data.key === key) { window.removeEventListener('message', listener); resolve(event.data.payload.value); // Extract the value } }; window.addEventListener('message', listener); }); return response; }, set: async function(key, value) { await _sendMessageToContentScript('setStorage', { key: key, value: value }); }, slog: async function(message) { await _sendMessageToContentScript('setStorage', { key: 'slog', value: message }); }, spts: async function(points) { await _sendMessageToContentScript('setStorage', { key: 'spts', value: points }); }, clearLogs: async function() { await _sendMessageToContentScript('clearLogs'); }, clearPoints: async function() { await _sendMessageToContentScript('clearPoints'); }, playBGM: async function(url) { await _sendMessageToContentScript('playBGM', { url: url }); }, stopBGM: async function() { await _sendMessageToContentScript('stopBGM'); } }; })(); `; const scriptToInject = document.createElement('script'); scriptToInject.setAttribute('type', 'text/javascript'); scriptToInject.setAttribute('data-injected-id', 'wii-shop-cdjs'); scriptToInject.textContent = prependedGlobals + classDefinitionsText; let targetElement = document.head || document.documentElement; if (targetElement && !document.querySelector('[data-injected-id="wii-shop-cdjs"]')) { targetElement.insertBefore(scriptToInject, targetElement.firstChild); console.log("Wii Shop Extension: classDefinitions.js injected into page context."); setTimeout(() => { if (scriptToInject.parentNode) { scriptToInject.parentNode.removeChild(scriptToInject); console.log("Wii Shop Extension: Injected classDefinitions.js script removed from DOM."); } }, 100); } else { console.warn("Wii Shop Extension: classDefinitions.js already injected or target element (head/html) not found."); } } catch (error) { console.error("Wii Shop Extension: Failed to load class definitions:", error); } }; // --- Message Listener (from injected page script to content script) --- window.addEventListener('message', async (event) => { if (event.source !== window || !event.data || event.data.type !== 'WiiShopExtension_MessageFromPage') { return; } const { action, payload } = event.data; console.log("Content script received message from page:", action, payload); try { let responsePayload; switch (action) { case 'getStorage': responsePayload = await runtimeAPI.runtime.sendMessage({ action: 'getStorage', key: payload.key }); window.postMessage({ type: 'WiiShopExtension_ResponseToPage', action: 'getStorage', key: payload.key, payload: responsePayload }, event.origin); break; case 'setStorage': await runtimeAPI.runtime.sendMessage({ action: 'setStorage', key: payload.key, value: payload.value }); break; case 'clearLogs': await runtimeAPI.runtime.sendMessage({ action: 'clearLogs' }); break; case 'clearPoints': await runtimeAPI.runtime.sendMessage({ action: 'clearPoints' }); break; case 'playBGM': await runtimeAPI.runtime.sendMessage({ action: 'playBGM', url: payload.url }); break; case 'stopBGM': await runtimeAPI.runtime.sendMessage({ action: 'stopBGM' }); break; case 'setBGMEnabled': await runtimeAPI.runtime.sendMessage({ action: 'setBGMEnabled', enabled: payload.enabled }); break; case 'getBGMEnabled': responsePayload = await runtimeAPI.runtime.sendMessage({ action: 'getBGMEnabled' }); window.postMessage({ type: 'WiiShopExtension_ResponseToPage', action: 'getBGMEnabled', payload: responsePayload }, event.origin); break; case 'setFontDisabled': await runtimeAPI.runtime.sendMessage({ action: 'setFontDisabled', disabled: payload.disabled }); break; case 'getFontDisabled': responsePayload = await runtimeAPI.runtime.sendMessage({ action: 'getFontDisabled' }); window.postMessage({ type: 'WiiShopExtension_ResponseToPage', action: 'getFontDisabled', payload: responsePayload }, event.origin); break; // Removed: case 'injectedScriptReady' as content.js no longer waits for it explicitly. default: console.warn("Content script: Unhandled message from page:", action, payload); } } catch (error) { console.error(`Content script error handling action "${action}":`, error); if (action === 'getStorage' || action === 'getBGMEnabled' || action === 'getFontDisabled') { window.postMessage({ type: 'WiiShopExtension_ResponseToPage', action: action, key: payload ? payload.key : undefined, error: error.message }, event.origin); } } }, false); // --- Main Initialization --- // Load the class definitions immediately at document_start. loadClassDefinitions(); checkFontState(); // Check and load font if enabled. // Removed: The specific window.onload listener for debug.jsp // The debug.jsp page will now execute its original onload attribute. // --- Listener for messages from the popup (if any) --- runtimeAPI.runtime.onMessage.addListener((request, sender, sendResponse) => { if (request.action === "toggleBGMImmediate") { if (request.enabled) { console.log("Content: Received toggleBGMImmediate from popup: Play BGM."); runtimeAPI.runtime.sendMessage({ action: "playBGM", url: generatedBgmUrl }).catch(error => console.error("Content: Error sending playBGM (immediate) to background:", error)); } else { console.log("Content: Received toggleBGMImmediate from popup: Stop BGM."); runtimeAPI.runtime.sendMessage({ action: "stopBGM" }).catch(error => console.error("Content: Error sending stopBGM (immediate) to background:", error)); } } });