You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@royale.apache.org by gr...@apache.org on 2019/11/01 03:19:41 UTC
[royale-asjs] 03/05: SharedObject emulation for JS that is closer
to swf (i.e. uses AMF),
plus a lighter-weight version that uses JSON serialization only (does not
support roundtripping of custom classes)
This is an automated email from the ASF dual-hosted git repository.
gregdove pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/royale-asjs.git
commit e6bc56822b6edafd79aabf8cdefde707de9ca74a
Author: greg-dove <gr...@gmail.com>
AuthorDate: Fri Nov 1 14:21:45 2019 +1300
SharedObject emulation for JS that is closer to swf (i.e. uses AMF), plus a lighter-weight version that uses JSON serialization only (does not support roundtripping of custom classes)
---
.../MXRoyale/src/main/royale/MXRoyaleClasses.as | 1 +
.../src/main/royale/mx/net/SharedObject.as | 251 ++++++++++++---------
.../src/main/royale/mx/net/SharedObjectJSON.as | 183 +++++++++++++++
.../main/royale/flexUnitTests/MXRoyaleTester.as | 4 +-
.../flexUnitTests/mxroyale/SharedObjectTest.as | 130 +++++++++++
5 files changed, 461 insertions(+), 108 deletions(-)
diff --git a/frameworks/projects/MXRoyale/src/main/royale/MXRoyaleClasses.as b/frameworks/projects/MXRoyale/src/main/royale/MXRoyaleClasses.as
index 625bb27..2dc9f9b 100644
--- a/frameworks/projects/MXRoyale/src/main/royale/MXRoyaleClasses.as
+++ b/frameworks/projects/MXRoyale/src/main/royale/MXRoyaleClasses.as
@@ -174,6 +174,7 @@ internal class MXRoyaleClasses
import mx.formatters.CurrencyFormatter; CurrencyFormatter;
import mx.events.CalendarLayoutChangeEvent; CalendarLayoutChangeEvent;
import mx.net.SharedObject; SharedObject;
+ import mx.net.SharedObjectJSON; SharedObjectJSON;
import mx.modules.ModuleManager; ModuleManager;
import mx.events.DragEvent; DragEvent;
import mx.formatters.DateBase; DateBase;
diff --git a/frameworks/projects/MXRoyale/src/main/royale/mx/net/SharedObject.as b/frameworks/projects/MXRoyale/src/main/royale/mx/net/SharedObject.as
index 14df1fd..4630fe0 100644
--- a/frameworks/projects/MXRoyale/src/main/royale/mx/net/SharedObject.as
+++ b/frameworks/projects/MXRoyale/src/main/royale/mx/net/SharedObject.as
@@ -19,59 +19,69 @@
package mx.net
{
-
-COMPILE::SWF
-{
- import flash.net.SharedObject;
-}
-
-import org.apache.royale.events.EventDispatcher;
-import org.apache.royale.utils.string.trim;
-
-
-
-public class SharedObject extends org.apache.royale.events.EventDispatcher
-{
- public static function getLocal(name:String, localPath:String = null, secure:Boolean = false):mx.net.SharedObject
- {
- var so:mx.net.SharedObject = new mx.net.SharedObject();
- so.setName(name);
- so.setLocalPath(localPath);
- COMPILE::SWF
- {
- so.createSO();
- }
- return so;
- }
COMPILE::SWF
- private var _so:flash.net.SharedObject;
-
- public function flush(minDiskSpace:int = 0):String
- {
- COMPILE::JS
+ {
+ import flash.net.SharedObject;
+ }
+
+ COMPILE::JS{
+ import org.apache.royale.net.remoting.amf.AMFBinaryData;
+ }
+
+ import org.apache.royale.events.EventDispatcher;
+
+ /**
+ * An emulation class to support the swf based Local SharedObject support.
+ * This implementation supports AMF encoded content (requires registerClassAlias before reading and writing to roundtrip instances of custom classes)
+ */
+ public class SharedObject extends org.apache.royale.events.EventDispatcher
+ {
+ private static const map:Object = {};
+ private static var unlocked:Boolean;
+
+ public static function getLocal(name:String, localPath:String = null, secure:Boolean = false):mx.net.SharedObject
+ {
+ var pathKey:String = localPath == null ? '$null$' : localPath;
+ COMPILE::JS {
+ localPath = pathKey;
+ }
+ var cached:mx.net.SharedObject = map[pathKey + '::' + name];
+ if (!cached)
+ {
+ unlocked = true;
+ cached = new mx.net.SharedObject();
+ unlocked = false;
+ map[pathKey + '::' + name] = cached;
+ cached.setName(name);
+ cached.setLocalPath(localPath);
+ cached.createSO(secure);
+ }
+ return cached;
+ }
+
+ public function SharedObject()
+ {
+ if (!unlocked) throw new Error('ArgumentError: Error #2012: SharedObject class cannot be instantiated.')
+ }
+
+ COMPILE::SWF
+ private var _so:flash.net.SharedObject;
+
+ COMPILE::JS
+ private var _ls:Storage;
+
+
+ public function flush(minDiskSpace:int = 0):String
+ {
+ COMPILE::JS
{
if (_data)
{
- var theseKeys:Object = {};
- for (var p:String in _data)
- {
- document.cookie = _name + p + '=' + _data[p];
- theseKeys[p] = 1;
- }
- var deleteKeys:Array = [];
- for (p in keys)
- {
- if (!theseKeys[p])
- {
- deleteKeys.push(p);
- }
- }
- for each (p in deleteKeys)
- {
- delete keys[p];
- document.cookie = _name + p + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT';
- }
+ var amf:AMFBinaryData = new AMFBinaryData();
+ amf.writeObject(_data);
+ var base64:String = window['btoa'](String.fromCharCode.apply(null, amf.array));
+ _ls.setItem(_localPath + "::" + _name, base64);
}
return SharedObjectFlushStatus.FLUSHED;
}
@@ -79,62 +89,91 @@ public class SharedObject extends org.apache.royale.events.EventDispatcher
{
return _so.flush(minDiskSpace);
}
- }
-
- private var keys:Object = {};
-
- private var _data:Object;
-
- public function get data():Object
- {
- COMPILE::SWF
- {
- return _so.data;
- }
- COMPILE::JS
- {
- if (!_data)
- {
- _data = {};
- var allCookies:String = String(document.cookie);
- var arrCookies:Array = allCookies.split(';');
- for each (var s:String in arrCookies)
- {
- s = trim(s);
- var parts:Array = s.split('=');
- if (parts[0].indexOf(_name) == 0)
- {
- var key:String = parts[0].substring(_name.length);
- keys[key] = 1;
- _data[key] = parts[1];
- }
- }
- }
- return _data;
- }
- }
-
- private var _name:String;
-
- public function setName(name:String):void
- {
- _name = name;
- }
-
- private var _localPath:String;
-
- public function setLocalPath(localPath:String):void
- {
- _localPath = localPath;
- }
-
- COMPILE::SWF
- public function createSO():void
- {
- _so = flash.net.SharedObject.getLocal(_name, _localPath);
- }
- }
-
-
-
+ }
+
+ public function clear():void{
+ COMPILE::SWF{
+ _so.clear();
+ }
+ COMPILE::JS{
+ if (_data) {
+ if (_ls) {
+ _ls.removeItem(_localPath + "::" + _name);
+ }
+ _data = null;
+ }
+ }
+ }
+
+ COMPILE::JS
+ private var _data:Object;
+
+ public function get data():Object
+ {
+ COMPILE::SWF
+ {
+ return _so.data;
+ }
+ COMPILE::JS
+ {
+ if (!_data)
+ {
+ _data = {};
+ }
+ return _data;
+ }
+ }
+
+ private var _name:String;
+ private function setName(name:String):void
+ {
+ COMPILE::JS{
+ //apply same limits as swf
+ if (/[~%&\\;:"',<>?#\s]/.test(name)) throw new Error('Error #2134: Cannot create SharedObject.')
+ }
+ _name = name;
+ }
+
+ private var _localPath:String;
+ private function setLocalPath(localPath:String):void
+ {
+ _localPath = localPath;
+ }
+
+ private function createSO(secure:Boolean):void
+ {
+ COMPILE::SWF{
+ _so = flash.net.SharedObject.getLocal(_name, _localPath, secure);
+ }
+ COMPILE::JS{
+ _ls = window.localStorage;
+ if (!_ls && typeof Storage != undefined) {
+ //this gets around an issue with local testing with file:// protocol in IE11
+ var p:String = window.location.pathname.replace(/(^..)(:)/, "$1$$");
+ window.location.href = window.location.protocol + "//127.0.0.1" + p;
+ _ls = window.localStorage;
+ }
+ if (!_ls) {
+ throw new Error('local storage not supported');
+ }
+ var base64:String = _ls.getItem(_localPath + "::" + _name);
+ if (base64)
+ {
+ var binary_string:String = window['atob'](base64);
+ var arr:Uint8Array = new Uint8Array(
+ binary_string.split('')
+ .map(
+ function (charStr:String):uint
+ {
+ return charStr.charCodeAt(0);
+ }
+ )
+ );
+ var amf:AMFBinaryData = new AMFBinaryData(arr.buffer);
+ _data = amf.readObject();
+ }
+ }
+ }
+ }
+
}
diff --git a/frameworks/projects/MXRoyale/src/main/royale/mx/net/SharedObjectJSON.as b/frameworks/projects/MXRoyale/src/main/royale/mx/net/SharedObjectJSON.as
new file mode 100644
index 0000000..e1c9e68
--- /dev/null
+++ b/frameworks/projects/MXRoyale/src/main/royale/mx/net/SharedObjectJSON.as
@@ -0,0 +1,183 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+package mx.net
+{
+
+ COMPILE::SWF
+ {
+ import flash.net.SharedObject;
+ }
+
+ import org.apache.royale.events.EventDispatcher;
+
+ /**
+ * An lighter weight emulation class to support the swf based Local SharedObject support.
+ * This implementation does not support AMF encoded content. It is intended for javascript
+ * implementations that require persistence, but do not already have the AMF support dependency as part of the application
+ */
+ public class SharedObjectJSON extends org.apache.royale.events.EventDispatcher
+ {
+ private static const map:Object = {};
+ private static var unlocked:Boolean;
+
+ public static function getLocal(name:String, localPath:String = null, secure:Boolean = false):mx.net.SharedObjectJSON
+ {
+ var pathKey:String = localPath == null ? '$null$' : localPath;
+ COMPILE::JS {
+ localPath = pathKey;
+ }
+ var cached:mx.net.SharedObjectJSON = map[pathKey + '::' + name];
+ if (!cached)
+ {
+ unlocked = true;
+ cached = new mx.net.SharedObjectJSON();
+ unlocked = false;
+ map[pathKey + '::' + name] = cached;
+ cached.setName(name);
+ cached.setLocalPath(localPath);
+ cached.createSO(secure);
+ }
+ return cached;
+ }
+
+ public function SharedObjectJSON()
+ {
+ if (!unlocked) throw new Error('ArgumentError: Error #2012: SharedObjectJSON class cannot be instantiated.')
+ }
+
+ COMPILE::SWF
+ private var _so:flash.net.SharedObject;
+
+ COMPILE::JS
+ private var _ls:Storage;
+
+
+ public function flush(minDiskSpace:int = 0):String
+ {
+ COMPILE::JS
+ {
+ if (_data)
+ {
+ _ls.setItem(_localPath + "::" + _name, JSON.stringify(_data));
+ }
+ return SharedObjectFlushStatus.FLUSHED;
+ }
+ COMPILE::SWF
+ {
+ if (_data) {
+ _so.data['jsonContent'] = JSON.stringify(_data);
+ }
+ return _so.flush(minDiskSpace);
+ }
+ }
+
+ public function clear():void{
+ COMPILE::SWF{
+ _so.data['jsonContent'] = '{}';
+ _so.flush();
+ _data = {};
+ }
+ COMPILE::JS{
+ if (_data) {
+ if (_ls) {
+ _ls.removeItem(_localPath + "::" + _name);
+ }
+ _data = null;
+ }
+ }
+ }
+
+
+ private var _data:Object;
+
+ public function get data():Object
+ {
+ COMPILE::SWF
+ {
+ if (!_data) {
+ if (_so.data['jsonContent']) {
+ try{
+ _data = JSON.parse(_so.data['jsonContent']);
+ } catch(e:Error) {
+ throw new Error('Error #2134: Bad data. Cannot create SharedObjectJSON.')
+ }
+ } else _data = {};
+ }
+ return _data;
+ }
+ COMPILE::JS
+ {
+ if (!_data)
+ {
+ _data = {};
+ }
+ return _data;
+ }
+ }
+
+ private var _name:String;
+ private function setName(name:String):void
+ {
+ COMPILE::JS{
+ //apply same limits as swf
+ if (/[~%&\\;:"',<>?#\s]/.test(name)) throw new Error('Error #2134: Cannot create SharedObjectJSON.')
+ }
+ _name = name;
+ }
+
+ private var _localPath:String;
+ private function setLocalPath(localPath:String):void
+ {
+ COMPILE::JS{
+ localPath+='$jsonContent$';
+ }
+ _localPath = localPath;
+ }
+
+ private function createSO(secure:Boolean):void
+ {
+ COMPILE::SWF{
+ _so = flash.net.SharedObject.getLocal(_name, _localPath, secure);
+
+ }
+ COMPILE::JS{
+ _ls = window.localStorage;
+ if (!_ls && typeof Storage != undefined) {
+ //this gets around an issue with local testing with file:// protocol in IE11
+ var p:String = window.location.pathname.replace(/(^..)(:)/, "$1$$");
+ window.location.href = window.location.protocol + "//127.0.0.1" + p;
+ _ls = window.localStorage;
+ }
+ if (!_ls) {
+ throw new Error('local storage not supported');
+ }
+ var json:String = _ls.getItem(_localPath + "::" + _name);
+ if (json) {
+ try{
+ _data = JSON.parse(json);
+ } catch(e:Error) {
+ throw new Error('Error #2134: Bad data. Cannot create SharedObjectJSON.')
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/manualtests/UnitTests/src/main/royale/flexUnitTests/MXRoyaleTester.as b/manualtests/UnitTests/src/main/royale/flexUnitTests/MXRoyaleTester.as
index 08edb08..f0c93c8 100644
--- a/manualtests/UnitTests/src/main/royale/flexUnitTests/MXRoyaleTester.as
+++ b/manualtests/UnitTests/src/main/royale/flexUnitTests/MXRoyaleTester.as
@@ -30,8 +30,8 @@ package flexUnitTests
public function MXRoyaleTester()
{
}
-
-
+
+ public var sharedObjectTest:SharedObjectTest;
public var objectUtilTest:ObjectUtilTest;
public var flexSDK_ObjectUtilTests:FlexSDK_ObjectUtil_Tests;
public var flexSDK_ObjectUtil_FLEX_34852_Tests:FlexSDK_ObjectUtil_FLEX_34852_Tests;
diff --git a/manualtests/UnitTests/src/main/royale/flexUnitTests/mxroyale/SharedObjectTest.as b/manualtests/UnitTests/src/main/royale/flexUnitTests/mxroyale/SharedObjectTest.as
new file mode 100644
index 0000000..49232ae
--- /dev/null
+++ b/manualtests/UnitTests/src/main/royale/flexUnitTests/mxroyale/SharedObjectTest.as
@@ -0,0 +1,130 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package flexUnitTests.mxroyale
+{
+
+
+ import mx.net.SharedObject;
+ import mx.net.SharedObjectJSON;
+ import org.apache.royale.reflection.registerClassAlias;
+ import org.apache.royale.test.asserts.*;
+ import flexUnitTests.mxroyale.support.*;
+ import testshim.RoyaleUnitTestRunner;
+
+
+
+ /**
+ * @royalesuppresspublicvarwarning
+ */
+ public class SharedObjectTest
+ {
+
+
+ public static var isJS:Boolean = COMPILE::JS;
+
+
+ [Before]
+ public function setUp():void
+ {
+ registerClassAlias('TestClass5', TestClass5);
+ }
+
+ [After]
+ public function tearDown():void
+ {
+
+ }
+
+ [BeforeClass]
+ public static function setUpBeforeClass():void
+ {
+ }
+
+ [AfterClass]
+ public static function tearDownAfterClass():void
+ {
+ }
+
+ public function getPlayerVersion():Number{
+ COMPILE::SWF{
+ import flash.system.Capabilities;
+ var parts:Array = Capabilities.version.split(' ')[1].split(',');
+ return Number( parts[0]+'.'+parts[1])
+ }
+ //something for js, indicating javascript 'playerversion' is consistent with more recent flash player versions:
+ return 30;
+ }
+
+
+ [Test]
+ public function testSharedObjectSimple():void{
+ var so:SharedObject = SharedObject.getLocal('myDynamicTest');
+ var content:Object = so.data;
+
+ var so2:SharedObject = SharedObject.getLocal('myDynamicTest');
+
+ var content2:Object = so2.data;
+
+ assertEquals(so,so2, 'unexpected non equivalency');
+ assertEquals(content,content2, 'unexpected non equivalency');
+
+ //content should either be empty or the last saved.
+ var validContents:Array = [ '{"test":{"name":"an Object"}}', '{}'];
+ assertTrue(validContents.indexOf(JSON.stringify(content)) != -1, 'contents not considered valid');
+ so.data['test'] = {'name':'an Object'};
+ so.flush();
+ }
+
+ [Test]
+ public function testSharedObjectAMF():void{
+ var so:SharedObject = SharedObject.getLocal('myTypedTest');
+
+ //this will work first when saving, and subsequently when retrieving
+ var object:Object = so.data['testClass'];
+ RoyaleUnitTestRunner.consoleOut('testSharedObjectAMF found TestClass5 instance? '+(object is TestClass5));
+ if (object == null) {
+ so.data['testClass'] = new TestClass5();
+ so.flush();
+ object = so.data['testClass']
+ }
+
+ assertTrue(object is TestClass5, 'contents not considered valid');
+ }
+
+
+ [Test]
+ public function testSharedObjectJSONSimple():void{
+ var so:SharedObjectJSON = SharedObjectJSON.getLocal('myJSONTest');
+ var content:Object = so.data;
+
+ var so2:SharedObjectJSON = SharedObjectJSON.getLocal('myJSONTest');
+
+ var content2:Object = so2.data;
+
+ assertEquals(so,so2, 'unexpected non equivalency');
+ assertEquals(content,content2, 'unexpected non equivalency');
+ RoyaleUnitTestRunner.consoleOut('testSharedObjectJSONSimple content is '+JSON.stringify(content));
+ //content should either be empty or the last saved.
+ var validContents:Array = [ '{"test":{"name":"an Object"}}', '{}'];
+ assertTrue(validContents.indexOf(JSON.stringify(content)) != -1, 'contents not considered valid');
+ so.data['test'] = {'name':'an Object'};
+ so.flush();
+ }
+ }
+}