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();
+        }
+    }
+}