How can I get a frame's content with mshtml? - parsing

Here's the issue:
I have a hook in IE that reacts on WebBrowser.OnNavigateComplete2 event to parse the content of the document for some precise info.
That document contains frames, so I look into the HTMLDocument.frames. For each one, I look into the document.body.outerHTML property to check for the content.
Problem is, the string I'm looking for never shows there, whereas it is displayed in the finale page. So, am I looking in the wrong place? If it is displayed when the page is fully loaded, then it's downloaded at some point, right? But in which object should I look ?
BTW, I Don't know if that is of any importance, but the page I'm searching into comes from a ASP.NET application.
public void OnNavigateComplete2(object pDisp, ref object url)
{
document = (HTMLDocument)webBrowser.Document;
mshtml.FramesCollection frames = document.frames;
for (int i = 0; i < frames.length; i++)
{
object refIdx = i;
IHTMLWindow2 frame = (IHTMLWindow2)frames.item(ref refIdx);
string frameContent = frame.document.body.outerHTML;
}
}
Thank your for your help.
#rams
This event is launched many times for each page, so I figured it was each time a framed is loaded, even if i don't get to catch the one I'm looking for. If not, what would be the event to catch the frames content?
What I want to do is detect some precise info on a precise frame, then save it. later, a web page is loaded triggered by some user action, where I need the info I got from parsing the frame.

Do you know the name/id of the frame you are looking for content? If so, in your navigateComplete2 event, can you get a reference to the frame like
iFrame frm = document.frames(<your frame id>);
int readyState=0;
while(frm.readystate !=4){
// do nothing. be careful to not create an endless loop
}
if(frm.readyState==4){
// get your content now
}
HTH

Are you using some kind of threading? Running the browser in a separate thread really messes up things. Try to execute it in an STAThread and check if you get the correct result.

The reason your string does not show is because of the frame. The web browser control fires the document navigate complete event after it has loaded the main document. At this point, the frames haven't yet requested their sources. After the document is parsed by the web browser control, requests for the frame sources are issues and downloaded.
Can you please describe what you are trying to accomplish?

Related

How to unfocus from a WebView in UWP

I'm working on a UWP app that hosts a WebView which runs in a separate process.
var webView = new Windows.UI.Xaml.Controls.WebView(WebViewExecutionMode.SeparateProcess)
This results in a behavior that if the WebView has the focus, the containing app can't regain the focus by itself by simply trying to focus on a UI element.
The app supports keyboard shortcuts which may result in different elements getting the focus, but it's not working correctly when the focus is captured by the WebView. The target element seems to be getting the focus but it seems as if the process itself is not activated (as the real focus resides in a different process I suppose...).
I'm currently trying to activate the app programmatically through protocol registration in an attempt to regain focus.
I added a declaration in the app manifest for a custom protocol mycustomprotocol coupled with the following activation overload
protected override void OnActivated(IActivatedEventArgs args)
{
if (eventArgs.Uri.Scheme == "mycustomprotocol")
{ }
}
And the following code to invoke the activation:
var result = await Windows.System.Launcher.LaunchUriAsync(new Uri("mycustomprotocol:"));
Seems to be working only on some computers, on others (not while debugging the app, only when executed unattached) instead of regaining focus the app's taskbar icon just flashes orange.
I've created a sample project showing the problem and the semi working solution here
Any insight on any of this would be great.
I can reproduce your issue. I found that when we switch the focus with the mouse, the focus can be transferred to the TextBlock. So you could solve this question through simulating mouse input.
Please use the following code to instead FocusTarget.Focus(FocusState.Programmatic).
As follows:
InputInjector inputInjector = InputInjector.TryCreate();
var infoDown = new InjectedInputMouseInfo();
// adjust your mouse position to the textbox through changing infoDown.DeltaX,infoDown.DeltaY
infoDown.DeltaX = 10; //change
infoDown.DeltaY = -150; //change
infoDown.MouseOptions = InjectedInputMouseOptions.LeftDown;
var infoUp = new InjectedInputMouseInfo();
infoUp.DeltaX = 0;
infoUp.DeltaY = 0;
infoUp.MouseOptions = InjectedInputMouseOptions.LeftUp;
inputInjector.InjectMouseInput(new[] { infoDown, infoUp });
Note: If you use the input injection APIs, you need to add inputInjectionBrokered Capabilitiy in your Package.appxmanifest.
But this Capabilitiy is a restricted Capabilitiy, you can’t publish this app in store, which can’t pass the verification.
I've been in discussions with a WebView software engineer. The problem is that the separate process still wants to own focus if you try to move the focus away from the webview. His solution is to ask the other process' web engine to give up focus with the following call:
_= webView.InvokeScriptAsync("eval", new string[] { "window.departFocus('up', { originLeft: 0, originTop: 0, originWidth: 0, originHeight: 0 });" });
You can call it before trying to change the focus to your target. I ran various tests and it works consistently.

