intercept / proxy window.postMessage - javascript

In my app, we are using a third party analytics library, which, apart from other things, is also using postMessage to post some info to the parent window (possibly for iframe use cases for the analytics library).
In our case, this info is sensitive and we do not want to send it to parent window (if our app is opened in an iframe or as a child window by someone) as we wouldn't have any control over what the parent window does with this information (even for valid parents). There is no configuration in the library to switch off this functionality.
As a potential solution, we figured out that if we can intercept all postMessages being sent out to the parent window and just filter this message , it would solve our purpose for now. To achieve this, I have been looking at proxying window.postMessage or redefining the method for adding this validation.
I am just trying to understand if this is feasible, and how?
is it possible from the current window to override the parentWindow.postMessage (or for that matter, override a childWindow.postMessage?)
What have i tried so far:
From window A, I am opening a window B, and then sending a postMessage from windowA to windowB. (This requires a reference to windowB in windowA, so that I can do windowB.postMessage)
If I proxy window.postMessage, it is not intercepting the windowB.postMessage call.
If I proxy windowB.postMessage, it is intercepting the windowB.postMessage call.
I am just trying to understand if the same can work for parentWindow, and what are the other constraints (same origin, etc)
codesandbox link: https://codesandbox.io/s/postmessage-forked-slv9b?file=/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Static Template</title>
</head>
<body>
<input class="s" type="text" />
<button class="s">send</button>
<script>
const handler = {
apply: function (target, thisArg, argumentsList) {
console.log(`window post message proxy: ${argumentsList[0]}`);
return target(...argumentsList);
}
};
const write = (str) =>
document.body.insertAdjacentHTML("beforeend", str + "<br>");
write("<hr>");
// --
const windowFeatures =
"menubar=yes,location=yes,resizable=yes,scrollbars=yes,status=yes";
/** #type { Window | undefined } */
let subWindow;
/** #type { HTMLButtonElement } */
const button = document.querySelector("button.s");
/** #type { HTMLInputElement } */
const input = document.querySelector("input.s");
button.onclick = () => {
if (!subWindow) {
subWindow = window.open("/reciver.html", "shipping", windowFeatures);
setTimeout(() => {
// THIS HANDLING HERE
subWindow.postMessage = new Proxy(subWindow.postMessage, handler);
subWindow.postMessage(input.value, window.location.origin);
}, 3000);
} else {
subWindow.postMessage(input.value, window.location.origin);
}
write(input.value);
};
</script>
</body>
</html>

Related

What is the best way to store multiple items in local storage?

There is no problem with one element. I need to make at least 10 buttons, and so that they do not depend on each other. That is, some buttons were with hidden text, and others with open text.
What is the best way to do this so that the code does not look like a freak?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<style>
#myid { display: none; }
</style>
<button onclick="onclickmyid()">Open</button><br><br><span id="myid">Hidden text</span>
<script>
var myid = document.getElementById('myid');
function onclickmyid() {
myid.style.display = (myid.style.display == 'block') ? '' : 'block';
localStorage.setItem('conceal', myid.style.display);
}
if(localStorage.getItem('conceal') == 'block') {
document.getElementById('myid').style.display = 'block';
}
</script>
</body>
</html>
Put all the persistent data you need into an array or an object, then serialize that (single) array or object into a single Local Storage key. Something along the lines of
const config = localStorage.config
? JSON.parse(localStorage.config)
: defaultConfig; // define defaultConfig earlier
cons saveConfig = () => localStorage.config = JSON.stringify(config);
// then, to save to the config when a change is made:
function onclickmyid() {
const newShow = myid.style.display === '';
config.myid = newShow;
updateUI();
saveConfig();
}
// and retrieve values on pageload:
const updateUI = () => {
myid.style.display = config.myid ? 'block' : '';
// etc for other elements
};
updateUI();
I'd also highly, highly recommend avoiding inline handlers, they're pretty universally considered to be quite terrible practice nowadays - attach listeners properly using JavaScript's addEventListener instead whenever possible.
For additional buttons, just add more properties to the config object, and toggle and access them in the click handlers.

Uncaught TypeError: translate is not a function

Keep getting an uncaught type error when I click the button that is supposed to trigger the translate function. Unsure what is causing this. I've tried changing the element from a button element to an input element with type button. Whatever I do I still get this error. Anyone able to help?
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="Content-Language" content="en-us">
<title>Example Web Editor</title>
<link rel="stylesheet" type="text/css" href="xtext/2.24.0/xtext-ace.css"/>
<link rel="stylesheet" type="text/css" href="style.css"/>
<script src="webjars/requirejs/2.3.6/require.min.js"></script>
<script type="text/javascript">
var baseUrl = window.location.pathname;
var fileIndex = baseUrl.indexOf("index.html");
if (fileIndex > 0)
baseUrl = baseUrl.slice(0, fileIndex);
require.config({
baseUrl: baseUrl,
paths: {
"jquery": "webjars/jquery/3.5.1/jquery.min",
"ace/ext/language_tools": "webjars/ace/1.3.3/src/ext-language_tools",
"xtext/xtext-ace": "xtext/2.24.0/xtext-ace"
}
});
require(["webjars/ace/1.3.3/src/ace"], function() {
require(["xtext/xtext-ace"], function(xtext) {
xtext.createEditor({
baseUrl: baseUrl,
syntaxDefinition: "xtext-resources/generated/mode-logic"
});
});
});
</script>
<script>
function translate() {
console.log("function being triggered.");
let req = new XMLHttpRequest();
console.log("request created");
req.open( "POST", "localhost:8003/translate", false);
req.send(ace.edit('xtext-editor').getSession().getValue());
return req.responseText;
}
</script>
</head>
<div class="container">
<div class="header">
<h1>Example LogicLang Web Editor</h1>
</div>
<div class="content">
<div id="xtext-editor" data-editor-xtext-lang="logic"></div>
</div>
</div>
<form onsubmit = "return false">
<input type="button" onclick="translate()"> Compile </input>
<button class="save-button" onclick="console.log(ace.edit('xtext-editor').getSession().getValue());"> Test </button>
</form>
</body>
</html>
You need to use a different name for that function.
translate() is already an existing method used for 2D Canvas drawings.
The translate() is a method of a 2D drawing context. The translate(x,y) method moves the canvas and its origin x units horizontally and y units vertically. source.
Solution
Avoid this by defining a JS event handler property with:
document.querySelector("input[type=button]").addEventListener("click", translate)
// or
jQuery('input[type="button"]').on("click", translate)
which will create a handler with a more predictable (probably global) scope. For best practice, also use event delegation.
Explanation
The lexical scope of a handler function defined as an html element attribute already has a translate property.
JS functions are bundled together with references to their surrounding state (the lexical environment). When you set an HTML attribute as a string of js code, you are implicitly defining a function within the scope of the HTML element.
Your <input> element will inherit properties from these interfaces: HTMLElement -> HTMLInputElement -> Node -> Event Target. We can check to see if any of them have a translate property:
console.log(Object.getOwnPropertyNames(EventTarget.prototype).includes("translate"))
// > false
console.log(Object.getOwnPropertyNames(Node.prototype).includes("translate"))
// > false
console.log(Object.getOwnPropertyNames(HTMLInputElement.prototype).includes("translate"))
// > false
console.log(Object.getOwnPropertyNames(HTMLElement.prototype).includes("translate"))
// > true
The HTMLElement interface includes a "translate" property, which is a Boolean value. The translate reference in your handler therefore gets resolved to that value, and you will get a TypeError telling you "translate is not a function" because you are trying to call a Boolean
More Explanation
If I run this in the Chrome DevTools console:
const div = document.createElement("div");
function translate() {
console.log(".")
};
div.setAttribute("onclick", "translate()");
getEventListeners(div).click[0]
I can inspect the listeners, and see the scope chain:
listener: ƒ onclick(event)
arguments: null
caller: null
length: 1
name: "onclick"
prototype: { constructor: ƒ }
__proto__: ƒ()
[[FunctionLocation]]:
[[Scopes]]: Scopes[4]
0: With Block { align: "", title: "", lang: "", translate: true, dir: "", ... }
1: With Block { location: Location, jQuery321081306834416800691: {…}, implementation: DOMImplementation, URL: "https://....com/", … }
2: Script { div: div }
3: Global { window: Window, self: Window, document: document, name: "", location: Location, … }
once: false
passive: false
type: "click"
useCapture: false
The first object in that scope chain list is the <div> that triggers the handler. Generally, the scope chains of functions defined in html element attributes way will be something like: (1) the call object -> (2) the HTML element -> (3) . . . stuff in between . . . -> (4) Document -> (5) Global.
When a variable is being resolved or looked up, js will go up the scope chain until it finds the definition. If you declare an un-nested function in your script the usual way, it will be a property of the global object. If anything between the global scope and the HTML element where the handler is defined has a property with the same name as your function, references to your function will be resolved to that property instead of your function.
Screenshot of DevTools comparing scopes of handlers defined as HTML attributes vs. with addEventListener()

