There are too many active WebGL contexts on this page, the oldest context will be lost. [dedup.min.js] - angularjs

I am getting this wiered error only on Safari browser. Don't know why.
I am using AngularJS 1.3.x.
So how do I detect which libraries may be using this.
And why only in safari I get this error ?
Is there a way via JS to enable or disable WebGL ?

Put this at the top of your HTML?
<script>
HTMLCanvasElement.prototype.getContext = (function(origFn) {
var bannedTypes = {
"webgl": true,
"webgl2": true,
"experimental-webgl":, true,
};
return function() {
var type = arguments[0];
return bannedTypes[type]
? null
: origFn.apply(this, arguments);
};
}(HTMLCanvasElement.prototype.getContext));
</script>
As long as this appears before any other scripts it should block webgl.
The script above changes replaces the function someCanvas.getContext so that when some other JavaScript tries to create a "webgl" context it returns null which means creating the context fails. Otherwise if JavaScript asks for a different kind of context it calls the original getContext function.
As long as this script is executed first it should prevent other JavaScript on the page from creating a webgl context. It won't prevent JavaScript in iframes from creating contexts. You'd need to add the same solution to each iframe.

Related

Trouble implementing Google Analytics gtag.js in React-based Chrome Extension

My problem in a nutshell: The window object that gtag.js operates on and the window object available in my react context (a content.js context) are different objects, and so I can't write events from my react code -- meaning I can't use analytics in my extension.
More deets:
In react <script> tags can't be loaded directly for various reasons. So I've change the documentation implementation:
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=GA_MEASUREMENT_ID"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'GA_MEASUREMENT_ID');
</script>
To
export const gtag = (...args) => {
window.dataLayer.push(args)
}
export const loadAnalytics = (ga_property) => {
const script = windowdocument.createElement("script")
script.src = `https://www.googletagmanager.com/gtag/js?id=${ga_property}`
script.async = true
window.document.body.appendChild(script)
window.dataLayer = window.dataLayer || []
gtag('js', new Date())
gtag('config', ga_property, { 'transport_type': 'beacon'})
gtag('event',
'test', {
event_category: 'lookup',
event_label: 'test'
}
)
}
...
componentDidMount() {
loadAnalytics("UA-175XXXXXX-1")
}
I've come to understand through much research and gnashing of teeth that the window object in my content.js and the window object that is acted on in gtag.js once it is loaded are not the same object, and are intentionally "shadows" of each other, but still separate objects. From the documentation:
"Content scripts live in an isolated world, allowing a content script to makes changes to its JavaScript environment without conflicting with the page or additional content scripts.
Isolated worlds do not allow for content scripts, the extension, and the web page to access any variables or functions created by the others."
From what I can tell this seems to be irreconcilable without a re-write of the gtag.js source.
For reasons I still don't understand this code which references window.document
const script = window.document.createElement("script")
script.src = `https://www.googletagmanager.com/gtag/js?id=${ga_property}`
script.async = true
window.document.body.appendChild(script)
And this code in the same file which references window.document
export const gtag = (...args) => {
window.dataLayer.push(args)
}
End up pointing to two different window objects.
This post seems to reinforce that these two contexts can't communicated directly in terms of objects and functions (only messages).
For gtag.js to work in an extension, I'd need to be able to call window.dataLayer.push(...) on the window of the main web page from inside my chrome extension. And that doesn't seem possible.
Any bright ideas out there as to how to either:
Make gtag.js be loaded in the proper window.document and/or refer to the content.js context of window
or
be able to access the window object of the main page from the content.js context
Since extension code can have multiple contexts, it would be wise use the principle of separation of concerns to avoid multiple document issue altogether.
When developing extensions it is advised to run majority of your code in the background, to make use of the separate JavaScript runtime allocated for your code by the browser (and avoid slowing down the pages user is visiting or the code which appears as your extension UI). Additionally, in most cases, it is a good idea to ship the code you want to run packaged within the extension bundle. If you want to load an external resource, to your background script "document", you can use XHR and eval to execute code.
When code is executed in the background, it is available to your extension UI and content scripts using the extension and DOM messaging protocols.
First, initialize your extension in the context of your background script(s).
Then, register a message handler which will evaluate messages sent by other extension code and look for a key (usually message.type) that identifies message as carrying analytics data (usually message.payload).
Read the content of the messages that match the criteria in the handler, and use the supplied information to invoke analytics APIs.
Finally, send analytics events occurring in your UI or content scripts as messages to the background script.
This way your background script is the only place where analytics is set up, clearing up document ambiguity, your code is cleaner, because there is only one place where analytics code is accessed and the extension runs smoother because it's UI and content scripts don't need to load or know about analytics code.

Running angular app inside shadow dom

I am developing an application, where I've to load other angular(1.x) application inside current page (As per project requirement). I am trying to achieve it using shadow DOM concept (This could be done using iframe, but I am looking for better approach). I've tried below code:
var templateUrl = "angular-app.html",
templateReceivedCallback = function (response) {
var templateHolder = $("#template-holder"),
div = document.getElementById('template-holder'),
shadowRoot = div.attachShadow({
mode: 'open'
});
shadowRoot.innerHTML = response;
};
$.get(templateUrl, templateReceivedCallback);
I am expecting that the angular application should be loaded inside template holder element:
<div id="template-holder"></div>
The other app is loading in the div which I mentioned, but expressions are not compiled as expected. Example - Angular app having variable this.greeting = "Welcome!" and I am expecting this:
Welcome!
But it rendered as:
{{greeting}}
Is there any better way to achieve this? If yes, please share running example with me.
Thanks.
I think it's not possible because Angular parsing engine won't look inside the Shadow DOM.
As a workaround, you could put the {{greeting}} in the normal DOM.
If you want to use Shadow DOM, you will always be able to access it with <slot> tags.

