Chrome扩展开发:跨域Iframe中无法访问Crunchyroll视频元素
问题描述
开发Crunchyroll视频多人同步观看Chrome扩展时,需访问页面内ID为player0的video元素,遇到以下问题:
- 初始代码无法定位动态加载的iframe
- 添加
setTimeout定时检测后成功找到iframe,但触发跨域安全错误:
Uncaught SecurityError: Failed to read a named property 'document' from 'Window': Blocked a frame with origin "https://www.crunchyroll.com" from accessing a frame with origin "https://static.crunchyroll.com". Protocols, domains, and ports must match.
已在manifest.json中启用all_frames配置,问题仍未解决。
现有代码与配置
初始检测代码
console.log("Content script loaded"); let iframe = document.getElementsByClassName('video-player')[0]; console.log("Iframe:", iframe); // Check if iframe exists if (iframe) { // Access the content window of the iframe let iframeContentWindow = iframe.contentWindow; console.log("Iframe content window:", iframeContentWindow); let iframeDocument = iframeContentWindow.document; console.log("Iframe document:", iframeDocument); let videoElement = iframeDocument.getElementById('player0'); console.log("Video element:", videoElement); // Check if video element exists if (videoElement) { // Perform actions on the video element console.log("Current time:", videoElement.currentTime); // Read currentTime property } else { console.log("Video element not found within the iframe."); } } else { console.log("Iframe not found on the page."); }
调整后的定时检测代码
function checkForIframe() { let iframe = document.getElementsByClassName('video-player')[0]; console.log("Iframe:", iframe); if (iframe) { let iframeContentWindow = iframe.contentWindow; console.log("Iframe content window:", iframeContentWindow); let iframeDocument = iframeContentWindow.document; console.log("Iframe document:", iframeDocument); let videoElement = iframeDocument.getElementById('player0'); console.log("Video element:", videoElement); if (videoElement) { console.log("Current time:", videoElement.currentTime); } else { console.log("Video element not found within the iframe."); // If video element not found, check again after a delay setTimeout(checkForIframe, 1000); } } else { console.log("Iframe not found on the page."); // If iframe not found, check again after a delay setTimeout(checkForIframe, 1000); } } checkForIframe();
manifest.json配置
{ "manifest_version": 3, "name": "JWatch", "version": "0.1", "description": "Sync Crunchyroll Video with friends to watch together", "permissions": [ "storage", "activeTab" ], "content_scripts": [ { "matches": ["*://www.crunchyroll.com/*"], "js": ["src/scripts/content.js"], "all_frames": true } ], "host_permissions": [ "*://www.crunchyroll.com/*", "*://static.crunchyroll.com/*" ], "background": { "service_worker": "src/scripts/background.js" }, "action": { "default_popup": "src/html/popup.html" } }
问题原因与解决方案
核心原因
Chrome扩展内容脚本遵循同源策略,即便配置了host_permissions,也无法直接跨域访问不同子域名iframe的contentWindow或document。当前content_scripts仅匹配www.crunchyroll.com,不会注入到static.crunchyroll.com的iframe中,导致无法直接操作内部的video元素。
修复步骤
更新内容脚本匹配规则
修改manifest.json的content_scripts部分,让脚本同时注入到static.crunchyroll.com域名下:"content_scripts": [ { "matches": ["*://www.crunchyroll.com/*", "*://static.crunchyroll.com/*"], "js": ["src/scripts/content.js"], "all_frames": true } ]调整内容脚本逻辑
在脚本中直接判断当前上下文是否为视频播放器iframe,无需从父页面跨域访问:function checkForVideo() { let videoElement = document.getElementById('player0'); console.log("Video element:", videoElement); if (videoElement) { console.log("Current time:", videoElement.currentTime); // 此处添加同步逻辑,如发送时间到后台脚本 } else { console.log("Video element not found, retrying..."); setTimeout(checkForVideo, 1000); } } // 判定当前页面是否为视频播放器iframe if (window.location.hostname === "static.crunchyroll.com" || document.querySelector('.video-player')) { checkForVideo(); }优化DOM检测方式
替换setTimeout轮询为MutationObserver,更高效监听video元素加载:const observer = new MutationObserver((mutations) => { for (const mutation of mutations) { if (mutation.addedNodes.length) { const videoElement = document.getElementById('player0'); if (videoElement) { console.log("Video element found:", videoElement); observer.disconnect(); // 执行同步操作 } } } }); observer.observe(document.body, { childList: true, subtree: true });跨上下文数据传递
若需在父页面与iframe间传递数据,使用Chrome扩展的chrome.runtime.sendMessage和chrome.runtime.onMessageAPI,避免直接跨域操作。
内容的提问来源于stack exchange,提问作者jacob

