Web NFC on Chrome Android - google-chrome

I am trying to explore on web NFC and found a simple sample (https://googlechrome.github.io/samples/web-nfc/). So, I copy the sample code to test it on local:
<html><head>
<title>Web NFC Sample</title>
<script>
// Add a global error event listener early on in the page load, to help ensure that browsers
// which don't support specific functionality still end up displaying a meaningful message.
window.addEventListener('error', function(error) {
if (ChromeSamples && ChromeSamples.setStatus) {
console.error(error);
ChromeSamples.setStatus(error.message + ' (Your browser may not support this feature.)');
error.preventDefault();
}
});
</script>
<link rel="stylesheet" href="Web%20NFC%20Sample_files/main.css">
</head>
<body>
<button id="scanButton">Scan</button>
<button id="writeButton">Write</button>
<script>
var ChromeSamples = {
log: function() {
var line = Array.prototype.slice.call(arguments).map(function(argument) {
return typeof argument === 'string' ? argument : JSON.stringify(argument);
}).join(' ');
document.querySelector('#log').textContent += line + '\n';
},
clearLog: function() {
document.querySelector('#log').textContent = '';
},
setStatus: function(status) {
document.querySelector('#status').textContent = status;
},
setContent: function(newContent) {
var content = document.querySelector('#content');
while(content.hasChildNodes()) {
content.removeChild(content.lastChild);
}
content.appendChild(newContent);
}
};
</script>
<h3>Live Output</h3>
<div id="output" class="output">
<div id="content"></div>
<div id="status">Web NFC is not available.
Please make sure the "Experimental Web Platform features" flag is enabled on Android.</div>
<pre id="log"></pre>
</div>
<script>
if (/Chrome\/(\d+\.\d+.\d+.\d+)/.test(navigator.userAgent)){
// Let's log a warning if the sample is not supposed to execute on this
// version of Chrome.
if (89 > parseInt(RegExp.$1)) {
ChromeSamples.setStatus('Warning! Keep in mind this sample has been tested with Chrome ' + 89 + '.');
}
}
</script>
<script>
log = ChromeSamples.log;
if (!("NDEFReader" in window))
ChromeSamples.setStatus(
"Web NFC is not available.\n" +
'Please make sure the "Experimental Web Platform features" flag is enabled on Android.'
);
</script>
<script>scanButton.addEventListener("click", async () => {
log("User clicked scan button");
try {
const ndef = new NDEFReader();
await ndef.scan();
log("> Scan started");
ndef.addEventListener("readingerror", () => {
log("Argh! Cannot read data from the NFC tag. Try another one?");
});
ndef.addEventListener("reading", ({ message, serialNumber }) => {
log(`> Serial Number: ${serialNumber}`);
log(`> Records: (${message.records.length})`);
});
} catch (error) {
log("Argh! " + error);
}
});
writeButton.addEventListener("click", async () => {
log("User clicked write button");
try {
const ndef = new NDEFReader();
await ndef.write("Hello world!");
log("> Message written");
} catch (error) {
log("Argh! " + error);
}
});
</script>
</body></html>
But when I run it, it shows Web NFC is not available. Please make sure the "Experimental Web Platform features" flag is enabled on Android. on the message. When I click on "scan" button, it shows Argh! ReferenceError: NDEFReader is not defined.
May I know why the sample code work well when it is on https://googlechrome.github.io but can't work when I have it on my local PC? Thank you.

As documented in https://web.dev/nfc/#security-and-permissions, Web NFC is only available in secure browsing contexts. It means you either have to serve your webpage over https:// or localhost such as http://127.0.0.1 or http://localhost.
If you have installed npm, you can use npx http-serve.
If you have installed Python 2, use python -m SimpleHTTPServer
If you have installed Python 3, use python -m http.server

Related

How to disable 'Let site view files?' message (when running locally)

I am looking for a way to disable the popup message which shows when using window.showDirectoryPicker. This page is served from localhost via WebView2 component in a WPF app.
<!DOCTYPE html>
<html>
<body>
<div class="fl" style="margin: 0 0 2rem 0"><button id="addToFolder">Give access to folder</button></div>
</body>
</html>
<script>
let directory;
document.getElementById('addToFolder').addEventListener('click', async () => {
try {
directory = await window.showDirectoryPicker({
startIn: 'desktop'
});
for await (const entry of directory.values()) {
let newEl = document.createElement('div');
newEl.innerHTML = `<strong>${entry.name}</strong> - ${entry.kind}`;
document.getElementById('folder-info').append(newEl);
}
} catch (e) {
console.log(e);
}
});
</script>
Note that I already have full file system access via the WPF app, so its not a security concern.

Chrome Extension embedding script in active web page, in MV3?

beautiful people on the internet. I am new to chrome extension not new to writing code though. I have implemented webpack to use external packages. One major one in my application is npm package by the name "mark.js".
My application works like this i want some specific words to be highlighted in active webpage using this package. I have written code for this to achieve the functionality but the problem is with loading the script in a web page. I have performed different aspect of loading script but that doesnot work. The new MV3 version have some strict rules.
I want to achieve anything similar of loading script in an active webpage. Please help.
btn.addEventListener("click", async () => {
console.log("BUTTON IS PRESSED!!");
try {
await chrome.tabs.query(
{ active: true, currentWindow: true },
async function (tabs) {
chrome.scripting.executeScript({
target: { tabId: tabs[0].id },
func: highlightText,
args: [arr],
});
}
);
} catch (e) {
console.log("ERROR AT CHROME TAB QUERY : ", e);
}
});
async function highlightText(arr) {
console.log(typeof Mark);
try {
var instance2 = new Mark(document.querySelector("body"));
// instance2.mark("is");
var success = [];
// const instance2 = new Mark(document.querySelector("body"));
await Promise.all(
arr.map(async function (obj) {
console.log("OBJECT TEXT : ", obj.text);
instance2.mark(obj.text, {
element: "span",
each: function (ele) {
console.log("STYLING : ");
ele.setAttribute("style", `background-color: ${obj.color};`);
if (obj.title && obj.title != "") {
ele.setAttribute("title", obj.title);
}
ele.innerHTML = obj.text;
success.push({
status: "Success",
text: obj.text,
});
},
});
})
);
console.log("SUCCESS : ", success);
} catch (error) {
console.log(error);
}
}
There's no need to use chrome.runtime.getURL. Since you use executeScript to run your code all you need is to inject mark.js before injecting the function.
Also, don't load popup.js in content_scripts, it's not a content script (these run in web pages), it's a script for your extension page. Actually, you don't need content_scripts at all.
btn.addEventListener('click', async () => {
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
const target = { tabId: tab.id };
const exec = v => (await chrome.scripting.executeScript({ target, ...v }))[0].result;
if (!await exec({ func: () => !!window.Mark })) {
await exec({files: ['mark.js.min'] });
await exec({func: highlightText, args: [arr] });
}
});
For V3 I assume you will want to use Content Scripts in your manifest to inject the javascript into every webpage it matches. I recently open-sourced TorpedoRead and had to do both V2 and V3, I recommend checking the repo as it sounds like I did something similar to you (Firefox is V2, Chrome is V3).
The code below need to be added to your manifest.json and this will execute on every page based on the matches property. You can read more about content scripts here: https://developer.chrome.com/docs/extensions/mv3/content_scripts/
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["yourscript.js"]
}
],

