如何在 iOS 上注入用户脚本?
我正在开发 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
下面是执行此操作的一个示例。我也遇到了同样的问题,直到我意识到需要将js注入到页面中。为了正确注入js,你需要将其写入dom的头部,如下所示。
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.
您应该直接将字符串传递给 stringByEvaluatingJavaScriptFromString: 方法,如下所示:
You should directly pass the string to the stringByEvaluatingJavaScriptFromString: method, like this: