I am creating a web worker using a data URL in a local html file. The file has a secure context but the worker does not. Specs say they should be the same. Have I messed up, is the spec wrong or chromium browsers? Here is code demonstrating the error.
<!DOCTYPE html><html><head><title>Worker Security Flaw?</title>
<meta charset="UTF-8"><script>
function main(){
out.textContent= 'window.isSecureContext= '+isSecureContext;
let worker= new Worker('data:text/javascript,onmessage= function(e){postMessage({flag:isSecureContext});};');
worker.onmessage= function(e){out.textContent+= ', worker.isSecureContext= '+e.data.flag;};
worker.postMessage({});
}
</script></head>
<body onload=main();><output id="out"></output></body></html>
There is a work around using blobs.
let blb=new Blob(['onmessage=function(e){postMessage({flag:isSecureContext});};'], {type: 'text/javascript'});
let worker=new Worker(URL.createObjectURL(blb));
This works as the spec calls out giving the worker the same security context as the opener.
Related
I'm trying to get Web NFC to work through the Web NFC API, but I can't get it past an error message of NotAllowedError: NFC permission request denied.
I'm using this on Chrome 89 Dev on a Windows 10 computer, and the source code is being run locally.
I have tried the examples posted on the Internet also, including the Google sample but it returns the same error. I'm not concerned with it being experimental at this point as referring to this does show it has successfully passed the necessary tests, including permissions.
The HTML/JS code I'm using is below, and I've read the specification point 9.3, but I can't make sense of it to write it as code, so is there a guideline algorithm that would be helpful here to resolve this?
async function readTag() {
if ("NDEFReader" in window) {
const reader = new NDEFReader();
try {
await reader.scan();
reader.onreading = event => {
const decoder = new TextDecoder();
for (const record of event.message.records) {
consoleLog("Record type: " + record.recordType);
consoleLog("MIME type: " + record.mediaType);
consoleLog("=== data ===\n" + decoder.decode(record.data));
}
}
} catch(error) {
consoleLog(error);
}
} else {
consoleLog("Web NFC is not supported.");
}
}
async function writeTag() {
if ("NDEFWriter" in window) {
const writer = new NDEFWriter();
try {
await writer.write("helloworld");
consoleLog("NDEF message written!");
} catch(error) {
consoleLog(error);
}
} else {
consoleLog("Web NFC is not supported.");
}
}
function consoleLog(data) {
var logElement = document.getElementById('log');
logElement.innerHTML += data + '\n';
};
<!DOCTYPE html>
<html>
<head>
<script src="webnfc.js"></script>
</head>
<body>
<p>
<button onclick="readTag()">Test NFC Read</button>
<button onclick="writeTag()">Test NFC Write</button>
</p>
<pre id="log"></pre>
</body>
</html>
From https://web.dev/nfc/#security-and-permissions
Web NFC is only available to top-level frames and secure browsing contexts (HTTPS only). Origins must first request the "nfc" permission while handling a user gesture (e.g a button click). The NDEFReader scan() and write() methods trigger a user prompt, if access was not previously granted.
I guess you are running from a file:// URL as you said "locally" which is not supported.
You need to host it from a local web server using a https:// URL
Once in the right scope trying to scan or write should trigger a user prompt.
You can also check permissions see https://web.dev/nfc/#check-for-permission
Update:
So I tried the sample page https://googlechrome.github.io/samples/web-nfc/
And this works for me on Android Chrome 87 with "Experimental Web Platform features" enabled
When you hit the scan button A dialog asking for permission pops up.
Comparing the code in this sample to yours I notice that does:-
ndef.addEventListener("reading" , ({ message, serialNumber }) => { ...
Where as yours does:-
ndef.onreading = event => { ...
I don't know if it is the style setting what happens on the Event or something else (Hey this is all experimental)
Update2
To answer the question from the comments of Desktop support.
So you should be some of the desktop/browser combinations at the moment and may be in the future there will be wider support as this is no longer experimental standards. Obviously as your test link suggest Chrome on a Linux Desktop should work as this is really similar to Android Support, with all the NFC device handling done by libnfc and the browser just has to know about this library instead of every type usb or other device than can do NFC.
From what seen of NFC support on Windows, most of this is focussed on direct controlling the NFC reader via USB as just another USB device, while there is a libnfc equivalent in Windows.Networking.Proximity API's I've not come across any NFC reader saying they support this or anybody using it.
For Mac Deskstop, given that Apple are behind the curve with NFC support in iOS, I feel their desktop support will be even further behind even though it could be similar to Linux.
As you can read at https://web.dev/nfc/#browser-support, Web NFC only supports Android for now which is why you get "NotAllowedError: NFC permission request denied." error on Windows.
Im trying to read NFC tags from chrome 81 on andriod with the following code:
<html>
<head>
<title>NFC</title>
</head>
<body>
<button onclick="reader()">Scan</button>
<script>
function reader(){
const reader = new NDEFReader();
reader.scan().then(() => {
alert("Scan started successfully.");
reader.onerror = () => {
alert("Cannot read data from the NFC tag. Try another one?");
};
reader.onreading = event => {
alert("NDEF message read.");
};
}).catch(error => {
alert(`Error! Scan failed to start: ${error}.`);
});
}
</script>
</body>
the problem im having with it is that it reads the entry from the nfc tag but doesnt give alerts like the code suggests, instead it trys to direct me to installed apps on my phone. However, when i use https://googlechrome.github.io/samples/web-nfc/ that is using the full API it works and displays in the webpage as data. The main difference is that im using the Enabling via chrome://flags method to allow the NFC API.
out of reading the tag, my only aim is to save the content to sessionStorage as a variable to be used by other parts of the website.
Thanks in advance
One difference between https://googlechrome.github.io/samples/web-nfc/ and your code that would matter is the fact this demo used to have an origin trial token in its web page.
For now, to experiment with Web NFC on Android, enable the #experimental-web-platform-features flag in chrome://flags as described in https://web.dev/nfc/#use
Hopefully this flag won't be required once it is shipped to the web platform.
How can I communicate from a JavaScript code of a webpage to the main code of the add-on?
For example, something like this: If some element is clicked, in the corresponding event handler of the page script, which is the syntax that can be used to send some message to the main code?
Specifically, something like this, where the frame now must be replaced by a generic webpage. Is it possible?
Edit: I have tried the suggested code, but how I had said, the application returns this error:
console.error: sherlock:
Message: ReferenceError: document is not defined
Stack:
A coding exception was thrown in a Promise resolution callback.
See https://developer.mozilla.org/Mozilla/JavaScript_code_modules/Promise.jsm/Promise
Full message: ReferenceError: document is not defined
Previously my question, I had infact tried something similar without any effect.
Yes it is possible.
document.onload = function() {
var elementYouWant = document.getElementById("someID");
elementYouWant.onclick = console.log("Yup.. It was clicked..");
};
Reference.
The answer to the question is not as trivial as it may seem at first sight. I had also thought of a logic of the type described in the Pogrindis' response.
But here, in the case of interaction between the main script (i.e. that of the add-on) and generic script of arbitrary documents, the pattern is different.
In summary, the interaction takes place in this way:
It is required the API page-mod.
Through the property includes of the object PageMod you create a reference to the document, specifying the URI (wildcards are allowed).
Via the contentScriptFile property it is set the URL of the .js file that will act as a vehicle between the main code and that of the document.
Here's an example that refers to the specific needs of the context in which I am. We have:
an add-on code (the main code);
a Sidebar type html document (gui1.html) loaded in the file that I
use as a simple UI (I advise against the use of Frames, since it does
not support many typical HTML features - eg the click on a link,
etc.) containing a link to a second document (gui2.html) which will then
be loaded into the browser tab (I needed this trick because the
Sidebar does not support localStorage, while it is necessary for me);
a script in the document.
We must create an exchange of information between the two elements. In my case the exchange is unidirectional, from the page script to the main one.
Here's the code (main.js):
var pageMod = require("sdk/page-mod");
pageMod.PageMod({
include: "resource://path/to/document/gui2.html",
contentScriptFile: data.url("listen.js"),
onAttach: function(worker) {
worker.port.on("gotElement", function(elementContent) {
console.log(elementContent);
});
}
});
and in the html page script:
<script type="text/javascript">
[...]
SOWIN = (navigator.userAgent.toLowerCase().indexOf("win") > -1) ? "win" : "nix";
if (SOWIN == "win") {
window.postMessage("win","*");
} else {
window.postMessage("Linux","*");
}
[...]
</script>
Finally in the JS file (listen.js) to be attached to the page script:
window.addEventListener('message', function(event) {
self.port.emit("gotElement", event.data);
}, false);
This is just a small example, but logic I would say that it is clear. The uploaded content scripts are not accessible directly from main.js (i.e. the add-on), but you can create a bidirectional communication through the exchange of messages. To achieve this we have to put ourselves in listening the event Attach of the page-mod. Then, it is passed a worker object to the listener; that worker may be used by the add-on for the exchange of messages.
Here are the references to have an exhaustive picture:
Interacting with page scripts
Communicating with other scripts
page-mod
port
Communicating using "port"
postMessage
Communicating using postMessage
My code works in firefox and when i visit w3schools using chrome to test my code in their editor it works fine too but when i launch my code in chrome from notepad++ it doesn't work.It seems that body onload is not working because i don't get the alert.My chrome is up to date.Help would be appreciated.
<!DOCTYPE html>
<html>
<head>
<script>
function setCookie(cname,cvalue,exdays){
var d=new Date();
d.setTime(d.getTime()+(exdays*24*60*60*1000));
var expires="expires="+d.toUTCString();
document.cookie=cname +"="+cvalue+"; "+expires;
}
function f(){
var user=prompt("What is your name?","");
if(user!="" && user!=null){
setCookie("username",user,30);}
}
function getC(cname){
var name=cname+"=";
var ca=document.cookie.split(";");
for(var i=0;i<ca.length;i++){
var c=ca[i];
while(c.charAt(0)==" ")c=c.substring(1);
if(c.indexOf(name)==0) return c.substring(name.length,c.length);
}
return "";
}
function checkcooki(){
var user=getC("username");
if(user!=""){
alert("Welcome back "+user);
}
}
</script>
</head>
<body onLoad="checkcooki()">
<input type="button" onclick="f()" value="klick">
</body>
</html>
For a fact: Using the file:// protocol does NOT guarantee the proper workings with cookies. Since cookies need 3 things:
A name-value pair containing the actual data
An expiry date after which it is no longer valid
The domain and path of the server it should be sent to
The domain tells the browser to which domain the cookie should be sent. If you don't specify it, it becomes the domain of the page that sets the cookie.
On a file:// protocol you don't have a domain.
Now some browsers might have found work-arounds for this, like FireFox and IE. You can test your code on these browsers but they will not use cookies in the same way as on a webserver.
Proper x-browser testing in your case requires the http:// protocol.
I suggest you start a jsfiddle or setup a webserver(IIS, apache).
Proper read on cookies: quircksmode
If you are still persistent to get it working on chrome through the file:// protocol you might have a small chance if you get the path correctly.
path: properly escaped path => encodeURIComponent(document.domain) or "c:\/my%20folder\/index.html" (along these lines but again, very untrustworthy information here)
domain: "/" (no idea what else you can try here)
Your user variable must be a blank string.
Put an alert at the very top of your checkcooki() function to verify that body onload works.
I'm trying to access the offline application cache of a shared webworker (HTML5) with no luck. I've been banging my head against this problem for many hours, so I must be missing something... Any help from a JavaScript Ninja out there would be highly appreciated!
The W3C the spec says that:
cache = self.applicationCache
(in a shared worker) should return the ApplicationCache object that applies to the current shared worker.
I'm spawning a shared worker from my app's main script via:
var worker = new SharedWorker('js/test.js');
worker.port.addEventListener('message', function(e) {
alert('got message: ' + e.data);
}, false);
worker.port.start();
worker.port.postMessage('hi there...');
And here's the code of my shared worker (test.js):
var cache = self.applicationCache;
onconnect = function(e) {
var port = e.ports[0];
port.onmessage = function(e) {
// test.html contains a <html manifest='test.manifest'> tag
var xmlHttp = new XMLHttpRequest();
xmlHttp.open("GET", "test.html", false);
xmlHttp.send(null);
var result = xmlHttp.responseText;
port.postMessage(result);
port.postMessage('cache: '+ cache);
}
}
The alerts I'm getting are:
the contents of test.html (as I expected)
the message "cache : undefined" (oops!)
I tried this on Google Chrome 7.0.517.44 and Safari 5.0.2 (Mac OS X 10.6.4). I also tried to trigger the HTTP GET before accessing the cache and many other variations, but all of these attempts resulted with the same outcome.
Am I missing something obvious? Is this is a known limitations of the browsers I've tested?
Many Thanks,
Ori
I found the same thing - though to be honest, I'm not even sure WHY we'd want access to the applicationCache object... I thought it just, cached things?! Anyway - when I was trying to get it to work I found this thread talking about it:
http://lists.w3.org/Archives/Public/public-webapps/2009OctDec/0519.html
I assumed that I could just stick and entry in my cache.manifest file of the main page that referenced the worker file and it would populate the applicationCache magically. But it didn't appear to (I just got undefined like you did).
In the w3c spec, in the Processing Model section it says:
If worker global scope is actually a SharedWorkerGlobalScope object (i.e. the worker is a shared worker), and there are any relevant application caches that are identified by a manifest URL with the same origin as url and that have url as one of their entries, not excluding entries marked as foreign, then associate the worker global scope with the most appropriate application cache of those that match.
But I can't make it work!