Ignore title parameter for navigator.registerProtocolHandler
[chromium-blink-merge.git] / mojo / public / js / bindings / codec.js
blob0b680172c2ddedc562e289f499ced46515c78c99
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.
5 define("mojo/public/js/bindings/codec", [
6 "mojo/public/js/bindings/unicode"
7 ], function(unicode) {
9 var kErrorUnsigned = "Passing negative value to unsigned";
11 // Memory -------------------------------------------------------------------
13 var kAlignment = 8;
14 var kHighWordMultiplier = 0x100000000;
15 var kHostIsLittleEndian = (function () {
16 var endianArrayBuffer = new ArrayBuffer(2);
17 var endianUint8Array = new Uint8Array(endianArrayBuffer);
18 var endianUint16Array = new Uint16Array(endianArrayBuffer);
19 endianUint16Array[0] = 1;
20 return endianUint8Array[0] == 1;
21 })();
23 function align(size) {
24 return size + (kAlignment - (size % kAlignment)) % kAlignment;
27 function getInt64(dataView, byteOffset, value) {
28 var lo, hi;
29 if (kHostIsLittleEndian) {
30 lo = dataView.getUint32(byteOffset, kHostIsLittleEndian);
31 hi = dataView.getInt32(byteOffset + 4, kHostIsLittleEndian);
32 } else {
33 hi = dataView.getInt32(byteOffset, kHostIsLittleEndian);
34 lo = dataView.getUint32(byteOffset + 4, kHostIsLittleEndian);
36 return lo + hi * kHighWordMultiplier;
39 function getUint64(dataView, byteOffset, value) {
40 var lo, hi;
41 if (kHostIsLittleEndian) {
42 lo = dataView.getUint32(byteOffset, kHostIsLittleEndian);
43 hi = dataView.getUint32(byteOffset + 4, kHostIsLittleEndian);
44 } else {
45 hi = dataView.getUint32(byteOffset, kHostIsLittleEndian);
46 lo = dataView.getUint32(byteOffset + 4, kHostIsLittleEndian);
48 return lo + hi * kHighWordMultiplier;
51 function setInt64(dataView, byteOffset, value) {
52 var hi = Math.floor(value / kHighWordMultiplier);
53 if (kHostIsLittleEndian) {
54 dataView.setInt32(byteOffset, value, kHostIsLittleEndian);
55 dataView.setInt32(byteOffset + 4, hi, kHostIsLittleEndian);
56 } else {
57 dataView.setInt32(byteOffset, hi, kHostIsLittleEndian);
58 dataView.setInt32(byteOffset + 4, value, kHostIsLittleEndian);
62 function setUint64(dataView, byteOffset, value) {
63 var hi = (value / kHighWordMultiplier) | 0;
64 if (kHostIsLittleEndian) {
65 dataView.setInt32(byteOffset, value, kHostIsLittleEndian);
66 dataView.setInt32(byteOffset + 4, hi, kHostIsLittleEndian);
67 } else {
68 dataView.setInt32(byteOffset, hi, kHostIsLittleEndian);
69 dataView.setInt32(byteOffset + 4, value, kHostIsLittleEndian);
73 function copyArrayBuffer(dstArrayBuffer, srcArrayBuffer) {
74 (new Uint8Array(dstArrayBuffer)).set(new Uint8Array(srcArrayBuffer));
77 // Buffer -------------------------------------------------------------------
79 function Buffer(sizeOrArrayBuffer) {
80 if (sizeOrArrayBuffer instanceof ArrayBuffer) {
81 this.arrayBuffer = sizeOrArrayBuffer;
82 } else {
83 this.arrayBuffer = new ArrayBuffer(sizeOrArrayBuffer);
86 this.dataView = new DataView(this.arrayBuffer);
87 this.next = 0;
90 Buffer.prototype.alloc = function(size) {
91 var pointer = this.next;
92 this.next += size;
93 if (this.next > this.arrayBuffer.byteLength) {
94 var newSize = (1.5 * (this.arrayBuffer.byteLength + size)) | 0;
95 this.grow(newSize);
97 return pointer;
100 Buffer.prototype.grow = function(size) {
101 var newArrayBuffer = new ArrayBuffer(size);
102 copyArrayBuffer(newArrayBuffer, this.arrayBuffer);
103 this.arrayBuffer = newArrayBuffer;
104 this.dataView = new DataView(this.arrayBuffer);
107 Buffer.prototype.trim = function() {
108 this.arrayBuffer = this.arrayBuffer.slice(0, this.next);
109 this.dataView = new DataView(this.arrayBuffer);
112 // Constants ----------------------------------------------------------------
114 var kArrayHeaderSize = 8;
115 var kStructHeaderSize = 8;
116 var kMessageHeaderSize = 16;
117 var kMessageWithRequestIDHeaderSize = 24;
119 // Decoder ------------------------------------------------------------------
121 function Decoder(buffer, handles, base) {
122 this.buffer = buffer;
123 this.handles = handles;
124 this.base = base;
125 this.next = base;
128 Decoder.prototype.skip = function(offset) {
129 this.next += offset;
132 Decoder.prototype.readInt8 = function() {
133 var result = this.buffer.dataView.getInt8(this.next, kHostIsLittleEndian);
134 this.next += 1;
135 return result;
138 Decoder.prototype.readUint8 = function() {
139 var result = this.buffer.dataView.getUint8(this.next, kHostIsLittleEndian);
140 this.next += 1;
141 return result;
144 Decoder.prototype.readInt16 = function() {
145 var result = this.buffer.dataView.getInt16(this.next, kHostIsLittleEndian);
146 this.next += 2;
147 return result;
150 Decoder.prototype.readUint16 = function() {
151 var result = this.buffer.dataView.getUint16(this.next, kHostIsLittleEndian);
152 this.next += 2;
153 return result;
156 Decoder.prototype.readInt32 = function() {
157 var result = this.buffer.dataView.getInt32(this.next, kHostIsLittleEndian);
158 this.next += 4;
159 return result;
162 Decoder.prototype.readUint32 = function() {
163 var result = this.buffer.dataView.getUint32(this.next, kHostIsLittleEndian);
164 this.next += 4;
165 return result;
168 Decoder.prototype.readInt64 = function() {
169 var result = getInt64(this.buffer.dataView, this.next, kHostIsLittleEndian);
170 this.next += 8;
171 return result;
174 Decoder.prototype.readUint64 = function() {
175 var result = getUint64(
176 this.buffer.dataView, this.next, kHostIsLittleEndian);
177 this.next += 8;
178 return result;
181 Decoder.prototype.readFloat = function() {
182 var result = this.buffer.dataView.getFloat32(
183 this.next, kHostIsLittleEndian);
184 this.next += 4;
185 return result;
188 Decoder.prototype.readDouble = function() {
189 var result = this.buffer.dataView.getFloat64(
190 this.next, kHostIsLittleEndian);
191 this.next += 8;
192 return result;
195 Decoder.prototype.decodePointer = function() {
196 // TODO(abarth): To correctly decode a pointer, we need to know the real
197 // base address of the array buffer.
198 var offsetPointer = this.next;
199 var offset = this.readUint64();
200 if (!offset)
201 return 0;
202 return offsetPointer + offset;
205 Decoder.prototype.decodeAndCreateDecoder = function() {
206 return new Decoder(this.buffer, this.handles, this.decodePointer());
209 Decoder.prototype.decodeHandle = function() {
210 return this.handles[this.readUint32()];
213 Decoder.prototype.decodeString = function() {
214 var numberOfBytes = this.readUint32();
215 var numberOfElements = this.readUint32();
216 var base = this.next;
217 this.next += numberOfElements;
218 return unicode.decodeUtf8String(
219 new Uint8Array(this.buffer.arrayBuffer, base, numberOfElements));
222 Decoder.prototype.decodeArray = function(cls) {
223 var numberOfBytes = this.readUint32();
224 var numberOfElements = this.readUint32();
225 var val = new Array(numberOfElements);
226 for (var i = 0; i < numberOfElements; ++i) {
227 val[i] = cls.decode(this);
229 return val;
232 Decoder.prototype.decodeStruct = function(cls) {
233 return cls.decode(this);
236 Decoder.prototype.decodeStructPointer = function(cls) {
237 return cls.decode(this.decodeAndCreateDecoder());
240 Decoder.prototype.decodeArrayPointer = function(cls) {
241 return this.decodeAndCreateDecoder().decodeArray(cls);
244 Decoder.prototype.decodeStringPointer = function() {
245 return this.decodeAndCreateDecoder().decodeString();
248 // Encoder ------------------------------------------------------------------
250 function Encoder(buffer, handles, base) {
251 this.buffer = buffer;
252 this.handles = handles;
253 this.base = base;
254 this.next = base;
257 Encoder.prototype.skip = function(offset) {
258 this.next += offset;
261 Encoder.prototype.writeInt8 = function(val) {
262 // NOTE: Endianness doesn't come into play for single bytes.
263 this.buffer.dataView.setInt8(this.next, val);
264 this.next += 1;
267 Encoder.prototype.writeUint8 = function(val) {
268 if (val < 0) {
269 throw new Error(kErrorUnsigned);
271 // NOTE: Endianness doesn't come into play for single bytes.
272 this.buffer.dataView.setUint8(this.next, val);
273 this.next += 1;
276 Encoder.prototype.writeInt16 = function(val) {
277 this.buffer.dataView.setInt16(this.next, val, kHostIsLittleEndian);
278 this.next += 2;
281 Encoder.prototype.writeUint16 = function(val) {
282 if (val < 0) {
283 throw new Error(kErrorUnsigned);
285 this.buffer.dataView.setUint16(this.next, val, kHostIsLittleEndian);
286 this.next += 2;
289 Encoder.prototype.writeInt32 = function(val) {
290 this.buffer.dataView.setInt32(this.next, val, kHostIsLittleEndian);
291 this.next += 4;
294 Encoder.prototype.writeUint32 = function(val) {
295 if (val < 0) {
296 throw new Error(kErrorUnsigned);
298 this.buffer.dataView.setUint32(this.next, val, kHostIsLittleEndian);
299 this.next += 4;
302 Encoder.prototype.writeInt64 = function(val) {
303 setInt64(this.buffer.dataView, this.next, val);
304 this.next += 8;
307 Encoder.prototype.writeUint64 = function(val) {
308 if (val < 0) {
309 throw new Error(kErrorUnsigned);
311 setUint64(this.buffer.dataView, this.next, val);
312 this.next += 8;
315 Encoder.prototype.writeFloat = function(val) {
316 this.buffer.dataView.setFloat32(this.next, val, kHostIsLittleEndian);
317 this.next += 4;
320 Encoder.prototype.writeDouble = function(val) {
321 this.buffer.dataView.setFloat64(this.next, val, kHostIsLittleEndian);
322 this.next += 8;
325 Encoder.prototype.encodePointer = function(pointer) {
326 if (!pointer)
327 return this.writeUint64(0);
328 // TODO(abarth): To correctly encode a pointer, we need to know the real
329 // base address of the array buffer.
330 var offset = pointer - this.next;
331 this.writeUint64(offset);
334 Encoder.prototype.createAndEncodeEncoder = function(size) {
335 var pointer = this.buffer.alloc(align(size));
336 this.encodePointer(pointer);
337 return new Encoder(this.buffer, this.handles, pointer);
340 Encoder.prototype.encodeHandle = function(handle) {
341 this.handles.push(handle);
342 this.writeUint32(this.handles.length - 1);
345 Encoder.prototype.encodeString = function(val) {
346 var base = this.next + kArrayHeaderSize;
347 var numberOfElements = unicode.encodeUtf8String(
348 val, new Uint8Array(this.buffer.arrayBuffer, base));
349 var numberOfBytes = kArrayHeaderSize + numberOfElements;
350 this.writeUint32(numberOfBytes);
351 this.writeUint32(numberOfElements);
352 this.next += numberOfElements;
355 Encoder.prototype.encodeArray = function(cls, val) {
356 var numberOfElements = val.length;
357 var numberOfBytes = kArrayHeaderSize + cls.encodedSize * numberOfElements;
358 this.writeUint32(numberOfBytes);
359 this.writeUint32(numberOfElements);
360 for (var i = 0; i < numberOfElements; ++i) {
361 cls.encode(this, val[i]);
365 Encoder.prototype.encodeStruct = function(cls, val) {
366 return cls.encode(this, val);
369 Encoder.prototype.encodeStructPointer = function(cls, val) {
370 var encoder = this.createAndEncodeEncoder(cls.encodedSize);
371 cls.encode(encoder, val);
374 Encoder.prototype.encodeArrayPointer = function(cls, val) {
375 var encodedSize = kArrayHeaderSize + cls.encodedSize * val.length;
376 var encoder = this.createAndEncodeEncoder(encodedSize);
377 encoder.encodeArray(cls, val);
380 Encoder.prototype.encodeStringPointer = function(val) {
381 var encodedSize = kArrayHeaderSize + unicode.utf8Length(val);
382 var encoder = this.createAndEncodeEncoder(encodedSize);
383 encoder.encodeString(val);
386 // Message ------------------------------------------------------------------
388 var kMessageExpectsResponse = 1 << 0;
389 var kMessageIsResponse = 1 << 1;
391 // Skip over num_bytes, num_fields, and message_name.
392 var kFlagsOffset = 4 + 4 + 4;
394 // Skip over num_bytes, num_fields, message_name, and flags.
395 var kRequestIDOffset = 4 + 4 + 4 + 4;
397 function Message(buffer, handles) {
398 this.buffer = buffer;
399 this.handles = handles;
402 Message.prototype.setRequestID = function(requestID) {
403 // TODO(darin): Verify that space was reserved for this field!
404 setUint64(this.buffer.dataView, kRequestIDOffset, requestID);
407 Message.prototype.getFlags = function() {
408 return this.buffer.dataView.getUint32(kFlagsOffset, kHostIsLittleEndian);
411 // MessageBuilder -----------------------------------------------------------
413 function MessageBuilder(messageName, payloadSize) {
414 // Currently, we don't compute the payload size correctly ahead of time.
415 // Instead, we resize the buffer at the end.
416 var numberOfBytes = kMessageHeaderSize + payloadSize;
417 this.buffer = new Buffer(numberOfBytes);
418 this.handles = [];
419 var encoder = this.createEncoder(kMessageHeaderSize);
420 encoder.writeUint32(kMessageHeaderSize);
421 encoder.writeUint32(2); // num_fields.
422 encoder.writeUint32(messageName);
423 encoder.writeUint32(0); // flags.
426 MessageBuilder.prototype.createEncoder = function(size) {
427 var pointer = this.buffer.alloc(size);
428 return new Encoder(this.buffer, this.handles, pointer);
431 MessageBuilder.prototype.encodeStruct = function(cls, val) {
432 cls.encode(this.createEncoder(cls.encodedSize), val);
435 MessageBuilder.prototype.finish = function() {
436 // TODO(abarth): Rather than resizing the buffer at the end, we could
437 // compute the size we need ahead of time, like we do in C++.
438 this.buffer.trim();
439 var message = new Message(this.buffer, this.handles);
440 this.buffer = null;
441 this.handles = null;
442 this.encoder = null;
443 return message;
446 // MessageWithRequestIDBuilder -----------------------------------------------
448 function MessageWithRequestIDBuilder(messageName, payloadSize, flags,
449 requestID) {
450 // Currently, we don't compute the payload size correctly ahead of time.
451 // Instead, we resize the buffer at the end.
452 var numberOfBytes = kMessageWithRequestIDHeaderSize + payloadSize;
453 this.buffer = new Buffer(numberOfBytes);
454 this.handles = [];
455 var encoder = this.createEncoder(kMessageWithRequestIDHeaderSize);
456 encoder.writeUint32(kMessageWithRequestIDHeaderSize);
457 encoder.writeUint32(3); // num_fields.
458 encoder.writeUint32(messageName);
459 encoder.writeUint32(flags);
460 encoder.writeUint64(requestID);
463 MessageWithRequestIDBuilder.prototype =
464 Object.create(MessageBuilder.prototype);
466 MessageWithRequestIDBuilder.prototype.constructor =
467 MessageWithRequestIDBuilder;
469 // MessageReader ------------------------------------------------------------
471 function MessageReader(message) {
472 this.decoder = new Decoder(message.buffer, message.handles, 0);
473 var messageHeaderSize = this.decoder.readUint32();
474 this.payloadSize =
475 message.buffer.arrayBuffer.byteLength - messageHeaderSize;
476 var numFields = this.decoder.readUint32();
477 this.messageName = this.decoder.readUint32();
478 this.flags = this.decoder.readUint32();
479 if (numFields >= 3)
480 this.requestID = this.decoder.readUint64();
481 this.decoder.skip(messageHeaderSize - this.decoder.next);
484 MessageReader.prototype.decodeStruct = function(cls) {
485 return cls.decode(this.decoder);
488 // Built-in types -----------------------------------------------------------
490 function Int8() {
493 Int8.encodedSize = 1;
495 Int8.decode = function(decoder) {
496 return decoder.readInt8();
499 Int8.encode = function(encoder, val) {
500 encoder.writeInt8(val);
503 Uint8.encode = function(encoder, val) {
504 encoder.writeUint8(val);
507 function Uint8() {
510 Uint8.encodedSize = 1;
512 Uint8.decode = function(decoder) {
513 return decoder.readUint8();
516 Uint8.encode = function(encoder, val) {
517 encoder.writeUint8(val);
520 function Int16() {
523 Int16.encodedSize = 2;
525 Int16.decode = function(decoder) {
526 return decoder.readInt16();
529 Int16.encode = function(encoder, val) {
530 encoder.writeInt16(val);
533 function Uint16() {
536 Uint16.encodedSize = 2;
538 Uint16.decode = function(decoder) {
539 return decoder.readUint16();
542 Uint16.encode = function(encoder, val) {
543 encoder.writeUint16(val);
546 function Int32() {
549 Int32.encodedSize = 4;
551 Int32.decode = function(decoder) {
552 return decoder.readInt32();
555 Int32.encode = function(encoder, val) {
556 encoder.writeInt32(val);
559 function Uint32() {
562 Uint32.encodedSize = 4;
564 Uint32.decode = function(decoder) {
565 return decoder.readUint32();
568 Uint32.encode = function(encoder, val) {
569 encoder.writeUint32(val);
572 function Int64() {
575 Int64.encodedSize = 8;
577 Int64.decode = function(decoder) {
578 return decoder.readInt64();
581 Int64.encode = function(encoder, val) {
582 encoder.writeInt64(val);
585 function Uint64() {
588 Uint64.encodedSize = 8;
590 Uint64.decode = function(decoder) {
591 return decoder.readUint64();
594 Uint64.encode = function(encoder, val) {
595 encoder.writeUint64(val);
598 function String() {
601 String.encodedSize = 8;
603 String.decode = function(decoder) {
604 return decoder.decodeStringPointer();
607 String.encode = function(encoder, val) {
608 encoder.encodeStringPointer(val);
612 function Float() {
615 Float.encodedSize = 4;
617 Float.decode = function(decoder) {
618 return decoder.readFloat();
621 Float.encode = function(encoder, val) {
622 encoder.writeFloat(val);
625 function Double() {
628 Double.encodedSize = 8;
630 Double.decode = function(decoder) {
631 return decoder.readDouble();
634 Double.encode = function(encoder, val) {
635 encoder.writeDouble(val);
638 function PointerTo(cls) {
639 this.cls = cls;
642 PointerTo.prototype.encodedSize = 8;
644 PointerTo.prototype.decode = function(decoder) {
645 return this.cls.decode(decoder.decodeAndCreateDecoder());
648 PointerTo.prototype.encode = function(encoder, val) {
649 var objectEncoder = encoder.createAndEncodeEncoder(this.cls.encodedSize);
650 this.cls.encode(objectEncoder, val);
653 function ArrayOf(cls) {
654 this.cls = cls;
657 ArrayOf.prototype.encodedSize = 8;
659 ArrayOf.prototype.decode = function(decoder) {
660 return decoder.decodeArrayPointer(this.cls);
663 ArrayOf.prototype.encode = function(encoder, val) {
664 encoder.encodeArrayPointer(this.cls, val);
667 function Handle() {
670 Handle.encodedSize = 4;
672 Handle.decode = function(decoder) {
673 return decoder.decodeHandle();
676 Handle.encode = function(encoder, val) {
677 encoder.encodeHandle(val);
680 var exports = {};
681 exports.align = align;
682 exports.Buffer = Buffer;
683 exports.Message = Message;
684 exports.MessageBuilder = MessageBuilder;
685 exports.MessageWithRequestIDBuilder = MessageWithRequestIDBuilder;
686 exports.MessageReader = MessageReader;
687 exports.kArrayHeaderSize = kArrayHeaderSize;
688 exports.kStructHeaderSize = kStructHeaderSize;
689 exports.kMessageHeaderSize = kMessageHeaderSize;
690 exports.kMessageExpectsResponse = kMessageExpectsResponse;
691 exports.kMessageIsResponse = kMessageIsResponse;
692 exports.Int8 = Int8;
693 exports.Uint8 = Uint8;
694 exports.Int16 = Int16;
695 exports.Uint16 = Uint16;
696 exports.Int32 = Int32;
697 exports.Uint32 = Uint32;
698 exports.Int64 = Int64;
699 exports.Uint64 = Uint64;
700 exports.Float = Float;
701 exports.Double = Double;
702 exports.String = String;
703 exports.PointerTo = PointerTo;
704 exports.ArrayOf = ArrayOf;
705 exports.Handle = Handle;
706 return exports;