Upstream version 11.39.266.0
[platform/framework/web/crosswalk.git] / src / mojo / public / js / bindings / validator.js
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 define("mojo/public/js/bindings/validator", [
6   "mojo/public/js/bindings/codec",
7 ], function(codec) {
8
9   var validationError = {
10     NONE: 'VALIDATION_ERROR_NONE',
11     MISALIGNED_OBJECT: 'VALIDATION_ERROR_MISALIGNED_OBJECT',
12     ILLEGAL_MEMORY_RANGE: 'VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE',
13     UNEXPECTED_STRUCT_HEADER: 'VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER',
14     UNEXPECTED_ARRAY_HEADER: 'VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER',
15     ILLEGAL_HANDLE: 'VALIDATION_ERROR_ILLEGAL_HANDLE',
16     UNEXPECTED_INVALID_HANDLE: 'VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE',
17     ILLEGAL_POINTER: 'VALIDATION_ERROR_ILLEGAL_POINTER',
18     UNEXPECTED_NULL_POINTER: 'VALIDATION_ERROR_UNEXPECTED_NULL_POINTER',
19     MESSAGE_HEADER_INVALID_FLAG_COMBINATION:
20         'VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAG_COMBINATION',
21     MESSAGE_HEADER_MISSING_REQUEST_ID:
22         'VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID'
23   };
24
25   var NULL_MOJO_POINTER = "NULL_MOJO_POINTER";
26
27   function isStringClass(cls) {
28     return cls === codec.String || cls === codec.NullableString;
29   }
30
31   function isHandleClass(cls) {
32     return cls === codec.Handle || cls === codec.NullableHandle;
33   }
34
35   function isNullable(type) {
36     return type === codec.NullableString || type === codec.NullableHandle ||
37         type instanceof codec.NullableArrayOf ||
38         type instanceof codec.NullablePointerTo;
39   }
40
41   function Validator(message) {
42     this.message = message;
43     this.offset = 0;
44     this.handleIndex = 0;
45   }
46
47   Object.defineProperty(Validator.prototype, "offsetLimit", {
48     get: function() { return this.message.buffer.byteLength; }
49   });
50
51   Object.defineProperty(Validator.prototype, "handleIndexLimit", {
52     get: function() { return this.message.handles.length; }
53   });
54
55   // True if we can safely allocate a block of bytes from start to
56   // to start + numBytes.
57   Validator.prototype.isValidRange = function(start, numBytes) {
58     // Only positive JavaScript integers that are less than 2^53
59     // (Number.MAX_SAFE_INTEGER) can be represented exactly.
60     if (start < this.offset || numBytes <= 0 ||
61         !Number.isSafeInteger(start) ||
62         !Number.isSafeInteger(numBytes))
63       return false;
64
65     var newOffset = start + numBytes;
66     if (!Number.isSafeInteger(newOffset) || newOffset > this.offsetLimit)
67       return false;
68
69     return true;
70   }
71
72   Validator.prototype.claimRange = function(start, numBytes) {
73     if (this.isValidRange(start, numBytes)) {
74       this.offset = start + numBytes;
75       return true;
76     }
77     return false;
78   }
79
80   Validator.prototype.claimHandle = function(index) {
81     if (index === codec.kEncodedInvalidHandleValue)
82       return true;
83
84     if (index < this.handleIndex || index >= this.handleIndexLimit)
85       return false;
86
87     // This is safe because handle indices are uint32.
88     this.handleIndex = index + 1;
89     return true;
90   }
91
92   Validator.prototype.validateHandle = function(offset, nullable) {
93     var index = this.message.buffer.getUint32(offset);
94
95     if (index === codec.kEncodedInvalidHandleValue)
96       return nullable ?
97           validationError.NONE : validationError.UNEXPECTED_INVALID_HANDLE;
98
99     if (!this.claimHandle(index))
100       return validationError.ILLEGAL_HANDLE;
101     return validationError.NONE;
102   }
103
104   Validator.prototype.validateStructHeader =
105       function(offset, minNumBytes, minNumFields) {
106     if (!codec.isAligned(offset))
107       return validationError.MISALIGNED_OBJECT;
108
109     if (!this.isValidRange(offset, codec.kStructHeaderSize))
110       return validationError.ILLEGAL_MEMORY_RANGE;
111
112     var numBytes = this.message.buffer.getUint32(offset);
113     var numFields = this.message.buffer.getUint32(offset + 4);
114
115     if (numBytes < minNumBytes || numFields < minNumFields)
116       return validationError.UNEXPECTED_STRUCT_HEADER;
117
118     if (!this.claimRange(offset, numBytes))
119       return validationError.ILLEGAL_MEMORY_RANGE;
120
121     return validationError.NONE;
122   }
123
124   Validator.prototype.validateMessageHeader = function() {
125     var err = this.validateStructHeader(0, codec.kMessageHeaderSize, 2);
126     if (err != validationError.NONE)
127       return err;
128
129     var numBytes = this.message.getHeaderNumBytes();
130     var numFields = this.message.getHeaderNumFields();
131
132     var validNumFieldsAndNumBytes =
133         (numFields == 2 && numBytes == codec.kMessageHeaderSize) ||
134         (numFields == 3 &&
135          numBytes == codec.kMessageWithRequestIDHeaderSize) ||
136         (numFields > 3 &&
137          numBytes >= codec.kMessageWithRequestIDHeaderSize);
138     if (!validNumFieldsAndNumBytes)
139       return validationError.UNEXPECTED_STRUCT_HEADER;
140
141     var expectsResponse = this.message.expectsResponse();
142     var isResponse = this.message.isResponse();
143
144     if (numFields == 2 && (expectsResponse || isResponse))
145       return validationError.MESSAGE_HEADER_MISSING_REQUEST_ID;
146
147     if (isResponse && expectsResponse)
148       return validationError.MESSAGE_HEADER_INVALID_FLAG_COMBINATION;
149
150     return validationError.NONE;
151   }
152
153   // Returns the message.buffer relative offset this pointer "points to",
154   // NULL_MOJO_POINTER if the pointer represents a null, or JS null if the
155   // pointer's value is not valid.
156   Validator.prototype.decodePointer = function(offset) {
157     var pointerValue = this.message.buffer.getUint64(offset);
158     if (pointerValue === 0)
159       return NULL_MOJO_POINTER;
160     var bufferOffset = offset + pointerValue;
161     return Number.isSafeInteger(bufferOffset) ? bufferOffset : null;
162   }
163
164   Validator.prototype.validateArrayPointer = function(
165       offset, elementSize, expectedElementCount, elementType, nullable) {
166     var arrayOffset = this.decodePointer(offset);
167     if (arrayOffset === null)
168       return validationError.ILLEGAL_POINTER;
169
170     if (arrayOffset === NULL_MOJO_POINTER)
171       return nullable ?
172           validationError.NONE : validationError.UNEXPECTED_NULL_POINTER;
173
174     return this.validateArray(
175         arrayOffset, elementSize, expectedElementCount, elementType);
176   }
177
178   Validator.prototype.validateStructPointer = function(
179         offset, structClass, nullable) {
180     var structOffset = this.decodePointer(offset);
181     if (structOffset === null)
182       return validationError.ILLEGAL_POINTER;
183
184     if (structOffset === NULL_MOJO_POINTER)
185       return nullable ?
186           validationError.NONE : validationError.UNEXPECTED_NULL_POINTER;
187
188     return structClass.validate(this, structOffset);
189   }
190
191   Validator.prototype.validateStringPointer = function(offset, nullable) {
192     return this.validateArrayPointer(
193         offset, codec.Uint8.encodedSize, 0, codec.Uint8, nullable);
194   }
195
196   // Similar to Array_Data<T>::Validate()
197   // mojo/public/cpp/bindings/lib/array_internal.h
198
199   Validator.prototype.validateArray =
200       function (offset, elementSize, expectedElementCount, elementType) {
201     if (!codec.isAligned(offset))
202       return validationError.MISALIGNED_OBJECT;
203
204     if (!this.isValidRange(offset, codec.kArrayHeaderSize))
205       return validationError.ILLEGAL_MEMORY_RANGE;
206
207     var numBytes = this.message.buffer.getUint32(offset);
208     var numElements = this.message.buffer.getUint32(offset + 4);
209
210     // Note: this computation is "safe" because elementSize <= 8 and
211     // numElements is a uint32.
212     var elementsTotalSize = (elementType === codec.PackedBool) ?
213         Math.ceil(numElements / 8) : (elementSize * numElements);
214
215     if (numBytes < codec.kArrayHeaderSize + elementsTotalSize)
216       return validationError.UNEXPECTED_ARRAY_HEADER;
217
218     if (expectedElementCount != 0 && numElements != expectedElementCount)
219       return validationError.UNEXPECTED_ARRAY_HEADER;
220
221     if (!this.claimRange(offset, numBytes))
222       return validationError.ILLEGAL_MEMORY_RANGE;
223
224     // Validate the array's elements if they are pointers or handles.
225
226     var elementsOffset = offset + codec.kArrayHeaderSize;
227     var nullable = isNullable(elementType);
228
229     if (isHandleClass(elementType))
230       return this.validateHandleElements(elementsOffset, numElements, nullable);
231     if (isStringClass(elementType))
232       return this.validateArrayElements(
233           elementsOffset, numElements, codec.Uint8, nullable)
234     if (elementType instanceof codec.PointerTo)
235       return this.validateStructElements(
236           elementsOffset, numElements, elementType.cls, nullable);
237     if (elementType instanceof codec.ArrayOf)
238       return this.validateArrayElements(
239           elementsOffset, numElements, elementType.cls, nullable);
240
241     return validationError.NONE;
242   }
243
244   // Note: the |offset + i * elementSize| computation in the validateFooElements
245   // methods below is "safe" because elementSize <= 8, offset and
246   // numElements are uint32, and 0 <= i < numElements.
247
248   Validator.prototype.validateHandleElements =
249       function(offset, numElements, nullable) {
250     var elementSize = codec.Handle.encodedSize;
251     for (var i = 0; i < numElements; i++) {
252       var elementOffset = offset + i * elementSize;
253       var err = this.validateHandle(elementOffset, nullable);
254       if (err != validationError.NONE)
255         return err;
256     }
257     return validationError.NONE;
258   }
259
260   // The elementClass parameter is the element type of the element arrays.
261   Validator.prototype.validateArrayElements =
262       function(offset, numElements, elementClass, nullable) {
263     var elementSize = codec.PointerTo.prototype.encodedSize;
264     for (var i = 0; i < numElements; i++) {
265       var elementOffset = offset + i * elementSize;
266       var err = this.validateArrayPointer(
267           elementOffset, elementClass.encodedSize, 0, elementClass, nullable);
268       if (err != validationError.NONE)
269         return err;
270     }
271     return validationError.NONE;
272   }
273
274   Validator.prototype.validateStructElements =
275       function(offset, numElements, structClass, nullable) {
276     var elementSize = codec.PointerTo.prototype.encodedSize;
277     for (var i = 0; i < numElements; i++) {
278       var elementOffset = offset + i * elementSize;
279       var err =
280           this.validateStructPointer(elementOffset, structClass, nullable);
281       if (err != validationError.NONE)
282         return err;
283     }
284     return validationError.NONE;
285   }
286
287   var exports = {};
288   exports.validationError = validationError;
289   exports.Validator = Validator;
290   return exports;
291 });