Custom HTML element attribute not showing - Web-Components

I'm exploring web-components custom HTML elements, and I am running into a problem adding custom attributes to my custom elements: any value I set in the markup is never honored.
For a simple element like this, which should show the text supplied in the "flagtext" attribute, it does always show the default value.
<test-flag flagtext="my text"></test-flag>
Full JSBin sample is here.
The JSBin uses the Polymer library (as this is the only thing I can pull in there). I am using webcomponents.js generally, same result. Both in Chrome 49 and in Firefox 45 gives same result. There is no error showing in the console or debugger.
Every sample I find on the web has similar code but I tried various versions and it always refuses to update. I even copied a few samples into JSBin and they do not work either.
What could be wrong? I understand it is experimental technology but the consistency with which this isn't working is still surprising. Has this standard been abandoned? (I see that the latest April 2016 W3C draft of custom elements has entirely changed its approach.)
When I define "attributeChangedCallback" function, it does not fire.
I can easily modify the property via Javascript, this is not a problem.
But why can I not specify the property in markup, as I am supposed to?
Edit - full code
Note that you'll need to put these into separate files for the HTML import, and that you need the "webcomponents-lite.js" library.
Main HTML file
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style>
test-flag
{
border: 1px solid blue;
}
</style>
</head>
<body>
<script src="lib/webcomponents-lite.min.js"></script>
<link rel="import" href="test-flag.html">
Here is the custom test-flag component:
<p>
<test-flag flagtext="my text"></test-flag>
</body>
</html>
File: test-flag.html
<template>
<style>
</style>
Content:
<div id="testflagID">
</div>
</template>
<script>
(function() {
var _currentScript = (document._currentScript || document.currentScript);
var importDoc = _currentScript.ownerDocument;
var customPrototype = Object.create(HTMLElement.prototype);
Object.defineProperty(customPrototype, 'flagtext', {value: '(default)', writable: true});
document.registerElement('test-flag', {prototype: customPrototype});
customPrototype.createdCallback = function()
{
var template = importDoc.querySelector('template');
var clone = document.importNode(template.content, true);
var idx = clone.querySelector("#testflagID");
idx.textContent = this.flagtext;
var root = this;
var createdElement = root.appendChild(clone);
};
})();
</script>
There are 2 concepts that are not automatically linked together.
In the HTML code:
<test-flag flagtext="my text"></test-flag>
...term flagtext is an HTML attribute (not a property).
In the JavaScript code:
Object.defineProperty(customPrototype, 'flagtext', {value: '(default)', writable: true})
...term flagtext is a JavaScript property (not an attribute).
For standard elements, the browser automatically binds the property value to the attribute value (and vice versa). For Custom Elements, too (with standard attributes). But if you want to add a custom attribute, you'll have to bind it manually.
For example, in the createdCallback() method, add:
this.flagtext = this.getAttribute( 'flagtext' ) || '(default)'
Live sample:
document.registerElement( 'test-flag' , {
prototype: Object.create( HTMLElement.prototype, {
flagtext: {
value: '(default)',
writable: true
},
createdCallback: {
value: function()
{
this.flagtext = this.getAttribute( 'flagtext' ) || this.flagtext
this.innerHTML = 'flagtext=' + this.flagtext
}
},
} )
} )
<test-flag flagtext='new content'>Hello</test-flag>
NB: the attributeChangedCallback() method is fired only when an attribute is changed after element creation (which is not the case here).

