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

Posted

技术标签:

【中文标题】如何在 iOS 上注入用户脚本?【英文标题】:How can I inject a user script on iOS? 【发布时间】:2011-10-21 05:44:11 【问题描述】:

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

我尝试通过读取 webView 的内容(使用 javascript)进行注入,然后使用 loadhtml:baseURL: 将其加载回来,但这会导致无限递归。 (完成的加载会导致脚本被注入,这反过来又会导致完成的“加载”。)

接下来,我尝试了这样的事情:

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

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

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

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

编辑:

这里还有一些代码,包括@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);

【问题讨论】:

-stringByEvaluatingJavaScriptFromString: 是在 webview 上调用 javascript 的正确方法,但看起来你不能这样设置 document.body.innerHTML。我猜苹果希望你用 -loadHTMLString:baseURL: 方法来做到这一点。但是请描述您想要做什么,或者向我们展示您想要运行的 javascript 代码。 这个想法是将用户脚本注入页面。我成功的唯一方法是使用单独的请求并将响应手动加载到 WebView 中。 当心不要将该编辑视为“更多代码”以外的任何内容(即“包括@rich 提出的更改”可能会产生误导) 1) 为什么将它包含在window.onload 中?我认为这暗示了一个时间问题(没有一个)2)无论如何,这不是你将某些东西分配给window.onload的方式(额外的括号)3)放置在scriptWrappedInATag中的代码永远不会被调用/传递给@987654329 @.*直接跳到下面富人的回答* 【参考方案1】:

下面是一个这样做的例子。在意识到需要将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();"];
    

【讨论】:

这是否规避了 Apple 对 UIWebView 施加的 10 秒执行时间限制? 我已经在几个应用程序中使用过它,它在页面加载之前执行,所以按照 Apple 标准去是很好的 有趣,谢谢!我会检查一下,然后告诉你进展如何。 您确定将 webView 委托设置为 self 吗?在这种情况下,这是一个常见的事故 是的,代码正在运行,但它不工作。该脚本似乎没有正确注入。【参考方案2】:

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

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

【讨论】:

以上是关于如何在 iOS 上注入用户脚本?的主要内容,如果未能解决你的问题,请参考以下文章

如何将用户脚本注入嵌入式网页内容?

XSS攻击

XSS跨站脚本总结

XSS跨站脚本攻击

在网页上的所有 iframe 中注入 iframe 的内容脚本

防止 Javascript 中的 HTML 和脚本注入