OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 // TODO(jacobr): copy this file from the correct pub directory rather than |
| 6 // manually copying it here once it is hosted in pub. |
| 7 // Bootstrap support for Dart scripts on the page as this script. |
| 8 if (navigator.webkitStartDart) { |
| 9 if (!navigator.webkitStartDart()) { |
| 10 document.body.innerHTML = 'This build has expired. Please download a new Da
rtium at http://www.dartlang.org/dartium/index.html'; |
| 11 } |
| 12 } else { |
| 13 // TODO: |
| 14 // - Support in-browser compilation. |
| 15 // - Handle inline Dart scripts. |
| 16 window.addEventListener("DOMContentLoaded", function (e) { |
| 17 // Fall back to compiled JS. Run through all the scripts and |
| 18 // replace them if they have a type that indicate that they source |
| 19 // in Dart code. |
| 20 // |
| 21 // <script type="application/dart" src="..."></script> |
| 22 // |
| 23 var scripts = document.getElementsByTagName("script"); |
| 24 var length = scripts.length; |
| 25 for (var i = 0; i < length; ++i) { |
| 26 if (scripts[i].type == "application/dart") { |
| 27 // Remap foo.dart to foo.dart.js. |
| 28 if (scripts[i].src && scripts[i].src != '') { |
| 29 var script = document.createElement('script'); |
| 30 script.src = scripts[i].src + '.js'; |
| 31 var parent = scripts[i].parentNode; |
| 32 parent.replaceChild(script, scripts[i]); |
| 33 } |
| 34 } |
| 35 } |
| 36 }, false); |
| 37 } |
| 38 |
| 39 // --------------------------------------------------------------------------- |
| 40 // Experimental support for JS interoperability |
| 41 // --------------------------------------------------------------------------- |
| 42 function SendPortSync() { |
| 43 } |
| 44 |
| 45 function ReceivePortSync() { |
| 46 this.id = ReceivePortSync.id++; |
| 47 ReceivePortSync.map[this.id] = this; |
| 48 } |
| 49 |
| 50 (function() { |
| 51 // Serialize the following types as follows: |
| 52 // - primitives / null: unchanged |
| 53 // - lists: [ 'list', internal id, list of recursively serialized elements ] |
| 54 // - maps: [ 'map', internal id, map of keys and recursively serialized value
s ] |
| 55 // - send ports: [ 'sendport', type, isolate id, port id ] |
| 56 // |
| 57 // Note, internal id's are for cycle detection. |
| 58 function serialize(message) { |
| 59 var visited = []; |
| 60 function checkedSerialization(obj, serializer) { |
| 61 // Implementation detail: for now use linear search. |
| 62 // Another option is expando, but it may prohibit |
| 63 // VM optimizations (like putting object into slow mode |
| 64 // on property deletion.) |
| 65 var id = visited.indexOf(obj); |
| 66 if (id != -1) return [ 'ref', id ]; |
| 67 var id = visited.length; |
| 68 visited.push(obj); |
| 69 return serializer(id); |
| 70 } |
| 71 |
| 72 function doSerialize(message) { |
| 73 if (message == null) { |
| 74 return null; // Convert undefined to null. |
| 75 } else if (typeof(message) == 'string' || |
| 76 typeof(message) == 'number' || |
| 77 typeof(message) == 'boolean') { |
| 78 return message; |
| 79 } else if (message instanceof Array) { |
| 80 return checkedSerialization(message, function(id) { |
| 81 var values = new Array(message.length); |
| 82 for (var i = 0; i < message.length; i++) { |
| 83 values[i] = doSerialize(message[i]); |
| 84 } |
| 85 return [ 'list', id, values ]; |
| 86 }); |
| 87 } else if (message instanceof LocalSendPortSync) { |
| 88 return [ 'sendport', 'nativejs', message.receivePort.id ]; |
| 89 } else if (message instanceof DartSendPortSync) { |
| 90 return [ 'sendport', 'dart', message.isolateId, message.portId ]; |
| 91 } else { |
| 92 return checkedSerialization(message, function(id) { |
| 93 var keys = Object.getOwnPropertyNames(message); |
| 94 var values = new Array(keys.length); |
| 95 for (var i = 0; i < keys.length; i++) { |
| 96 values[i] = doSerialize(message[keys[i]]); |
| 97 } |
| 98 return [ 'map', id, keys, values ]; |
| 99 }); |
| 100 } |
| 101 } |
| 102 return doSerialize(message); |
| 103 } |
| 104 |
| 105 function deserialize(message) { |
| 106 return deserializeHelper(message); |
| 107 } |
| 108 |
| 109 function deserializeHelper(message) { |
| 110 if (message == null || |
| 111 typeof(message) == 'string' || |
| 112 typeof(message) == 'number' || |
| 113 typeof(message) == 'boolean') { |
| 114 return message; |
| 115 } |
| 116 switch (message[0]) { |
| 117 case 'map': return deserializeMap(message); |
| 118 case 'sendport': return deserializeSendPort(message); |
| 119 case 'list': return deserializeList(message); |
| 120 default: throw 'unimplemented'; |
| 121 } |
| 122 } |
| 123 |
| 124 function deserializeMap(message) { |
| 125 var result = { }; |
| 126 var id = message[1]; |
| 127 var keys = message[2]; |
| 128 var values = message[3]; |
| 129 for (var i = 0, length = keys.length; i < length; i++) { |
| 130 var key = deserializeHelper(keys[i]); |
| 131 var value = deserializeHelper(values[i]); |
| 132 result[key] = value; |
| 133 } |
| 134 return result; |
| 135 } |
| 136 |
| 137 function deserializeSendPort(message) { |
| 138 var tag = message[1]; |
| 139 switch (tag) { |
| 140 case 'nativejs': |
| 141 var id = message[2]; |
| 142 return new LocalSendPortSync(ReceivePortSync.map[id]); |
| 143 case 'dart': |
| 144 var isolateId = message[2]; |
| 145 var portId = message[3]; |
| 146 return new DartSendPortSync(isolateId, portId); |
| 147 default: |
| 148 throw 'Illegal SendPortSync type: $tag'; |
| 149 } |
| 150 } |
| 151 |
| 152 function deserializeList(message) { |
| 153 var values = message[2]; |
| 154 var length = values.length; |
| 155 var result = new Array(length); |
| 156 for (var i = 0; i < length; i++) { |
| 157 result[i] = deserializeHelper(values[i]); |
| 158 } |
| 159 return result; |
| 160 } |
| 161 |
| 162 window.registerPort = function(name, port) { |
| 163 var stringified = JSON.stringify(serialize(port)); |
| 164 window.localStorage['dart-port:' + name] = stringified; |
| 165 }; |
| 166 |
| 167 window.lookupPort = function(name) { |
| 168 var stringified = window.localStorage['dart-port:' + name]; |
| 169 return deserialize(JSON.parse(stringified)); |
| 170 }; |
| 171 |
| 172 ReceivePortSync.id = 0; |
| 173 ReceivePortSync.map = {}; |
| 174 |
| 175 ReceivePortSync.dispatchCall = function(id, message) { |
| 176 // TODO(vsm): Handle and propagate exceptions. |
| 177 var deserialized = deserialize(message); |
| 178 var result = ReceivePortSync.map[id].callback(deserialized); |
| 179 return serialize(result); |
| 180 }; |
| 181 |
| 182 ReceivePortSync.prototype.receive = function(callback) { |
| 183 this.callback = callback; |
| 184 }; |
| 185 |
| 186 ReceivePortSync.prototype.toSendPort = function() { |
| 187 return new LocalSendPortSync(this); |
| 188 }; |
| 189 |
| 190 ReceivePortSync.prototype.close = function() { |
| 191 delete ReceivePortSync.map[this.id]; |
| 192 }; |
| 193 |
| 194 if (navigator.webkitStartDart) { |
| 195 window.addEventListener('js-sync-message', function(event) { |
| 196 var data = JSON.parse(getPortSyncEventData(event)); |
| 197 var deserialized = deserialize(data.message); |
| 198 var result = ReceivePortSync.map[data.id].callback(deserialized); |
| 199 // TODO(vsm): Handle and propagate exceptions. |
| 200 dispatchEvent('js-result', serialize(result)); |
| 201 }, false); |
| 202 } |
| 203 |
| 204 function LocalSendPortSync(receivePort) { |
| 205 this.receivePort = receivePort; |
| 206 } |
| 207 |
| 208 LocalSendPortSync.prototype = new SendPortSync(); |
| 209 |
| 210 LocalSendPortSync.prototype.callSync = function(message) { |
| 211 // TODO(vsm): Do a direct deepcopy. |
| 212 message = deserialize(serialize(message)); |
| 213 return this.receivePort.callback(message); |
| 214 } |
| 215 |
| 216 function DartSendPortSync(isolateId, portId) { |
| 217 this.isolateId = isolateId; |
| 218 this.portId = portId; |
| 219 } |
| 220 |
| 221 DartSendPortSync.prototype = new SendPortSync(); |
| 222 |
| 223 function dispatchEvent(receiver, message) { |
| 224 var string = JSON.stringify(message); |
| 225 var event = document.createEvent('CustomEvent'); |
| 226 event.initCustomEvent(receiver, false, false, string); |
| 227 window.dispatchEvent(event); |
| 228 } |
| 229 |
| 230 function getPortSyncEventData(event) { |
| 231 return event.detail; |
| 232 } |
| 233 |
| 234 DartSendPortSync.prototype.callSync = function(message) { |
| 235 var serialized = serialize(message); |
| 236 var target = 'dart-port-' + this.isolateId + '-' + this.portId; |
| 237 // TODO(vsm): Make this re-entrant. |
| 238 // TODO(vsm): Set this up set once, on the first call. |
| 239 var source = target + '-result'; |
| 240 var result = null; |
| 241 var listener = function (e) { |
| 242 result = JSON.parse(getPortSyncEventData(e)); |
| 243 }; |
| 244 window.addEventListener(source, listener, false); |
| 245 dispatchEvent(target, [source, serialized]); |
| 246 window.removeEventListener(source, listener, false); |
| 247 return deserialize(result); |
| 248 } |
| 249 })(); |
OLD | NEW |