{
const kps = this.kmpCompiler.loadKpsFile(kpsFilename);
+ if(!kps) {
+ // errors will already have been reported by loadKpsFile
+ return null;
+ }
// Check existence of required files
for(const filename of [sources.licenseFilename, sources.msiFilename, sources.setupExeFilename]) {
diff --git a/developer/src/kmc-package/test/fixtures/invalid/error_invalid_package_file.kps b/developer/src/kmc-package/test/fixtures/invalid/error_invalid_package_file.kps
new file mode 100644
index 00000000000..e18eaa26a06
--- /dev/null
+++ b/developer/src/kmc-package/test/fixtures/invalid/error_invalid_package_file.kps
@@ -0,0 +1,32 @@
+
+
+
+ 15.0.266.0
+ 7.0
+
+
+ SENĆOŦEN (Saanich Dialect) Keyboard
+
+ © 2019 National Research Council Canada & this test
+ Eddie Antonio Santos
+ 1.0
+
+
+
+ basic.kmx
+ Keyboard Basic
+ 0
+ .kmx
+
+
+
+
+ Basic
+ basic
+ 1.0
+
+ Khmer
+
+
+
+
diff --git a/developer/src/kmc-package/test/test-messages.ts b/developer/src/kmc-package/test/test-messages.ts
index f9649e9c090..24fb658b599 100644
--- a/developer/src/kmc-package/test/test-messages.ts
+++ b/developer/src/kmc-package/test/test-messages.ts
@@ -226,4 +226,11 @@ describe('CompilerMessages', function () {
CompilerMessages.HINT_PackageContainsSourceFile);
});
+ // ERROR_InvalidPackageFile
+
+ it('should generate ERROR_InvalidPackageFile if package source file contains invalid XML', async function() {
+ testForMessage(this, ['invalid', 'error_invalid_package_file.kps'],
+ CompilerMessages.ERROR_InvalidPackageFile);
+ });
+
});
diff --git a/developer/src/kmc/src/util/projectLoader.ts b/developer/src/kmc/src/util/projectLoader.ts
index 078f2e31ded..10cd58fc818 100644
--- a/developer/src/kmc/src/util/projectLoader.ts
+++ b/developer/src/kmc/src/util/projectLoader.ts
@@ -44,8 +44,9 @@ function loadDefaultProjectFromFolder(infile: string, callbacks: CompilerCallbac
function loadProjectFromFile(infile: string, callbacks: CompilerCallbacks): KeymanDeveloperProject {
const kpjData = callbacks.loadFile(infile);
const reader = new KPJFileReader(callbacks);
- const kpj = reader.read(kpjData);
+ let kpj = null;
try {
+ kpj = reader.read(kpjData);
reader.validate(kpj);
} catch(e) {
callbacks.reportMessage(InfrastructureMessages.Error_InvalidProjectFile({message: (e??'').toString()}));
diff --git a/developer/src/server/src/site/packages.js b/developer/src/server/src/site/packages.js
index 9a13b1afacc..2255fe9ebd6 100644
--- a/developer/src/server/src/site/packages.js
+++ b/developer/src/server/src/site/packages.js
@@ -11,12 +11,12 @@ menuDropdown.onclick = (value) => {
menuDropdown.set(''); // we never show an 'active' package
if(value == '#install-keyman') {
let href = '';
- switch(keyman.util.device.OS) {
- case 'iOS': href = 'https://keyman.com/go/developer/'+versionMajor+'/ios-app'; break;
- case 'Android': href = 'https://keyman.com/go/developer/'+versionMajor+'/android-app'; break;
- case 'Linux': href = 'https://keyman.com/linux/download'; break;
- case 'Windows': href = 'https://keyman.com/go/download/keyman-windows'; break;
- case 'MacOSX': href = 'https://keyman.com/go/download/keyman-mac'; break;
+ switch(keyman.config.hostDevice.OS) { // note: KeymanWeb internal API
+ case 'ios': href = 'https://keyman.com/go/developer/'+versionMajor+'/ios-app'; break;
+ case 'android': href = 'https://keyman.com/go/developer/'+versionMajor+'/android-app'; break;
+ case 'linux': href = 'https://keyman.com/linux/download'; break;
+ case 'windows': href = 'https://keyman.com/go/download/keyman-windows'; break;
+ case 'macosx': href = 'https://keyman.com/go/download/keyman-mac'; break;
default: href = 'https://keyman.com/downloads'; break;
}
location.href = href;
diff --git a/developer/src/server/src/site/test.js b/developer/src/server/src/site/test.js
index 60a27cc484f..6014c8ab264 100644
--- a/developer/src/server/src/site/test.js
+++ b/developer/src/server/src/site/test.js
@@ -216,14 +216,14 @@ window.onload = function() {
if(newOSK) {
document.getElementById('osk-host').removeChild(newOSK.element);
- keyman.osk = null;
+ keyman.osk = null; // Note: undocumented KeymanWeb API
}
// Create a new on screen keyboard view and tell KeymanWeb that
// we are using the targetDevice for context input.
- newOSK = new keyman.views.InlinedOSKView(keyman, { device: targetDevice });
- keyman.core.contextDevice = targetDevice;
- keyman.osk = newOSK;
+ newOSK = new keyman.views.InlinedOSKView(keyman, { device: targetDevice }); // Note: KeymanWeb internal API
+ keyman.core.contextDevice = targetDevice; // Note: KeymanWeb internal API
+ keyman.osk = newOSK; // Note: undocumented KeymanWeb API
if(document.body.offsetWidth < targetDevice.dimensions[0]) {
newOSK.setSize('320px', '200px');
@@ -237,8 +237,8 @@ window.onload = function() {
keyman.addEventListener('keyboardchange', function(keyboardProperties) {
if(newOSK) {
- keyman.osk = newOSK;
- newOSK.activeKeyboard = keyman.contextManager.activeKeyboard; // Private API refs on both sides
+ keyman.osk = newOSK; // Note: undocumented KeymanWeb API
+ newOSK.activeKeyboard = keyman.contextManager.activeKeyboard; // Note: undocumented KeymanWeb API refs on both sides
}
keyboardDropdown.set(keyboardProperties.internalName);
window.sessionStorage.setItem('current-keyboard', keyboardProperties.internalName);
@@ -284,7 +284,7 @@ function unloadKeyboardsAndModels() {
const lastModel = keyman.core.activeModel;
if(lastModel) {
console.log('Unregistering model '+lastModel.id);
- keyman.removeModel(lastModel.id);
+ keyman.removeModel(lastModel.id); // Note: undocumented KeymanWeb API
}
modelDropdown.removeAll();
diff --git a/developer/src/tike/xml/layoutbuilder/builder.xsl b/developer/src/tike/xml/layoutbuilder/builder.xsl
index 2d6f5299cd1..3b75cd81a3e 100644
--- a/developer/src/tike/xml/layoutbuilder/builder.xsl
+++ b/developer/src/tike/xml/layoutbuilder/builder.xsl
@@ -108,7 +108,7 @@
-
+
@@ -118,7 +118,7 @@
-
+
@@ -221,7 +221,7 @@
-
+