Monitor ajax calls from IE BHO - javascript

I'm trying to find a way to detect changes on a page from a BHO. For the Firefox version of the plugin I'm using DomNodeInserted, but this isn't available in IE. I've also looked at using onpropertychange, but this only allows you to monitor a single element (not children).
I'm now wondering if it's possible to monitor when AJAX requests are called. If so, I can make the changes to the page after the AJAX request has completed. Only problem is, I can't find a way to do it.
Here's my attempt so far, based on jeffamaphone's suggestions, it doesn't work but maybe it'll jog someone's memory.
public class ChangeMonitor : IHTMLChangeSink {
public void Notify()
{
Trace.WriteLine("notified");
}
}
void registerMonitor()
{
HTMLDocument document = _webBrowser2.Document;
ChangeMonitor monitor = new ChangeMonitor();
IHTMLChangeSink changeSink = monitor;
IHTMLChangeLog changeLog = null;
((IMarkupContainer2)document).CreateChangeLog(changeSink, out changeLog, 1, 1);
}

For IE, onreadystatechange will work as an equivalent to DomNodeInserted. A DHTML behavior must be attached to the element via htc, but the htc file does not have to exist:
document.documentElement.addBehavior("foo.htc");
document.documentElement.attachEvent("onreadystatechange", Notify);
The BHO can inject the script to handle the event.

I've never done it, but my theory is you can use IMarkupContainer2::CreateChangeLog().

Related

GWT code splitting synchronous request

I´m using code splitting in GWT to reduce the size of the initial JavaScript.
While my application initializes, I want to prefetch the other (bigger) part of my code as explained in the docs (www.gwtproject.org/doc/latest/DevGuideCodeSplitting.html).
private void doSth(final boolean prefetch) {
GWT.runAsync(new RunAsyncCallback() {
public void onFailure(Throwable caught) {
Log.error("Loading the code failed!");
}
public void onSuccess() {
if(prefetch)
return; //do nothing. just a prefetch
//here is the loaded code
}
});
}
But I cannot recognize a performance improvement. As I analyzed the browser logs, I recognized, that the request for loading the JavaScript is not marked as XHR. Does GWT load the code of a split point synchronously?
The performance improvement is in the initial downloaded code, assuming nothing else references that code. If anything else does the work of //here is the loaded code, then there will be either very little or no code to break out into a separately downloaded JS file.
This feature can be disabled in several ways, including by using dev mode or setting a compiler flag to skip this process. In this case, yes, the split point runs synchronously, since it makes no sense to wait. Additionally, after the file has been loaded once, it does not need to be loaded the next time the code is invoked within the same page load.
If your server is set to cache correctly, then after the first visit the savings is even smaller since there is no download to do - you only save the time taken to parse that code into the browser's JS VM.
But beyond that, we're going to need more information.
Here's a quick demo showing how the split point can be written with a little more meat to it, and letting you use your browser to notice how the split point code was brought in separately.
public class SampleEntryPoint implements EntryPoint {
public void onModuleLoad() {
Label label = new Label("Hello, World!");
label.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
GWT.runAsync(new RunAsyncCallback() {
public void onFailure(Throwable var1) {/*ignore*/}
public void onSuccess() {
Window.alert("Clicked, and loaded in split point!");
}
});
}
});
RootPanel.get().add(label);
}
}
Code and sample:
https://viola.colinalworth.com/proj/755e224e7f48a047703d44eb6903d926/project/client/SampleEntryPoint.java
Standalone sample:
https://viola.colinalworth.com:444/compiled/755e224e7f48a047703d44eb6903f76c/
When you load this page, the nocache file loads, as does the initial download (as seen through Chrome's inspector's Network tab):
Then, when you click the Label widget, the onClick fires which triggers the runAsync and downloads the extra split point (plus the "leftover" fragment):
After those two new entries have been added to your Network tab, you see the alert message appear. Subsequent clicks do not result in this slight delay, nor do they force this extra JS to download again.
Also note that these are not loaded as AJAX/XHR calls, but as a script tag to be added to the page. Clicking on the details in the Initiator column (not pictured) leads to this (formatted for readability):
function fb(a) {
var b, c, d;
d = (bb(), window);
b = d.document;
c = b.createElement('script');
(!!a.a || a.b) && cb(c, a.a, a.b);
eb(c, a.c);
(b.head || b.getElementsByTagName('head')[0]).appendChild(c);
return c
}
Getting through the obfuscated code, we see that a <script> tag is created, and appended to the <head> of the page.
Digging in deeper, we can find that the AsyncFragmentLoader.LoadingStrategy interface describes how to go get this fragment, and that com/google/gwt/core/AsyncFragmentLoader.gwt.xml wires this by default to XhrLoadingStrategy. However, both the xs and xsiframe linkers change this, to CrossSiteLoadingStrategy and ScriptTagLoadingStrategy respectively. And as of recent versions of GWT (you didn't specify, so I'm assuming you are using a recent version), the xsiframe linker is the default. From Core.gwt.xml:
<add-linker name="xsiframe" />
We can customize this by switching to the old linker, or just replacing the strategy. Note that switching the an XHR strategy will prevent cross-domain loading from working correctly (such as SuperDevMode), so be careful with this.
Much as AsyncFragmentLoader.gwt.xml wired the interface to XhrLoadingStrategy, and CrossSiteIframeLinker.gwt.xml changed it to ScriptTagLoadingStrategy, we can change it back. We create a rule that replaces LoadingStrategy with XhrLoadingStrategy, and list it after our GWT inherits statements in our .gwt.xml file:
<replace-with class="com.google.gwt.core.client.impl.XhrLoadingStrategy">
<when-type-is class="com.google.gwt.core.client.impl.AsyncFragmentLoader.LoadingStrategy" />
</replace-with>
This is what the old default used to rely on as part of the std linker (com.google.gwt.core.linker.IFrameLinker), though this is no longer encouraged and may be removed in a later release.

