0

我正在开发一个firefox扩展使用webExtensions,这将帮助我放松我的工作与下面的情况。从浏览器扩展调用网页JavaScript方法

我必须点击网站上的50-60个按钮来更新任务状态。点击此按钮后,网页正在调用网页的updTask(id) JavaScript函数,然后进行网络服务调用以更新任务。

我不能够使用下面的代码从我的内容脚本做到这一点:

manifest.json的

"permissions": [ 
    "activeTab", 
    "cross-domain-content": ["http://workdomain.com/","http://workdomain.org/","http://www.workdomain.com/","http://www.workdomain.org/"] 
    ] 

内容脚本代码:

function taskUpdate(request, sender, sendResponse) { 
    console.log(request.start + 'inside task update'); 
    updateTask(45878); 
    chrome.runtime.onMessage.removeListener(taskUpdate); 
} 

function updateTask(id) { 
    //TODO: code to get all buttons and task id's 
    updTask(id); // Not working 
} 

插件Script:

document.addEventListener("click", function(e) { 
    if (e.target.classList.contains("startButton")) { 

    chrome.tabs.executeScript(null, { 
     file: "/content_scripts/taskUpdate.js" 
    }); 

    chrome.tabs.query({active: true, currentWindow: true}, function(tabs) { 
     chrome.tabs.sendMessage(tabs[0].id, {start: "start"}); 
    }); 
    return; 
    } 
    else if (e.target.classList.contains("clear")) { 
    chrome.tabs.reload(); 
    window.close(); 
    return; 
    } 
}); 

有人能指出我在正确的方向,我在这里错过了什么?

+0

[在Chrome扩展不工作jQuery的关()]的可能的复制(http://stackoverflow.com/questions/40320529/jquery-off-not-working-in-chrome-extension) – Makyen

+0

高度相关: https://stackoverflow.com/questions/9515704/insert-code-into-the-page-context-using-a-content-script – Xan

回答

2

您的内容脚本与页面脚本(网页中已存在的脚本)的脚本位于不同的上下文/范围内。您的内容脚本具有比授予页面脚本更高的权限。将内容脚本与页面脚本分开是浏览器扩展的常规体系结构,这是出于安全原因而完成的。

为了在页面脚本上下文中执行代码,您已经创建并在页面的DOM中插入了一个<script>元素。

你可以这样做:

function updateTask(id) { 
    let newScript = document.createElement('script'); 
    newScript.innerHTML='updTask(' + id + ');'; 
    document.head.appendChild(newScript); 
    //newScript.remove(); //Can be removed, if desired. 
} 

添加的脚本获取运行在页面上下文,因为它现在是DOM中的<script>元素。浏览器识别出<script>元素已被添加,并在插入该元素的脚本不再处理时立即对其进行评估(执行包含的代码)。它对添加到DOM的任何其他元素基本上都是一样的。由于它是页面的一部分,get内部的代码在页面脚本上下文/作用域中运行。

广义代码在页面上下文从内容脚本

保持你会在页面的上下文是把它写在你的内容脚本函数执行代码的最简单的方法执行,再注入该功能进入页面上下文。下面是一些通用代码,它可以在将参数传递给在页面上下文中执行的函数时执行此操作:

此实用程序函数executeInPage()将在页面上下文中执行一个函数,并将所提供的任何参数传递给该函数。参数必须是ObjectArrayfunctionRegExpDate,和/或其它primitivesBooleannullundefinedNumberString,但不是Symbol)。

