OLD | NEW |
| (Empty) |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 /** | |
6 * @fileoverview Deferred resource loader for OOBE/Login screens. | |
7 */ | |
8 | |
9 cr.define('cr.ui.login.ResourceLoader', function() { | |
10 'use strict'; | |
11 | |
12 // Deferred assets. | |
13 var ASSETS = {}; | |
14 | |
15 /** | |
16 * Register assets for deferred loading. When the bundle is loaded | |
17 * assets will be added to the current page's DOM: <link> and <script> | |
18 * tags pointing to the CSS and JavaScript will be added to the | |
19 * <head>, and HTML will be appended to a specified element. | |
20 * | |
21 * @param {Object} desc Descriptor for the asset bundle | |
22 * @param {string} desc.id Unique identifier for the asset bundle. | |
23 * @param {Array=} desc.js URLs containing JavaScript sources. | |
24 * @param {Array=} desc.css URLs containing CSS rules. | |
25 * @param {Array.<Object>=} desc.html Descriptors for HTML fragments, | |
26 * each of which has a 'url' property and a 'targetID' property that | |
27 * specifies the node under which the HTML should be appended. | |
28 * | |
29 * Example: | |
30 * ResourceLoader.registerAssets({ | |
31 * id: 'bundle123', | |
32 * js: ['//foo.com/src.js', '//bar.com/lib.js'], | |
33 * css: ['//foo.com/style.css'], | |
34 * html: [{ url: '//foo.com/tmpls.html' targetID: 'tmpls'}] | |
35 * }); | |
36 * | |
37 * Note: to avoid cross-site requests, all HTML assets must be served | |
38 * from the same host as the rendered page. For example, if the | |
39 * rendered page is served as chrome://oobe, then all the HTML assets | |
40 * must be served as chrome://oobe/path/to/something.html. | |
41 */ | |
42 function registerAssets(desc) { | |
43 var html = desc.html || []; | |
44 var css = desc.css || []; | |
45 var js = desc.js || []; | |
46 ASSETS[desc.id] = { | |
47 html: html, css: css, js: js, | |
48 loaded: false, | |
49 count: html.length + css.length + js.length | |
50 }; | |
51 } | |
52 | |
53 /** | |
54 * Determines whether an asset bundle is defined for a specified id. | |
55 * @param {string} id The possible identifier. | |
56 */ | |
57 function hasDeferredAssets(id) { | |
58 return id in ASSETS; | |
59 } | |
60 | |
61 /** | |
62 * Determines whether an asset bundle has already been loaded. | |
63 * @param {string} id The identifier of the asset bundle. | |
64 */ | |
65 function alreadyLoadedAssets(id) { | |
66 return hasDeferredAssets(id) && ASSETS[id].loaded; | |
67 } | |
68 | |
69 /** | |
70 * Load a stylesheet into the current document. | |
71 * @param {string} id Identifier of the stylesheet's asset bundle. | |
72 * @param {string} url The URL resolving to a stylesheet. | |
73 */ | |
74 function loadCSS(id, url) { | |
75 var link = document.createElement('link'); | |
76 link.setAttribute('rel', 'stylesheet'); | |
77 link.setAttribute('href', url); | |
78 link.onload = resourceLoaded.bind(null, id); | |
79 document.head.appendChild(link); | |
80 } | |
81 | |
82 /** | |
83 * Load a script into the current document. | |
84 * @param {string} id Identifier of the script's asset bundle. | |
85 * @param {string} url The URL resolving to a script. | |
86 */ | |
87 function loadJS(id, url) { | |
88 var script = document.createElement('script'); | |
89 script.src = url; | |
90 script.onload = resourceLoaded.bind(null, id); | |
91 document.head.appendChild(script); | |
92 } | |
93 | |
94 /** | |
95 * Move DOM nodes from one parent element to another. | |
96 * @param {HTMLElement} from Element whose children should be moved. | |
97 * @param {HTMLElement} to Element to which nodes should be appended. | |
98 */ | |
99 function moveNodes(from, to) { | |
100 Array.prototype.forEach.call(from.children, to.appendChild, to); | |
101 } | |
102 | |
103 /** | |
104 * Tests whether an XMLHttpRequest has successfully finished loading. | |
105 * @param {string} url The requested URL. | |
106 * @param {XMLHttpRequest} xhr The XHR object. | |
107 */ | |
108 function isSuccessful(url, xhr) { | |
109 var fileURL = /^file:\/\//; | |
110 return xhr.readyState == 4 && | |
111 (xhr.status == 200 || fileURL.test(url) && xhr.status == 0); | |
112 } | |
113 | |
114 /* | |
115 * Load a chunk of HTML into the current document. | |
116 * @param {string} id Identifier of the page's asset bundle. | |
117 * @param {Object} html Descriptor of the HTML to fetch. | |
118 * @param {string} html.url The URL resolving to some HTML. | |
119 * @param {string} html.targetID The element ID to which the retrieved | |
120 * HTML nodes should be appended. | |
121 */ | |
122 function loadHTML(id, html) { | |
123 var xhr = new XMLHttpRequest(); | |
124 xhr.open('GET', html.url); | |
125 xhr.onreadystatechange = function() { | |
126 if (isSuccessful(html.url, xhr)) { | |
127 moveNodes(this.responseXML.body, $(html.targetID)); | |
128 resourceLoaded(id); | |
129 } | |
130 }; | |
131 xhr.responseType = 'document'; | |
132 xhr.send(); | |
133 } | |
134 | |
135 /** | |
136 * Record that a resource has been loaded for an asset bundle. When | |
137 * all the resources have been loaded the callback that was specified | |
138 * in the loadAssets call is invoked. | |
139 * @param {string} id Identifier of the asset bundle. | |
140 */ | |
141 function resourceLoaded(id) { | |
142 var assets = ASSETS[id]; | |
143 assets.count--; | |
144 if (assets.count == 0) | |
145 finishedLoading(id); | |
146 } | |
147 | |
148 /** | |
149 * Finishes loading an asset bundle. | |
150 * @param {string} id Identifier of the asset bundle. | |
151 */ | |
152 function finishedLoading(id) { | |
153 var assets = ASSETS[id]; | |
154 console.log('Finished loading asset bundle', id); | |
155 assets.loaded = true; | |
156 window.setTimeout(function() { | |
157 assets.callback(); | |
158 chrome.send('screenAssetsLoaded', [id]); | |
159 }, 0); | |
160 } | |
161 | |
162 /** | |
163 * Load an asset bundle, invoking the callback when finished. | |
164 * @param {string} id Identifier for the asset bundle to load. | |
165 * @param {function()=} callback Function to invoke when done loading. | |
166 */ | |
167 function loadAssets(id, callback) { | |
168 var assets = ASSETS[id]; | |
169 assets.callback = callback || function() {}; | |
170 console.log('Loading asset bundle', id); | |
171 if (alreadyLoadedAssets(id)) | |
172 console.warn('asset bundle', id, 'already loaded!'); | |
173 if (assets.count == 0) { | |
174 finishedLoading(id); | |
175 } else { | |
176 assets.css.forEach(loadCSS.bind(null, id)); | |
177 assets.js.forEach(loadJS.bind(null, id)); | |
178 assets.html.forEach(loadHTML.bind(null, id)); | |
179 } | |
180 } | |
181 | |
182 return { | |
183 alreadyLoadedAssets: alreadyLoadedAssets, | |
184 hasDeferredAssets: hasDeferredAssets, | |
185 loadAssets: loadAssets, | |
186 registerAssets: registerAssets | |
187 }; | |
188 }); | |
OLD | NEW |