Triggering shouldStartLoadWithRequest with multiple window.location.href calls - iphone

Im trying to pass multiple things from a webpage inside a UIWebView back to my iPhone app via the shouldStartLoadWithRequest method of the UIWebView.
Basically my webpage calls window.location.href = "command://foo=bar" and i am able to intercept that in my app no problem. Now if i create a loop and do multiple window.location.href calls at once, then shouldStartLoadWithRequest only appears to get called on once and the call it gets is the very last firing of window.location.href at the end of the loop.
The same thing happens with the webview for Android, only the last window.location.href gets processed.

iFrame = document.createElement("IFRAME");
iFrame.setAttribute("src", "command://foo=bar");
document.body.appendChild(iFrame);
iFrame.parentNode.removeChild(iFrame);
iFrame = null;
So this creates an iframe, sets its source to a command im trying to pass to the app, then as soon as its appended to the body shouldStartLoadWithRequest gets called, then we remove the iframe from the body, and set it to null to free up the memory.
I also tested this on an Android webview using shouldOverrideUrlLoading and it also worked properly!

I struck this problem also and here is my solution that works for me.
All my JavaScript functions use this function __js2oc(msg) to pass data
and events to Objective-C via shouldStartLoadWithRequest:
P.S. replace "command:" with your "appname:" trigger you use.
/* iPhone JS2Objective-C bridge interface */
var __js2oc_wait = 300; // min delay between calls in milliseconds
var __prev_t = 0;
function __js2oc(m) {
// It's a VERY NARROW Bridge so traffic must be throttled
var __now = new Date();
var __curr_t = __now.getTime();
var __diff_t = __curr_t - __prev_t;
if (__diff_t > __js2oc_wait) {
__prev_t = __curr_t;
window.location.href = "command:" + m;
} else {
__prev_t = __curr_t + __js2oc_wait - __diff_t;
setTimeout( function() {
window.location.href = "command:" + m;
}, (__js2oc_wait - __diff_t));
}
}

No, iframe's url changing won't trigger shouldOverrideUrlLoading, at least no in Android 2.2.

Related

postMessage() event listener not working

I am working with a 3rd party who supplies a URL to be put into an iFrame to display some hosted video playback.
this is cross-domain
they use JWPlayer as their player of choice
I requested a way to 'know' when the video playback is complete. From reading, looks like the postMessage() callback is what many use.. and is what the 3rd vendor suggested, and mentioned they would implement.
I was given a TEST url that has this 'call back' function in it... and to see if I can could use it.
I can not seem to get any alert from the callback/listener functions?
As this is the first time I am implementing this, Im not sure if the error stems from my end or theirs?
I'm thinking it may be the path form the postMessage() function?
After firebugging the code.. I eventually fund their JS/callback set up here:
jwPI.on('complete', function(event){
playbackTime= playbackTime + (jwPI.getPosition() - positionA);
positionA=jwPI.getPosition();
parent.postMessage('EndVideo','*');
});
My side of things has the simple event listener added like so:
window.addEventListener("message", function(evt) {
//do whatever
alert("VIDEO CALLBACK FIRED");
});
My questions are:
1.) Why is this not working? a target/scope issue?
2.) Do I need to have the 3rd party vendor update the path in their postMessage() callback? where does '.parent' actually point to? (if this is an embedded iFrame?) and there are DIV's..etc..etc..etc housing the nested iFrame content?
my listener function is in the main parent file that loads this iFrame?
3.) Can I just leave it as 'as-is' and somehow change the path/target in my listener?
Solution posted:
here is a both a jQuery and JS solution
** note the jQuery approach need to use originalEvent in the scope
//jQuery approach
$(window).on("message onmessage", function(evt) {
//message
var targetData = evt.originalEvent.data;
//origin
var targetOrigin = evt.originalEvent.origin;
//check origin for security and to make Scott proud
if(targetOrigin !== 'https://example.com'){
//no same origin, exploit attempt in process possibly
}
//do whatever
});
//Javascript approach
window.addEventListener("message", function(evt) {
//message
var targetData = evt.data;
//source
var targetSource = evt.source; //iframe source message stems from - doesnt work
//origin
var targetOrigin = evt.origin;
if(targetOrigin !== 'https://example.com'){
//no same origin, exploit attempt in process possibly
}
//do whatever
});
(Posted an answer on behalf of the question author).
Here is a both a jQuery and JS solution. Note the jQuery approach need to use originalEvent in the scope.
//jQuery approach
$(window).on("message onmessage", function(evt) {
//message
var targetData = evt.originalEvent.data;
//origin
var targetOrigin = evt.originalEvent.origin;
//check origin for security and to make Scott proud
if(targetOrigin !== 'https://example.com'){
//no same origin, exploit attempt in process possibly
}
//do whatever
});
//Javascript approach
window.addEventListener("message", function(evt) {
//message
var targetData = evt.data;
//source
var targetSource = evt.source; //iframe source message stems from - doesnt work
//origin
var targetOrigin = evt.origin;
if(targetOrigin !== 'https://example.com'){
//no same origin, exploit attempt in process possibly
}
//do whatever
});