/* executeInPage takes a function defined in this context, converts it to a string 
* and inserts it into the page context inside a <script>. It is placed in an IIFE and 
* passed all of the additional parameters passed to executeInPage. 
* Parameters: 
* func   The function which you desire to execute in the page. 
* leaveInPage If this does not evaluate to a truthy value, then the <script> is 
*     immediately removed from the page after insertion. Immediately 
*     removing the script can normally be done. In some corner cases, 
*     it's desirable for the script to remain in the page. However, 
*     even for asynchronous functionality it's usually not necessary, as 
*     the context containing the code will be kept with any references 
*     (e.g. the reference to a callback function). 
* id   If this is a non-blank string, it is used as the ID for the <script> 
* All additional parameters are passed to the function executing in the page. 
*     This is done by converting them to JavaScript code-text and back. 
*     All such parameters must be Object, Array, functions, RegExp, 
*     Date, and/or other primitives (Boolean, null, undefined, Number, 
*     String, but not Symbol). Circular references are not supported. 
*     If you need to communicate DOM elements, you will need to 
*     pass selectors, or other descriptors of them (e.g. temporarily 
*     assign them a unique class), or otherwise communicate them to the 
*     script (e.g. you could dispatch a custom event once the script is 
*     inserted into the page context). 
*/ 
function executeInPage(functionToRunInPage, leaveInPage, id) { 
    //Execute a function in the page context. 
    // Any additional arguments passed to this function are passed into the page to the 
    // functionToRunInPage. 
    // Such arguments must be JSON-ifiable (also Date, Function, and RegExp) (prototypes 
    // are not copied). 
    // Using() => doesn't set arguments, so can't use it to define this function. 
    // This has to be done without jQuery, as jQuery creates the script 
    // within this context, not the page context, which results in 
    // permission denied to run the function. 
    function convertToText(args) { 
     //This uses the fact that the arguments are converted to text which is 
     // interpreted within a <script>. That means we can create other types of 
     // objects by recreating their normal JavaScript representation. 
     // It's actually easier to do this without JSON.strigify() for the whole 
     // Object/Array. 
     var asText = ''; 
     var level = 0; 
     function lineSeparator(adj, isntLast) { 
      level += adj - ((typeof isntLast === 'undefined' || isntLast) ? 0 : 1); 
      asText += (isntLast ? ',' : '') +'\n'+ (new Array(level * 2 + 1)).join(''); 
     } 
     function recurseObject(obj) { 
      if (Array.isArray(obj)) { 
       asText += '['; 
       lineSeparator(1); 
       obj.forEach(function(value, index, array) { 
        recurseObject(value); 
        lineSeparator(0, index !== array.length - 1); 
       }); 
       asText += ']'; 
      } else if (obj === null) { 
       asText +='null'; 
      //undefined 
      } else if (obj === void(0)) { 
       asText +='void(0)'; 
      //Special cases for Number 
      } else if (Number.isNaN(obj)) { 
       asText +='Number.NaN'; 
      } else if (obj === 1/0) { 
       asText +='1/0'; 
      } else if (obj === 1/-0) { 
       asText +='1/-0'; 
      //function 
      } else if (obj instanceof RegExp || typeof obj === 'function') { 
       asText += obj.toString(); 
      } else if (obj instanceof Date) { 
       asText += 'new Date("' + obj.toJSON() + '")'; 
      } else if (typeof obj === 'object') { 
       asText += '{'; 
       lineSeparator(1); 
       Object.keys(obj).forEach(function(prop, index, array) { 
        asText += JSON.stringify(prop) + ': '; 
        recurseObject(obj[prop]); 
        lineSeparator(0, index !== array.length - 1); 
       }); 
       asText += '}'; 
      } else if (['boolean', 'number', 'string'].indexOf(typeof obj) > -1) { 
       asText += JSON.stringify(obj); 
      } else { 
       console.log('Didn\'t handle: typeof obj:', typeof obj, ':: obj:', obj); 
      } 
     } 
     recurseObject(args); 
     return asText; 
    } 
    var newScript = document.createElement('script'); 
    if(typeof id === 'string' && id) { 
     newScript.id = id; 
    } 
    var args = []; 
    //using .slice(), or other Array methods, on arguments prevents optimization 
    for(var index=3;index<arguments.length;index++){ 
     args.push(arguments[index]); 
    } 
    newScript.textContent = '(' + functionToRunInPage.toString() + ').apply(null,' 
          + convertToText(args) + ");"; 
    (document.head || document.documentElement).appendChild(newScript); 
    if(!leaveInPage) { 
     //Synchronous scripts are executed immediately and can be immediately removed. 
     //Scripts with asynchronous functionality of any type must remain in the page 
     // until complete. 
     document.head.removeChild(newScript); 
    } 
    return newScript; 
}; 

使用excuteInPage()

function logInPageContext(arg0,arg1,arg2,arg3){ 
 
    console.log('arg0:', arg0); 
 
    console.log('arg1:', arg1); 
 
    console.log('arg2:', arg2); 
 
    console.log('arg3:', arg3); 
 
} 
 

 
executeInPage(logInPageContext, false, '', 'This', 'is', 'a', 'test'); 
 

 

 
/* executeInPage takes a function defined in this context, converts it to a string 
 
* and inserts it into the page context inside a <script>. It is placed in an IIFE and 
 
* passed all of the additional parameters passed to executeInPage. 
 
* Parameters: 
 
* func   The function which you desire to execute in the page. 
 
* leaveInPage If this does not evaluate to a truthy value, then the <script> is 
 
*     immediately removed from the page after insertion. Immediately 
 
*     removing the script can normally be done. In some corner cases, 
 
*     it's desirable for the script to remain in the page. However, 
 
*     even for asynchronous functionality it's usually not necessary, as 
 
*     the context containing the code will be kept with any references 
 
*     (e.g. the reference to a callback function). 
 
* id   If this is a non-blank string, it is used as the ID for the <script> 
 
* All additional parameters are passed to the function executing in the page. 
 
*     This is done by converting them to JavaScript code-text and back. 
 
*     All such parameters must be Object, Array, functions, RegExp, 
 
*     Date, and/or other primitives (Boolean, null, undefined, Number, 
 
*     String, but not Symbol). Circular references are not supported. 
 
*     If you need to communicate DOM elements, you will need to 
 
*     pass selectors, or other descriptors of them (e.g. temporarily 
 
*     assign them a unique class), or otherwise communicate them to the 
 
*     script (e.g. you could dispatch a custom event once the script is 
 
*     inserted into the page context). 
 
*/ 
 