PWA: Chrome warning "Service worker does not have the 'fetch' handler"

I'm currently unsuccessfully trying to make my PWA installable. I have registered a SertviceWorker and linked a manifest as well as I am listening on the beforeInstallPromt event.
My ServiceWorker is listening to any fetch event.
My problem is, that the created beforeInstall banner is just being shown on Chrome desktop but on mobile I get a warning in Chrome inspection tab "Application" in the "Manifest" section:
Installability
Service worker does not have the 'fetch' handler
You can check the message on https://dev.testapp.ga/
window.addEventListener('beforeinstallprompt', (e) => {
// Stash the event so it can be triggered later.
deferredPrompt = e;
mtShowInstallButton();
});
manifest.json
{"name":"TestApp","short_name":"TestApp","start_url":"https://testapp.ga/loginCheck","icons":[{"src":"https://testapp.ga/assets/icons/launcher-ldpi.png","sizes":"36x36","density":0.75},{"src":"https://testapp.ga/assets/icons/launcher-mdpi.png","sizes":"48x48","density":1},{"src":"https://testapp.ga/assets/icons/launcher-hdpi.png","sizes":"72x72","density":1.5},{"src":"https://testapp.ga/assets/icons/launcher-xhdpi.png","sizes":"96x96","density":2},{"src":"https://testapp.ga/assets/icons/launcher-xxhdpi.png","sizes":"144x144","density":3},{"src":"https://testapp.ga/assets/icons/launcher-xxxhdpi.png","sizes":"192x192","density":4},{"src":"https://testapp.ga/assets/icons/launcher-web.png","sizes":"512x512","density":10}],"display":"standalone","background_color":"#ffffff","theme_color":"#0288d1","orientation":"any"}
ServiceWorker:
//This array should NEVER contain any file which doesn't exist. Otherwise no single file can be cached.
var preCache=[
'/favicon.png',
'/favicon.ico',
'/assets/Bears/bear-standard.png',
'/assets/jsInclude/mathjax.js',
'/material.js',
'/main.js',
'functions.js',
'/material.css',
'/materialcolors.css',
'/user.css',
'/translations.json',
'/roboto.css',
'/sw.js',
'/'
];
//Please specify the version off your App. For every new version, any files are being refreched.
var appVersion="v0.2.1";
//Please specify all files which sould never be cached
var noCache=[
'/api/'
];
//On installation of app, all files from preCache are being stored automatically.
self.addEventListener('install', function(event) {
event.waitUntil(
caches.open(appVersion+'-offline').then(function(cache) {
return cache.addAll(preCache).then(function(){
console.log('mtSW: Given files were successfully pre-cached')
});
})
);
});
function shouldCache(url) {
//Checking if url is market as noCache
var isNoCache=noCache.includes(url.substr(8).substr(url.substr(8).indexOf("/")))||noCache.includes((url.substr(8).substr(url.substr(8).indexOf("/"))).substr(0,(url.substr(8).substr(url.substr(8).indexOf("/"))).indexOf("?")));
//Checking of hostname of request != current hostname
var isOtherHost=url.substr(8).substr(0,url.substr(8).indexOf("/"))!=location.hostname&&url.substr(7).substr(0,url.substr(7).indexOf("/"))!=location.hostname;
return((url.substr(0,4)=="http"||url.substr(0,3)=="ftp") && isNoCache==false && isOtherHost==false);
}
//If any fetch fails, it will look for the request in the cache and serve it from there first
self.addEventListener('fetch', function(event) {
//Trying to answer with "online" version if fails, using cache.
event.respondWith(
fetch(event.request).then(function (response) {
if(shouldCache(response.url)) {
console.log('mtSW: Adding file to cache: '+response.url);
caches.open(appVersion+'-offline').then(function(cache) {
cache.add(new Request(response.url));
});
}
return(response);
}).catch(function(error) {
console.log( 'mtSW: Error fetching. Serving content from cache: ' + error );
//Check to see if you have it in the cache
//Return response
//If not in the cache, then return error page
return caches.open(appVersion+'-offline').then(function (cache) {
return cache.match(event.request).then(function (matching) {
var report = !matching || matching.status == 404?Promise.reject('no-match'): matching;
return report
});
});
})
);
})
I checked the mtShowInstallButton function. It's fully working on desktop.
What does this mean? On the Desktop, I never got this warning, just when using a handheld device/emulator.
Fetch function is used to fetch JSon manifest file. Try reading google docs again.
For adding PWA in Mobile you need manifest file to be fetched which is fetched using service-worker using fetch function.
Here is the code :
fetch('examples/example.json')
.then(function(response) {
// Do stuff with the response
})
.catch(function(error) {
console.log('Looks like there was a problem: \n', error);
});
for more about fetch and manifest try this.