OnContextCreated() in Cef not being called

I have a similar problem to the person in this post; I'm trying to extend the cefsimple.exe app included with the chromium embedded framework binaries to include a V8 handler. I implemented the OnContextCreated() method and made sure to extend RenderProcessHandler in the SimpleHandler class. I'm trying to implement a simple window bound variable called test_string; here's what my code looks like;
void SimpleHandler::OnContextCreated(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context)
{
CefRefPtr<CefV8Value> object = context->GetGlobal();
object->SetValue("test_string", CefV8Value::CreateString("this is a test"), V8_PROPERTY_ATTRIBUTE_NONE);
}
But the program never arrives at any breakpoints I add within the method, and the variable is undefined on any webpages I load within the app. I saw that one of the solutions in the other thread is to enable the settings.single_process flag, which i've done, but my code still doesn't reach the breakpoint.
To be clear, I'm accessing the variable on pages with window.test_string.
Make sure that you are sending that CefApp to CefExecuteProcess.
CefRefPtr<SimpleApp> app(new SimpleApp);
// CEF applications have multiple sub-processes (render, plugin, GPU, etc)
// that share the same executable. This function checks the command-line and,
// if this is a sub-process, executes the appropriate logic.
int exit_code = CefExecuteProcess(main_args, app, sandbox_info);
if (exit_code >= 0) {
// The sub-process has completed so return here.
return exit_code;
}
Found this solution here
Have you read through the General Usage guide? Some key points below
https://bitbucket.org/chromiumembedded/cef/wiki/GeneralUsage#markdown-header-cefapp
https://bitbucket.org/chromiumembedded/cef/wiki/GeneralUsage#markdown-header-processes
The single_process mode is not supported so I've never used it. In general I'd avoid it. The multi process architecture means you need to attach the debugger to the process. The Chromium guide is relevant to CEF in this instance.
https://www.chromium.org/developers/how-tos/debugging-on-windows#TOC-Attaching-to-the-renderer
you need to ensure your App is derived from CefRenderProcessHandler
not SimpleHandler!!!
class SimpleApp : public CefApp
, public CefRenderProcessHandler
{
virtual void OnContextCreated(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) OVERRIDE;
valdemar-rudolfovich says you need to pass instance of SimpleApp in
CefExecuteProcess

GWT - "Access is Denied" JavaScript error when setting document.domain in Internet Explorer only

Background Information
I am working on a web application that utilizes GWT (v2.4). For the application, I am creating an iframe that will display some information from another website. I need to access some information from that iframe that is normally restricted via the Same Origin Policy (SOP). However, both sites (the parent and iframe) are hosted on the same super-domain, just under different sub-domains. So, something like this:
Parent: dev.app.mySite.com
frame: someOtherContent.mySite.com
I know the usual solution for this problem is to set the property: document.domain = 'mySite.com' on both parent and iframe site to allow passage of SOP. This works for all browsers (that I'm concerned with) except Internet Explorer 8 (and probably other versions).
The Problem
In IE, when I attempt to load my web application, I get a completely blank page with the following JS exception, "Access is denied." The source of this problem is in GWT's myGwtAppName.nochache.js where GWT generates some code during the compilation process that it needs (see below).
From the research I've done on this problem in general, the root cause of this issue seems to be that in IE, unlike all other browsers, iframes don't inherit their parent's document.domain settings. From what I understand, the code generated by GWT runs in an iframe (based on this comment: https://stackoverflow.com/a/5161888). So, what I think is happening based on my limited knowledge of JS:
I set document.domain = 'mySite.com' in the parent index page via JS and it is processed.
myGwtAppName.nochache.js is processed.
In nochache.js, code is ran to setup the GWT iframe sand-box environment
In that code, a call is being made to a SOP restricted property of the sand-box iframe
An exception is thrown because the site's parent document domain has been set to 'mySite.com' and the iframe's document.domain doesn't inherit that setting, so it's still 'dev.app.mySite.com'. This won't pass SOP because the domain has to be exactly the same.
The generated code that causes the exception
The below code, looks like it's setting up the GWT sandbox iframe environment.
var $intern_4 = 'myGwtAppName',
$intern_7 = 'body',
$intern_8 = 'iframe',
$intern_9 = 'javascript:""',
$intern_10 = 'position:absolute; width:0; height:0; border:none; left: -1000px; top: -1000px; !important',
$intern_11 = '<html><head><\/head><body><\/body><\/html>',
$intern_12 = 'script',
$intern_13 = 'javascript',
$intern_14 = 'var $wnd = window.parent;''
....
....
function setupInstallLocation(){
if (frameDoc) {
return;
}
var scriptFrame = $doc.createElement($intern_8);
scriptFrame.src = $intern_9;
scriptFrame.id = $intern_4;
scriptFrame.style.cssText = $intern_10;
scriptFrame.tabIndex = -1;
$doc.body.appendChild(scriptFrame);
frameDoc = scriptFrame.contentDocument;
if (!frameDoc) {
frameDoc = scriptFrame.contentWindow.document; //THIS CAUSES THE EXCEPTION
}
frameDoc.open();
frameDoc.write($intern_11);
frameDoc.close();
var frameDocbody = frameDoc.getElementsByTagName($intern_7)[0];
var script = frameDoc.createElement($intern_12);
script.language = $intern_13;
var temp = $intern_14;
script.text = temp;
frameDocbody.appendChild(script);
}
....
....
My Questions
Is my analysis of the situation completely off-base?
Has anyone seen a solution for this problem that will work in a GWT environment in IE?
Information Sources
IE doesn't inherit document.domain settings: https://stackoverflow.com/a/1888711 (and many other threads).
GWT runs in an iframe sand-box environment: https://stackoverflow.com/a/5161888
You may use html5 web messaging to communicate between iframe and parent.
Be aware that Internet Explorer has following bugs. You can send only string as messages. You can't send object like other browser support.
Some people advice to encode object into JSON if you wish to send more then just a string but sometimes it is cheaper to send URL encoded string just like query string in URL.
Here are examples 2 top results from my google
http://tutorials.jenkov.com/html5/messaging.html
https://thenewcircle.com/bookshelf/html5_tutorial/messaging.html
Take a look that they use different code to listen for messages
window.attachEvent("onmessage", handleMessage);
window.addEventListener("message", handleMessage, true);
First works with IE and old Opera and last works with a rest of world.
I've run into the same issue and have found no elegant solution, but...
Right now I have an ant task that manually alters 3 specific points in the GWT nocache.js file after compilation to workaround the issue. You have to use regular expressions to make sure the injected code can reference a couple specific variables in the obfuscated code. It's all terribly ugly...
If you've found a more elegant solution please do post, since my solution is a hack. Details below...
Note - This assumes you have compiled GWT in "PRETTY" mode, since that's the only way I could reference variable/method names.
The problem we really need to solve here is that IE does not inherit altered document.domain values. So IFrames will have invalid document.domains. You can, however, force an IFrame to set its own document.domain, so that it is in sync with the outer page. However - this method requires letting the IFrame load and execute first. Which means that further operations must be executed in a callback after the iframe has loaded.
1) You need to add the follow two methods to the gwt .js file:
function waitForSetupInstallLocation(callback){
if (frameDoc) {
return;
}
var scriptFrame = $doc_0.createElement('iframe');
scriptFrame.src="javascript:document.open();document.domain='<domainvalue>';document.close();document.body.contentEditable=true;";
scriptFrame.id = '<MyWidgetSetName>';
scriptFrame.style.cssText = 'position:absolute; width:0; height:0; border:none; left: -1000px;' + ' top: -1000px;';
scriptFrame.tabIndex = -1;
$doc_0.body.appendChild(scriptFrame);
var addedContent = false;
try {
setFrameDoc(scriptFrame);
callback();
}
catch(e){
scriptFrame.onload = function(){
if(!addedContent){
addedContent = true;
setFrameDoc(scriptFrame);
callback();
}
};
}
}
function setFrameDoc(scriptFrame){
frameDoc = scriptFrame.contentDocument;
if (!frameDoc) {
frameDoc = scriptFrame.contentWindow.document;
}
frameDoc.open();
var doctype = document.compatMode == 'CSS1Compat'?'<!doctype html>':'';
frameDoc.write(doctype + '<html><head><\/head><body><\/body><\/html>');
frameDoc.close();
}
These two methods allow GWT to inject code into the page while also waiting for IE to load an IFRAME which then changes its own document.domain. You can see that the first accepts a callback. The callback is executed only after the IFrame is loaded.
The next issue is that these are asynchronous methods, and only accept callbacks. GWT currently does all setup synchronously. So the next modification is that the two methods that need to use it must be altered. All of the inner content of the following methods:
function installCode(code_0){...}
<WidgetSetName>.__installRunAsyncCode = function(code_0){...}
Needs to be wrapped in a function, and passed to the waitForSetupInstallLocation method as a callback. So that essentially you have turned those methods into asynchronous methods.
An example of what this looks like is:
function installCode(code_0){
waitForSetupInstallLocation(function(){
<real method content>
});
}
Once you have done all this - it should work in IE, and should remain functional in other browsers, since youve only added the use of a callback.

Suspend Javascript execution in UIWebView

I am successfuly using an invisible UIWebView as an execution engine for Javascript snippets. Contrary to what certain pages suggest, it does not require to be visible. If declared dead simply as
UIWebView* myJSExecutor = [UIWebView new];
not only it executes any stringByEvaluatingJavaScriptFromString: thrown at it, it even bubbles alert() to the desktop! (Not that it's something you would like to do often) Disclaimer: tested only on iOS 5+
Now, I have another normally visible UIWebView with normal webpage content, and I want it to suspend JavaScript execution: that is stop acting on timer and DOM events. I thought that removeFromSuperview with stopLoading and setting delegate = nil would do the trick. But no, as soon as I add the UIWebView back to some visible view, I can see that the timers were running all the time.
I understand the schizophreny of my requirement. I appreciate it working in the background on one hand (contrary to some observations), but I want to suspend it on the other hand. But still I would like to ask if there is any, perhaps even private way to suspend it. Safari for iOS is capable of suspending browser tabs in the background. Chrome for iOS can't, which may be a sad negative proof :(
If you're looking for crazy, I have an idea.
The only way I know to pause JavaScript is to show an alert, confirm, prompt, or sending an ajax request with async=false.
Is the alert hidden when the UIWebView is hidden? If so, you could send an alert when you want it to freeze. The user would have to dismiss it when you showed the view again:
[web stringByEvaluatingJavaScriptFromString:#"alert('Press OK to continue...')"];
Or maybe you could dismiss it programmatically. I'm sure you'd agree that this whole suggestion is bound for trouble, but it's a thought.
Do you need a full UIWebView? If not you could just invoke JavaScriptCore directly.
- (NSString *)runJS:(NSString *)aJSString
{
JSContextRef ctx = JSGlobalContextCreate(NULL);
JSStringRef scriptJS = JSStringCreateWithUTF8CString([aJSString UTF8String]);
JSValueRef exception = NULL;
JSValueRef result = JSEvaluateScript([self JSContext], scriptJS, NULL, NULL, 0, &exception);
JSGlobalContextRelease(ctx);
...
This would give you more control over the entire JS runtime, but unfortunately I've not found an API to suspend execution of timers other than releasing the whole context.
For a more complete example of how to use JavaScriptCore see https://github.com/jfahrenkrug/AddressBookSpy/blob/master/AddressBookSpy/AddressBookSpy/ABSEngine.m
This might sound obvious, but if you can edit the JS you're running in the UIWebView, can't you just store a global variable in the JS called 'paused', and check that when your timers are firing?
var paused = false;
var thread = setInterval(function() {
if(!paused) {
// normal timer checking etc
}
}, 300);
Then just fire [webView stringByEvaluatingJavaScriptFromString:#"paused = true;"] when you remove the webview, and set paused = false when you add it back to the view.

How to code firefox extension which run javascript code in the page's context like firebug does

I know that for safety reasons that this is not easy to achieve, however there would be a way to do so as firebug does...
Please help, would like to invoke some script in the page's context to achieve some effect...
Basically, I would like to achieve two functionality:
1. add jQuery to any web page automatically if not already exist.
2. when open certain address, call a method of that page to auto notify the server. (an ajax functionality of the page)
I have tried to inject on the body, no luck.
tried to get the window object, which however do not have access to call the function.
Will try to change the location to something like: javascript:alert('test inject');
Many thx.
OK, after reading some official documentation and the GreaseMonkey's source, I get the following method which basically works for me.
Hope it will save sb's hour:
var appcontent = document.getElementById("appcontent"); // browser
if (appcontent) {
appcontent.addEventListener("DOMContentLoaded", function (evnt) {
var doc = evnt.originalTarget;
var win = doc.defaultView;
var unsafeWin = win.wrappedJSObject;
// vote.up is the function on the page's context
// which is take from this site as example
unsafeWin.vote.up(...);
}, true);
}
}
Greasemonkey does that. If you are developing your own extension with similar functionality, you can use Components.utils.evalInSandbox.

Categories