forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			869 lines
		
	
	
	
		
			21 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			869 lines
		
	
	
	
		
			21 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| // Ideally this would be an xpcshell test, but Troubleshoot relies on things
 | |
| // that aren't initialized outside of a XUL app environment like AddonManager
 | |
| // and the "@mozilla.org/xre/app-info;1" component.
 | |
| 
 | |
| ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
 | |
| ChromeUtils.import("resource://gre/modules/Services.jsm");
 | |
| ChromeUtils.import("resource://gre/modules/Troubleshoot.jsm");
 | |
| 
 | |
| function test() {
 | |
|   waitForExplicitFinish();
 | |
|   function doNextTest() {
 | |
|     if (!tests.length) {
 | |
|       finish();
 | |
|       return;
 | |
|     }
 | |
|     tests.shift()(doNextTest);
 | |
|   }
 | |
|   doNextTest();
 | |
| }
 | |
| 
 | |
| registerCleanupFunction(function() {
 | |
|   // Troubleshoot.jsm is imported into the global scope -- the window -- above.
 | |
|   // If it's not deleted, it outlives the test and is reported as a leak.
 | |
|   delete window.Troubleshoot;
 | |
| });
 | |
| 
 | |
| var tests = [
 | |
| 
 | |
|   function snapshotSchema(done) {
 | |
|     Troubleshoot.snapshot(function(snapshot) {
 | |
|       try {
 | |
|         validateObject(snapshot, SNAPSHOT_SCHEMA);
 | |
|         ok(true, "The snapshot should conform to the schema.");
 | |
|       } catch (err) {
 | |
|         ok(false, "Schema mismatch, " + err);
 | |
|       }
 | |
|       done();
 | |
|     });
 | |
|   },
 | |
| 
 | |
|   function modifiedPreferences(done) {
 | |
|     let prefs = [
 | |
|       "javascript.troubleshoot",
 | |
|       "troubleshoot.foo",
 | |
|       "javascript.print_to_filename",
 | |
|       "network.proxy.troubleshoot",
 | |
|     ];
 | |
|     prefs.forEach(function(p) {
 | |
|       Services.prefs.setBoolPref(p, true);
 | |
|       is(Services.prefs.getBoolPref(p), true, "The pref should be set: " + p);
 | |
|     });
 | |
|     Troubleshoot.snapshot(function(snapshot) {
 | |
|       let p = snapshot.modifiedPreferences;
 | |
|       is(p["javascript.troubleshoot"], true,
 | |
|          "The pref should be present because it's whitelisted " +
 | |
|          "but not blacklisted.");
 | |
|       ok(!("troubleshoot.foo" in p),
 | |
|          "The pref should be absent because it's not in the whitelist.");
 | |
|       ok(!("javascript.print_to_filename" in p),
 | |
|          "The pref should be absent because it's blacklisted.");
 | |
|       ok(!("network.proxy.troubleshoot" in p),
 | |
|          "The pref should be absent because it's blacklisted.");
 | |
|       prefs.forEach(p => Services.prefs.deleteBranch(p));
 | |
|       done();
 | |
|     });
 | |
|   },
 | |
| 
 | |
|   function unicodePreferences(done) {
 | |
|     let name = "font.name.sans-serif.x-western";
 | |
|     let utf8Value = "\xc4\x8capk\xc5\xafv Krasopis";
 | |
|     let unicodeValue = "\u010Capk\u016Fv Krasopis";
 | |
| 
 | |
|     // set/getCharPref work with 8bit strings (utf8)
 | |
|     Services.prefs.setCharPref(name, utf8Value);
 | |
| 
 | |
|     Troubleshoot.snapshot(function(snapshot) {
 | |
|       let p = snapshot.modifiedPreferences;
 | |
|       is(p[name], unicodeValue, "The pref should have correct Unicode value.");
 | |
|       Services.prefs.deleteBranch(name);
 | |
|       done();
 | |
|     });
 | |
|   }
 | |
| ];
 | |