How to change the background color of a Button inside a webview in iOS

I have a WebView that loads an URL (say login.salesforce.com) & asks the user to login to his instance.
Now, I want to get rid of the default login screen that it shows up & want to play around with the color of the login button.
Is it possible to change the color of a button inside a website that loads in UIWebView or WKWebView?
Absolutely Yes. For this you need to have little bit knowledge of html,css and java script.
Step 1. Prepare a java script as a string like below:
let changeHtmlbuttonScript = """
var property = document.getElementById(btn);
if (count == 0) {
property.style.backgroundColor = "#FFFFFF"
count = 1;
} else {
property.style.backgroundColor = "#7FFF00"
count = 0;
}
"""
Step 2: Evaluate java script on your current webview
self.webView.evaluateJavaScript(self.changeHtmlbuttonScript)
You can do it with UIWebView if you override the resource loading with a custom NSURLProtocol, and load your own CSS instead of theirs. "evaluateJavaScript" might work as well if you wait until the page is loaded somehow (see other reply).
Most likely what you ask violates the Salesforce's terms of service, so I wouldn't do it.
If they have a REST API for something that you try to achieve, then you can provide your own UI.

Print styles overriding screen styles after AirPrint in iOS webview

I am working on a hybrid HTML5/iOS app that uses the Safari Webview. We are using AirPrint to allow the user print the contents of the webview. The problem I am having is that after the print dialog is opened, the print styles are taking affect on the screen, and even after printing is complete or canceled do not go away. This does not happen in our Windows or Android versions of the app, which use CEF and Android System Webview respectively. Print styles in those versions of the application are only applied to the print out, as expected.
Anyone have any experience using AirPrint with Safari Webview that could shed some light on a solution? I have considered just adding/removing the link tag containing the CSS with javascript before and after printing, but that feels hacky, and doesn't answer the curious question of why print styles are being applied to the screen.
Any help appreciated! Sorry there is no real way to attach code to this!
Yes, this is indeed a not expected behaviour. However, we can try to solve this using JavaScript.
Theory: When the print is done, let's reload the stylesheets. The browser will paint the page again and hopefully using screen definitions.
Practice: As we don't have a JavaScript callback after printing, you could try reload your stylesheets using the window.onfocus event, as follows:
function updateStylesheets(){
var links = document.getElementsByTagName("link");
for (var x in links) {
var link = links[x];
if (link.getAttribute("type").indexOf("css") > -1) {
link.href = link.href + "?id=" + new Date().getMilliseconds();
}
}
}
window.onfocus = updateStylesheets;
In detail, it grabs all <link> tags and appends a random number after, forcing a reload on the stylesheets.
Please let me know if that worked, I'd be glad to help.

IOS Memory buildup when changing IFRAME src with angular/Embedding youtube in cordova?