function executeInPage(functionToRunInPage, leaveInPage, id) { 
 
    //Execute a function in the page context. 
 
    // Any additional arguments passed to this function are passed into the page to the 
 
    // functionToRunInPage. 
 
    // Such arguments must be JSON-ifiable (also Date, Function, and RegExp) (prototypes 
 
    // are not copied). 
 
    // Using() => doesn't set arguments, so can't use it to define this function. 
 
    // This has to be done without jQuery, as jQuery creates the script 
 
    // within this context, not the page context, which results in 
 
    // permission denied to run the function. 
 
    function convertToText(args) { 
 
     //This uses the fact that the arguments are converted to text which is 
 
     // interpreted within a <script>. That means we can create other types of 
 
     // objects by recreating their normal JavaScript representation. 
 
     // It's actually easier to do this without JSON.strigify() for the whole 
 
     // Object/Array. 
 
     var asText = ''; 
 
     var level = 0; 
 
     function lineSeparator(adj, isntLast) { 
 
      level += adj - ((typeof isntLast === 'undefined' || isntLast) ? 0 : 1); 
 
      asText += (isntLast ? ',' : '') +'\n'+ (new Array(level * 2 + 1)).join(''); 
 
     } 
 
     function recurseObject(obj) { 
 
      if (Array.isArray(obj)) { 
 
       asText += '['; 
 
       lineSeparator(1); 
 
       obj.forEach(function(value, index, array) { 
 
        recurseObject(value); 
 
        lineSeparator(0, index !== array.length - 1); 
 
       }); 
 
       asText += ']'; 
 
      } else if (obj === null) { 
 
       asText +='null'; 
 
      //undefined 
 
      } else if (obj === void(0)) { 
 
       asText +='void(0)'; 
 
      //Special cases for Number 
 
      } else if (Number.isNaN(obj)) { 
 
       asText +='Number.NaN'; 
 
      } else if (obj === 1/0) { 
 
       asText +='1/0'; 
 
      } else if (obj === 1/-0) { 
 
       asText +='1/-0'; 
 
      //function 
 
      } else if (obj instanceof RegExp || typeof obj === 'function') { 
 
       asText += obj.toString(); 
 
      } else if (obj instanceof Date) { 
 
       asText += 'new Date("' + obj.toJSON() + '")'; 
 
      } else if (typeof obj === 'object') { 
 
       asText += '{'; 
 
       lineSeparator(1); 
 
       Object.keys(obj).forEach(function(prop, index, array) { 
 
        asText += JSON.stringify(prop) + ': '; 
 
        recurseObject(obj[prop]); 
 
        lineSeparator(0, index !== array.length - 1); 
 
       }); 
 
       asText += '}'; 
 
      } else if (['boolean', 'number', 'string'].indexOf(typeof obj) > -1) { 
 
       asText += JSON.stringify(obj); 
 
      } else { 
 
       console.log('Didn\'t handle: typeof obj:', typeof obj, ':: obj:', obj); 
 
      } 
 
     } 
 
     recurseObject(args); 
 
     return asText; 
 
    } 
 
    var newScript = document.createElement('script'); 
 
    if(typeof id === 'string' && id) { 
 
     newScript.id = id; 
 
    } 
 
    var args = []; 
 
    //using .slice(), or other Array methods, on arguments prevents optimization 
 
    for(var index=3;index<arguments.length;index++){ 
 
     args.push(arguments[index]); 
 
    } 
 
    newScript.textContent = '(' + functionToRunInPage.toString() + ').apply(null,' 
 
          + convertToText(args) + ");"; 
 
    (document.head || document.documentElement).appendChild(newScript); 
 
    if(!leaveInPage) { 
 
     //Synchronous scripts are executed immediately and can be immediately removed. 
 
     //Scripts with asynchronous functionality of any type must remain in the page 
 
     // until complete. 
 
     document.head.removeChild(newScript); 
 
    } 
 
    return newScript; 
 
};

此答案的文字主要是从我的其他答案采取:this onethis one

+0

感谢您的答案...我只是想知道我如何访问控件在后台/弹出页面的扩展? – Hitin

+0

我不确定你的问题的背景。你想要访问后台脚本中的控件和/或从内容脚本中弹出吗?内容脚本和后台页面之间进行通信的唯一方法是通过消息传递。您应该阅读[内容脚本:与后台脚本通信](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Content_scripts#Communicating_with_background_scripts) – Makyen

+0

我的意思是popup.html中的控件(我在那里为插件定义UI)。我将把第一个任务的ID放在文本框中,然后在按钮上单击开始任务,带ID的按钮就是开始点。 – Hitin

相关问题