6 |
7 |
11 |
12 |
--------------------------------------------------------------------------------
/demo/postMessage-app.html:
--------------------------------------------------------------------------------
1 | postMessage API test
2 |
postMessage API test
3 |
4 |
5 |
message event content
origin url
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/demo/page-storage.js:
--------------------------------------------------------------------------------
1 | var url = location.href.substring(location.href.lastIndexOf('/')+1);
2 | localStorage.setItem( 'a',"localStorage " + url);
3 | window.localStorage.setItem( 'b',"window.localStorage " + url);
4 | sessionStorage.setItem( 'a',"sessionStorage " + url);
5 | window.sessionStorage.setItem( 'b',"window.sessionStorage " + url);
--------------------------------------------------------------------------------
/test/page-dom-api.js:
--------------------------------------------------------------------------------
1 | let cb = document.getElementById("external");
2 | cb.checked=true;
3 | [...document.getElementsByTagName('button')].forEach( b => b.onclick = ()=>ToggleCb(b) );
4 |
5 | function ToggleCb( b )
6 | { let a = document.getElementsByClassName( b.getAttribute('for') );
7 | for( let x of a )
8 | x.checked = !x.checked;
9 | }
--------------------------------------------------------------------------------
/test/EpaParser.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/demo/current-script-microapp.html:
--------------------------------------------------------------------------------
1 | expected ABCD
2 | expected ABCD
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/forms.md:
--------------------------------------------------------------------------------
1 | # Forms handling by embed-page
2 |
3 | ## On click
4 |
5 | 1. for A or FORM dom tree ancestor
6 | * target set to embed-page IFRAME name
7 | * blank form.action set to this.src
8 | * other action is kept intact to preserve the actual submit into IFRAME
9 | 2. browser submits(get/post) into IFRAME
10 | 3. IFRAME onload handler (on before load TBD)
11 | this.src = frame.src
12 | * which triggers content load
13 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | embed-page, redirecting...
7 |
8 |
9 | Visit demo/index.html to see live examples of your element running.
10 | This page will automatically redirect you there when run in the browser
11 | with `polymer serve`.
12 |
13 |
14 |
--------------------------------------------------------------------------------
/demo/postMessage-app.js:
--------------------------------------------------------------------------------
1 | const url = location.href.substring(location.href.lastIndexOf('/')+1)
2 | , dopost = x=>(window.parent ||window.opener).postMessage(url,'*');
3 |
4 | window.addEventListener('message', ev=>
5 | document.querySelector('tbody').innerHTML += `
6 |
${ev.data}
${ev.origin}
` );
7 |
8 | window.addEventListener("load", x=>
9 | {
10 | document.querySelector('button').addEventListener('click', dopost );
11 | dopost();
12 | });
--------------------------------------------------------------------------------
/events.md:
--------------------------------------------------------------------------------
1 | # storage
2 | The `storage` event is propagated as usual window event in a context `` with common scope.
3 | Depend of **scope** attribute either **src** URL or **name** value is used to define `` common
4 | scope instances.
5 |
6 | Each `` instance listens for `storage` window event.
7 | If event **key** matches the own prefix (made out of name, uid, or URL depend of **scope** )
8 | , the event is dispatched by own EpaWindow instance triggering the listeners within `` context.
9 |
--------------------------------------------------------------------------------
/z-notes.md:
--------------------------------------------------------------------------------
1 | # temp notes on dev process
2 | Have a sense along with commits. Content would be cleared on commit following
3 | the annotated here commit.
4 | # build process
5 | ## Goal
6 | * `esm-unbundled` build with demo files.
7 | * `esm-bundled/embed-page.js` bundled with Polymer for 1-file embedding into page.
8 |
9 | ## esm-bundled
10 | 89Kb - too much
11 | 50Kb - src
12 | 40Kb - compiled( unbundled )
13 | way to reduce:
14 | * remove PolymerElement as parent
15 | * remove comments
16 | * ? replace regex-ex with parsing algorithms
17 |
18 |
--------------------------------------------------------------------------------
/test/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/demo/sw.js:
--------------------------------------------------------------------------------
1 | // The SW will be shutdown when not in use to save memory,
2 | // be aware that any global state is likely to disappear
3 | console.log("SW startup");
4 |
5 | self.addEventListener('install', function(event) {
6 | console.log("SW installed");
7 | });
8 |
9 | self.addEventListener('activate', function(event) {
10 | console.log("SW activated");
11 | });
12 |
13 | self.addEventListener('fetch', function(event) {
14 | console.log("Caught a fetch!");
15 | debugger;
16 | event.respondWith(new Response("console.log('document.location.protocol',document.location.protocol)"));
17 | });
18 |
--------------------------------------------------------------------------------
/test/page-dom-api.html:
--------------------------------------------------------------------------------
1 |
2 |
Purple header
3 | Embedded and external script inclusion sample
4 |
5 | checked by embedded script.
6 |
7 | checked by external script
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/demo/current-script.html.js:
--------------------------------------------------------------------------------
1 | var cs = document.currentScript;
2 |
3 | if( cs )
4 | { scriptTitle2Input('.current-script-related', cs.parentNode );
5 | // console.log( 'executed',cs );
6 | appendText( 'called ' + cs.outerHTML.substring(0,47).replace( / has given a flexibility of embedded DOM and IFRAME kind of browsing
9 | context insulation.
10 |
11 | Unlike direct injection of 3rd party script EPA executes JS in host page with
12 | insulation layer preventing access to document, window and major APIs.
13 |
14 | \ at this stage yet a proof of concept for
15 | [Embeddable Progressive Application](https://github.com/EPA-WG/EPA-concept) and potentially
16 | the polyfill for standard-to-be implemented natively by browser with all security concerns addressed.
17 |
18 |
19 |
--------------------------------------------------------------------------------
/demo/html-include.html.test.js:
--------------------------------------------------------------------------------
1 |
2 | suite('embed-page HTML include', () =>
3 | {
4 | test('initial src set, loaded event fired & content ready', function(done)
5 | {
6 | const E = document.querySelector('#html-include-test');
7 | assert.equal(E.src, "demo-menu.html?inline");
8 | assert.equal( E.getAttribute('src'), "demo-menu.html?inline" );
9 | if( E.readyState === "complete")
10 | {
11 | const m = document.querySelector('nav');
12 | assert.equal(m.id, 'menu');
13 | done()
14 | }
15 | else E.addEventListener('load', x=>
16 | {
17 | const m = document.querySelector('nav');
18 | assert.equal(m.id, 'menu');
19 | done()
20 | });
21 | });
22 | });
--------------------------------------------------------------------------------
/demo/html-as-component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | HTML as Component
5 |
9 |
10 |
11 | Set background to:
12 |
13 | green
14 |
15 |
16 | yellow
17 |
18 |
19 | blue
20 |
21 |
22 | reload page
23 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/demo/poc/x-origin.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | cross-domain js module dependency
6 |
7 |
8 | cross-domain js module dependency load
9 |
43 | CSS
44 | 1. The H3 header above should be intact(default) in color and text size
45 | AND h3 within embed-page use chocolate text color.
46 | 2. The H3 from embedded content should be in monospace font.
47 | JS 3. Click on 'make blue' the embedded H3 should be blue colored.
48 | 4. After 'innerHTML' click the H3 from embedded content should become green.
49 |
58 | The menu should be shown on page right top side and in demo snipplet above.
59 | CSS
60 | 1. The menu should have gray inner shadow.
61 | 2. The page menu should be located on the right side.
62 | JS 3. current page( HTML include ) on menu should be in bold.
63 | 4. Links on menu should open the page in same browser page.
64 |
The <embed-page/> web component is acting as IFRAME.
37 | Its content is insulated on JS, DOM, CSS and browsing context level( A links and FORM get/post ).
38 |
Unlike IFRAME it is embedded inline into parent page DOM and automatically resizing parent node.
<embed-page/> mimicks the window object in context of postMessage() API.
36 | See postMessage for details.
37 |
38 |
39 |
epa 0
40 |
41 |
42 |
43 |
44 |
epa 1
45 |
46 |
47 |
48 |
49 |
56 |
89 |
90 |
94 |
95 |
96 |
97 |
98 |
112 |
113 |
114 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # \
2 | Proof of concept for
3 | [Embeddable Progressive Application](https://github.com/EPA-WG/EPA-concept)
4 | - a microapplication container, a WebComponent acting as seamless IFRAME and html include
5 |
6 | [ GitHub](https://github.com/EPA-WG/embed-page)
7 | | [demo](https://cdn.xml4jquery.com/ajax/libs/embed-page/0.0.20/build/esm-unbundled/demo/index.html)
8 | [![NPM version][npm-image]][npm-url]
9 | [](https://www.webcomponents.org/element/embed-page)
10 |
11 | ## Where to use?
12 | <embed-page/> covers 2 extreme cases.
13 |
14 | 1. Super-simple development with library of pre-made microapplications and plain html codebase.
15 | It assumes no web component knowledge and development.
16 | 2. Super-complex apps where on same page need to mix UX made with different frameworks and their incompatible otherwise revisions.
17 | The JS Context insulation of embed-page provides "evolutionary architecture" support to web page.
18 |
19 | ## Security
20 | * General browser and application [security improvements overview](security.md).
21 |
22 | Briefly, increases security by jailing 3rd party content and JS, a secure alternative to directly including of 3rd party
23 | JS into page.
24 |
25 | The scope insulation for DOM and CSS is done by WebComponet shadow dom, API for JS
26 | are insulated by closure for global objects with wrappers limiting the dom access root
27 | to component content. Similar approach is applied for url, storage, cookies, etc.
28 |
29 | ## Use
30 | 1. Add to project via npm, bower, or simply placing `embed-page.js` into project tree
31 | 2. Import into page/module either by ES6 import, simple SCRIPT tag, webcomponent `link rel="import"`, or AMD require
32 | 3. Develop your reusable widgets as insulated HTML and include into page by `````` or
33 |
34 | Add some useful 3rd party [microapplication](https://github.com/EPA-WG/EPA-concept/blob/master/microapplication.md)
35 | into your page same way.
36 |
37 | The content could be set either by **src** attribute or by Polymer {{data}} binding of content;
38 | including the insulated content in TEMPLATE; or binding content via **html** attribute.
39 | ```html
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
In chocolate only itself, no fumes spread.
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 | ```
57 |
58 | At the moment ``` ``` resides in Polymer echosystem, file the
59 | [change request](https://github.com/EPA-WG/embed-page/issues) if need other or no framework compatibility.
60 |
61 | ### Dependencies
62 | Polymer 3 Element is a base for embed-page. No other dependencies in run time planned before first release
63 | ( currently project is in pre-release alpha stage). Polymer Paper Elements and Vaadin are used for demo and
64 | are not required to use \
65 |
66 | ## To see in action
67 | See the live basic [DEMO on CDN](https://cdn.xml4jquery.com/ajax/libs/embed-page/0.0.20/build/esm-unbundled/demo/index.html)
68 | , [JSFiddle](https://jsfiddle.net/suns2015/k2sdfrt1/)
69 | , or locally run
70 | ```bash
71 | $ polymer serve --open
72 | ```
73 | It will open the demo page in browser.
74 | In demo the external page is loaded into shadow dom and its embedded and referenced JS
75 | will work with document via wrapper in same way as standalone page.
76 |
77 | The host page document is not available from embedded content, which is validated by using
78 | same DOM selectors as in host page as in instances of ``` ```.
79 |
80 | # Project
81 | ## Install the Polymer-CLI
82 |
83 | First, make sure you have npm (packaged with [Node.js](https://nodejs.org)) installed.
84 | Run `npm install` to install your element's dependencies, then run `polymer serve`
85 | to serve your element locally.
86 |
87 | ## Viewing Your Element
88 |
89 | ```
90 | $ polymer serve
91 | ```
92 |
93 | ## Running Tests
94 |
95 | ```
96 | $ polymer test
97 | ```
98 |
99 | Demo pages are set up to be tested via [web-component-tester](https://github.com/Polymer/web-component-tester). Run `polymer test` to run your application's test suite locally.
100 |
101 | [npm-image]: https://img.shields.io/npm/v/embed-page.svg
102 | [npm-url]: https://npmjs.org/package/embed-page
103 |
--------------------------------------------------------------------------------
/demo/scriptlets.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Scriptlet - embed-page demo
8 |
9 |
10 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
23 |
24 |
25 |
26 |
27 |
28 |
Scriptlet - embed-page demo
29 |
30 | In context of embed-page the scripltet is a script tag acting as inline component.
31 | It uses script tag attributes as parameters, is aware of DOM where it is located,
32 | and modifies the surrounding DOM tree if desired.
33 |
34 |
Samples of scriptlets
35 |
You could think of scriptlet as light-weighted web component which does not require a separate dependency inclusion
36 | as content would be used in location of SCRIPT tag.
37 |
38 |
39 |
40 |
41 | For example, number-format.js scriptlet bellow
42 | would render <span>1,234,567,890</span> right before own SCRIPT tag.
43 |
44 |
45 |
46 | span with rendered content will be injected here :
47 |
48 |
49 | Also here:
53 |
54 |
55 |
56 |
57 |
58 |
59 | Scriptlet change-bind.js
60 | would change value of number-format scriptlet on change event of following input field.
61 |
62 |
63 |
64 |
65 | bind unformatted:
66 |
67 | formatted by scriptlet:
68 |
72 |
73 |
78 |
84 |
85 |
86 |
87 |
Of course the binding within JS framework is shorter. But this scriptlet will run with HTML without writing any
88 | JS, keeping all in HTML. Which is a sample of declarative programing.
89 |
90 |
91 |
92 |
93 |
94 |
Difference with W3C
95 |
96 | Scriptlet is inline, hence runs every time where it is located in DOM.
97 | Scriplet could be written as in old-fashion way as in "modern" module type syntax.
98 | The type="module" allows to use
99 | modern JS features like import/export but if used outside of embed-page will run only once.
100 | document.currentScript is not available in script type="module" if used outside of embed-page.
101 |
Local and session Storage in browser are serving as key-value data storage as communication across browser windows
34 | layer. This concept is also working for <embed-page/> except of scope. Rather treating
35 | whole page as insulation context, the scope is defined for <embed-page/>instances.
36 | The instances with common scope are sharing the data and notify each other of its data change via
37 | storage event. See sessionStorage
38 | and localStorage for details.
39 |
40 |
46 | EPA-WG (Embeddable Progressive Applications Working Group)
47 |
The <embed-page/> is an open source (GitHub) proof of concept for Embeddable Progressive Application
48 | - a microapplication
49 | container implemented as WebComponent acting as
50 |
51 |
Seamless IFRAME.
52 |
Its content is embedded into page DOM, but insulated on JS, DOM, CSS and browsing context level( A links and
53 | FORM get/post ).
54 | Unlike IFRAME it is embedded inline into parent page DOM and automatically resizing parent node.
55 |
56 |
HTML include.
57 |
With plain html and JS/css injection into page content.
58 |
Super-simple development with library of pre-made microapplications and plain html codebase.
66 | It assumes no web component knowledge and development.
67 |
68 |
Super-complex apps where on same page need to mix UX made with different frameworks and their
69 | incompatible otherwise revisions.
70 | The JS Context insulation of embed-page provides
71 | "Evolutionary architecture/Continuous Design"
72 | support to web page.
73 |
74 |
75 |
76 |
77 |
Is it safe?
78 |
79 | <embed-page/> has been made with security considerations in mind.
80 | It is definitely safer than exposing the whole page to 3rd part scripts which is a usual practice as of now.
81 | Within <embed-page/> scripts and CSS are insulated and which makes the container page relatively safe.
82 | It is not prone for hackers though. IFRAME given some extra safeguard with loosing on integration into web page
83 | and still having some holes addressed by <embed-page/>.
84 |
85 | The ultimate goal of <embed-page/> is to bring the web 3.0 needs to browser teams attention.
86 | Eventually making its functionality available on native level, safe and sound.
87 | Please comment, file the bug, star <embed-page/> project
88 | to support the cause.
89 |
The <embed-page/> gives ability to use primitive HTML with global variables and event handlers as
73 | web page Component. It insulates global variables and functions and let then be used in onXXX event handlers.
74 |
75 |
Globals.
76 |
In micro-frontend hosted by <embed-page> there is no need to worry about scoping of variables.
77 | Globals could be defined by element id attribute, by using var/let/const, or just by name:
78 |
79 | While in complex page it is a bad habit to use globals, in microapplication served by embed-page or IFRAME
80 | it is safe and advised as reduces the code complexity, hence increase readability and reliability.
81 | As long as UI follows the microapplication pattern: serve only own limited content.
82 |
83 |
Event handlers.
84 |
Could be written as html onXXX attributes.
85 | See html-as-component.html sample bellow
86 | While in complex page it is a bad habit to mix JS and HTML, in microapplication served by embed-page or IFRAME
87 | it is safe and advised as reduces the code complexity, hence increase readability and reliability.
88 | As long as UI follows the microapplication pattern: serve only own limited content.
89 |
90 |
Globals across pages
91 |
Additional perk of <embed-page> : globals as uninitialized variables keep values across page navigation.
92 | Of course be careful when appending data without trimming: JS memory pool is limited.
93 |
9 |
10 |
11 |
42 |
52 |
68 |
99 |
112 |
113 | |
114 | |
115 |
116 |
117 | In each scope:
118 | |
119 | |
120 |
121 | The SCRIPT type=module (scope 2 here) has proper insulation without the need for recover own context(globals)
122 | before running event handler. It also support the 'import' syntax for modules and ability to override window
123 | object as local const variable as Proxy trapping the window.XXX assignment and
124 | evaluating as variable in local scope.
125 |
126 |
Scripts and event handlers
127 | In order to reuse the scope, content of script tags and event handlers should be executed in same scope.
128 | Which could be achieved either by
129 |
130 | * running all code within single SCRIPT tag associated with embed-page.
131 | * wrapping each script with all pre- post- processing
132 |
133 |
Global script handling
134 |
135 |
136 |
137 |
138 |
139 |
140 |
Collect all scripts code, extract all variables as keywords with exception of
141 |
142 |
143 |
JS keywords
144 |
"clean" window properties from blank iframe window
145 |
EPA_ prefixed variables
146 |
EpaWindow properties
147 |
148 |
149 |
in rendered script declare all collected variables.
150 |
clone all EpaWindow properties into variables.
151 | For each script code
152 |
temporary clear container window properties to avoid leaking container globals into embed-page scope
153 |
154 |
155 |
preserve "unclean" window properties
156 |
remove those properties from window
157 |
158 |
159 |
in try{ section insert code }
160 |
catch(ex){ console.error(ex)} will permit to run following SCRIPTs
161 |
finally{}
162 |
163 |
164 |
move added to container window properties into EpaWindow ( detect by comparing with reference iframe )
Applications ( web pages ) in browser environment are referencing each other via direct Window object
35 | reference and by name via several APIs like window.frames, window.open, and target
36 | attribute on FORM or A link.
37 |
38 |
<embed-page> uses same API to reference other microapplication windows
39 | ( instances of <embed-page> ) and parent application ( parent window ).
40 |
41 |
42 |
While the direct reference to window object via window.open assumes the straight ownership relation
43 | ( only creator has reference to child and child has back reference to parent ),
44 | the named windows have a "global" in browser scope. Meaning different apps could use same name
45 | ( legit or not aside )
46 | to reference another application(window). In JS it is counted a bad practice to use global variables as it
47 | threatens the integrity of application. On application level it threatens even more: cross-application integrity
48 | and user safety.
49 |
50 |
51 |
52 | To solve the issue of global nature of named application window, <embed-page/>
53 | limits the iteration of named apps to child/parent and scoping by target groups defined by parent. This way the
54 | visibility (and referencing by target ) is limited to parent-child tree. The child could see the
55 | parent and only siblings under same target scope.
56 |
1. This H3 element has default styles, but H3 within this embed-page content should have a
33 | chocolate text color
34 |
35 |
36 |
37 |
38 |
1st instance - chocolate defined within embed-page inline.
39 | Note the TEMPLATE wrapper which prevents execution of code
40 | within EMBED-PAGE in page context
41 |
42 |
43 |
44 |
45 |
46 |
47 |
2. This H3 element should NOT be in Courier(monospace) font and NOT in green.
48 |
49 |
50 |
51 |
2nd instance. Insulated HTML in monospace font set by embedded JS
52 |
53 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | by setting content from from text area:
67 |
68 |
74 |
75 |
76 |
77 |
3rd instance. Should be changed by setting innerHTML from textarea content above
78 |
79 |
80 |
81 |
82 |
83 |
Visual demo steps
84 |
85 | CSS
86 | 1. The H3 header above should be intact(default) in color and text size
87 | AND h3 within embed-page use chocolate text color.
88 | 2. The H3 from embedded content should be in monospace font.
89 | JS 3. Click on 'make blue' the embedded H3 should be blue colored.
90 | 4. After 'innerHTML' click the H3 from embedded content should become green.
91 |
72 | EPA-WG (Embeddable Progressive Applications Working Group)
73 |
The <embed-page/> is a proof of concept for Embeddable Progressive Application
74 | - a microapplication container implemented as WebComponent acting as
75 |
76 |
Seamless IFRAME.
77 |
Its content is embedded into page DOM, but insulated on JS, DOM, CSS and browsing context level( A links and
78 | FORM get/post ).
79 | Unlike IFRAME it is embedded inline into parent page DOM and automatically resizing parent node.
80 |
81 |
HTML include.
82 |
With plain html and JS/css injection into page content.
83 |
Super-simple development with library of pre-made microapplications and plain html codebase.
89 | It assumes no web component knowledge and development.
90 |
91 |
Super-complex apps where on same page need to mix UX made with different frameworks and their
92 | incompatible otherwise revisions.
93 | The JS Context insulation of embed-page provides "evolutionary architecture" support to web page.
94 |
95 |
96 |
Demo cases
97 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
119 |
120 |
131 |
132 |
217 |
218 |
219 |
220 |
221 |
--------------------------------------------------------------------------------
/demo/appList.html.test.js:
--------------------------------------------------------------------------------
1 | suite('embed-page window frames APIs ', () =>
2 | {
3 | let E0, XA, XB, YA, YB, FA, FB
4 | , $E0, $XA, $XB,$YA, $YB, $FA, $FB
5 | , $$ = css => document.querySelector(css);
6 |
7 | const FA_URL = "appsList-microapp.html?src=iframeA"
8 | , FB_URL = "appsList-microapp.html?src=iframeB"
9 | , E0_URL = "appsList-microapp.html?from=epa0"
10 | , XA_URL = "appsList-microapp.html?name=A&target=X"
11 | , XB_URL = "appsList-microapp.html?name=B&target=X"
12 | , YA_URL = "appsList-microapp.html?name=A&target=Y"
13 | , YB_URL = "appsList-microapp.html?name=B&target=Y"
14 | , DEMO_URL = absUrl('../demo/');
15 |
16 | let AllReady, id_A, id_B, epaA, epaB, urlA, urlB, $A, $B;
17 |
18 | setup( ()=> AllReady || (AllReady = wait4all().then( args =>
19 | { [ E0, XA, XB, YA, YB, FA, FB ] = args;
20 | [ $E0, $XA, $XB,$YA, $YB, $FA, $FB ] = args.map( epa => ( css => ( epa.shadowRoot || epa.contentDocument ).querySelector(css) ) );
21 | }))
22 | );
23 |
24 | test('Initial src set', function()
25 | {
26 | assert.include( E0.src, E0_URL ); assert.equal( E0.getAttribute('src'), E0_URL );
27 | assert.include( XA.src, XA_URL ); assert.equal( XA.getAttribute('src'), XA_URL );
28 | assert.include( XB.src, XB_URL ); assert.equal( XB.getAttribute('src'), XB_URL );
29 | assert.include( YA.src, YA_URL ); assert.equal( YA.getAttribute('src'), YA_URL );
30 | assert.include( YB.src, YB_URL ); assert.equal( YB.getAttribute('src'), YB_URL );
31 | assert.include( FA.src, FA_URL ); assert.equal( FA.getAttribute('src'), FA_URL );
32 | assert.include( FB.src, FB_URL ); assert.equal( FB.getAttribute('src'), FB_URL );
33 | });
34 |
35 | testPair( "FA,FB" ); // reference check on IFRAME, of course it should pass. If not the test is written wrong.
36 | testPair( "XA,XB" ); // same test cases on embed-page pair
37 | testPair( "YA,YB" ); // second pair assures there is no window name collision in window.frames
38 |
39 | function
40 | testPair( pair )
41 | {
42 |
43 | suite( pair, ()=>
44 | {
45 | suiteSetup( ()=>
46 | { [ id_A, id_B ] = pair.split(',');
47 | [ epaA, epaB ] = eval(`[${pair}]`);
48 | [ urlA, urlB ] = pair.split(',').map( id=>eval(`${id}_URL`) );
49 | [ $A, $B ] = pair.split(',').map( id=>eval(`$${id}`) );
50 | });
51 | test(`1. epa.contentWindow.location matches SRC attribute`, function()
52 | {
53 | // epa.contentWindow.location === src
54 | epaA.window && assert.include( epaA.window.location .href, urlA );
55 | assert.include( epaA.contentWindow.location.href, urlA );
56 | epaB.window && assert.include( epaB.window.location .href, urlB );
57 | assert.include( epaB.contentWindow.location.href, urlB );
58 | });
59 |
60 | test(`2. parent.frames[A|B] matches URL`, function()
61 | { // for X and Y targeted pairs it ensures insulation of parent.frames
62 | assert.include( epaA.contentWindow.parent.frames["A"].location.href, urlA );
63 | assert.include( epaA.contentWindow.parent.frames["B"].location.href, urlB );
64 | });
65 |
66 | test('3. epa.open() into C, close() ', function()
67 | {
68 | const parentFramesCount0 = epaA.contentWindow.parent.frames.length;
69 | const url = `appsList-microapp.html?pair=${pair}&case=3`
70 | , w = epaA.contentWindow.open( url,"C" );
71 | if( "IFRAME" === epaA.tagName )
72 | { // standard window behavior just for reference and epa test clarity
73 | assert( !w.closed );
74 | const initialUrl = w.location.href;
75 | if( initialUrl !== "about:blank" )
76 | assert.include( initialUrl, url );
77 | w.close();
78 | const isClosed = w.closed;
79 | assert( isClosed );
80 | }else
81 | { assert.equal( 2, parentFramesCount0 );
82 | assert.equal( 3, w.parent.frames.length );
83 | assert( !w.closed );
84 | assert( w.parent.frames["C"] );
85 | assert.include( w.parent.frames["C"].location.href, url );
86 | assert.equal( 3, w.parent.frames.length );
87 | w.close();
88 | assert( w.closed );
89 | assert( !w.parent.frames["C"] );
90 | assert.equal( 2, w.parent.frames.length );
91 | }
92 |
93 | // about:blank as initial state
94 | // url as final
95 | // initial doc != final doc
96 | });
97 |
98 | test('4. epa.open replaces existing B window ', function()
99 | {
100 | assert( epaA.contentWindow.parent.frames["B"] );
101 |
102 | const parentFramesCount0 = epaA.contentWindow.parent.frames.length;
103 | const url = `appsList-microapp.html?pair=${pair}&case=4`
104 | , w = epaA.contentWindow.open( url,"B" )
105 | ,initialUrl = w.location.href;
106 |
107 | assert.equal( parentFramesCount0, w.parent.frames.length ); // window B is reused
108 | assert( !w.closed );
109 | assert( w.parent.frames["B"] );
110 |
111 | return loadPromise(epaB).then( finalUrl =>
112 | {
113 | assert.include( w.location.href, url );
114 | assert.include( w.parent.frames["B"].location.href, url );
115 |
116 | if( "IFRAME" === epaA.tagName )
117 | { // standard window behavior just for reference and epa test clarity
118 | }else
119 | {
120 | assert.equal( 2, w.parent.frames.length );
121 | }
122 | return true;
123 | });
124 | });
125 |
126 | test('5. link with NO target', function()
127 | {
128 | const p = loadPromise( epaB ).then( x=>
129 | {
130 | assert.include( epaA.contentWindow.location.href, urlA ); // unchanged
131 | assert.include( epaB.contentWindow.location.href, "link=A" );
132 | });
133 | SimClick( $B('a.no-target') );
134 | return p;
135 | });
136 | test('5a. link with named target - existing window', function()
137 | {
138 | const p = loadPromise( epaB ).then( x=>
139 | {
140 | assert.include( epaA.contentWindow.location.href, urlA ); // unchanged
141 | assert.include( epaB.contentWindow.location.href, "link=B" );
142 | });
143 | SimClick( $B('a[target=B]') );
144 | return p;
145 | });
146 | test('5b. link with named target - new window', function()
147 | {
148 | if( id_A.startsWith('F') )
149 | return;
150 | const c0 = $$(`embed-page[name=C][target=${id_A.charAt(0)}]`);
151 | assert( !c0 );
152 | assert.include( epaA.contentWindow.location.href, urlA );
153 | assert.include( epaB.contentWindow.location.href, "link=B" );
154 |
155 | SimClick( $B('a[target=C]') );
156 |
157 | const c1 = $$(`embed-page[name=C][target=${id_A.charAt(0)}]`);
158 | assert.include( c1 .contentWindow.location.href, "link=C" );
159 | assert.include( epaA.contentWindow.location.href, urlA ); // unchanged
160 | assert.include( epaB.contentWindow.location.href, "link=B" ); // unchanged
161 | c1.contentWindow.close();
162 | });
163 | test('5c. link with target=_self', function()
164 | {
165 | if( id_A.startsWith('F') )
166 | return;
167 | const c0 = $$(`embed-page[name=C][target=${id_A.charAt(0)}]`);
168 | assert( !c0 );
169 | assert.include( epaB.contentWindow.location.href, "link=B" );
170 |
171 | SimClick( $B('a[target=_self]') );
172 |
173 | const c1 = $$(`embed-page[name=C][target=${id_A.charAt(0)}]`);
174 | assert( !c1 );
175 | assert.include( epaB.contentWindow.location.href, "link=_self" ); // unchanged
176 | });
177 | test('5d. link with target=_parent', function()
178 | {
179 | if( id_A.startsWith('F') )
180 | return;
181 | SimClick( $B('a[target=_parent]') );
182 | assert.include( epaB.contentWindow.location.href, "link=_parent" ); // unchanged
183 | });
184 | test('5e. link with target=_top', function()
185 | {
186 | if( id_A.startsWith('F') )
187 | return;
188 | SimClick( $B('a[target=_top]') );
189 | assert.include( epaB.contentWindow.location.href, "link=_top" ); // unchanged
190 | });
191 | //
192 | // test('6. form with NO target', function()
193 | // {
194 | //
195 | // });
196 | //
197 | // test('6a. form with named target - existing window', function()
198 | // {
199 | //
200 | // });
201 | // test('6a. form with named target - new window', function()
202 | // {
203 | //
204 | // });
205 | // test('6b. form with target=_self', function()
206 | // {
207 | // });
208 | // test('6c. form with target=_parent', function()
209 | // {
210 | // });
211 | // test('6c. form with target=_top', function()
212 | // {
213 | // });
214 |
215 | });
216 | }
217 | function
218 | loadPromise( el )
219 | {
220 | return new Promise( ( resolve, reject ) =>
221 | {
222 | const loadCb = x=>{ release(); resolve( el.contentWindow.location.href ) }
223 | , errorCb = x=>{ release(); reject ( el.contentWindow.location.href ) };
224 |
225 | el.addEventListener( 'load', loadCb );
226 | el.addEventListener( 'error', errorCb );
227 |
228 | function
229 | release()
230 | {
231 | el.removeEventListener('load' , loadCb );
232 | el.removeEventListener('error', errorCb );
233 | }
234 | });
235 | }
236 | function
237 | SimClick( el )
238 | { var mouseEvent = document.createEvent('MouseEvent');
239 | mouseEvent.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
240 | el.dispatchEvent( mouseEvent );
241 | }
242 |
243 | function
244 | absUrl( rel )
245 | { const A = document.createElement('a');
246 | A.setAttribute( 'href', rel );
247 | return A.href;
248 | }
249 | function
250 | wait4all()
251 | {
252 | return Promise.all( ["e0","xa","xb","ya","yb","fa","fb"].map( wait4load ) );
253 | }
254 | function
255 | wait4load( id )
256 | {
257 | if( id.startsWith('f') )
258 | return Promise.resolve( $$( '#'+id ) );
259 | const E = document.getElementById( id );
260 | assert.notEqual( E, null );
261 | return E.promiseNext;
262 | }
263 | });
264 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/demo/current-script.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | embed-page demo - type=module, currentScript and rootNode
8 |
9 |
10 |
14 |
15 |
22 |
23 |
24 |
25 |
embed-page demo - SCRIPT tag, currentScript and rootNode
26 |
27 | As html container, embed-page reads and executes content of SCRIPT tag, and since its content is a subject of
28 | scope insulation, the rootNode, currentScript and other related "web page" scoped API should be scoped to
29 | content of embed-page.
30 |
31 |
32 | script type="module" meant to be run only once like with
33 | will be run within each embed-page container. Within same container those will not be executed multiple times.
34 | But for each container the script will be executed separately. Script titled Y is executed and Z would be ignored.
35 |
36 |
37 | script type="module" does not have document.currentScript set under current standard.
38 | Unlike in standard generic page, microapplication assumes minimal reuse of scripts and it is a
39 | convenience feature to use module-formatted script as embedding-aware one. I.e. it could serve only element
40 | it is embedded into.
41 | The Y and Z titled scripts would behave differently in generic page and within embed-page.
42 |
43 | document.currentScript is only(?) way to get reference to current node and traverse up to find
45 | the root node. For embed-page content rootNode is a shadow root of EPA container.
46 |
API
47 |
48 |
script src and embedded load is covered by main demo page, a coverage for core
49 | embed-page functionality.
50 |
51 |
52 | script type="module" loaded only once per embed-page.
53 |
document.currentScipt.getRootNode() - the way to access current scope.
58 | It is a shortcut of traversing up the dom tree to find top or content root.
59 |
60 |
document.currentScipt.rootNode - legacy replaced by getRootNode()
script type="module" loaded only once per embed-page
73 |
74 |
75 |
0. Demo of browser standard behavior. Running 4 scripts on page level.
76 |
77 | via currentScript should be WX
78 | : twice without "type". None with type="module" as currentScript is not set in browser page scope.
79 |
80 | via document should start with WX?U
81 | : twice without "type". Once with type="module" (Y) as module script should be execute only once.
82 |
83 |
84 |
85 |
86 |
87 |
88 |
94 |
95 |
151 | Within unscoped embed-page the expected is different than in scoped. Actual values are "KL" and blank respectively.
152 | M,N scripts are skipped as those are a type=module and were executed in case 0 above.
153 | Blank value here is due to scripts looking for first occurrence of input class="document-selected"
154 | which is defined in case 0 above.
155 |
156 |
157 |
158 | expected KLM
159 | expected KLM mixed in field of sample #0
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 | Within unscoped embed-page the expected is different than in scoped. Expected "ABCD" and blank respectively.
172 | Blank value here is due to scripts looking for first occurrence of input class="document-selected"
173 | which is defined in case 0 above. Expected ABCD mixed in field of sample #0
174 |
175 |
176 |
177 |
178 |
179 |