Chrome can't load web worker - javascript

I am working on a project that uses a web worker.
In my head section I have this code:
var worker = new Worker("worker.js");
// More code
This works fine in Safari, but Chrome reports the following error:
Uncaught SecurityError: Failed to create a worker: script at '(path)/worker.js' cannot be accessed from origin 'null'.
Why does this work perfectly in Safari but not Chrome? How do I fix this?
Thank you.

Chrome doesn't let you load web workers when running scripts from a local file.

I use a workaround. Chrome blocks Worker but not <script>. Hence the best way to make a universal solution is this:
function worker_function() {
// all code here
}
// This is in case of normal worker start
// "window" is not defined in web worker
// so if you load this file directly using `new Worker`
// the worker code will still execute properly
if(window!=self)
worker_function();
You then link it as normal <script src="...". And once the function is defined, you use this abomination of a code:
new Worker(URL.createObjectURL(new Blob(["("+worker_function.toString()+")()"], {type: 'text/javascript'})));

The problem has been properly explained by Noble Chicken but I have a more general solution for it. Instead of installing wamp or xamp, with python you can navigate to the folder your project is hosted in and type: python -m http.server
Just that and you will have a running server on that folder, reachable from localhost.

You can also use the --allow-file-access-from-files flag when you launch Chrome.
Example for MacOsX :
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --allow-file-access-from-files
More info : Web worker settings for chrome

It is because of the security restrictions. You need to use http:// or https:// protocol instead of file:///.
If you have NodeJS installed, you can simply do the following.
- Note that this is one of many options available
Install local-web-server
$ npm install -g local-web-server
Now you can use it in any folder that you want to access the contents through http .
$ ws
Navigate to http://localhost:8000 (default port: 8000)

I had the same problem as your post too. The solution is that you have to run it with localhost (wamp or xamp). It will done.

Another workaround is use Google's web server for Chrome extension. Choose your work directory and start the server, Done!

This is inspired by Thomas answer above. But with one caveat that
I wanted to distribute only the HTML, so i manually converted the js to dataURL. and enabling the data URL check box in it.
const myWorker = new Worker("data:application/x-javascript;base64,b25tZXNzYW...");

Easy way to make local http server in chrome is this app:
Web Server for Chrome
https://chrome.google.com/webstore/detail/web-server-for-chrome/ofhbbkphhbklhfoeikjpcbhemlocgigb/related
Description:
A Web Server for Chrome, serves web pages from a local folder over the network, using HTTP. Runs offline.
Web Server for Chrome is an open source (MIT) HTTP server for Chrome.
It runs anywhere that you have Chrome installed, so you can take it anywhere. It even works on ARM chromebooks.
It now has the option to listen on the local network, so other computers can access your files. Additionally, it can try and get an internet address.
Many people use this to do basic web development on a chromebook. It is also handy for sharing files over a local network between computers, or even on the internet.
Once you install it, navigate to http://127.0.0.1:8887
And it is not unsecure as flag --allow-file-access-from-files

you need a web server for request from HTTP protocol Instead of local file
and work correctly :)

Chrome load the file but cannot run it. Use Firefox. It's working for me.

With Python 2.x being more widely deployed than Python 3.x, something like python -m SimpleHTTPServer 8000 is more generally applicable, and not just for Mac OS X. I found it necessary for use under Cygwin, for instance.
With that in place, this example worked like a champ.

function worker_fun(num){
num ++
// console.log(num)
postMessage(num);
setTimeout(worker_fun.bind(null,num), 500)
}
var w
function startWorker(){
var blob = new Blob([
"onmessage = function(e){\
" + worker_fun.toString() + "\
worker_fun(e.data.num);}"
]);
var blobURL = window.URL.createObjectURL(blob);
if (typeof(Worker) != 'undefined'){
if (typeof(w) == 'undefined'){
w = new Worker(blobURL);
w.onmessage = function(event){
document.getElementById('num').innerHTML = event.data;
}
w.postMessage({
num:parseInt(document.getElementById('num').innerHTML)})
}
}
}
function stopWorker() {
w.terminate();
w = undefined;
}
As mentioned chrome does not support it. I like to define my workers in the same file. This is a working workaround which will increase a number found in innerHTML of the element the with id=num every 500ms.