How to wait the page to test is loaded in non angular site?

I've tried this:
browser.wait(function () {
return browser.executeScript('return document.readyState==="complete" &&' +
' jQuery !== undefined && jQuery.active==0;').then(function (text) {
return text === true;
});
}, 30000);
If jQuery.active==0 then page is completely loaded. This should work for sites with JQuery and non angular pages.
However, I have many problems of instability to test for non angular sites.
How to fix this?
By default protractor waits until the page is loaded completely. If you are facing any error then it is because protractor is waiting for the default time to be completed, that you have specified in your conf.js file to wait until page loads. Change the value to wait a for longer time if you think your app is slow -
// How long to wait for a page to load.
getPageTimeout: 10000, //Increase this time to whatever you think is better
You can also increase the defaultTimeoutInterval to make protractor wait a little longer before the test fails -
jasmineNodeOpts: {
// Default time to wait in ms before a test fails.
defaultTimeoutInterval: 30000
},
If you want to wait for any particular element, then you can do so by using wait() function. Probably waiting for last element to load is the best way to test it. Here's how -
var EC = protractor.ExpectedConditions;
var lastElement = element(LOCATOR_OF_LAST_ELEMENT);
browser.wait(EC.visibilityOf(lastElement), 10000).then(function(){ //Alternatively change the visibilityOf to presenceOf to check for the element's presence only
//Perform operation on the last element
});
Hope it helps.
I use ExpectedConditions to wait for, and verify page loads. I walk through it a bit on my site, and example code on GitHub. Here's the gist...
Base Page: (gets extended by all page objects)
// wait for & verify correct page is loaded
this.at = function() {
var that = this;
return browser.wait(function() {
// call the page's pageLoaded method
return that.pageLoaded();
}, 5000);
};
// navigate to a page
this.to = function() {
browser.get(this.url, 5000);
// wait and verify we're on the expected page
return this.at();
};
...
Page Object:
var QsHomePage = function() {
this.url = 'http://qualityshepherd.com';
// pageLoaded uses Expected Conditions `and()`, that allows us to use
// any number of functions to wait for, and test we're on a given page
this.pageLoaded = this.and(
this.hasText($('h1.site-title'), 'Quality Shepherd')
...
};
QsHomePage.prototype = basePage; // extend basePage
module.exports = new QsHomePage();
The page object may contain a url (if direct access is possible), and a pageLoaded property that returns the ExepectedCondition function that we use to prove the page is loaded (and the right page).
Usage:
describe('Quality Shepherd blog', function() {
beforeEach(function() {
// go to page
qsHomePage.to();
});
it('home link should navigate home', function() {
qsHomePage.homeLink.click();
// wait and verify we're on expected page
expect(qsHomePage.at()).toBe(true);
});
});
Calling at() calls the ExpectedCondidion (which can be be an and() or an or(), etc...).
Hope this helps...

Titanium xhr events not firing

I have been trying to get a simple xhr request to work but for some unknown reasons nothing happens, not even the onerror function fires off.
var xhr = Ti.Network.createHTTPClient();
xhr.onload = function() {
Titanium.API.log('Success');
}
xhr.onerror = function() {
Titanium.API.log('Error');
}
xhr.open("GET","http://www.google.com/");
xhr.send();
I have tried this with a new created project and still no luck. Using little snitch I noticed that a connection is made by the app to the given url ... but still nothing fires off.
What am I missing?
Also I'm developing on an iPhone Simulator.
I don't think there's anything wrong with the XHR request - the Titanium.API.log function takes two arguments, but you're only giving it one, so it's probably just not printing to the console. The Titanium documentation is down at the moment so I can't link you to the correct API, but if you change your code to use Ti.API.info, for example, you should see something printed. This works for me:
var xhr = Ti.Network.createHTTPClient();
xhr.onload = function() {
Titanium.API.info('Success');
}
xhr.onerror = function() {
Titanium.API.info('Error');
}
xhr.open("GET","http://www.google.com/");
xhr.send();

JavaScript: Event listener on iFrame resize / height attribute change (FB comment box)

I have the following problem: I need to add an event listener for an iframe (a Facebook comment box) when it changes its height.
I can not access or change the contentWindow because it is cross domain. But there must be a callback function or something that changes the height attribute.
Is there a way to add an event listener on attribute changes or something? I alreade tried onResize and onChange.
I'm getting crazy with that... Anyone has an idea?
Thank you so much!!
Short answer: No.
However, you can use "postMessage" and "receiveMessage" to send from one iframe to another cross domain. (Of course, only if you have access to the iframed content - I suspect not as it's on facebook.)
In any case... for future help....
(on the iframed page)
var ii = {}
ii.window_height = 800;
var sendHeight = function () {
var window_height = $('body').outerHeight(true);
if (window_height != ii.window_height) {
ii.window_height = window_height;
window.parent.postMessage(ii.window_height, "http://containerDomain.com");
}
}
setInterval(sendHeight, 2000);
(on the container page)
function receiveMessage(evt) {
if (evt.origin === 'https://iframedDomain.com')
{
var iframe_content_height = evt.data;
$('#iframe_form').animate({height: iframe_content_height });
}
}
if ($.browser.msie) {
window.attachEvent('onmessage', receiveMessage);
} else {
window.addEventListener('message', receiveMessage, false);
}
Remember to change the domains in each script.
Note: that's using jQuery - It works, but I'm sure someone can write that better then me? Also not too proud of the interval checking the height... might update if i can.

PhoneGap ChildBrowser Executing JavaScript

I wonder if this is possible to execute JavaScript inside phonegap childbrowser window so we can manipulate websites under phonegap app?
Looking at the big picture as one can create a function in Objective-C which executes that JS into childbrowser (modifying childbrowser.m and childbrowser.h files) and creating JS wrapper of it so one can call JS function to execute JS inside childbrowser.
I want you to modify ChildBrowser for me to have that functionality so I shouldn't lost doing it. At least give me initial steps.
Alright I just tried and it worked in a single go. That was amazing! I just modified ChildBrowser plugin of PhoneGap and it worked.
UPDATED
I finally got few minutes to update the answer for those who will encounter the same issue.
ChildBrowserCommand.h
- (void) jsExec:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
ChildBrowserCommand.m
- (void) jsExec:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options; {
[childBrowser executeJS:(NSString *)[arguments objectAtIndex:0]];
}
ChildBrowserViewController.h
- (void)executeJS:(NSString *)js;
ChildBrowserViewController.m
- (void) executeJS:(NSString *)js {
[webView stringByEvaluatingJavaScriptFromString:js];
}
ChildBrowser.js
/* MIT licensed */
// (c) 2010 Jesse MacFadyen, Nitobi
function ChildBrowser()
{
}
// Callback when the location of the page changes
// called from native
ChildBrowser._onLocationChange = function(newLoc)
{
window.plugins.childBrowser.onLocationChange(newLoc);
}
// Callback when the user chooses the 'Done' button
// called from native
ChildBrowser._onClose = function()
{
window.plugins.childBrowser.onClose();
}
// Callback when the user chooses the 'open in Safari' button
// called from native
ChildBrowser._onOpenExternal = function()
{
window.plugins.childBrowser.onOpenExternal();
}
// Pages loaded into the ChildBrowser can execute callback scripts, so be careful to
// check location, and make sure it is a location you trust.
// Warning ... don't exec arbitrary code, it's risky and could cause your app to fail.
// called from native
ChildBrowser._onJSCallback = function(js, loc)
{
// Not Implemented
window.plugins.childBrowser.onJSCallback(js, loc);
}
/* The interface that you will use to access functionality */
// Show a webpage, will result in a callback to onLocationChange
ChildBrowser.prototype.showWebPage = function(loc)
{
PhoneGap.exec("ChildBrowserCommand.showWebPage",loc);
}
// close the browser, will NOT result in close callback
ChildBrowser.prototype.close = function()
{
PhoneGap.exec("ChildBrowserCommand.close");
}
// Not Implemented
ChildBrowser.prototype.jsExec = function(jsString)
{
// Not Implemented!!
PhoneGap.exec("ChildBrowserCommand.jsExec", jsString);
}
// Note: this plugin does NOT install itself, call this method some time after deviceready to install it
// it will be returned, and also available globally from window.plugins.childBrowser
ChildBrowser.install = function()
{
if(!window.plugins)
{
window.plugins = {};
}
window.plugins.childBrowser = new ChildBrowser();
return window.plugins.childBrowser;
}
My global variable.
var CB = null;
On my DeviceReady event.
CB = ChildBrowser.install();
if (CB != null) {
CB.onLocationChange = onCBLocationChanged;
}
I can execute any JS into webpage using.
CB.jsExec("alert('I am from ChildBrowser!');");
I hope my contribution to this will bring smile on your face.