Typescript window.onload not being called after adding requirejs for phaser game

I am trying to make a game with phaser and Typescript. I followed the instructions here and it worked initialy. The problems came when I tried to modularise my code using AMD and requirejs
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Resonate</title>
<link rel="stylesheet" href="app.css" type="text/css" />
<script src="phaser.js"></script>
<script src="http://requirejs.org/docs/release/2.1.20/minified/require.js" data-main="app"></script>
</head>
<body>
<h1>RESONATE</h1>
<div id="content"></div>
</body>
</html>
Player.ts
export class Player {
color: string;
constructor() {
this.color = "#0000ff";
}
}
app.ts
import player = require("Player");
class PhaserDemo {
game: Phaser.Game;
constructor() {
this.game = new Phaser.Game(800, 600, Phaser.WEBGL, 'content', { preload: this.preload, create: this.create });
}
preload() {
this.game.load.image('phaser_run', 'run.png');
}
create() {
console.log("Before");
var p = new player.Player();
this.game.stage.backgroundColor = p.color;
}
}
window.onload = () => {
console.log("ONLOAD");
var game = new PhaserDemo();
};
Now when I load it up window.onload is never called, I tried following
window.onload not calling function
for the Javascript solution but I can't get it to work in typescript.
Also here is the generated Javascript if you want to take a look https://gist.github.com/koreus7/06bee4a30d22bdc76d62
for the Javascript solution but I can't get it to work in typescript.
Basically if the window is already loaded attaching to window.onload will not call the attached function.
Check for document.readyState === "complete". Alternatively use something like jquery ready : https://api.jquery.com/ready/
I believe it is caused by the require.js loader, it is without I explicitly know it a bit similar to jspm system.js , and so we don't need to ensure that the window is loaded because the loader already has ensured that.
I just removed the window.onload and it worked fine :)

Require.js nested requires

I'm trying to use requireJS but I want to build a hierarchy of dependencies: main requires obr.platcom and obr.platcom requires obr (for example).
I have this hierarchy of files:
- index.html
-> js
- main.js
-> lib
- jquery.js
- require.js
-> obr [my own 'libraries']
- obr.js
- obr.platcom.js
index.html
<!DOCTYPE html>
<html lang="es">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="initial-scale=1.0, width=device-width" />
<title>Platcom</title>
<script type="text/javascript" src="js/lib/jquery.js"></script>
<script data-main="js/main" src="js/lib/require.js"></script>
</head>
<body>
</body>
</html>
main.js
$(document).ready(function() {
require(["obr/obr.platcom"], function() {
obr.hola();
var myPlatcom = obr.platcom();
myPlatcom.test();
});
});
obr.js
var obr = {};
obr.hola = function() {
alert('Hola OBR');
};
obr.platcom.js
require(["obr.js"],function() {
obr.platcom = function(params) {
var that = {};
var test = function test() {
alert('Hola Platcom!');
}
that.test = test;
return that;
}
});
If I require both obr and obr.platcom files in the main all works, but if I use this nested style I receive the next error:
Uncaught ReferenceError: obr is not defined main.js:3
Do you know what I'm doing wrong?
Thank you in advance.
Alright, you've done several things wrong.
You need to specify as an argument the dependency you're injecting. For example, require(["obr/obr.platcom"], function() { won't do much unless you specify how the required module can be called. You should need this:
require(["obr/obr.platcom"], function( obr ) {
This way, you know in which variable your required object is.
The obr.js variables are in the global scope. You need to wrap them in a require or define function call. The following would work:
define(function() {
var obr = {};
obr.hola = function() {};
return obr;
});
You may have noticed some things that are wrong with your last file.
If you want your module to be imported somewhere, you have to define it. So you have to use the define function, not the require one. And the define function must return an object. Here is a fixed obr.platcom.js file:
// If you don't use "define" for the obr.js file too, this won't work
define(['obr'], function( obr ) {
obr.platcom = function() {};
// Don't forget to return the obr object, or the require of
// the main file won't return anything
return obr;
});
This way, things are done the correct way. Or at least, the way require.js wants you to do stuff.
I hope this reveals you how require.js can be effectively used to easily separate your code in modules :)

Categories