A probably reason is chrome doesn't let you load web workers when running scripts from a local file. And I try run the code on my firefox, can not either.

To load web worker from file in a project set up with Webpack and TypeScript I used a script as Tomáš Zato suggested. However, I had to modify the worker file.
worker.ts
(() => {
console.log("worker_function loaded");
// #ts-ignore
window.worker_function = () => {
self.onmessage = ({ data: { question } }) => {
// #ts-ignore
self.postMessage({
answer: 42,
});
};
}
})();
index.ts
async function run() {
console.log('run()');
const worker = new Worker(
// #ts-ignore
URL.createObjectURL(new Blob(["("+worker_function.toString()+")()"], { type: 'text/javascript' }))
);
worker.postMessage({
question: 'The Answer to the Ultimate Question of Life, The Universe, and Everything.',
});
worker.onmessage = ({ data: { answer } }) => {
console.log(answer);
};
}
run();
index.html
<html lang="en-us">
<head>
<meta charset="utf-8" />
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
<title>Offscreen canvas with web worker sample project</title>
<script async type="text/javascript" src="worker.js"></script>
<script async type="text/javascript" src="app.js"></script>
</head>
<body>
<h1>web worker sample project</h1>
</body>
</html>
webpack.config.js (version 5)
const path = require("path");
const CopyPlugin = require("copy-webpack-plugin");
module.exports = {
mode: "production",
entry: {
app: "./src/index.ts",
worker: "/src/worker.ts"
},
output: {
filename: "[name].js",
path: path.resolve(__dirname, "build")
},
performance: {
hints: false
},
module: {
rules: [
{
test: /\.tsx?$/,
use: "ts-loader",
exclude: /node_modules/
},
]
},
resolve: {
extensions: [".js", ".ts"]
},
plugins: [
new CopyPlugin({
patterns: [
{ from: "src/index.html", to: "" }
]
})
]
};

Yes, It will not work in chorome if your are loading local file. But it will work fine in firefox browser. And you have to add below code in HTML file.
<head>
<meta charset="UTF-8" />
</head>

Related

How do I initialize a webworker in NextJS 10?

I have a Next 10 project where I am trying to use WebWorkers. The worker is being initialized like so:
window.RefreshTokenWorker = new Worker(new URL('../refreshToken.worker.js', import.meta.url))
I also have the Worker defined as
self.addEventListener('message', (e) => {
console.info("ON MESSAGE: ", e)
// some logic with e.data
})
Its also being called like this:
const worker = getWorker() // gets worker that is attached at the window level
worker.postMessage('start')
My next.config.js file is defined as
const nextConfig = {
target: 'serverless',
env: getBuildEnvVariables(),
redirects,
rewrites,
images: {
domains: []
},
future: { webpack5: true },
webpack (config) {
config.resolve.alias['#'] = path.join(__dirname, 'src')
return config
}
}
// more definitions
module.exports = nextConfig
The issue I have is the console.info in the Web Worker definition does not receive the message being sent from postMessage on the build version (yarn build && yarn start) but it does on the dev version (yarn dev). Any ways to fix this?
This is not a solution. But can be a messy way to do the job. This turned out to be a nightmare for me.
I have the same setup as yours. I was initializing web worker as you have shown in your question. I got this idea from the nextjs doc itself: https://nextjs.org/docs/messages/webpack5
const newWebWorker = new Worker(new URL('../worker.js', import.meta.url))
Everything working correctly when I work in dev mode. it is picking up the worker.js file correctly and everything looks alright.
But when I build the nextjs and try it, then web worker won't work. When I dive deeply into the issues, I found out that the worker.js chunk file is created directly under the .next folder. It should come under .next/static/chunk/[hash].worker.js ideally.
I could not resolve this issue in a proper way.
So what i did, i placed my worker.js file directly under public directory. I put my worker.js file transpiled and optimized and put the code in the public/worker.js file.
After this, I modified the worker initialization like this:
const newWebWorker = new Worker('/worker.js', { type: 'module' });
it is working in the production build now. I will report once I get a cleaner solution for this.

