如何在 iOS 上注入用户脚本?

发布于 2024-11-28 07:49:32 字数 2465 浏览 1 评论 0原文

我正在开发 Fahrii,它本质上是一个支持用户脚本的移动浏览器。目前,我在调用加载到 UIWebView 的网页上的脚本时遇到问题。如果可能的话,我想保留这个公共 API。如果没有,概念证明至少会很好,这样我可以更好地理解它是如何工作的。我正在尝试在 webViewDidFinishLoad: 方法中执行此操作。

我尝试通过读出 webView 的内容(使用 JavaScript)然后使用 loadHTML:baseURL: 加载它来注入,但这会导致无限递归。 (完成的加载会导致脚本被注入,这又会导致完成的“加载”。)

接下来,我尝试了这样的操作:

    [self.browser stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"document.body.innerHTML = %@", originalHTMLWithScriptsInjected]];

脚本似乎被注入,但没有运行。

我让它工作的唯一方法是使用 ASIHTTPRequest 库发出实际请求,但最终我在登录表单方面遇到了麻烦,除此之外,在这种情况下我必须为每个请求发出两次。

我可能正在做一些不可能的事情,或者我只是做错了。 那么,如何在现有的 UIWebView 上调用从网络加载内容的用户脚本?

编辑:

这里有更多代码,包括@rich 提出的更改:

 //
 //  Run the remaining scripts
 //

NSMutableString *scriptTextToInject = [[NSMutableString alloc]init];

//
//  Add each script into the page
//

for (Userscript *script in scriptsToExecute) {

    //
    //  Read the userscript
    //

    NSString *scriptToLoad = [NSString stringWithContentsOfURL:[NSURL URLWithString:script.pathToScript] encoding:NSUTF8StringEncoding error:&error];

    NSLog(@"Script to load: %@", scriptToLoad);

    [scriptTextToInject appendFormat:@"%@\n\n",scriptToLoad];
}

NSString *documentHeadBefore = [self.browser stringByEvaluatingJavaScriptFromString:@"document.getElementsByTagName('head')[0].innerHTML"];

//
//  Inject the scripts
//

NSString *scriptWrappedInATag = [NSString stringWithFormat:@"window.onload() = function(){var script = document.createElement('script');\n"
                                 "script.charset = 'UTF-8'"
                                 "script.setAttribute(\"type\",\"text/javascript\");\n"
                                 "text = \"function u(){\n"
                                 "%@"
                                 "}\";\n"
                                 "document.getElementsByTagName('head')[0].appendChild(script);}", scriptTextToInject];
NSLog(@"Scripts wrapped in a tag: %@", scriptWrappedInATag);

NSString *runResults = [self.browser stringByEvaluatingJavaScriptFromString:@"u();"];

NSString *documentHeadAfter = [self.browser stringByEvaluatingJavaScriptFromString:@"document.getElementsByTagName('head')[0].innerHTML"];

NSLog(@"Before: %@ \n\n\n\n After: %@", documentHeadBefore, documentHeadAfter);

I'm working on Fahrii, which is, essentially, a userscript enabled mobile browser. At the moment, I'm having trouble invoking the scripts on a web page that's loaded into a UIWebView. I'd like to keep this public APIs if possible. If not, a proof of concept would at least be nice, so I can get a better understanding of how this works. I'm trying to do it in the webViewDidFinishLoad: method.

I've tried injecting by reading out the webView's content (using JavaScript) and then loading it back with loadHTML:baseURL:, but that causes infinite recursion. (A completed load causes a script to be injected, which is in turn causing a completed "load".)

Next, I've tried something like this:

    [self.browser stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"document.body.innerHTML = %@", originalHTMLWithScriptsInjected]];

The script seems to be injected, but not run.

The only way I got this to work was to use the ASIHTTPRequest library to make the actual requests, but then I ended up having trouble with login forms, besides for which, I have to make each request twice in that case.

I might be doing something that's either impossible, or I'm just doing it the wrong away. So, how can I invoke a user script on an existing UIWebView with content loaded from the web?

Edit:

Here's some more code, including the changes proposed by @rich:

 //
 //  Run the remaining scripts
 //

NSMutableString *scriptTextToInject = [[NSMutableString alloc]init];

//
//  Add each script into the page
//

for (Userscript *script in scriptsToExecute) {

    //
    //  Read the userscript
    //

    NSString *scriptToLoad = [NSString stringWithContentsOfURL:[NSURL URLWithString:script.pathToScript] encoding:NSUTF8StringEncoding error:&error];

    NSLog(@"Script to load: %@", scriptToLoad);

    [scriptTextToInject appendFormat:@"%@\n\n",scriptToLoad];
}

NSString *documentHeadBefore = [self.browser stringByEvaluatingJavaScriptFromString:@"document.getElementsByTagName('head')[0].innerHTML"];

//
//  Inject the scripts
//

NSString *scriptWrappedInATag = [NSString stringWithFormat:@"window.onload() = function(){var script = document.createElement('script');\n"
                                 "script.charset = 'UTF-8'"
                                 "script.setAttribute(\"type\",\"text/javascript\");\n"
                                 "text = \"function u(){\n"
                                 "%@"
                                 "}\";\n"
                                 "document.getElementsByTagName('head')[0].appendChild(script);}", scriptTextToInject];
NSLog(@"Scripts wrapped in a tag: %@", scriptWrappedInATag);

NSString *runResults = [self.browser stringByEvaluatingJavaScriptFromString:@"u();"];

NSString *documentHeadAfter = [self.browser stringByEvaluatingJavaScriptFromString:@"document.getElementsByTagName('head')[0].innerHTML"];

NSLog(@"Before: %@ \n\n\n\n After: %@", documentHeadBefore, documentHeadAfter);

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

夏の忆 2024-12-05 07:49:32

下面是执行此操作的一个示例。我也遇到了同样的问题,直到我意识到需要将js注入到页面中。为了正确注入js,你需要将其写入dom的头部,如下所示。

- (void)webViewDidFinishLoad:(UIWebView *)webView {
 [webView stringByEvaluatingJavaScriptFromString:@"var script = document.createElement('script');"  
 "script.type = 'text/javascript';"  
 "script.text = \"function myFunction() { "  
 "window.scrollTo(100,100)"
 "}\";"  
 "document.getElementsByTagName('head')[0].appendChild(script);"];  

 [webView stringByEvaluatingJavaScriptFromString:@"myFunction();"];
}    

An example of doing just this is below. I had the same problem until I realized that the js needed to be injected into the page. To properly inject the js you need to write it into the head of the dom as shown below.

- (void)webViewDidFinishLoad:(UIWebView *)webView {
 [webView stringByEvaluatingJavaScriptFromString:@"var script = document.createElement('script');"  
 "script.type = 'text/javascript';"  
 "script.text = \"function myFunction() { "  
 "window.scrollTo(100,100)"
 "}\";"  
 "document.getElementsByTagName('head')[0].appendChild(script);"];  

 [webView stringByEvaluatingJavaScriptFromString:@"myFunction();"];
}    
瑕疵 2024-12-05 07:49:32

您应该直接将字符串传递给 stringByEvaluatingJavaScriptFromString: 方法,如下所示:

NSString *result = [webView stringByEvaluatingJavaScriptFromString:@"Script To Be Run"];

You should directly pass the string to the stringByEvaluatingJavaScriptFromString: method, like this:

NSString *result = [webView stringByEvaluatingJavaScriptFromString:@"Script To Be Run"];
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文