I'm afraid im having the same probem as this unasnwered question.
I have a blank IFRAME In my cordova ionic IOS App for embedded YouTube videos.
I change the NG-SRC="" of iframe by clicking Next Video. But upon testing it appears that the phone/cordova/webview is caching the old content of the iframes. I can only get through 20 videos or so before crashing out of memory.
I have tried using angular.element.remove() to remove the iframe as well as setting the iframe src to blank first, and neither seemed to affect how much memory is in use, per Xcode. I've also tried the cordova plugin ClearCache and that didnt clear any memory either.
Please help! Is there a better way to embed youtube in a cordova app?
I have spent weeks working on this all to have it crashing down around me (no pun intended)
My Video view is like:
<ion-view view-title="Random Video">
<iframe id="youtube" sandbox="allow-scripts allow-same-origin" ng-src="{{video.url | trustAsResourceUrl}}" frameborder="0" allowfullscreen></iframe>
<h4>{{video.title}}</h4>
<button ng-click="nextVideo()">
</ion-view>
My controller is like:
angular.module('starter.controllers')
.controller('VideoCtrl', function(VideoService) {
$scope.video = {};
$scope.nextVideo = function() {
$scope.video = null; //doesnt seem to help
//$scope.$destroy(); //doesn't help
//angular.element(document.querySelector( '#youtube' )).attr("src", " ");
//angular.element(document.querySelector( '#youtube' )).remove();
//neither of the above 2 remove any memory
VideoService.getVideo().then(function(response){
$scope.video = response.data;
});
}
$scope.nextVideo();
});
Note, when I load my app onto a website instead, and load in chrome, I can cycle through videos without seeing the memory usage go up (looking at taskmgr.exe at least)
It might seem like setting the iframe to an empty string should be enough, but for some browsers and some situations it isn't. It might be necessary to recursively delete event listeners and elements one by one. Maybe surprisingly, the recursive method (1) below is faster than just setting to an empty string (2):
1.Recursive
while (box.lastChild) {
box.removeChild(box.lastChild);
}
2. Setting empty string
myNode.innerHTML = '';
See https://stackoverflow.com/a/3955238/1158376 for reference.
Additionally, in the recursive approach, one might need to apply special treatment to some items, for example first remove event listeners, nullify functions (http://javascript.crockford.com/memory/leak.html), and use dedicated cleanup methods, like with jQuery (http://javascript.info/tutorial/memory-leaks).
Another strategy you could try is to load a new web page with a fresh iframe for every video you play. Loading a new page should enable the browser to release the previously claimed memory.

UIAutomation with Instruments - How to tap copy/paste buttons?

I'm using Instruments for iOS automation and I can't seem to figure out how to tap options on the copy/paste menu. When I do a logElementTree(),I see that we are returning a UIEditingMenu and then three elements (which correspond to options of that menu, such as copy/paste, etc..). I am attempting to place this into a variable, and then trying to "tap" that variable but I cannot get that to work. Here is a sample of my code:
var target = UIATarget.localTarget();
var app = target.frontMostApp();
var window = app.mainWindow();
//This generates the highlighted text
app.dragInsideWithOptions({startOffset:{x:0.45, y:0.6}, endOffset:{x:0.45, y:0.6}, duration:1.5});
var copy = app.editingMenu.elements.withName("copyButton");
copy.tap();
Instruments returns, "0) UIAElementNil". In addition to the above, I've also tried:
app.elements.withName("copyButton")
window.elements.withName("copyButton")
So, I can get the editingMenu to produce the available options, but I cannot figure out a way to tap or select one of those options. I'm not quite sure I know how to reference those options to begin with.
Does anyone have any ideas?
Thanks!
You should try app.editingMenu().elements()[index].tap() where index is the index of the option you want to tap from the array of elements returned. I got my one working this way.
Hey.
First of all, I was always using .elements() not .elements... but it is JS, so it may be invoking function that is assigned to object property..?
Anyway, maybe this edit menu is not internal window of the app, but it is system level menu, that is invoked, when you do the drag? If that is true, try:
UIATarget.localTarget().frontMostApp().elements().withName("copyButton").tap();
But as I see in apple reference your version with calling app.editingMenu() should be fine...
Maybe try calling buttons by position, and you will see which respond:
UIATarget.localTarget().frontMostApp().editingMenu().elements()[0].tap;
UIATarget.localTarget().frontMostApp().editingMenu().elements()[1].tap;
UIATarget.localTarget().frontMostApp().editingMenu().elements()[2].tap;
You should find position of correct one this way. When you have it's position you can check its properties by button.logElement();. With this inf you you should be able to switch back to .withName method instead hardcoded position.
I did this similar to yoosiba but with editingMenu element names.
Using Xcode 4.5.1 and device running iOS 6.
Using Alex Vollmer's excellent tuneup_js for target, app and vtap().
Otherwise you can use UIATarget.localTarget().frontMostApp() and tap().
NOTE: vtap() will delay and retry tapping. Without this you may need to add your own delays.
// tap in textFieldA to see editingMenu.
app.mainWindow().textFields()["textFieldA"].vtap();
app.editingMenu().elements()["Select All"].vtap();
app.editingMenu().elements()["Copy"].vtap();
// must delay before attempting next tap
target.delay(2);
// ... navigate to different section of the app
// tap in textFieldB to see editingMenu.
app.mainWindow().textFields()["textFieldB"].vtap();
// paste clipboard contents copied from textFieldA into textFieldB
app.editingMenu().elements()["Paste"].vtap();
target.delay(2);

Resources