Issue with webpack in production mode ( minification issue )

I am working on this little project for Augmented Reality, ARnft it is based on a lighter version of Jsartoolkit5, JsartoolkitNFT for only NFT markers. The code follows the ES6 standard (partially) and use webpack as bundler. All is fine in development mode but when i go in production mode, the example stuck with this error:
05ff8846-4121-4380-86c3-9612f404732a:1 Uncaught SyntaxError: Function statements require a function name
It stop at the embedded Worker. The app don't enter inside because i otherwise i will receive some messages in the dev console.
I Inject the Worker in a Blob object:
// create a Worker to handle loading of NFT marker and tracking of it
const workerBlob = new Blob(
[workerRunner.toString().replace(/^function .+\{?|\}$/g, '')],
{ type: 'text/js-worker' }
)
const workerBlobUrl = URL.createObjectURL(workerBlob)
worker = new Worker(workerBlobUrl)
https://github.com/kalwalt/ARnft/blob/8322585aa0f863917c6d1cee541356ff3b7c36a0/src/utils/Utils.js#L207-L213
workerRunner defined at this line:
https://github.com/kalwalt/ARnft/blob/8322585aa0f863917c6d1cee541356ff3b7c36a0/src/utils/Utils.js#L272
I think that is a minification issue i tried to add --optimize-minimize in the script:
"build-es6": "webpack --mode production --optimize-minimize",
, but not helped. How can i solve this?
Thank you
This issue can be solved with the worker-loader plugin.
Instead of inlining the worker in a Blob as explained in the question:
create an external Worker.js and import in the file (in this case Utils.js):
import Worker from './Worker.js'
use the worker as usual:
let worker
// other code
worker = new Worker()
// other code with postMessage and onmesssage...
in wepback.config.js
{
test: /\worker\.js$/,
use: {
loader: 'worker-loader',
options: { inline: true, fallback: false }
}
}
You can also see this commit and the issue on webpack.

How to probe if a file was download using Selenium/WebdriverIO