| 
 | |
| // This is inspired by JSON Schema, or by the example on its Wikipedia page
 | |
| // anyway.
 | |
| const SNAPSHOT_SCHEMA = {
 | |
|   type: "object",
 | |
|   required: true,
 | |
|   properties: {
 | |
|     application: {
 | |
|       required: true,
 | |
|       type: "object",
 | |
|       properties: {
 | |
|         name: {
 | |
|           required: true,
 | |
|           type: "string",
 | |
|         },
 | |
|         version: {
 | |
|           required: true,
 | |
|           type: "string",
 | |
|         },
 | |
|         buildID: {
 | |
|           required: true,
 | |
|           type: "string",
 | |
|         },
 | |
|         userAgent: {
 | |
|           required: true,
 | |
|           type: "string",
 | |
|         },
 | |
|         osVersion: {
 | |
|           required: true,
 | |
|           type: "string",
 | |
|         },
 | |
|         vendor: {
 | |
|           type: "string",
 | |
|         },
 | |
|         updateChannel: {
 | |
|           type: "string",
 | |
|         },
 | |
|         supportURL: {
 | |
|           type: "string",
 | |
|         },
 | |
|         remoteAutoStart: {
 | |
|           type: "boolean",
 | |
|           required: true,
 | |
|         },
 | |
|         autoStartStatus: {
 | |
|           type: "number",
 | |
|         },
 | |
|         numTotalWindows: {
 | |
|           type: "number",
 | |
|         },
 | |
|         numRemoteWindows: {
 | |
|           type: "number",
 | |
|         },
 | |
|         currentContentProcesses: {
 | |
|           type: "number",
 | |
|         },
 | |
|         maxContentProcesses: {
 | |
|           type: "number",
 | |
|         },
 | |
|         policiesStatus: {
 | |
|           type: "number",
 | |
|         },
 | |
|         keyGoogleFound: {
 | |
|           type: "boolean",
 | |
|         },
 | |
|         keyMozillaFound: {
 | |
|           type: "boolean",
 | |
|         },
 | |
|         safeMode: {
 | |
|           type: "boolean",
 | |
|         },
 | |
|       },
 | |
|     },
 | |
|     crashes: {
 | |
|       required: false,
 | |
|       type: "object",
 | |
|       properties: {
 | |
|         pending: {
 | |
|           required: true,
 | |
|           type: "number",
 | |
|         },
 | |
|         submitted: {
 | |
|           required: true,
 | |
|           type: "array",
 | |
|           items: {
 | |
|             type: "object",
 | |
|             properties: {
 | |
|               id: {
 | |
|                 required: true,
 | |
|                 type: "string",
 | |
|               },
 | |
|               date: {
 | |
|                 required: true,
 | |
|                 type: "number",
 | |
|               },
 | |
|               pending: {
 | |
|                 required: true,
 | |
|                 type: "boolean",
 | |
|               },
 | |
|             },
 | |
|           },
 | |
|         },
 | |
|       },
 | |
|     },
 | |
|     extensions: {
 | |
|       required: true,
 | |
|       type: "array",
 | |
|       items: {
 | |
|         type: "object",
 | |
|         properties: {
 | |
|           name: {
 | |
|             required: true,
 | |
|             type: "string",
 | |
|           },
 | |
|           version: {
 | |
|             required: true,
 | |
|             type: "string",
 | |
|           },
 | |
|           id: {
 | |
|             required: true,
 | |
|             type: "string",
 | |
|           },
 | |
|           isActive: {
 | |
|             required: true,
 | |
|             type: "boolean",
 | |
|           },
 | |
|         },
 | |
|       },
 | |
|     },
 | |
|     securitySoftware: {
 | |
|       required: false,
 | |
|       type: "object",
 | |
|       properties: {
 | |
|         registeredAntiVirus: {
 | |
|           required: true,
 | |
|           type: "string",
 | |
|         },
 | |
|         registeredAntiSpyware: {
 | |
|           required: true,
 | |
|           type: "string",
 | |
|         },
 | |
|         registeredFirewall: {
 | |
|           required: true,
 | |
|           type: "string",
 | |
|         },
 | |
|       },
 | |
|     },
 | |
|     features: {
 | |
|       required: true,
 | |
|       type: "array",
 | |
|       items: {
 | |
|         type: "object",
 | |
|         properties: {
 | |
|           name: {
 | |
|             required: true,
 | |
|             type: "string",
 | |
|           },
 | |
|           version: {
 | |
|             required: true,
 | |
|             type: "string",
 | |
|           },
 | |
|           id: {
 | |
|             required: true,
 | |
|             type: "string",
 | |
|           },
 | |
|         },
 | |
|       },
 | |
|     },
 | |
|     modifiedPreferences: {
 | |
|       required: true,
 | |
|       type: "object",
 | |
|     },
 | |
|     lockedPreferences: {
 | |
|       required: true,
 | |
|       type: "object",
 | |
|     },
 | |
|     graphics: {
 | |
|       required: true,
 | |
|       type: "object",
 | |
|       properties: {
 | |
|         numTotalWindows: {
 | |
|           required: true,
 | |
|           type: "number",
 | |
|         },
 | |
|         numAcceleratedWindows: {
 | |
|           required: true,
 | |
|           type: "number",
 | |
|         },
 | |
|         windowLayerManagerType: {
 | |
|           type: "string",
 | |
|         },
 | |
|         windowLayerManagerRemote: {
 | |
|           type: "boolean",
 | |
|         },
 | |
|         windowUsingAdvancedLayers: {
 | |
|           type: "boolean",
 | |
|         },
 | |
|         numAcceleratedWindowsMessage: {
 | |
|           type: "array",
 | |
|         },
 | |
|         adapterDescription: {
 | |
|           type: "string",
 | |
|         },
 | |
|         adapterVendorID: {
 | |
|           type: "string",
 | |
|         },
 | |
|         adapterDeviceID: {
 | |
|           type: "string",
 | |
|         },
 | |
|         adapterSubsysID: {
 | |
|           type: "string",
 | |
|         },
 | |
|         adapterRAM: {
 | |
|           type: "string",
 | |
|         },
 | |
|         adapterDrivers: {
 | |
|           type: "string",
 | |
|         },
 | |
|         driverVersion: {
 | |
|           type: "string",
 | |
|         },
 | |
|         driverDate: {
 | |
|           type: "string",
 | |
|         },
 | |
|         adapterDescription2: {
 | |
|           type: "string",
 | |
|         },
 | |
|         adapterVendorID2: {
 | |
|           type: "string",
 | |
|         },
 | |
|         adapterDeviceID2: {
 | |
|           type: "string",
 | |
|         },
 | |
|         adapterSubsysID2: {
 | |
|           type: "string",
 | |
|         },
 | |
|         adapterRAM2: {
 | |
|           type: "string",
 | |
|         },
 | |
|         adapterDrivers2: {
 | |
|           type: "string",
 | |
|         },
 | |
|         driverVersion2: {
 | |
|           type: "string",
 | |
|         },
 | |
|         driverDate2: {
 | |
|           type: "string",
 | |
|         },
 | |
|         isGPU2Active: {
 | |
|           type: "boolean",
 | |
|         },
 | |
|         direct2DEnabled: {
 | |
|           type: "boolean",
 | |
|         },
 | |
|         directWriteEnabled: {
 | |
|           type: "boolean",
 | |
|         },
 | |
|         directWriteVersion: {
 | |
|           type: "string",
 | |
|         },
 | |
|         usesTiling: {
 | |
|           type: "boolean",
 | |
|         },
 | |
|         contentUsesTiling: {
 | |
|           type: "boolean",
 | |
|         },
 | |
|         offMainThreadPaintEnabled: {
 | |
|           type: "boolean",
 | |
|         },
 | |
|         offMainThreadPaintWorkerCount: {
 | |
|           type: "number",
 | |
|         },
 | |
|         clearTypeParameters: {
 | |
|           type: "string",
 | |
|         },
 | |
|         webgl1Renderer: {
 | |
|           type: "string",
 | |
|         },
 | |
|         webgl1Version: {
 | |
|           type: "string",
 | |
|         },
 | |
|         webgl1DriverExtensions: {
 | |
|           type: "string",
 | |
|         },
 | |
|         webgl1Extensions: {
 | |
|           type: "string",
 | |
|         },
 | |
|         webgl1WSIInfo: {
 | |
|           type: "string",
 | |
|         },
 | |
|         webgl2Renderer: {
 | |
|           type: "string",
 | |
|         },
 | |
|         webgl2Version: {
 | |
|           type: "string",
 | |
|         },
 | |
|         webgl2DriverExtensions: {
 | |
|           type: "string",
 | |
|         },
 | |
|         webgl2Extensions: {
 | |
|           type: "string",
 | |
|         },
 | |
|         webgl2WSIInfo: {
 | |
|           type: "string",
 | |
|         },
 | |
|         info: {
 | |
|           type: "object",
 | |
|         },
 | |
|         failures: {
 | |
|           type: "array",
 | |
|           items: {
 | |
|             type: "string",
 | |
|           },
 | |
|         },
 | |
|         indices: {
 | |
|           type: "array",
 | |
|           items: {
 | |
|             type: "number",
 | |
|           },
 | |
|         },
 | |
|         featureLog: {
 | |
|           type: "object",
 | |
|         },
 | |
|         crashGuards: {
 | |
|           type: "array",
 | |
|         },
 | |
|         direct2DEnabledMessage: {
 | |
|           type: "array",
 | |
|         },
 | |
|       },
 | |
|     },
 | |
|     media: {
 | |
|       required: true,
 | |
|       type: "object",
 | |
|       properties: {
 | |
|         currentAudioBackend: {
 | |
|           required: true,
 | |
|           type: "string",
 | |
|         },
 | |
|         currentMaxAudioChannels: {
 | |
|           required: true,
 | |
|           type: "number",
 | |
|         },
 | |
|         currentPreferredSampleRate: {
 | |
|           required: true,
 | |
|           type: "number",
 | |
|         },
 | |
|         audioOutputDevices: {
 | |
|           required: true,
 | |
|           type: "array",
 | |
|           items: {
 | |
|             type: "object",
 | |
|             properties: {
 | |
|               name: {
 | |
|                 required: true,
 | |
|                 type: "string",
 | |
|               },
 | |
|               groupId: {
 | |
|                 required: true,
 | |
|                 type: "string",
 | |
|               },
 | |
|               vendor: {
 | |
|                 required: true,
 | |
|                 type: "string",
 | |
|               },
 | |
|               type: {
 | |
|                 required: true,
 | |
|                 type: "number",
 | |
|               },
 | |
|               state: {
 | |
|                 required: true,
 | |
|                 type: "number",
 | |
|               },
 | |
|               preferred: {
 | |
|                 required: true,
 | |
|                 type: "number",
 | |
|               },
 | |
|               supportedFormat: {
 | |
|                 required: true,
 | |
|                 type: "number",
 | |
|               },
 | |
|               defaultFormat: {
 | |
|                 required: true,
 | |
|                 type: "number",
 | |
|               },
 | |
|               maxChannels: {
 | |
|                 required: true,
 | |
|                 type: "number",
 | |
|               },
 | |
|               defaultRate: {
 | |
|                 required: true,
 | |
|                 type: "number",
 | |
|               },
 | |
|               maxRate: {
 | |
|                 required: true,
 | |
|                 type: "number",
 | |
|               },
 | |
|               minRate: {
 | |
|                 required: true,
 | |
|                 type: "number",
 | |
|               },
 | |
|               maxLatency: {
 | |
|                 required: true,
 | |
|                 type: "number",
 | |
|               },
 | |
|               minLatency: {
 | |
|                 required: true,
 | |
|                 type: "number",
 | |
|               }
 | |
|             },
 | |
|           },
 | |
|         },
 | |
|         audioInputDevices: {
 | |
|           required: true,
 | |
|           type: "array",
 | |
|           items: {
 | |
|             type: "object",
 | |
|             properties: {
 | |
|               name: {
 | |
|                 required: true,
 | |
|                 type: "string",
 | |
|               },
 | |
|               groupId: {
 | |
|                 required: true,
 | |
|                 type: "string",
 | |
|               },
 | |
|               vendor: {
 | |
|                 required: true,
 | |
|                 type: "string",
 | |
|               },
 | |
|               type: {
 | |
|                 required: true,
 | |
|                 type: "number",
 | |
|               },
 | |
|               state: {
 | |
|                 required: true,
 | |
|                 type: "number",
 | |
|               },
 | |
|               preferred: {
 | |
|                 required: true,
 | |
|                 type: "number",
 | |
|               },
 | |
|               supportedFormat: {
 | |
|                 required: true,
 | |
|                 type: "number",
 | |
|               },
 | |
|               defaultFormat: {
 | |
|                 required: true,
 | |
|                 type: "number",
 | |
|               },
 | |
|               maxChannels: {
 | |
|                 required: true,
 | |
|                 type: "number",
 | |
|               },
 | |
|               defaultRate: {
 | |
|                 required: true,
 | |
|                 type: "number",
 | |
|               },
 | |
|               maxRate: {
 | |
|                 required: true,
 | |
|                 type: "number",
 | |
|               },
 | |
|               minRate: {
 | |
|                 required: true,
 | |
|                 type: "number",
 | |
|               },
 | |
|               maxLatency: {
 | |
|                 required: true,
 | |
|                 type: "number",
 | |
|               },
 | |
|               minLatency: {
 | |
|                 required: true,
 | |
|                 type: "number",
 | |
|               }
 | |
|             },
 | |
|           },
 | |
|         },
 | |
|       },
 | |
|     },
 | |
|     javaScript: {
 | |
|       required: true,
 | |
|       type: "object",
 | |
|       properties: {
 | |
|         incrementalGCEnabled: {
 | |
|           type: "boolean",
 | |
|         },
 | |
|       },
 | |
|     },
 | |
|     accessibility: {
 | |
|       required: true,
 | |
|       type: "object",
 | |
|       properties: {
 | |
|         isActive: {
 | |
|           required: true,
 | |
|           type: "boolean",
 | |
|         },
 | |
|         forceDisabled: {
 | |
|           type: "number",
 | |
|         },
 | |
|         handlerUsed: {
 | |
|           type: "boolean",
 | |
|         },
 | |
|         instantiator: {
 | |
|           type: "string"
 | |
|         }
 | |
|       },
 | |
|     },
 | |
|     libraryVersions: {
 | |
|       required: true,
 | |
|       type: "object",
 | |
|       properties: {
 | |
|         NSPR: {
 | |
|           required: true,
 | |
|           type: "object",
 | |
|           properties: {
 | |
|             minVersion: {
 | |
|               required: true,
 | |
|               type: "string",
 | |
|             },
 | |
|             version: {
 | |
|               required: true,
 | |
|               type: "string",
 | |
|             },
 | |
|           },
 | |
|         },
 | |
|         NSS: {
 | |
|           required: true,
 | |
|           type: "object",
 | |
|           properties: {
 | |
|             minVersion: {
 | |
|               required: true,
 | |
|               type: "string",
 | |
|             },
 | |
|             version: {
 | |
|               required: true,
 | |
|               type: "string",
 | |
|             },
 | |
|           },
 | |
|         },
 | |
|         NSSUTIL: {
 | |
|           required: true,
 | |
|           type: "object",
 | |
|           properties: {
 | |
|             minVersion: {
 | |
|               required: true,
 | |
|               type: "string",
 | |
|             },
 | |
|             version: {
 | |
|               required: true,
 | |
|               type: "string",
 | |
|             },
 | |
|           },
 | |
|         },
 | |
|         NSSSSL: {
 | |
|           required: true,
 | |
|           type: "object",
 | |
|           properties: {
 | |
|             minVersion: {
 | |
|               required: true,
 | |
|               type: "string",
 | |
|             },
 | |
|             version: {
 | |
|               required: true,
 | |
|               type: "string",
 | |
|             },
 | |
|           },
 | |
|         },
 | |
|         NSSSMIME: {
 | |
|           required: true,
 | |
|           type: "object",
 | |
|           properties: {
 | |
|             minVersion: {
 | |
|               required: true,
 | |
|               type: "string",
 | |
|             },
 | |
|             version: {
 | |
|               required: true,
 | |
|               type: "string",
 | |
|             },
 | |
|           },
 | |
|         },
 | |
|       },
 | |
|     },
 | |
|     userJS: {
 | |
|       required: true,
 | |
|       type: "object",
 | |
|       properties: {
 | |
|         exists: {
 | |
|           required: true,
 | |
|           type: "boolean",
 | |
|         },
 | |
|       },
 | |
|     },
 | |
|     sandbox: {
 | |
|       required: false,
 | |
|       type: "object",
 | |
|       properties: {
 | |
|         hasSeccompBPF: {
 | |
|           required: AppConstants.platform == "linux",
 | |
|           type: "boolean"
 | |
|         },
 | |
|         hasSeccompTSync: {
 | |
|           required: AppConstants.platform == "linux",
 | |
|           type: "boolean"
 | |
|         },
 | |
|         hasUserNamespaces: {
 | |
|           required: AppConstants.platform == "linux",
 | |
|           type: "boolean"
 | |
|         },
 | |
|         hasPrivilegedUserNamespaces: {
 | |
|           required: AppConstants.platform == "linux",
 | |
|           type: "boolean"
 | |
|         },
 | |
|         canSandboxContent: {
 | |
|           required: false,
 | |
|           type: "boolean"
 | |
|         },
 | |
|         canSandboxMedia: {
 | |
|           required: false,
 | |
|           type: "boolean"
 | |
|         },
 | |
|         contentSandboxLevel: {
 | |
|           required: AppConstants.MOZ_CONTENT_SANDBOX,
 | |
|           type: "number"
 | |
|         },
 | |
|         effectiveContentSandboxLevel: {
 | |
|           required: AppConstants.MOZ_CONTENT_SANDBOX,
 | |
|           type: "number"
 | |
|         },
 | |
|         syscallLog: {
 | |
|           required: AppConstants.platform == "linux",
 | |
|           type: "array",
 | |
|           items: {
 | |
|             type: "object",
 | |
|             properties: {
 | |
|               index: {
 | |
|                 required: true,
 | |
|                 type: "number",
 | |
|               },
 | |
|               pid: {
 | |
|                 required: true,
 | |
|                 type: "number",
 | |
|               },
 | |
|               tid: {
 | |
|                 required: true,
 | |
|                 type: "number",
 | |
|               },
 | |
|               procType: {
 | |
|                 required: true,
 | |
|                 type: "string",
 | |
|               },
 | |
|               syscall: {
 | |
|                 required: true,
 | |
|                 type: "number",
 | |
|               },
 | |
|               args: {
 | |
|                 required: true,
 | |
|                 type: "array",
 | |
|                 items: {
 | |
|                   type: "string",
 | |
|                 },
 | |
|               },
 | |
|             },
 | |
|           },
 | |
|         },
 | |
|       },
 | |
|     },
 | |
|     intl: {
 | |
|       required: true,
 | |
|       type: "object",
 | |
|       properties: {
 | |
|         localeService: {
 | |
|           required: true,
 | |
|           type: "object",
 | |
|           properties: {
 | |
|             requested: {
 | |
|               required: true,
 | |
|               type: "array"
 | |
|             },
 | |
|             available: {
 | |
|               required: true,
 | |
|               type: "array"
 | |
|             },
 | |
|             supported: {
 | |
|               required: true,
 | |
|               type: "array"
 | |
|             },
 | |
|             regionalPrefs: {
 | |
|               required: true,
 | |
|               type: "array"
 | |
|             },
 | |
|             defaultLocale: {
 | |
|               required: true,
 | |
|               type: "string"
 | |
|             },
 | |
|           },
 | |
|         },
 | |
|         osPrefs: {
 | |
|           required: true,
 | |
|           type: "object",
 | |
|           properties: {
 | |
|             systemLocales: {
 | |
|               required: true,
 | |
|               type: "array"
 | |
|             },
 | |
|             regionalPrefsLocales: {
 | |
|               required: true,
 | |
|               type: "array"
 | |
|             },
 | |
|           },
 | |
|         },
 | |
|       },
 | |
|     },
 | |
|   },
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Throws an Error if obj doesn't conform to schema.  That way you get a nice
 | |
|  * error message and a stack to help you figure out what went wrong, which you
 | |
|  * wouldn't get if this just returned true or false instead.  There's still
 | |
|  * room for improvement in communicating validation failures, however.
 | |
|  *
 | |
|  * @param obj    The object to validate.
 | |
|  * @param schema The schema that obj should conform to.
 | |
|  */
 | |
| function validateObject(obj, schema) {
 | |
|   if (obj === undefined && !schema.required)
 | |
|     return;
 | |
|   if (typeof(schema.type) != "string")
 | |
|     throw schemaErr("'type' must be a string", schema);
 | |
|   if (objType(obj) != schema.type)
 | |
|     throw validationErr("Object is not of the expected type", obj, schema);
 | |
|   let validatorFnName = "validateObject_" + schema.type;
 | |
|   if (!(validatorFnName in this))
 | |
|     throw schemaErr("Validator function not defined for type", schema);
 | |
|   this[validatorFnName](obj, schema);
 | |
| }
 | |
| 
 | |
| function validateObject_object(obj, schema) {
 | |
|   if (typeof(schema.properties) != "object")
 | |
|     // Don't care what obj's properties are.
 | |
|     return;
 | |
|   // First check that all the schema's properties match the object.
 | |
|   for (let prop in schema.properties)
 | |
|     validateObject(obj[prop], schema.properties[prop]);
 | |
|   // Now check that the object doesn't have any properties not in the schema.
 | |
|   for (let prop in obj)
 | |
|     if (!(prop in schema.properties))
 | |
|       throw validationErr("Object has property " + prop + " not in schema", obj, schema);
 | |
| }
 | |
| 
 | |
| function validateObject_array(array, schema) {
 | |
|   if (typeof(schema.items) != "object")
 | |
|     // Don't care what the array's elements are.
 | |
|     return;
 | |
|   array.forEach(elt => validateObject(elt, schema.items));
 | |
| }
 | |
| 
 | |
| function validateObject_string(str, schema) {}
 | |
| function validateObject_boolean(bool, schema) {}
 | |
| function validateObject_number(num, schema) {}
 | |
| 
 | |
| function validationErr(msg, obj, schema) {
 | |
|   return new Error("Validation error: " + msg +
 | |
|                    ": object=" + JSON.stringify(obj) +
 | |
|                    ", schema=" + JSON.stringify(schema));
 | |
| }
 | |
| 
 | |
| function schemaErr(msg, schema) {
 | |
|   return new Error("Schema error: " + msg + ": " + JSON.stringify(schema));
 | |
| }
 | |
| 
 | |
| function objType(obj) {
 | |
|   let type = typeof(obj);
 | |
|   if (type != "object")
 | |
|     return type;
 | |
|   if (Array.isArray(obj))
 | |
|     return "array";
 | |
|   if (obj === null)
 | |
|     return "null";
 | |
|   return type;
 | |
| }
 | 
