1 // Copyright 2013 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.
6 * @fileoverview Deferred resource loader for OOBE/Login screens.
9 cr.define('cr.ui.login.ResourceLoader', function() {
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.
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.
30 * ResourceLoader.registerAssets({
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'}]
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.
42 function registerAssets(desc) {
43 var html = desc.html || [];
44 var css = desc.css || [];
45 var js = desc.js || [];
47 html: html, css: css, js: js,
49 count: html.length + css.length + js.length
54 * Determines whether an asset bundle is defined for a specified id.
55 * @param {string} id The possible identifier.
57 function hasDeferredAssets(id) {
62 * Determines whether an asset bundle has already been loaded.
63 * @param {string} id The identifier of the asset bundle.
65 function alreadyLoadedAssets(id) {
66 return hasDeferredAssets(id) && ASSETS[id].loaded;
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.
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);
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.
87 function loadJS(id, url) {
88 var script = document.createElement('script');
90 script.onload = resourceLoaded.bind(null, id);
91 document.head.appendChild(script);
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.
99 function moveNodes(from, to) {
100 Array.prototype.forEach.call(from.children, to.appendChild, to);
104 * Tests whether an XMLHttpRequest has successfully finished loading.
105 * @param {string} url The requested URL.
106 * @param {XMLHttpRequest} xhr The XHR object.
108 function isSuccessful(url, xhr) {
109 var fileURL = /^file:\/\//;
110 return xhr.readyState == 4 &&
111 (xhr.status == 200 || fileURL.test(url) && xhr.status == 0);
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.
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));
131 xhr.responseType = 'document';
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.
141 function resourceLoaded(id) {
142 var assets = ASSETS[id];
144 if (assets.count == 0)
149 * Finishes loading an asset bundle.
150 * @param {string} id Identifier of the asset bundle.
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() {
158 chrome.send('screenAssetsLoaded', [id]);
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.
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) {
176 assets.css.forEach(loadCSS.bind(null, id));
177 assets.js.forEach(loadJS.bind(null, id));
178 assets.html.forEach(loadHTML.bind(null, id));
183 alreadyLoadedAssets: alreadyLoadedAssets,
184 hasDeferredAssets: hasDeferredAssets,
185 loadAssets: loadAssets,
186 registerAssets: registerAssets