I want to know how I can verify if a file was downloaded using Selenium Webdriver after I click the download button.
Your question doesn't say whether you want to confirm it locally or remotely(like browserstack) . If it is remotely then my answer will be "NO" as you can see that the file is getting downloaded but you can not access the folder. So you wont be able to assert that the file has been downloaded.
If you want to achieve this locally(in Chrome) then the answer is "YES", you can do it something like this:
In wdio.conf.js(To know where it is getting downloaded)
var path = require('path');
const pathToDownload = path.resolve('chromeDownloads');
// chromeDownloads above is the name of the folder in the root directory
exports.config = {
capabilities: [{
maxInstances: 1,
browserName: 'chrome',
os: 'Windows',
chromeOptions: {
args: [
'user-data-dir=./chrome/user-data',
],
prefs: {
"download.default_directory": pathToDownload,
}
}
}],
And your spec file(To check if the file is downloaded or not ?)
const fsExtra = require('fs-extra');
const pathToChromeDownloads = './chromeDownloads';
describe('User can download and verify a file', () =>{
before(() => {
// Clean up the chromeDownloads folder and create a fresh one
fsExtra.removeSync(pathToChromeDownloads);
fsExtra.mkdirsSync(pathToChromeDownloads);
});
it('Download the file', () =>{
// Code to download
});
it('Verify the file is downloaded', () =>{
// Code to verify
// Get the name of file and assert it with the expected name
});
});
more about fs-extra : https://www.npmjs.com/package/fs-extra
Hope this helps.
TL;DR: Unless your web-app has some kind of visual/GUI trigger once the download finishes (some text, an image/icon-font, push-notification, etc.), then the answer is a resounding NO.
Webdriver can't go outside the scope of your browser, but your underlying framework can. Especially if you're using NodeJS. :)
Off the top of my head I can think of a few ways I've been able to do this in the past. Choose as applicable:
1. Verify if the file has been downloaded using Node's File System (aka fs)
Since you're running WebdriverIO, under a NodeJS environment, then you can make use its powerful lib tool-suite. I would use fs.exists, or fs.existsSync to verify if the file is in the expected folder.
If you want to be diligent, then also use fs.statSync in conjunction with fs.exists & poll the file until it has the expected size (e.g.: > 2560 bytes)
There are multiple examples online that can help you put together such a script. Use the fs documentation, but other resources as well. Lastly, you can add said script inside your it/describe statement (I remember your were using Mocha).
2. Use child_process's exec command to launch third-party scripts
Though this method requires more work to setup, I find it more relevant on the long run.
!!! Caution: Apart from launching the script, you need to write a script in a third-party framework.
Using an AutoIT script;
Using a Sikuli script;
Using a TestComplete (not linking it, I don't like it that much), or [insert GUI verification script here] script;
Note: All the above frameworks can generate an .exe file that you can trigger from your WebdriverIO test-cases in order to check if your file has been downloaded, or not.
Steps to take:
create one of the stand-alone scripts like mentioned above;
place the script's .exe file inside your project in a known folder;
use child_process.exec to launch the script and assert its result after it finishes its execution;
Example:
exec = require('child_process').exec;
// Make sure you also remove the .exe from scriptName
var yourScript = pathToScript + scriptName;
var child = exec(yourScript);
child.on('close', function (code, signal) {
if (code!==0) {
callback.fail(online.online[module][code]);
} else {
callback();
}
});
Finally: I'm sure there are other ways to do it. But, your main take-away from such a vague question should be: YES, you can verify if the file has been downloaded if you absolutely must, expecially if this test-case is CRITICAL to your regression-run.

Is it feasible to use web worker (multi-threading) in Angular and Typescript in NativeScript?

I'm currently develop an App that is based on NativeScript and Angular2.
My screen freeze for while when my App fetching data through HTTP, and I'd like to put the fetching action into another thread.
I did a lot of search on the web, and all I got is the code in javascript like the official doc - https://docs.nativescript.org/angular/core-concepts/multithreading-model.html
Is there any way to implement the muli-threading with WebWorker in "Typescript"(which contain the support of Angular injected HTTP service) instead of the "Javascript" code(the code from the official doc)
It's appreciated if someone could give me some guide or hint, and it'll be great if I could got some relative example code.
Thanks.
There shouldn't be any big draw back for using WebWorkers in {N} + Angular but be aware that currently the WebWorker is not "exactly" compatible with Angular AoT compilation.
For me when creating an WebwWrker (var myWorker = new Worker('~/web.worker.js');) throws and error after bundling the application with AoT. I have seen soem talk about this in the community and possible the way to fix this is by editing the webpack.common.js and adding an "loaded" like so:
{
test: /\.worker.js$/,
loaders: [
"worker-loader"
]
}
Disclaimer: I have not tried this approach for fixing the error.
If someone have some problems adding workers in Nativescript with Angular and Webpack, you must follow the steps listed here.
Keep special caution in the next steps:
When you import the worker, the route to the worker file comes after nativescript-worker-loader!.
In the webpack.config.js keep caution adding this piece of code:
{
test: /.ts$/, exclude: /.worker.ts$/, use: [
"nativescript-dev-webpack/moduleid-compat-loader",
"#ngtools/webpack",
]
},
because is probable that you already have configured the AoT compilation, like this:
{
test: /(?:\.ngfactory\.js|\.ngstyle\.js|\.ts)$/,
use: [
"nativescript-dev-webpack/moduleid-compat-loader",
"#ngtools/webpack",
]
},
and you only need to add the exclude: /.worker.ts$/,
Finally, there is an example of a worker, in this case it use an Android native library:
example.worker.ts:
import "globals";
const context: Worker = self as any;
declare const HPRTAndroidSDK;
context.onmessage = msg => {
let request = msg.data;
let port = request.port;
let result = HPRTAndroidSDK.HPRTPrinterHelper.PortOpen("Bluetooth," + port.portName);
context.postMessage(result);
};
example.component.ts (../../workers/example.worker is the relative route to my worker):
import * as PrinterBTWorker from "nativescript-worker-loader!../../workers/example.worker";
import ...
connect(printer: HPRTPrinter): Observable<boolean> {
if (this.isConnected()){
this.disconnect(); //Disconnect first if it's already connected
}
return Observable.create((observer) => {
const worker = new PrinterBTWorker();
worker.postMessage({ port: printer });
worker.onmessage = (msg: any) => {
worker.terminate();
if (msg.data == 0) { // 0: Connected, -1: Disconnected
observer.next(true);
}
else {
observer.next(false);
}
};
worker.onerror = (err) => {
worker.terminate();
observer.next(false);
}
}).pipe(timeout(5000), catchError(err => of(false)));
}
Note: I use an Observable to make my call to the worker async and to add a timeout to the call to the native code, because in the case that it is not possible to connect to the printer (ex. it's turned off), it takes almost 10 seconds to notify, and this caused in my case the frezing of the app for all that time.
Important: It seem that it's necessary to run again the code manually every time that a change is made, because the worker isn't compiled using AoT.

Opening a file with protractor

Every protractor example I can find on the internet seems to use browser.get with a web URI.
browser.get('http://localhost:8000');
I'd like to use Selenium to simple navigate to a file:// path so that I don't need a local web server running in order to perform tests. All I need is a simple HTML page and some assets.
That doesn't seem to work though.
browser.get('file:///Users/myusername/dev/mpproject/spec/support/index.html');
When I paste that URI into my browser window I get a HTML page. When I try to open it with protractor I get a timeout.
How can I run tests on this page with protractor? The ideal answer will work with a relative file path from the myproject root.
I am posting the solution I've found in here which helped me run Protractor with a file protocol.
By default, Protractor use data:text/html,<html></html> as resetUrl, but location.replace from the data: to the file: protocol is not allowed (we'll get "not allowed local resource" error), so we replace resetUrl with one with the file: protocol:
exports.config = {
// ...
baseUrl: 'file:///absolute/path/to/your/project/index.html',
onPrepare: function() {
// By default, Protractor use data:text/html,<html></html> as resetUrl, but
// location.replace from the data: to the file: protocol is not allowed
// (we'll get ‘not allowed local resource’ error), so we replace resetUrl with one
// with the file: protocol (this particular one will open system's root folder)
browser.resetUrl = 'file://';
}
// ...
};
If you want to run a relative path to your project folder, then you could just use Node.js tools, because Protractor runs in a Node.js environment. For example, __dirname will return an absolute path to a directory where your Protractor config file is saved. As a result use:
exports.config = {
// ...
baseUrl: 'file://' + __dirname + '/spec/support/index.html'
// ...
};
Also, if you application does XHR requests to some endpoints, which are not allowed from file:, you may have to run your test browser with custom flags. In my case it was Chrome:
exports.config = {
// ...
capabilities: {
browserName: 'chrome',
chromeOptions: {
// --allow-file-access-from-files - allow XHR from file://
args: ['allow-file-access-from-files']
}
}
// ...
}
I had the same error and fixed by applying Michael Radionov's fix but removing the baseUrl. Here is my setup:
protractor.config.js:
exports.config = {
capabilities: {
browserName: 'chrome'
},
specs: [
'*.js'
],
onPrepare: function() {
// By default, Protractor use data:text/html,<html></html> as resetUrl, but
// location.replace from the data: to the file: protocol is not allowed
// (we'll get ‘not allowed local resource’ error), so we replace resetUrl with one
// with the file: protocol (this particular one will open system's root folder)
browser.ignoreSynchronization = true;
browser.waitForAngular();
browser.sleep(500);
browser.resetUrl = 'file:///';
}
};
e2etest.js:
'use strict';
describe("Buttons' tests are started", function() {
it('Should retrieve 20 records into table', function() {
browser.get('file:///C:/Users/path_to_file/index.html');
/* Test codes here */
});
});
What is the error log?
This could be something related to 'Loading' angular. For that you can try
browser.driver.ignoreSynchronization = true;
The error log will surely help in trying to understand the problem.
I think there is typo mistake. In "get" method you should include the URL in double quotes "".
Try using double quotes like below:
WebDriver driver=new FirefoxDriver();
driver.get('file:///E:/Programming%20Samples/HTML%20Samples/First%20Program.html');

Categories