Angular infinite change detection loop caused by oidc-client library - javascript

I copied the solution people posted on github linked below, check it out for further comments by library creators, if they ever solve this.
I'm writing an Angular app, using version 12. I opened up Angular devtools and found that I have ~150 change detection cycles each second, nonstop.
It's worth mentioning that it is an ASP.NET Core WebAPI app, which comes with ApiAuthorizationModule that adds logic and elements regarding login, logout, navigation etc. I can't prove it definitely, but everything points to it being the root cause.
Each passes through different components, but they all have the source in common:
Window.addEventListener:message
Can anyone explain this behavior? Or at least point me in the right direction? I've got absolutely no idea what causes this particular event to be added to window.
I do get this error message in regular Chrome console:
Content Security Policy of your site blocks the use of 'eval' in
JavaScript` The Content Security Policy (CSP) prevents the evaluation
of arbitrary strings as JavaScript to make it more difficult for an
attacker to inject unathorized code on your site. To solve this issue,
avoid using eval(), new Function(), setTimeout([string], ...) and
setInterval([string], ...) for evaluating strings.
Maybe all of this has something to do with some silent refresh or something, I think they're related somehow?
https://github.com/manfredsteyer/angular-oauth2-oidc/issues/785
[EDIT]: Now that I've read through this entire github thread, yeah, that's exactly the issue. Any tips on where to put this command from original post this.ngZone.runOutsideAngular(() => {...}?

The solution, as seen on the github link form my question, is to disable zone patches for message event and hope that creators of the library will solve the bug at some pont. I followed the advice and created zone-patches.ts like this:
(window as any).__Zone_ignore_on_properties = [
{
target: window,
ignoreProperties: ['message'],
},
];
(window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['message'];
export { };
And imported it in pollyfills.ts like so:
import './zone-patches';
import 'zone.js/dist/zone'; // Included with Angular CLI.
Just make sure you respect the order and include your custom import before the zone itself.
This leaves sporadic detection cycles caused by some setInterval somewhere, but it's a few cycles per second so - much better than before!
If someone solves that one as well, please shoot a comment for the rest of us :)

Related

Issue with Google Tag Manager implementation. "Uncaught TypeError: Wb.set is not a function"

Recently I started having Issues with the Google Tag Manager.
I can't track it to a Tag or a Trigger (activated and deactivated individually to check).
The error started occurring this week (no updates were made to the system or template). I noticed when testing the implementation of a new Tag in GTM. That shortly worked but then this error message started showing in the console. Removing the new Tag didn't change the situation and since the code worked for a while (some 30minutes) I don't expect a direct relationship between the new tag and the error.
All I have is the console in the frontend that shows the following error message:
Uncaught TypeError: Wb.set is not a function
The error stems from this file:
https://www.googletagmanager.com/gtm.js?id=GTM-XXXXXXX
Wb is started as a Map just a few steps earlier:
Wb=new Map(Vb.h.F);
Wb.set("style",{ya:4});
The GTM Script is copied and pasted from the GTM-Admin without any change made to it and is included in the header.php file of the Wordpress template. I tried copying it again and replacing the script that was in the header.php previously but the result was the same.
If looking at the actual error message in the console helps, the issue can be seen on the following URL:
https://www.bindella.ch
Any help in solving this or at least pointer to where to go look for errors would be very much appreciated.
Thanks
Very interesting, and yes, you're right, the error in the minified code makes no sense:
Let's try something different. First, just export your GTM container (the workspace that is synced with prod/live) as a JSON file in Admin section.
Open the file, look for something like set("style" See if you can find that Wb. Well, don't expect it to be Wb. I guess GTM minifies the code. It may be some weird chat bot, or something like that. From there, you'll be able to find the tag or variable that causes the issue.
I didn't look too deep into it, but that part of code looks sophisticated enough to be a part of the core container code. Actually, we can check it right here, on SO cuz it uses GTM too, let's do that.
Yes, SO has identical code. Looks like it's indeed core GTM. Well, there's a tiny probability that when you built the prod library last time, GTM had an issue and it deployed an artefact.
Try making a new workspace, make a nonsensical change in it and publish it to production. See if it lets you publish. Then see if that fixes the issue.
Now, if it doesn't then the next thing I'd try is trying to re-importing the exported JSON, forcing GTM compare the import to what's there and find differences.
Now if that shows no issues, then I would make a brand new container, load the config in it and try replacing the gtm loading script to see if the error still happens. It sounds like you can do that. If you can't, look a plugin like redirector to redirect the request to a different container.
Still the error with the new container? How about if you load a completely empty container? Got any errors?
There's a very slim chance that your front-end deploys one of a few vars GTM uses too, thus conflicting with your GTM. But it's very unlikely. Why would you use something like google_tag_manager or google_tag_data in the global scope...
If nothing helps and only the empty container doesn't give errors, then... well, then make a list of all tags firing along that error and start disabling them one by one. Or use breakpoints to narrow it down to the tag/library that causes it.

How to start building a Custom Component in Formio.js? Where are docs?

I am trying to get a custom component working in Formio.js. I would love a complete, nontrivial working example.
I am not using angular, ng, react or the form.io service.
The documentation is terrible. I can copy out the Checkmatrix example code and run it (after much fiddling) but even it doesn't work correctly: in the formbuilder, the edit and delete controls don't show up. (There an bug issue open on this, but no resolution, which is distinctly worrisome.)
There are dead links all over the SDK reference documentation.. like for example for "Component" which seems particularly important.
There is no documentation of any of code used by the example. For example, it uses the 'renderTemplate' call, but the arguments are not described anywhere.
It appears that the only way to understand any part of this system to try to figure out all of the source code. There are no instructions for adding code.
It's not even clear what the best way to proceed is: whether I should fork the formio.js repo, learn TypeScript, and add components directly (creating a hassle if I ever want to keep formio.js up to date) or continue trying to work by registering components from add-on scripts in the browser.
** Can anyone give concrete advice on the best way to go? **
#nathaniel Tagg I couldn't find form.io proper form examples, so i would like to see your form.io examples if you are like to provide. Here is my email 'udara#staff.medicalwizard.com.au'

How to debug timed out waiting for asynchronous Angular tasks? Failure to find elements on angular page occurring

Edit: Note that I found the root of my problem after help from #ernst-zwingli, so if you have this same error one of his noted fixes might help you out. My problem is a known issue with Protractor itself, if you think this may be you, I've expanded on my steps to pinpoint the root of the problem after my original question.
I'm trying to use Protractor in an Angular2 (just Angular) application built using angular-cli.
My problem: Elements on an Angular app page are not being found when browser.waitForAngularEnabledis at it's default setting of true (as in 'I believe I am on an angular page and would like for Protractor to do it's magic'). They are being found just fine if I set browser.waitForAngularEnabledto false (as in 'I am not on an angular page and would like to handle this myself, take a seat Protractor'). How do I track down what's causing this on my definitely Angular pages?
I have a product with a non-Angular Auth0 login page that gates access to the rest of the product that is written in Angular (Angular 4.3.2 to be exact). I have successfully traversed logging in on the non-Angular login page. I flipped the waitForAngularEnabled switched to false to facilitate the non-Angular login. I turned it back to true at the point where I expected my initial landing page (Angular) to be loaded, after clicking the submit button. Code is as follows:
browser.waitForAngularEnabled(false);
browser.driver.get('https://dashboard.net/projects');
browser.driver.sleep(10000);
browser.driver.findElement(By.css("[type='email']"));
browser.driver.findElement(By.css("[type='email']")).sendKeys("email#example.com");
browser.driver.findElement(By.css(".auth0-label-submit")).click();
browser.driver.findElement(By.id("passwordInput")).sendKeys("password");
browser.driver.findElement(By.id("submitButton")).click();
browser.driver.sleep(5000); // needed if not waiting for Angular
//browser.waitForAngularEnabled(true); // Back to Protractor land we go
let elementToFind = element(by.className("header-text"));
elementToFind.isDisplayed().then(function() {grabTheDarnLocalStorage()});
expect(elementToFind.isDisplayed()).toBeTruthy();
If I uncomment the browser.waitForAngularEnabled(true); line to state that I'm back in Angular code I get the error trace as follows:
Failed: Timed out waiting for asynchronous Angular tasks to finish after 30 seconds. This may be because the current page is not an Angular application. Please see the FAQ for more details: https://github.com/angular/protractor/blob/master/docs/timeouts.md#waiting-for-angular
While waiting for element with locator - Locator: By(css selector, .header-text)
ScriptTimeoutError: asynchronous script timeout: result was not received in 30 seconds
(Session info: chrome=61.0.3163.100)
(Driver info: chromedriver=2.32.498550 (9dec58e66c31bcc53a9ce3c7226f0c1c5810906a),platform=Windows NT 10.0.14393 x86_64)
at WebDriverError (C:\Users\c-shouston\AppData\Roaming\npm\node_modules\protractor\node_modules\selenium-webdriver\lib\error.js:27:5)
at ScriptTimeoutError (C:\Users\c-shouston\AppData\Roaming\npm\node_modules\protractor\node_modules\selenium-webdriver\lib\error.js:203:5)
at Object.checkLegacyResponse (C:\Users\c-shouston\AppData\Roaming\npm\node_modules\protractor\node_modules\selenium-webdriver\lib\error.js:505:15)
at parseHttpResponse (C:\Users\c-shouston\AppData\Roaming\npm\node_modules\protractor\node_modules\selenium-webdriver\lib\http.js:509:13)
at doSend.then.response (C:\Users\c-shouston\AppData\Roaming\npm\node_modules\protractor\node_modules\selenium-webdriver\lib\http.js:440:13)
at process._tickCallback (internal/process/next_tick.js:109:7)
From: Task: Protractor.waitForAngular() - Locator: By(css selector, .header-text)
I've referenced the FAQ: https://github.com/angular/protractor/blob/master/docs/timeouts.md#waiting-for-angular
I have my devs stating that they don't use $timeout (they use (Edit: NOT $interval) Observable Interval thank you very much) and they're not sure about $http.
I found this solution about the canonical way to debug protractor Angular sync issue issue: Canonical way to debug Protractor-to-Angular sync issues
but I'm not sure the solution works without access to modifying the dev code to run the programmatic tracker. (Edit: I never did figure out how to get this to work)
I also found this about a long timeout you add before each test, but I feel this is unnecessary overhead that makes your overall test execution take longer than it should without understanding the root cause of the problem: https://stackoverflow.com/a/37217167/2718402 (Edit: yeah, this is a bad idea and adds unnecessary time to your tests, please don't do this)
The frustrating bit is that this seems to be a common occurrence and there doesn't seem to be a streamlined documentation on how to deal with it. Logging in with a non-Angular page only to transition to an Angular page. Angular pages not being picked up properly by Protractor. All of the examples I find online are bits of code that I don't have a reference for where they should be at in my overall test framework. I would kill for a full example of someone testing a non-Angular login that transitions to a fully Angular website, with a setup config and real world test cases. (Edit: This is still true, but I can't make one myself as my application is in a bad grey area, note my RCA below for more details.)
I just want the ability to do my login and then successfully transition over to my Angular pages and be able to rely on Protractor to work with my Angular pages. I need to know what to look for that may be a long running asynchronous process (What specifically can I check for in the Chrome dev tools?). I would love to understand what Protractor needs as defaults in order to successfully work with the Angular parts of my app/website (Is there something beyond the presence of <app-root _nghost-c0="" ng-version="4.3.2"> in the HTML?). Before this job I worked in Java, so all of this asynchronicity and Angular is new to me, so I know I'm missing the known things that a seasoned Javascript dev is aware of.
My Solution/Root Cause Analysis
Starting down the list suggested by #ernst-zwingli:
for Angular(2) Check if the object window.getAllAngularRootElements returns at least one value.
It returned at least one value, so I moved on.
useAllAngular2AppRoots: true,
I tried this and still ran into the async timeout.
And if $interval or other long lasting asynchronous tasks are used, there can be issues, because of the zones
Previously #ernst-zwingli also mentioned looking at the testability method, except it was the old way. Through research and testing I found the window object also has a getAllAngularTestabilities method. This led down an interesting rabbit hole. An example output from the Chrome console (put window.getAllAngularTestabilities() in the Chrome console window, look at the resulting list) is as follows:
t:
_callbacks:...,
_didWork:true,
_isZoneStable: true (this looks promising, but why isn't Protractor working then?!?)
_ngZone:
hasPendingMacrotasks: true,
hasPendingMicrotasks: false,
isStable: true
I would think isZoneStable would be enough, but apparently not so for Protractor. Then looking at Macrotasks being true, I had to look up what the heck a Macrotask was: What does hasPendingMacrotasks and hasPendingMicrotasks check for?.
A macrotask can be:
i.e. setTimeout, setInterval, setImmediate
Thus #ernst-zwingli's note about interval's causing problems in the zones was remembered and something finally clicked.
First github issue, about zone instability
Another github issue complaining about the necessity of using browser.driver to get things done along with browser.waitForAngularEnabled. Apparently this is expected behavior, it led me to issue #3349
Issue #3349 - The actual root cause of my issue. My developers do not actively jump in and out of zones around observables. Even though these observables only have one subscriber. Since they live in the angular zone at this time, they are a long running "Macrotask" that Protractor waits infinitely on.
I can't rewrite the code with these wrappers as I am not currently versed enough in Angular to do it safely and we are currently hurtling toward a November deadline. I think I'll have to deal with using browser.driver for the time being and hope I can't get it fixed later. Hopefully my RCA was helpful for you.
In the following I list a set of potential causes and possibilities to fix/resolve them.
How does AngularJS and Angular(2) Work / What can I check in the Browser Dev Mode
I can't explain it as well as Andrey Agibalov in his Blog here, so check it out (also for developers).
Basically, the objects required by Protractor you can check in your Chrome Dev.
for AngularJS
Check if the object window.angular is properly defined, i.e. lookup window.angular.version and also try window.angular.getTestability of your Root element
for Angular(2)
Check if the object window.getAllAngularRootElements returns at least one value.
Root Element (AngularJS)
Potentially your Angular App is somewhere wrapped within the Body as something like <div ng-app="my-app">.
In that case, you must adjust your rootElement: body inside config.ts. Check this answer for details.
Angular(2)
If you're using Angular (aka Angular2), then there are ngZone's introduced. In this case your config.js should additionally contain this:
exports.config = {
framework: 'jasmine',
seleniumAddress: 'http://localhost:4444/wd/hub',
specs: ['spec.js'],
useAllAngular2AppRoots: true,
// rootElement: 'root-element'
};
check in your browser for window.getAllAngularRootElements as the additional line in conf.js is about this.
If you can, maybe use the advantage of multiple zones possible. Create a 2nd zone, configure rootElement: 'root-element'
to only focus on one zone and then move some asynchronous tasks into the other zone until you found, which task(s) lead to timeout. Keep those tasks (if possible) in the separate zone, so Protractor ignores those tasks.
And if $interval or other long lasting asynchronous tasks are used, there can be issues, because of the zones. Repeatedly or long lasting tasks should be started outside the zone and then be moved into the zone as else Protractor could run into timeouts. There is a workaround for developers to apply, in order to avoid these problems for Protractor.
read all about it here
browser.driver. - side remark
browser.driver.get() works as if ignoreSynchronization = true, since you directly assign the Browser Driver and you kind of bypass the synchronization logic of Protractor.
Read more about it in this answer here.
Hope I could give you some more input and you can solve your issue. Please let me know the results.
Could you please set
browser.ignoreSynchronization = true;
and try

Angular Universal and browser feature checks

When developing a web app with jQuery or normal JavaScript, it is commonplace to check for feature availability first. So for example, if I want to use the document.oncopy event, I should first have something like this to ensure my code doesn't break for lesser browsers:
if ("oncopy" in document) {
// Feature is available
}
I'm a bit puzzled about how this would work in Angular2. I could still use the same if I expect to only run in the browser, but I'm specifically told to leave the DOM alone if I want to use Angular Universal and depend on templates or the DomRenderer instead. This allows the page to be pre-rendered on the server and provides a truly impressive performance gain.
But suppose I want a specific div to be invisible if the document.oncopy is unavailable. My understanding is that this is not recommended:
<div *ngIf="hasFeature()">...</div>
and
hasFeature() {
return 'oncopy' in document;
}
because then I'm still manipulating the DOM. Note that my example is about the document.oncopy but I could choose any feature whatsoever that doesn't have universal support.
I tested this using Chris Nwamba's tutorial on Scotch and added the following to the end of his Home template:
<div *ngIf="hasFeature()">Feature is supported</div>
<div *ngIf="!hasFeature()">Feature is NOT supported</div>
Update: Interestingly, it gave different results on different browsers. On Chrome 55, it executed as it would normally and showed the "Feature is supported" message. On IE11, I received the "not supported" message. In both instances the server log shows a EXCEPTION: document is not defined message, but the page still seems perfectly okay.
So what is the correct way to check for browser features if I want to use Angular Universal?
Update:
I also toyed around with using a field in the template and assigning that field from one of the life cycle hooks. ngAfterContentInit seemed like a fine candidate, but also causes an error on the server. It still runs fine in the browser with no weird effects (that I have noticed so far).
There are two ways to approach this:
Do the check only once the server is done rendering and the client is completely initialised (including the replay of user events done by preboot.js).
Return a reasonable default when the page is running on the server and perform the actual check only in the browser.
I started looking at the first option, but none of the Angular2 life cycle events will help with this. In fact, you can clearly see them all executing on the server and only then on the client.
I then started looking for something usable in preboot.js but quickly realised it was more complex than it needed to be.
So onto option 2 I went. It turns out checking for the browser is as easy as importing and checking isBrowser.
import { isBrowser } from "angular2-universal";
#Component({
// All the usual stuff
})
export class MyComponent {
// ...
hasFeature(): boolean {
return isBrowser && 'oncopy' in document;
}
// ...
}
And then use the template as I showed in the question.
To check if you're running on the server, import and use isNode in exactly the same way. There doesn't seem to be an obvious way to distinguish between Node and ASP.NET Core, but perhaps it's best not to write too much code that specific to platform.

How can I hook into new message windows using a bootstrapped Thunderbird extension?

I am currently trying to create a Thunderbird extension that write the recipients of a draft into the title of the composing window.
The default behaviour of Thunderbird is Compose: [Subject] (translated from German, YMMV), but I'd like to have something like this: Compose to [recipient(s)]: [Subject], so my time tracking software can keep track of the time I spend writing emails to clients.
My general idea for this extension is:
Hook into every composing window that is created / opened (I know they are reused) via event listeners
For every window, listen to the event recipients_changed, if such a thing exists. Otherwise, it's a bit more work (monitor all recipient input fields), but that should not be a problem.
Also listen for subject_changed (easier, because it's only one input) to prevent the default title overwriting our extended title.
I have already created a skeleton extension (bootstrapped), and the startup function in bootstrap.js is actually getting called. Since this is my first extension to Firefox/Thunderbird, this is the first success! :)
However, the only reason I know the function is called because the many exceptions my different trials have thrown are logged in the error console...
I have read many texts, but the learning curve seems to be pretty steep.
I learned that at the time startup is called, pretty much nothing is defined yet (my first attempts were alert and then settimeout).
This answer looked promising, but it throws the exception 'wm' is not defined:
https://stackoverflow.com/a/14291575/1886765
Maybe I used the code wrongly - hard to say for a beginner.
It would probably be easier to make a non-bootstrapped extension (I could overlay the compose window and wouldn't need step #1), but now that I've started this way, I'd like to finish it this way.
Maybe the solution is some XPCOM connector, but this field is still completely dark for me.
Could you kindly point me in the right direction?

Categories

Resources