Using Angular2 from a script inside an iframe of the main document

Note: I am new to Angular, any version.
I am trying to add some Angular2 parts to the web UI that is otherwise implemented using other frameworks. In the scenario I have (and cannot get away from) the main document does not have (much) JavaScript running in it at all - all scripts are inside iframe element(s) inside that document, as managed by the main framework.
I didn't even try to load Angular2 in that iframe, assuming that would not work. When it is needed (rarely), I "eval" all required Angular scripts on the main/root window to introduce Angular to it (once), but I have to have the JavaScript code configuring it inside the iframe, at least a bit of it and, because of that, it would be simpler for me to put all of it there. Inside that iframe I declared "ng" to be the "ng" from the main window. That enabled me to run the Angular2 5 minute quick start and worked fine. However, when I started adding more content via directives (I followed http://www.gurustop.net/blog/2015/12/16/angular2-beta-javascript-component) this didn't work. Specifically, it threw exceptions from this Angular2 function:
TypeScript:
function isValidType(value: Type): boolean {
return isPresent(value) && (value instanceof Type);
}
Actual JavaScript:
function isValidType(value) {
return lang_1.isPresent(value) && (value instanceof lang_1.Type);
}
Essentially it is was missing lang_1. The exception message has complaining of about "class0" and "class1".
If I move the exact same code from the iframe to the main window, it works. Also, If I hack the Angular2's isValidType() function to simply return true as follows it also works:
function isValidType(value) {
return true;
}
I tried bringing lang_1 in from the main window to the iframe as well, but that did not help.
Can someone, at least, tell me what is going on here, if not how to solve this? Thanks!

angularjs - Disable access to $scope and stop manipulation from the console

I have a web-page in AngularJS and I want to perform some validations on the client-side itself. So I compare the $scope's values and validate the user's events. For example:
$scope.limit = 5;
$scope.reached = 5;
$scope.check = function () {
if ($scope.reached >= $scope.limit) {
alert("Sorry, limit reached.");
} else {
alert("Success!");
}
};
But, it is possible to access and change the $scope with after selecting the element in Elements tab and then running this command in the Console:
angular.element($0).scope().limit = 100;
//or by running $scope.limit = 100; if you're using Batarang
After running this command successfully, I will get the alert as Success. I have created a sample page for testing purpose: http://keval5531.github.io/angular.html
So, is it possible to disable access or manipulation to the $scope? I can always use the server for validating, but I am sure there must be some way to keep a fool-proof client-side validation.
EDIT: I mean something near to fool-proof, which would require more efforts and expertise for the user to manipulate the data being sent and not just DOM manipulation.
You can never ensure fool proof validation-safety on the client side. But to answer your specific question on whether access to scope can be restricted - Yes, to some extend (with 1.3+). You could disable the debug data that batarang and other plugins uses by disabling the debug info. With that scope() accessor function will no longer be attached on the DOM element. The purpose of this is not for providing safety but it is for performance. Keep your server side validations strong enough to block any such attempt.
.config(['$compileProvider', function ($compileProvider) {
$compileProvider.debugInfoEnabled(false);
}]);
However be aware that anybody can reload the app with the debug info from the console with:
angular.reloadWithDebugInfo();

Google+ signin button with ReactJS

I am trying to get Google+ login button to work in my React code. The react code is as following (the actual code has proper value for CLIENT_ID).
React.DOM.div({className: "signin-with-google"},
React.DOM.span({id: "signinButton"},
React.DOM.span({
className: "g-signin",
'data-callback': this.signinCallback,
'data-clientid': "CLIENT_ID",
'data-cookiepolicy': "single_host_origin",
'data-scope': "https://www.googleapis.com/auth/userinfo.email"}
)
)
)
The button shows up properly on the page, clicking on it brings up the OAuth dialog and hitting Accept makes it disappear and no errors/warnings are generated either in the dialog or on the javascript console. So to the best of my knowledge everything is working as expected.
However, the callback method this.signinCallback that I am specifying doesn't get invoked. Any, ideas on what I am doing wrong here?
Thanks
As stated in the Google+ Sign-in Button docs, data-callback is expected to be "A function in the global namespace". That is because Google's code likely calls your callback by name since all HTML attributes are just strings. It will do something like (warning, not real code):
window[element.dataset["callbackName"]]();
You are passing a reference to your callback, which is not globally accessible. You can expose it when the component is mounted and delete it from the global namespace when it is unmounted:
componentWillMount: function() {
// Somehow generate a unique ID for every G+ button. This uses Underscore's
// `uniqueId` function[1].
//
// [1] http://underscorejs.org/#uniqueId
this.callbackName = _.uniqueId("gPlusCallback-");
window[this.callbackName] = this.signinCallback;
},
componentWillUnmount: function() {
delete window[this.callbackName];
},
render: function() {
...
'data-callback': this.callbackName
...
}

Resources