Getting chrome.getAuthToken to work inside a script

I'm building a Chrome extension that retrieves data from a user's Google Drive and inserts it into an arbitrary page. I've gotten the OAuth to work, but I can't seem to get access to the token (which I can see is set via chrome://identity-internals).
The issue here is that when the Chrome extension nav bar button is clicked, I fire a request to execute test.js. test.js apparently has no concept of chrome.identity, but it needs that information to make an XHR request. I've tried
Storing the auth token in localStorage so that test.js can retrieve it (no luck)
Nesting the test.js inside the AuthToken request (not sure how to actually pass the variable into the file and retrieve it).
Are there any ideas?
Thank you in advance!
chrome.browserAction.onClicked.addListener(function (tab) {
chrome.identity.getAuthToken({ 'interactive': true }, function(token) {
// This works
alert(token);
// This doesn't work
localStorage.setItem("authtoken", token);
});
chrome.tabs.executeScript(tab.id, {
// This file needs access to the authtoken
// but within test.js, chrome.identity is undefined.
"file": "test.js"
}, function () {
});
});
localStorage (effectively it's window.localStorage) is stored per origin (scheme + hostname + port number), and extensions have their own one in privileged components that can access restricted chrome.* API (some are listed as exceptions in content scripts docs), namely popup and background/event page, options, and other pages with a URL like chrome-extension://abc..... (abc... is an extension ID).
localStorage of a web page belongs to its own origin such as https://www.google.com.
Content scripts run in the context of web page, so they can't access extension's localStorage directly. They see localStorage of their web page's origin only.
Solution 1: use another executeScript to set a variable that will be used by the content script injected from a file:
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.identity.getAuthToken({interactive: true}, function(token) {
chrome.tabs.executeScript(tab.id, {
code: 'var token=' + JSON.stringify(token) + ';'
}, function() {
chrome.tabs.executeScript(tab.id, {file: "test.js"}, function() {
});
});
});
});
JSON-serialization is used in order not to bother escaping special characters and be able to pass objects.
Solution 2: use messaging API to pass data once the content script is injected:
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.identity.getAuthToken({interactive: true}, function(token) {
chrome.tabs.executeScript(tab.id, {file: "test.js"}, function() {
chrome.tabs.sendMessage(tab.id, {token: token});
});
});
});
content script:
chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) {
if (msg.token) {
document.getElementById('token').textContent = msg.token;
//nowYouCanProcessToken(msg.token);
}
});
Solution 3: use chrome.storage API accessible both from a content script and the abovementioned privileged parts of an extension.
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.identity.getAuthToken({interactive: true}, function(token) {
chrome.storage.local.set({token: token}, function() {
chrome.tabs.executeScript(tab.id, {file: "test.js"}, function() {
});
});
});
});
content script:
chrome.storage.local.get('token', function(data) {
if (data.token) {
document.getElementById('token').textContent = data.token;
//nowYouCanProcessToken(data.token);
}
});

framework.realtime.co and phonegap for Android

I'm making a web with framework.realtime.co. It works perfect on web, but when I compile mi web to an app with phonegap, the app works perfect but the connection realtime never works.
On the web version I have msj "Connected" but on the compiled version with phonegap, it just says nothing.
here is the html
<div id="status">
Loading...
</div>
Here is the code js
loadOrtcFactory(IbtRealTimeSJType, function (factory, error) {
if (error != null) {
//////console.log("Factory error: " + error.message);
} else {
client = factory.createClient();
client.setClusterUrl('https://ortc-developers-useast1.realtime.co/server/ssl/2.1');
client.onConnected = function(c) {
document.getElementById('status').innerHTML = "connected";
// send("New hello world");
c.subscribe('channelAlerta', true, elem_received);
}
client.onDisconnected = function() {
document.getElementById('status').innerHTML = "disconnected";
}
client.onReconnecting = function() {
document.getElementById('status').innerHTML = "reconnected ...";
}
client.connect(appkey, token);
}
});
You need to use a websocket PhoneGap plugin. Check the example at
https://github.com/realtime-framework/Messaging/tree/master/PhoneGap