I am trying to optimize a web app for iPad (iOS 5.1.1) and I was wondering how to measure the time spent on a function.
On the desktop there is console.time() and console.timeEnd() for both firebug and webkit's console. Unfortunately I cannot make it work on iOS, only console.log() seems to be supported.
Any alternatives?
var start = new Date().getTime();
// ....
var end = new Date().getTime();
console.log(end-start);
Based on xdazz answer I tried to replace the two functions in order for it to work on iOS without refactoring all timers :
if(!console){var console = {};} // for those without a console - mind the context
console.timers = {};
console.time = function(timer)
{
if(!timer){timer="Timer";}
console.timers[timer] = new Date().getTime();
};
console.timeEnd = function(timer)
{
if(!timer){timer="Timer";}
console.log(timer+": "+(new Date().getTime()-console.timers[timer]));
};
Warning: Doing so will replace the original API and therefore might provide less accurate data on your desktop browsers.
As of an iPhone 4 CDMA running iOS 6, mobile Safari supports console.time() and console.timeEnd().
Related
I am using web audio API to stream audio to my remote server. I am using OfflineAudioContext. The code works fine in chrome and firefox but in safari, it gives the above mentioned error when trying to use OfflineAudioContext. I have tried adding webkit prefix to OfflineAudioContext, then it gives me this error:
SyntaxError: The string did not match the expected pattern.
I have tried adding different values to the OfflineAudioContext constructor but its always giving me the same error.
I went through Mozilla developers page for browser compatibility and I found this :
So, here its mentioned that for OfflineAudioContext constructor the compatibility is unknown for edge and safari. So, is this the reason, why I am not able to use OfflineAudioContext in safari? Is it not supported yet? or Am I doing it wrong? Or Is there another way to solve this in safari?
This is the first time I am using the Web Audio API. So, I hope somebody can clear my doubt if I missed out somewhere. Thank You.
Code of OfflineAudioContext added below:
let sourceAudioBuffer = e.inputBuffer; // directly received by the audioprocess event from the microphone in the browser
let TARGET_SAMPLE_RATE = 16000;
let OfflineAudioContext =
window.OfflineAudioContext || window.webkitOfflineAudioContext;
let offlineCtx = new OfflineAudioContext(
sourceAudioBuffer.numberOfChannels,
sourceAudioBuffer.duration *
sourceAudioBuffer.numberOfChannels *
TARGET_SAMPLE_RATE,
TARGET_SAMPLE_RATE
);
(if more code is needed of the js file to know the problem better. Just comment for it. I will add that but I thought the snippet is enough to understand the problem)
It's a bit confusing, but a SyntaxError is what Safari throws if it doesn't like the arguments. And unfortunately Safari doesn't like a wide range of arguments which should normally be supported.
As far as I know Safari only accepts a first parameter from 1 to 10. That's the parameter for numberOfChannels.
The second parameter (the length) just needs to be positive.
The sampleRate can only be a number between 44100 and 96000.
However it is possible to translate all the computations from 16kHz to another sampleRate which then works in Safari. Let's say this is the computation you would like to do at 16kHz:
const oac = new OfflineAudioContext(1, 10, 16000);
const osciallator = oac.createOscillator();
osciallator.frequency.value = 400;
osciallator.connect(oac.destination);
osciallator.start(0);
oac.startRendering()
.then((renderedBuffer) => {
console.log(renderedBuffer.sampleRate);
console.log(renderedBuffer.getChannelData(0));
});
You can do almost the same at 48kHz. Only the sampleRate will be different but the channelData of the rendered AudioBuffer will be the same.
const oac = new webkitOfflineAudioContext(1, 10, 48000);
const osciallator = oac.createOscillator();
osciallator.frequency.value = 1200;
osciallator.connect(oac.destination);
osciallator.start(0);
oac.oncomplete = (event) => {
console.log(event.renderedBuffer.sampleRate);
console.log(event.renderedBuffer.getChannelData(0));
};
oac.startRendering();
Aside: Since I'm the author of standardized-audio-context which is a library that tries to ease out inconsistencies between browser implementations, I have to mention it here. :-) It won't help with the parameter restrictions in Safari, but it will at least throw the expected error if the parameter is out of range.
Also please note that the length is independent of the numberOfChannels. If IfsourceAudioBuffer.duration in your example is the duration in seconds, then you just have to multiply it with the TARGET_SAMPLE_RATE to get the desired length.
I'm building an app that contains a WebView that runs some JavaScript code. That JavaScript code is quite allocation heavy and can require a lot of memory.
Sometimes, the amount of required memory exceeds the amount JavaScript can require and crashes the Chromium process of the WebView which crashes my app.
I listen to onMemoryTrim in my application - but it is never called in this scenario on devices with more than 1GB of memory. (Not even with TRIM_MEMORY_RUNNING_LOW).
Is there any way I could detect my WebView is running low on memory and either kill it or let it know (so it can free memory)?
I've tried polling performance.memory but it did not work. The following script crashes the WebView if executed in it:
var a = [];
var kek = () => {
var b = [];
for(var i = 0; i < 1024 * 1024 * 2; i++) b.push(Math.random());
return b;
}
var ival = setInterval(() => {
let m = performance.memory;
if(m.jsHeapSizeLimit - m.usedJSHeapSize < 1e5) {
console.log("Memory limited")
} else {
a.push(kek());
}
});
Is there any way to detect memory is about to run out so I can handle it gracefully without the app crashing?
I have discussed this with the Chromium team and the Android team and at the moment (they think and I believe them) that this is impossible.
Sometimes, the amount of required memory exceeds the amount JavaScript can require and crashes the Chromium process of the WebView which crashes my app.
You can however catch out of memory crashes in Android 8.0+ using the new termination handle API. So this works around my problem by not having to check the available memory required in the first place.
By overriding onRenderProcessGone - we get to catch the bug and recreate the WebView.
How can I reliably detect using javascript that a page is loaded inside a WKWebView? I'd like to be able to detect these scenarios:
iOS & WKWebView
iOS & Safari
not iOS
There is a similar question about UIWebView here. But it's quite old and I'm not sure if same still applies to WKWebView.
The accepted answer doesn't work as tested using the WKWebView vs UIWebView app
As the article mentions, the only HTML5 feature difference is IndexedDB support. So I'd go for a more reliable pattern with:
if (navigator.platform.substr(0,2) === 'iP'){
//iOS (iPhone, iPod or iPad)
var lte9 = /constructor/i.test(window.HTMLElement);
var nav = window.navigator, ua = nav.userAgent, idb = !!window.indexedDB;
if (ua.indexOf('Safari') !== -1 && ua.indexOf('Version') !== -1 && !nav.standalone){
//Safari (WKWebView/Nitro since 6+)
} else if ((!idb && lte9) || !window.statusbar.visible) {
//UIWebView
} else if ((window.webkit && window.webkit.messageHandlers) || !lte9 || idb){
//WKWebView
}
}
You may ask: Why not using the UserAgent? That's because Android browsers use it as settings! So, we should never trust any UAs. Only browser features and property checks as such.
Also I noticed that the QuickTime plugin was always loaded as part of Older Safari and other Browsers in UIWebView. But the plugin is no longer present in WKWebView. So you can use the QuickTime plugin presence as an extra check.
9/23/16 Edit: I adjusted the code for Safari 10 which no longer allowed the sole idb check to be reliable, as mentioned by #xmnboy. To discard Safari 10, it checks for the old webkit engine bug, which only applied until Safari 9.2; and i use a window.statusbar.visible fallback which appears to be a reliable indicator signal after a few comparison tests between iOS 9 and 10. (please check though)
Given the change in behavior to the UIWebView that was introduced by Apple in iOS 10, here's a new answer that combines the original response by #Justin-Michael and the follow-up favorite by #hexalys.
var isWKWebView = false ;
if( navigator.platform.substr(0,2) === 'iP' ) { // iOS detected
if( window.webkit && window.webkit.messageHandlers ) {
isWKWebView = true ;
}
}
It turns out that Justin's answer was really the better feature detection mechanism, because it works for both iOS 9 and iOS 10.
No telling what happens when we get to iOS 11. :-)
Qualification: this test will work if you are using the official Cordova WKWebView plugin to build your webview app, because that plugin does initialize the addScriptMessageHandler method, as noted by #hexalys in the comments to this post. That mechanism is being used by Cordova to define a new JS to native bridge when the WKWebView plugin is present.
Search for addScriptMessageHandler in that plugin repo and see the very end of the ios-wkwebview-exec.js file in that repo for some implementation details (or search for the string window.webkit.messageHandlers in that file).
In iOS, you could add this code to establish communication between javascript and objective-c:
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
WKUserContentController *controller = [[WKUserContentController alloc] init];
[controller addScriptMessageHandler:self name:#"javascript_observer"];
configuration.userContentController = controller;
...
webview = [[WKWebView alloc] initWithFrame:... configuration: configuration];
In javascript, you could test the connection like this:
if ( window.webkit != undefined ){
//javascript is running in webview
}
It seems that because the latest iOS Chrome uses WKWebView as a rendering engine, Chrome is detected as WKWebView.
ua.indexOf('CriOS') !== -1
will helps to distinguish Chrome from WKWebView in App.
You can check for the existence of window.webkit.messageHandlers which WKWebKit uses to receive messages from JavaScript. If it exists, you're inside a WKWebView.
That combined with a simple user agent check should do the trick:
var iOS = (navigator.userAgent.match(/(iPad|iPhone|iPod)/g) ? true : false);
var isWKWebView = false;
if (window.webkit && window.webkit.messageHandlers) {
isWKWebView = true;
}
var locator = new ActiveXObject("WbemScripting.SWbemLocator");
var service = locator.ConnectServer(".");
var properties = service.ExecQuery("SELECT * FROM Win32_NetworkAdapterConfiguration");
var e = new Enumerator(properties);
var MACaddress = '';
alert("Its Inside");
for (; !e.atEnd(); e.moveNext()) {
var p = e.item();
if (p.MACAddress) {
MACaddress = MACaddress + p.MACAddress + ',';
}
}
MACaddress = MACaddress.substring(0, MACaddress.length - 1);
MACaddress = replaceAll(MACaddress, ':', '-');
location.href = location.href + '?CAT=MAC&MACAddr=' + MACaddress;
This function is working fine in IE but its breaking in mozilla firefox at the first line itself. I changed locator.ConnectServer(".") to locator.ConnectServer("MACHINE") but still its not working in Mozilla Firefox.
Simple answer: you can't.
Modern browsers sandboxes (or try to) everything that goes on in the browser for security reason. Sand-boxing prevents any direct access to a system incl. files system, hardware etc. (it doesn't mean the browser does not communicate with the hardware but as users we have not direct access to it).
IE is the only browser which supports ActiveX (which is Microsoft's own technology) but it shouldn't be relied upon for the same reason, (mainly..) security.
If you want to use the MAC-address for some sort of unique identifier/security you can instead look into the new Web Cryptography API, however, at the time of this writing it is still in draft mode and not widely supported (but will be, or intents to be, cross-browser sometime in the future) so perhaps not so very useful advice at the moment.
You can in any case use server side to generate a unique identifier based on various factors and store it locally in the browser using either cookies or localStorage and so forth.
JSON.stringify is not working in blackberry mobile it is working great in iphone and other browser. it is not prompting it in below example in blackberry mobile:
function sup() {
this.name;
}
var SUP = new sup();
SUP.name = 'XYZ' ;
var tt = JSON.stringify(SUP);
alert(tt);
You should create a fallback mechanism so browser uses the native JSON support if present, otherwise it download the library that #T.J. Crowder pointed out
Something like this should do the trick
<script>window.JSON||
document.write("<script src='js/my-json-library.js'>\x3C/script>")
</script>
It sounds like that version of the Blackberry browser doesn't support the new JSON object, which was introduced in ES5 (so, just recently). You can find several polyfills/shims, including ones from the "introducer" of JSON himself.