• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(function() {
6  var internal = mojo.internal;
7
8  var validationError = {
9    NONE: 'VALIDATION_ERROR_NONE',
10    MISALIGNED_OBJECT: 'VALIDATION_ERROR_MISALIGNED_OBJECT',
11    ILLEGAL_MEMORY_RANGE: 'VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE',
12    UNEXPECTED_STRUCT_HEADER: 'VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER',
13    UNEXPECTED_ARRAY_HEADER: 'VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER',
14    ILLEGAL_HANDLE: 'VALIDATION_ERROR_ILLEGAL_HANDLE',
15    UNEXPECTED_INVALID_HANDLE: 'VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE',
16    ILLEGAL_POINTER: 'VALIDATION_ERROR_ILLEGAL_POINTER',
17    UNEXPECTED_NULL_POINTER: 'VALIDATION_ERROR_UNEXPECTED_NULL_POINTER',
18    ILLEGAL_INTERFACE_ID: 'VALIDATION_ERROR_ILLEGAL_INTERFACE_ID',
19    UNEXPECTED_INVALID_INTERFACE_ID:
20        'VALIDATION_ERROR_UNEXPECTED_INVALID_INTERFACE_ID',
21    MESSAGE_HEADER_INVALID_FLAGS:
22        'VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS',
23    MESSAGE_HEADER_MISSING_REQUEST_ID:
24        'VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID',
25    DIFFERENT_SIZED_ARRAYS_IN_MAP:
26        'VALIDATION_ERROR_DIFFERENT_SIZED_ARRAYS_IN_MAP',
27    INVALID_UNION_SIZE: 'VALIDATION_ERROR_INVALID_UNION_SIZE',
28    UNEXPECTED_NULL_UNION: 'VALIDATION_ERROR_UNEXPECTED_NULL_UNION',
29    UNKNOWN_ENUM_VALUE: 'VALIDATION_ERROR_UNKNOWN_ENUM_VALUE',
30  };
31
32  var NULL_MOJO_POINTER = "NULL_MOJO_POINTER";
33  var gValidationErrorObserver = null;
34
35  function reportValidationError(error) {
36    if (gValidationErrorObserver) {
37      gValidationErrorObserver.lastError = error;
38    } else {
39      console.warn('Invalid message: ' + error);
40    }
41  }
42
43  var ValidationErrorObserverForTesting = (function() {
44    function Observer() {
45      this.lastError = validationError.NONE;
46    }
47
48    Observer.prototype.reset = function() {
49      this.lastError = validationError.NONE;
50    };
51
52    return {
53      getInstance: function() {
54        if (!gValidationErrorObserver) {
55          gValidationErrorObserver = new Observer();
56        }
57        return gValidationErrorObserver;
58      }
59    };
60  })();
61
62  function isTestingMode() {
63    return Boolean(gValidationErrorObserver);
64  }
65
66  function clearTestingMode() {
67    gValidationErrorObserver = null;
68  }
69
70  function isEnumClass(cls) {
71    return cls instanceof internal.Enum;
72  }
73
74  function isStringClass(cls) {
75    return cls === internal.String || cls === internal.NullableString;
76  }
77
78  function isHandleClass(cls) {
79    return cls === internal.Handle || cls === internal.NullableHandle;
80  }
81
82  function isInterfaceClass(cls) {
83    return cls instanceof internal.Interface;
84  }
85
86  function isInterfaceRequestClass(cls) {
87    return cls === internal.InterfaceRequest ||
88        cls === internal.NullableInterfaceRequest;
89  }
90
91  function isAssociatedInterfaceClass(cls) {
92    return cls === internal.AssociatedInterfacePtrInfo ||
93        cls === internal.NullableAssociatedInterfacePtrInfo;
94  }
95
96  function isAssociatedInterfaceRequestClass(cls) {
97    return cls === internal.AssociatedInterfaceRequest ||
98        cls === internal.NullableAssociatedInterfaceRequest;
99  }
100
101  function isNullable(type) {
102    return type === internal.NullableString ||
103        type === internal.NullableHandle ||
104        type === internal.NullableAssociatedInterfacePtrInfo ||
105        type === internal.NullableAssociatedInterfaceRequest ||
106        type === internal.NullableInterface ||
107        type === internal.NullableInterfaceRequest ||
108        type instanceof internal.NullableArrayOf ||
109        type instanceof internal.NullablePointerTo;
110  }
111
112  function Validator(message) {
113    this.message = message;
114    this.offset = 0;
115    this.handleIndex = 0;
116    this.associatedEndpointHandleIndex = 0;
117    this.payloadInterfaceIds = null;
118    this.offsetLimit = this.message.buffer.byteLength;
119  }
120
121  Object.defineProperty(Validator.prototype, "handleIndexLimit", {
122    get: function() { return this.message.handles.length; }
123  });
124
125  Object.defineProperty(Validator.prototype, "associatedHandleIndexLimit", {
126    get: function() {
127      return this.payloadInterfaceIds ? this.payloadInterfaceIds.length : 0;
128    }
129  });
130
131  // True if we can safely allocate a block of bytes from start to
132  // to start + numBytes.
133  Validator.prototype.isValidRange = function(start, numBytes) {
134    // Only positive JavaScript integers that are less than 2^53
135    // (Number.MAX_SAFE_INTEGER) can be represented exactly.
136    if (start < this.offset || numBytes <= 0 ||
137        !Number.isSafeInteger(start) ||
138        !Number.isSafeInteger(numBytes))
139      return false;
140
141    var newOffset = start + numBytes;
142    if (!Number.isSafeInteger(newOffset) || newOffset > this.offsetLimit)
143      return false;
144
145    return true;
146  };
147
148  Validator.prototype.claimRange = function(start, numBytes) {
149    if (this.isValidRange(start, numBytes)) {
150      this.offset = start + numBytes;
151      return true;
152    }
153    return false;
154  };
155
156  Validator.prototype.claimHandle = function(index) {
157    if (index === internal.kEncodedInvalidHandleValue)
158      return true;
159
160    if (index < this.handleIndex || index >= this.handleIndexLimit)
161      return false;
162
163    // This is safe because handle indices are uint32.
164    this.handleIndex = index + 1;
165    return true;
166  };
167
168  Validator.prototype.claimAssociatedEndpointHandle = function(index) {
169    if (index === internal.kEncodedInvalidHandleValue) {
170      return true;
171    }
172
173    if (index < this.associatedEndpointHandleIndex ||
174        index >= this.associatedHandleIndexLimit) {
175      return false;
176    }
177
178    // This is safe because handle indices are uint32.
179    this.associatedEndpointHandleIndex = index + 1;
180    return true;
181  };
182
183  Validator.prototype.validateEnum = function(offset, enumClass) {
184    // Note: Assumes that enums are always 32 bits! But this matches
185    // mojom::generate::pack::PackedField::GetSizeForKind, so it should be okay.
186    var value = this.message.buffer.getInt32(offset);
187    return enumClass.validate(value);
188  }
189
190  Validator.prototype.validateHandle = function(offset, nullable) {
191    var index = this.message.buffer.getUint32(offset);
192
193    if (index === internal.kEncodedInvalidHandleValue)
194      return nullable ?
195          validationError.NONE : validationError.UNEXPECTED_INVALID_HANDLE;
196
197    if (!this.claimHandle(index))
198      return validationError.ILLEGAL_HANDLE;
199
200    return validationError.NONE;
201  };
202
203  Validator.prototype.validateAssociatedEndpointHandle = function(offset,
204      nullable) {
205    var index = this.message.buffer.getUint32(offset);
206
207    if (index === internal.kEncodedInvalidHandleValue) {
208      return nullable ? validationError.NONE :
209          validationError.UNEXPECTED_INVALID_INTERFACE_ID;
210    }
211
212    if (!this.claimAssociatedEndpointHandle(index)) {
213      return validationError.ILLEGAL_INTERFACE_ID;
214    }
215
216    return validationError.NONE;
217  };
218
219  Validator.prototype.validateInterface = function(offset, nullable) {
220    return this.validateHandle(offset, nullable);
221  };
222
223  Validator.prototype.validateInterfaceRequest = function(offset, nullable) {
224    return this.validateHandle(offset, nullable);
225  };
226
227  Validator.prototype.validateAssociatedInterface = function(offset,
228      nullable) {
229    return this.validateAssociatedEndpointHandle(offset, nullable);
230  };
231
232  Validator.prototype.validateAssociatedInterfaceRequest = function(
233      offset, nullable) {
234    return this.validateAssociatedEndpointHandle(offset, nullable);
235  };
236
237  Validator.prototype.validateStructHeader = function(offset, minNumBytes) {
238    if (!internal.isAligned(offset))
239      return validationError.MISALIGNED_OBJECT;
240
241    if (!this.isValidRange(offset, internal.kStructHeaderSize))
242      return validationError.ILLEGAL_MEMORY_RANGE;
243
244    var numBytes = this.message.buffer.getUint32(offset);
245
246    if (numBytes < minNumBytes)
247      return validationError.UNEXPECTED_STRUCT_HEADER;
248
249    if (!this.claimRange(offset, numBytes))
250      return validationError.ILLEGAL_MEMORY_RANGE;
251
252    return validationError.NONE;
253  };
254
255  Validator.prototype.validateStructVersion = function(offset, versionSizes) {
256    var numBytes = this.message.buffer.getUint32(offset);
257    var version = this.message.buffer.getUint32(offset + 4);
258
259    if (version <= versionSizes[versionSizes.length - 1].version) {
260      // Scan in reverse order to optimize for more recent versionSizes.
261      for (var i = versionSizes.length - 1; i >= 0; --i) {
262        if (version >= versionSizes[i].version) {
263          if (numBytes == versionSizes[i].numBytes)
264            break;
265          return validationError.UNEXPECTED_STRUCT_HEADER;
266        }
267      }
268    } else if (numBytes < versionSizes[versionSizes.length-1].numBytes) {
269      return validationError.UNEXPECTED_STRUCT_HEADER;
270    }
271
272    return validationError.NONE;
273  };
274
275  Validator.prototype.isFieldInStructVersion = function(offset, fieldVersion) {
276    var structVersion = this.message.buffer.getUint32(offset + 4);
277    return fieldVersion <= structVersion;
278  };
279
280  Validator.prototype.validateMessageHeader = function() {
281    var err = this.validateStructHeader(0, internal.kMessageV0HeaderSize);
282    if (err != validationError.NONE) {
283      return err;
284    }
285
286    var numBytes = this.message.getHeaderNumBytes();
287    var version = this.message.getHeaderVersion();
288
289    var validVersionAndNumBytes =
290        (version == 0 && numBytes == internal.kMessageV0HeaderSize) ||
291        (version == 1 && numBytes == internal.kMessageV1HeaderSize) ||
292        (version == 2 && numBytes == internal.kMessageV2HeaderSize) ||
293        (version > 2 && numBytes >= internal.kMessageV2HeaderSize);
294
295    if (!validVersionAndNumBytes) {
296      return validationError.UNEXPECTED_STRUCT_HEADER;
297    }
298
299    var expectsResponse = this.message.expectsResponse();
300    var isResponse = this.message.isResponse();
301
302    if (version == 0 && (expectsResponse || isResponse)) {
303      return validationError.MESSAGE_HEADER_MISSING_REQUEST_ID;
304    }
305
306    if (isResponse && expectsResponse) {
307      return validationError.MESSAGE_HEADER_INVALID_FLAGS;
308    }
309
310    if (version < 2) {
311      return validationError.NONE;
312    }
313
314    var err = this.validateArrayPointer(
315        internal.kMessagePayloadInterfaceIdsPointerOffset,
316        internal.Uint32.encodedSize, internal.Uint32, true, [0], 0);
317
318    if (err != validationError.NONE) {
319      return err;
320    }
321
322    this.payloadInterfaceIds = this.message.getPayloadInterfaceIds();
323    if (this.payloadInterfaceIds) {
324      for (var interfaceId of this.payloadInterfaceIds) {
325        if (!internal.isValidInterfaceId(interfaceId) ||
326            internal.isMasterInterfaceId(interfaceId)) {
327          return validationError.ILLEGAL_INTERFACE_ID;
328        }
329      }
330    }
331
332    // Set offset to the start of the payload and offsetLimit to the start of
333    // the payload interface Ids so that payload can be validated using the
334    // same messageValidator.
335    this.offset = this.message.getHeaderNumBytes();
336    this.offsetLimit = this.decodePointer(
337        internal.kMessagePayloadInterfaceIdsPointerOffset);
338
339    return validationError.NONE;
340  };
341
342  Validator.prototype.validateMessageIsRequestWithoutResponse = function() {
343    if (this.message.isResponse() || this.message.expectsResponse()) {
344      return validationError.MESSAGE_HEADER_INVALID_FLAGS;
345    }
346    return validationError.NONE;
347  };
348
349  Validator.prototype.validateMessageIsRequestExpectingResponse = function() {
350    if (this.message.isResponse() || !this.message.expectsResponse()) {
351      return validationError.MESSAGE_HEADER_INVALID_FLAGS;
352    }
353    return validationError.NONE;
354  };
355
356  Validator.prototype.validateMessageIsResponse = function() {
357    if (this.message.expectsResponse() || !this.message.isResponse()) {
358      return validationError.MESSAGE_HEADER_INVALID_FLAGS;
359    }
360    return validationError.NONE;
361  };
362
363  // Returns the message.buffer relative offset this pointer "points to",
364  // NULL_MOJO_POINTER if the pointer represents a null, or JS null if the
365  // pointer's value is not valid.
366  Validator.prototype.decodePointer = function(offset) {
367    var pointerValue = this.message.buffer.getUint64(offset);
368    if (pointerValue === 0)
369      return NULL_MOJO_POINTER;
370    var bufferOffset = offset + pointerValue;
371    return Number.isSafeInteger(bufferOffset) ? bufferOffset : null;
372  };
373
374  Validator.prototype.decodeUnionSize = function(offset) {
375    return this.message.buffer.getUint32(offset);
376  };
377
378  Validator.prototype.decodeUnionTag = function(offset) {
379    return this.message.buffer.getUint32(offset + 4);
380  };
381
382  Validator.prototype.validateArrayPointer = function(
383      offset, elementSize, elementType, nullable, expectedDimensionSizes,
384      currentDimension) {
385    var arrayOffset = this.decodePointer(offset);
386    if (arrayOffset === null)
387      return validationError.ILLEGAL_POINTER;
388
389    if (arrayOffset === NULL_MOJO_POINTER)
390      return nullable ?
391          validationError.NONE : validationError.UNEXPECTED_NULL_POINTER;
392
393    return this.validateArray(arrayOffset, elementSize, elementType,
394                              expectedDimensionSizes, currentDimension);
395  };
396
397  Validator.prototype.validateStructPointer = function(
398      offset, structClass, nullable) {
399    var structOffset = this.decodePointer(offset);
400    if (structOffset === null)
401      return validationError.ILLEGAL_POINTER;
402
403    if (structOffset === NULL_MOJO_POINTER)
404      return nullable ?
405          validationError.NONE : validationError.UNEXPECTED_NULL_POINTER;
406
407    return structClass.validate(this, structOffset);
408  };
409
410  Validator.prototype.validateUnion = function(
411      offset, unionClass, nullable) {
412    var size = this.message.buffer.getUint32(offset);
413    if (size == 0) {
414      return nullable ?
415          validationError.NONE : validationError.UNEXPECTED_NULL_UNION;
416    }
417
418    return unionClass.validate(this, offset);
419  };
420
421  Validator.prototype.validateNestedUnion = function(
422      offset, unionClass, nullable) {
423    var unionOffset = this.decodePointer(offset);
424    if (unionOffset === null)
425      return validationError.ILLEGAL_POINTER;
426
427    if (unionOffset === NULL_MOJO_POINTER)
428      return nullable ?
429          validationError.NONE : validationError.UNEXPECTED_NULL_UNION;
430
431    return this.validateUnion(unionOffset, unionClass, nullable);
432  };
433
434  // This method assumes that the array at arrayPointerOffset has
435  // been validated.
436
437  Validator.prototype.arrayLength = function(arrayPointerOffset) {
438    var arrayOffset = this.decodePointer(arrayPointerOffset);
439    return this.message.buffer.getUint32(arrayOffset + 4);
440  };
441
442  Validator.prototype.validateMapPointer = function(
443      offset, mapIsNullable, keyClass, valueClass, valueIsNullable) {
444    // Validate the implicit map struct:
445    // struct {array<keyClass> keys; array<valueClass> values};
446    var structOffset = this.decodePointer(offset);
447    if (structOffset === null)
448      return validationError.ILLEGAL_POINTER;
449
450    if (structOffset === NULL_MOJO_POINTER)
451      return mapIsNullable ?
452          validationError.NONE : validationError.UNEXPECTED_NULL_POINTER;
453
454    var mapEncodedSize = internal.kStructHeaderSize +
455                         internal.kMapStructPayloadSize;
456    var err = this.validateStructHeader(structOffset, mapEncodedSize);
457    if (err !== validationError.NONE)
458        return err;
459
460    // Validate the keys array.
461    var keysArrayPointerOffset = structOffset + internal.kStructHeaderSize;
462    err = this.validateArrayPointer(
463        keysArrayPointerOffset, keyClass.encodedSize, keyClass, false, [0], 0);
464    if (err !== validationError.NONE)
465        return err;
466
467    // Validate the values array.
468    var valuesArrayPointerOffset = keysArrayPointerOffset + 8;
469    var valuesArrayDimensions = [0]; // Validate the actual length below.
470    if (valueClass instanceof internal.ArrayOf)
471      valuesArrayDimensions =
472          valuesArrayDimensions.concat(valueClass.dimensions());
473    var err = this.validateArrayPointer(valuesArrayPointerOffset,
474                                        valueClass.encodedSize,
475                                        valueClass,
476                                        valueIsNullable,
477                                        valuesArrayDimensions,
478                                        0);
479    if (err !== validationError.NONE)
480        return err;
481
482    // Validate the lengths of the keys and values arrays.
483    var keysArrayLength = this.arrayLength(keysArrayPointerOffset);
484    var valuesArrayLength = this.arrayLength(valuesArrayPointerOffset);
485    if (keysArrayLength != valuesArrayLength)
486      return validationError.DIFFERENT_SIZED_ARRAYS_IN_MAP;
487
488    return validationError.NONE;
489  };
490
491  Validator.prototype.validateStringPointer = function(offset, nullable) {
492    return this.validateArrayPointer(
493        offset, internal.Uint8.encodedSize, internal.Uint8, nullable, [0], 0);
494  };
495
496  // Similar to Array_Data<T>::Validate()
497  // mojo/public/cpp/bindings/lib/array_internal.h
498
499  Validator.prototype.validateArray =
500      function (offset, elementSize, elementType, expectedDimensionSizes,
501                currentDimension) {
502    if (!internal.isAligned(offset))
503      return validationError.MISALIGNED_OBJECT;
504
505    if (!this.isValidRange(offset, internal.kArrayHeaderSize))
506      return validationError.ILLEGAL_MEMORY_RANGE;
507
508    var numBytes = this.message.buffer.getUint32(offset);
509    var numElements = this.message.buffer.getUint32(offset + 4);
510
511    // Note: this computation is "safe" because elementSize <= 8 and
512    // numElements is a uint32.
513    var elementsTotalSize = (elementType === internal.PackedBool) ?
514        Math.ceil(numElements / 8) : (elementSize * numElements);
515
516    if (numBytes < internal.kArrayHeaderSize + elementsTotalSize)
517      return validationError.UNEXPECTED_ARRAY_HEADER;
518
519    if (expectedDimensionSizes[currentDimension] != 0 &&
520        numElements != expectedDimensionSizes[currentDimension]) {
521      return validationError.UNEXPECTED_ARRAY_HEADER;
522    }
523
524    if (!this.claimRange(offset, numBytes))
525      return validationError.ILLEGAL_MEMORY_RANGE;
526
527    // Validate the array's elements if they are pointers or handles.
528
529    var elementsOffset = offset + internal.kArrayHeaderSize;
530    var nullable = isNullable(elementType);
531
532    if (isHandleClass(elementType))
533      return this.validateHandleElements(elementsOffset, numElements, nullable);
534    if (isInterfaceClass(elementType))
535      return this.validateInterfaceElements(
536          elementsOffset, numElements, nullable);
537    if (isInterfaceRequestClass(elementType))
538      return this.validateInterfaceRequestElements(
539          elementsOffset, numElements, nullable);
540    if (isAssociatedInterfaceClass(elementType))
541      return this.validateAssociatedInterfaceElements(
542          elementsOffset, numElements, nullable);
543    if (isAssociatedInterfaceRequestClass(elementType))
544      return this.validateAssociatedInterfaceRequestElements(
545          elementsOffset, numElements, nullable);
546    if (isStringClass(elementType))
547      return this.validateArrayElements(
548          elementsOffset, numElements, internal.Uint8, nullable, [0], 0);
549    if (elementType instanceof internal.PointerTo)
550      return this.validateStructElements(
551          elementsOffset, numElements, elementType.cls, nullable);
552    if (elementType instanceof internal.ArrayOf)
553      return this.validateArrayElements(
554          elementsOffset, numElements, elementType.cls, nullable,
555          expectedDimensionSizes, currentDimension + 1);
556    if (isEnumClass(elementType))
557      return this.validateEnumElements(elementsOffset, numElements,
558                                       elementType.cls);
559
560    return validationError.NONE;
561  };
562
563  // Note: the |offset + i * elementSize| computation in the validateFooElements
564  // methods below is "safe" because elementSize <= 8, offset and
565  // numElements are uint32, and 0 <= i < numElements.
566
567  Validator.prototype.validateHandleElements =
568      function(offset, numElements, nullable) {
569    var elementSize = internal.Handle.encodedSize;
570    for (var i = 0; i < numElements; i++) {
571      var elementOffset = offset + i * elementSize;
572      var err = this.validateHandle(elementOffset, nullable);
573      if (err != validationError.NONE)
574        return err;
575    }
576    return validationError.NONE;
577  };
578
579  Validator.prototype.validateInterfaceElements =
580      function(offset, numElements, nullable) {
581    var elementSize = internal.Interface.prototype.encodedSize;
582    for (var i = 0; i < numElements; i++) {
583      var elementOffset = offset + i * elementSize;
584      var err = this.validateInterface(elementOffset, nullable);
585      if (err != validationError.NONE)
586        return err;
587    }
588    return validationError.NONE;
589  };
590
591  Validator.prototype.validateInterfaceRequestElements =
592      function(offset, numElements, nullable) {
593    var elementSize = internal.InterfaceRequest.encodedSize;
594    for (var i = 0; i < numElements; i++) {
595      var elementOffset = offset + i * elementSize;
596      var err = this.validateInterfaceRequest(elementOffset, nullable);
597      if (err != validationError.NONE)
598        return err;
599    }
600    return validationError.NONE;
601  };
602
603  Validator.prototype.validateAssociatedInterfaceElements =
604      function(offset, numElements, nullable) {
605    var elementSize = internal.AssociatedInterfacePtrInfo.prototype.encodedSize;
606    for (var i = 0; i < numElements; i++) {
607      var elementOffset = offset + i * elementSize;
608      var err = this.validateAssociatedInterface(elementOffset, nullable);
609      if (err != validationError.NONE) {
610        return err;
611      }
612    }
613    return validationError.NONE;
614  };
615
616  Validator.prototype.validateAssociatedInterfaceRequestElements =
617      function(offset, numElements, nullable) {
618    var elementSize = internal.AssociatedInterfaceRequest.encodedSize;
619    for (var i = 0; i < numElements; i++) {
620      var elementOffset = offset + i * elementSize;
621      var err = this.validateAssociatedInterfaceRequest(elementOffset,
622          nullable);
623      if (err != validationError.NONE) {
624        return err;
625      }
626    }
627    return validationError.NONE;
628  };
629
630  // The elementClass parameter is the element type of the element arrays.
631  Validator.prototype.validateArrayElements =
632      function(offset, numElements, elementClass, nullable,
633               expectedDimensionSizes, currentDimension) {
634    var elementSize = internal.PointerTo.prototype.encodedSize;
635    for (var i = 0; i < numElements; i++) {
636      var elementOffset = offset + i * elementSize;
637      var err = this.validateArrayPointer(
638          elementOffset, elementClass.encodedSize, elementClass, nullable,
639          expectedDimensionSizes, currentDimension);
640      if (err != validationError.NONE)
641        return err;
642    }
643    return validationError.NONE;
644  };
645
646  Validator.prototype.validateStructElements =
647      function(offset, numElements, structClass, nullable) {
648    var elementSize = internal.PointerTo.prototype.encodedSize;
649    for (var i = 0; i < numElements; i++) {
650      var elementOffset = offset + i * elementSize;
651      var err =
652          this.validateStructPointer(elementOffset, structClass, nullable);
653      if (err != validationError.NONE)
654        return err;
655    }
656    return validationError.NONE;
657  };
658
659  Validator.prototype.validateEnumElements =
660      function(offset, numElements, enumClass) {
661    var elementSize = internal.Enum.prototype.encodedSize;
662    for (var i = 0; i < numElements; i++) {
663      var elementOffset = offset + i * elementSize;
664      var err = this.validateEnum(elementOffset, enumClass);
665      if (err != validationError.NONE)
666        return err;
667    }
668    return validationError.NONE;
669  };
670
671  internal.validationError = validationError;
672  internal.Validator = Validator;
673  internal.ValidationErrorObserverForTesting =
674      ValidationErrorObserverForTesting;
675  internal.reportValidationError = reportValidationError;
676  internal.isTestingMode = isTestingMode;
677  internal.clearTestingMode = clearTestingMode;
678})();
679