I'm focusing on offline web applications with HTML5 at the moment. I came to the point where I need a reliable method of checking the user's connection to determine whether he/she is online or offline at the moment. Since I learned that the navigator.onLine property is highly unreliable I found a very nice method using the Fallback area of the cache manifest. I implemented two similar solutions from two different books, one being "Introducing HTML5" (Lawson/Sharp) and one "HTML5: The Missing Manual" (MacDonald). I guess this is an issue of HTTP caching (I use Apache and localhost), which I don't really know too much about. I pasted my code, it's a few files, but very little code.
The adapted solution from Introducing HTML5:
http://pastebin.com/UGsmnAtK
The adapted solution from HTML5 - the missing manual:
http://pastebin.com/8v5ck3E6
Tested with Chrome 16...
=== What I want ===
start app with empty cache and running apache
click the button -> alert "Online" is shown
stop apache
click the button -> alert "Offline" is shown
start apache
click the button -> alert "Online" is shown
=== What happens ===
Introducing HTML5 solution:
- start app with empty cache and running apache
- click the button -> alert "Online" is shown
- stop apache
- click the button -> alert "Online" is shown
- start apache
- click the button -> alert "Online" is shown
HTML5: the missing manual solution:
- start app with empty cache and running apache
- click the button -> alert "Online" is shown
- stop apache
- click the button -> alert "Online" is shown
- start apache
- click the button -> alert "Online" is shown
Other scenarios and use cases fail in similar fashion. The books promise that you can check the user's connectivity any time using their methods. So I guess I'm doing something wrong here. I would thankfully embrace any ideas on this topic.
Cheers,
Felix
The problem is that the online.js is loaded when you click the button with the Introducing HTML5 or when the page is loaded with the HTML5 - the missing manual code and the site is available through Apache. Next time the scripts isn't loaded as it is already in the browser cache.
The coce from Introducing HTML5 looks like it should work if you add a random value to the script URL. Something like:
function testOnline(fn) {
var script = document.createElement('script');
script.src = 'online.js?r=' + Math.random();
window.setOnline = function(online) {
document.body.removeChild(script);
fn(online);
}
document.body.appendChild(script);
}
Related
I am implementing an appium test on remote android driver, with chrome browser for loading urls.
Some of the Urls are pdfs, and chrome asks to store those files. and appears that chrome doesnt have access to filesystem to store those files, which results in a dialog like below.
Please help me pass that dialog without any manual inputs.
Upon clicking continue, it will load actual permissions dialog from Android.
Here is my code initialize appium capabilities
DesiredCapabilities caps = DesiredCapabilities.android();
caps.setCapability("appiumVersion", "1.9.1");
caps.setCapability("deviceName","Samsung Galaxy S9 Plus HD GoogleAPI Emulator");
caps.setCapability("deviceOrientation", "portrait");
caps.setCapability("browserName", "Chrome");
caps.setCapability("platformVersion", "8.1");
caps.setCapability("platformName","Android");
caps.setCapability("autoAcceptAlerts", true);
caps.setCapability("autoGrantPermissions", true);
caps.setCapability("chromedriverArgs", "--allow-file-access-from-files");
caps.setCapability("maxDuration", 10000);
and this is the snippet I use to load a Url
driver.navigate().to("http://kmmc.in/wp-content/uploads/2014/01/lesson2.pdf");
autoGrantPermission also doesnt work in this case because chrome is already installed. Appium team has already rejected this issue -
https://github.com/appium/appium/issues/10008
Please help!
Indeed I had very hard time finding out the solution, but eventually I found a workaround.
The best workaround would have been reinstalling the chrome package. I tried that, but I could not start chrome after reinstalling it, as I had no access to shell, and chromedriver complained. So I left that track.
I tried getting hold of adb command or mobile:changePermissions but for that you need to use server flag --relaxed-security while starting the server, and saucelabs doesnt provide any handy interface to start the server with this flag.
The last resort, I found a solution here - https://stackoverflow.com/a/51241899/4675277 . But just that was not sufficient, because it helped me fix chrome alert, but later on it popped up with another alert with allow and deny, for which another solution in the same question helped me. So this is the code I eventually used -
driver.navigate().to("http://kmmc.in/wp-content/uploads/2014/01/lesson2.pdf");
String webContext = ((AndroidDriver)driver).getContext();
Set<String> contexts = ((AndroidDriver)driver).getContextHandles();
for (String context: contexts){
if (context.contains("NATIVE_APP")){
((AndroidDriver)driver).context(context);
break;
}
}
driver.findElement(By.id("android:id/button1")).click();
contexts = ((AndroidDriver)driver).getContextHandles();
for (String context: contexts){
if (context.contains("NATIVE_APP")){
((AndroidDriver)driver).context(context);
break;
}
}
driver.findElement(By.id("com.android.packageinstaller:id/permission_allow_button")).click();
((AndroidDriver)driver).context(webContext);
This helps allow all permissions required.
This issue has taken up all my day, and I can't figure out whats going on
I can see that my service worker is registered , however "sometimes" when I click offline in developer tools the ServiceWorker for my domain just disappears!!
But this is the main problem when I reload the app I see the following behaviour.
You can see the ngsw.json is loaded twice and the main.js is loaded 3 times! main.d3ae2084xxxx && main.bbe5073dxxxx && then main.d3ae2084xxxx again!
If I inspect the response of both ngsw.json requests you can see that both show main.d3ae2084xxxx as the correct version of main.js but it still loads main.bbe5073dxxxx...
First ngsw.json request
Second ngsw.json request
Whats even more frustrating is the actual loaded version is the previous main.bbe5073dxxxx...!!!!
If anyone has any ideas how this can be happening please let me know.
Update... So found out about this excellent little endpoint
https://you-app-url/ngsw/state
This will give you lots of debug information about your service worker.
In my case this
Driver state: EXISTING_CLIENTS_ONLY (Degraded due to failed
initialization: Hash mismatch (cacheBustedFetchFromNetwork):
https://dev-xxxx.net/main.eb8468bb3ed28f02d7c2.js: expected
b5601102b721e0cf777691d327dc965d40d1c96e, got
83c18fdb4a5942c964a31c119a57e0b8e16fe46e (after cache busting)
So looks like this is going to be a CDN issue of some sort in my case, will update with an answer when I know for sure.
You've probably resolved this by now, but I had the same issue which turned out to be due to the CDN (Cloudflare in my instance) was optimising the content.
In Cloudflare the key option is 'Auto minify' that needs to be disabled.
I have reviewed this question/answer as well:
Communicating between Chrome DevTools and content script in extension
It looks like they are doing something slightly different than I am trying to do, so I don't know how much it applies. Maybe I absolutely need a background.js file?
I have also reviewed this question:
extension using background, content and devtools together
Here it looks like they are not using long-lived connections as documented here (which is what I need):
https://developer.chrome.com/extensions/messaging#connect
Anyway, previous question aside here is my problem:
I have tried this a few ways over the span of a few hours so I am pretty convinced I am just missing something here to make this work.
The crux of my issue is that:
chrome.runtime.onConnect.addListener(function(){...})
the listener here will never fire.
Here's my setup:
My extension uses a Content Script and a DevTools page. From both locations, the Content Script and DevTools page, I have tried to enabling messaging though chrome.runtime. My boilerplate initialization looks like this for starting the connection:
console.log('initializing connection');
var port = chrome.runtime.connect({name: 'My Extension'});
console.log('port', port.name);
and this for waiting for onConnect:
chrome.runtime.onConnect.addListener(function(port){
console.log('got connection!!!!!!');
});
My onConnect handler will never be invoked. I have tried placing the connection code (chrome.runtime.connect({...})) in the Content Script and in the DevTools page JS while placing the handler initialization the opposite location to no avail.
In other words, if I place the connection code in the Content Script, I will place the handler initialization into the DevTools page JS. If I place the connection code into the DevTools page JS I will place the handler initialization into the Content Script.
In both cases, I receive no runtime errors, however, I also never see the console.log('got connection!!!!!!'); get called. Yes, I am looking at the DevTools page console when I have the handler initialization located in the DevTools page JS.
I simply must just be misunderstanding something or missing something in the docs. Can anyone point me in the right direction re: having DevTools Page JS communicate with a Content Script?
As per Xan comment, communication between devtools extension and content scripts should be done through the use of a background script. The process is basically:
(devtools script) - create the connection ( and sends or listens to messages through the connection port opened)
(background script) - listen for the connection to be created, receiving the port and using it to listen or broadcast messages
This is useful if you want to keep a long lived connection, so basically you will need a couple of messages to be passed back and forward for a single process. If you want simple messages to be passed from time to time, but don't need multiple messages being passed back and forth then you might implement a more simple communication:
(devtools script) - sends a message using the chrome.runtime.sendMessage
(background script) - listens for messages send by any extension associated with it using the chrome.runtime.onMessage.addListener()
I am new to chrome extensions.I used chrome.runtime.onInstalled to load a html page whenever the extension is installed or updated.But when i am testing it in chrome, whenever i check/uncheck Allow in incognito the same html page loads each time.How to avoid this behaviour? I used "incognito":"split" in manifest.
I wish you'd posted the code so I could try to replicate the problem and give a specific solution but the easy solution is to use chrome storage API to save the extension's version when welcome.html is opened and compare it to the current version next time onInstalled is fired.
If the stored version is the same don't open it. If it's undefined or older, open it.
Get your extension's version by extracting it from chrome.extension.getURL("manifest.json")
Edit:
After a bit of googling it seems you can access the manifest more directly. Get the version number using the code below.
var version = chrome.runtime.getManifest().version;
Edit:
It seems the previous version is supplied in the callback when you update so you don't need to store anything. The object provided can be compared to the current version using chrome.runtime.getManifest().version
Something like this:
chrome.runtime.onInstalled.addListener(function (details) {
if(details.reason === "install"){
chrome.tabs.create({url: "welcome.html"});
}
else if(details.reason === "update"){
var currentVersion = chrome.runtime.getManifest().version;
var previousVersion = details.previousVersion;
if(previousVersion !== currentVersion){
chrome.tabs.create({url: "welcome.html"});
}
}
});
I don't think you can. I assume that when you uncheck "Allow in incognito", Chrome nukes the local state of the (split) incognito instance.
I have a rssfeed application made in Adobe AIR. It uses the UrlLoader to read the feeds. I need the application to never prompt the user to enter password for a website or accept a certificate of a website if is no longer valid. I prefer to have it fail instead.
I have setup the event listeners for IO+Error and Security_ERROR but that is not enough
Sample urls(this may not work in future)
http://www.dawnanddrew.com/rss2.xml (asks for user and pass atm)
I had another issue with a feed that even if it was a http url the website redirected the loader to https and then the certificate validation failed and the user was asked to accept the certificate. I need this to fail (I mark failed feeds and the user can check them when they want to find the issue and fix them or remove them)
Using AIR (which you are), you can actually tell the application to NOT handle authentication.
var request:URLRequest =new URLRequest("http://www.dawnanddrew.com/rss2.xml");
request.authenticate = false; //default is true, so you need to tell your request to not handle authentication
Then it will fail with an IO error instead of prompting for credentials.
Documentation
Note however, this will only work in AIR and is not a supported property for flash player.
You cannot do it this way. Now, the server returns specific status and the browser reacts to it by showing you a login dialog box. You cannot do this directly in flash and you cannot get default login button. You need to implement this by your own. The perfect way to do so is to check (server side) if the user is logged in and if not - return status ok (200) with an error message (let's say JSON). This specific message would be read by the flash client and a login screen would be shown (custom made). You will also need to implement the login feature by yourself :)
Sorry for the bad news, but this is the way it is with flash.