Changeset 6832

Show
Ignore:
Timestamp:
12/06/06 13:18:47 (21 months ago)
Author:
BradNeuberg
Message:

Bug fix for 2128: Support Dojo Storage when loaded from file:// or chrome:// URLs with a FileStorageProvider?; works for Internet Explorer and Firefox, QAed on IE 6, IE 7, Firefox 1.5, and Firefox 2 on Mac OS X, Ubuntu Linux, and Windows XP - tested on Safari to make sure does not break existing functionality for http:// URLs

Location:
trunk
Files:
4 added
5 modified

Legend:

Unmodified
Added
Removed
  • trunk/buildscripts/build.xml

    r6717 r6832  
    592592                        <fileset dir="${root}/"> 
    593593                                <include name="**/*.swf" /> 
     594                                <include name="*.jar" /> 
    594595                        </fileset> 
    595596                </copy> 
     
    766767                ]]> 
    767768                </script> 
     769                 
     770                <!--  
     771                        Generate optional Java class needed for Safari and Opera 
     772                        for dojo.storage.browser.FileStorageProvider 
     773                --> 
     774                <antcall target="buildFileStorageProvider" /> 
    768775 
    769776                <antcall target="-docs-and-tests" /> 
     
    813820        </target> 
    814821        <!-- end tar task --> 
     822         
     823        <!-- buildFileStorageProvider task --> 
     824        <target name="buildFileStorageProvider" 
     825                        description="Builds a Java class needed by dojo.storage.browser.FileStorageProvider"> 
     826                <echo> 
     827Building optional Java class for dojo.storage.browser.FileStorageProvider. Don't 
     828worry if this fails; the generated class file is checked into Subversion and is 
     829available in src/storage/java/ without rebuilding it. 
     830                </echo>          
     831                 
     832                <javac  srcdir="${root}/src/storage/java/" 
     833                                destdir="${root}/src/storage/java/" 
     834                                debug="on" 
     835                                target="1.1" 
     836                                source="1.1" 
     837                                failonerror="false"/> 
     838                                 
     839                <jar destfile="${root}/DojoFileStorageProvider.jar"> 
     840                    <fileset dir="${root}/src/storage/java/" 
     841                             includes="*.class"/> 
     842                 </jar> 
     843        </target> 
     844        <!-- end buildFileStorageProvider task --> 
    815845 
    816846        <!-- buildDojoFlash task --> 
  • trunk/demos/storage/editor.js

    r6598 r6832  
    55dojo.require("dojo.widget.Editor2"); 
    66dojo.require("dojo.storage.*"); 
     7 
     8// IE 7.0 does not support XHR loading from file:// URLs 
     9// when loaded from a file:// URL itself - this breaks 
     10// us for loading all of our Editor2 widget resources. 
     11// This is if we want to use Moxie in conjunction with the  
     12// dojo.storage.browser.FileStorageProvider for web apps 
     13// loaded from the native file system 
     14if(dojo.render.html.ie70 == true 
     15        && window.location.protocol.indexOf("file") != -1){ 
     16        alert("Moxie on Internet Explorer 7.0 is not yet supported when loaded from the file:// URL");           
     17} 
    718 
    819var Moxie = { 
  • trunk/src/storage/browser.js

    r6804 r6832  
    55dojo.require("dojo.json"); 
    66dojo.require("dojo.uri.*"); 
     7 
     8 
     9 
     10dojo.storage.browser.FileStorageProvider = function(){ 
     11        // summary: 
     12        //              Storage provider that uses the native file system as 
     13        //              a storage back end, across Internet Explorer, Firefox, 
     14        //              Safari, and Opera. 
     15        // description:  
     16        //              This storage provider will autodetect if access 
     17        //              to the native file system is available. This is 
     18        //              only available if a page is loaded through a file:// 
     19        //              or chrome:// URL.  
     20        //               
     21        //              We support three different browsers for native file storage: 
     22        //                      * Firefox - Uses XPCOM 
     23        //                      * Internet Explorer - Uses ActiveX 
     24        //                      * Safari/Opera - Uses Java and LiveConnect 
     25        // 
     26        //              This storage provider saves all files in the same directory 
     27        //              as the HTML file it is invoked from. Each key 
     28        //              is a separate file, with the file's contents being 
     29        //              the value for that key and the filename being the 
     30        //              key plus ".txt". A special file named __dojoAllKeys.txt is also 
     31        //              created, which is a list of all the keys available. 
     32        // 
     33        //              The technique to create this storage provider was learned 
     34        //              by studying TiddlyWiki (http://tiddlywiki.com).  
     35        //              Thanks for figuring out how to do this Jeremy Ruston! 
     36        // 
     37        //              TODO: Automagically create a directory named .dojo_storage in 
     38        //              the user's home directory, cross-platform, and then create a 
     39        //              subdirectory based off the HTML filename of this page, such as 
     40        //              myapp.html/ to store all of our key files into. Saving the files 
     41        //              into the same directry as the HTML file is dangerous and messy, 
     42        //              and might not be allowed based on directory permissioning for some 
     43        //              scenarios. 
     44        // 
     45        //              Authors of this storage provider-        
     46        //                      Brad Neuberg, bkn3@columbia.edu  
     47} 
     48 
     49dojo.inherits(dojo.storage.browser.FileStorageProvider, dojo.storage); 
     50 
     51// The filename to the index file that stores all available keys 
     52dojo.storage.browser.FileStorageProvider._KEY_INDEX_FILENAME = "__dojoAllKeys"; 
     53 
     54// The ID of the hidden applet we use for file operations on Safari and Opera 
     55dojo.storage.browser.FileStorageProvider._APPLET_ID = "__dojoFileJavaObj"; 
     56 
     57// instance methods and properties 
     58dojo.lang.extend(dojo.storage.browser.FileStorageProvider, { 
     59        namespace: "default", 
     60        initialized: false, 
     61         
     62        _available: null, 
     63        _statusHandler: null, 
     64        _keyIndex: new Array(), 
     65         
     66        initialize: function(){ 
     67                if(djConfig["disableFileStorage"] == true){ 
     68                        return; 
     69                } 
     70                 
     71                // write out applet if Java is needed 
     72                // for file operations 
     73                /* 
     74                if(this._isAvailableJava()){ 
     75                        this._writeApplet(); 
     76                }*/ 
     77                 
     78                // load our keys 
     79                this._loadKeyIndex(); 
     80                 
     81                // indicate that this storage provider is now loaded 
     82                this.initialized = true; 
     83                dojo.storage.manager.loaded();   
     84        }, 
     85         
     86        isAvailable: function(){ 
     87                // see if we are a file:// or chrome:// URL 
     88                this._available = false; 
     89                var protocol = window.location.protocol; 
     90                if(protocol.indexOf("file") != -1 || protocol.indexOf("chrome") != -1){ 
     91                        // try each of the file access types 
     92                        this._available = this._isAvailableXPCOM(); 
     93                         
     94                        if(this._available == false){ 
     95                                this._available = this._isAvailableActiveX(); 
     96                        } 
     97                         
     98                        /*if(this._available == false){ 
     99                                this._available = this._isAvailableJava(); 
     100                        }*/ 
     101                } 
     102                 
     103                return this._available; 
     104        }, 
     105 
     106        put: function(key, value, resultsHandler){ 
     107                if(this.isValidKey(key) == false){ 
     108                        dojo.raise("Invalid key given: " + key); 
     109                }                        
     110                 
     111                this._statusHandler = resultsHandler; 
     112                 
     113                // try to save the value        as a file 
     114                try{ 
     115                        this._save(key, value); 
     116                        // indicate we succeeded 
     117                        resultsHandler.call(null, dojo.storage.SUCCESS, key); 
     118                }catch(e){ 
     119                        // indicate we failed 
     120                        this._statusHandler.call(null, dojo.storage.FAILED,  
     121                                                                        key, e.toString()); 
     122                } 
     123        }, 
     124 
     125        get: function(key){ 
     126                if(this.isValidKey(key) == false){ 
     127                        dojo.raise("Invalid key given: " + key); 
     128                } 
     129                 
     130                // FIXME: what to do with underlying file exceptions? 
     131                // pass to caller? 
     132                var results = this._load(key); 
     133                 
     134                return results; 
     135        }, 
     136 
     137        getKeys: function(){ 
     138                return this._keyIndex; 
     139        }, 
     140         
     141        hasKey: function(key){ 
     142                if(this.isValidKey(key) == false){ 
     143                        dojo.raise("Invalid key given: " + key); 
     144                }        
     145                 
     146                // reload the index in case someone hand edited 
     147                // it in the file system 
     148                this._loadKeyIndex(); 
     149                 
     150                // make sure this key exists 
     151                var exists = false; 
     152                for(var i = 0; i < this._keyIndex.length; i++){ 
     153                        if(this._keyIndex[i] == key){ 
     154                                exists = true; 
     155                        }        
     156                } 
     157                 
     158                return exists; 
     159        }, 
     160 
     161        clear: function(){ 
     162                // reload the index in case someone hand edited 
     163                // it in the file system 
     164                this._loadKeyIndex(); 
     165                 
     166                // make a copy of the keyIndex, since we will be changing 
     167                // it while looping 
     168                var keyIndex = new Array(); 
     169                 
     170                for(var i = 0; i < this._keyIndex.length; i++){ 
     171                        keyIndex[keyIndex.length] = new String(this._keyIndex[i]);       
     172                } 
     173                 
     174                // now wipe everything out 
     175                for(var i = 0; i < keyIndex.length; i++){ 
     176                        this.remove(keyIndex[i]);        
     177                } 
     178        }, 
     179         
     180        remove: function(key){ 
     181                if(this.isValidKey(key) == false){ 
     182                        dojo.raise("Invalid key given: " + key); 
     183                } 
     184                 
     185                // first, remove the key from the index 
     186                 
     187                // load the index in case it has been changed 
     188                // by hand in the file system 
     189                this._loadKeyIndex(); 
     190                 
     191                // find and delete this key 
     192                for(var i = 0; i < this._keyIndex.length; i++){ 
     193                        if(this._keyIndex[i] == key){ 
     194                                this._keyIndex.splice(i, 1); 
     195                                break; 
     196                        } 
     197                } 
     198                 
     199                // resave the index 
     200                this._save(dojo.storage.browser.FileStorageProvider._KEY_INDEX_FILENAME, 
     201                                        this._keyIndex, 
     202                                        false); 
     203                 
     204                // now delete the underlying key's file 
     205                 
     206                // the filename for this key entry 
     207                var fullPath = this._getPagePath() + key + ".txt"; 
     208                 
     209                if(this._isAvailableXPCOM()){ 
     210                        this._removeXPCOM(fullPath); 
     211                }else if(this._isAvailableActiveX()){ 
     212                        this._removeActiveX(fullPath); 
     213                } /*else if(this._isAvailableJava()){ 
     214                        this._removeJava(fullPath); 
     215                }*/ 
     216        }, 
     217         
     218        isPermanent: function(){ 
     219                return true; 
     220        }, 
     221 
     222        getMaximumSize: function(){ 
     223                return dojo.storage.SIZE_NO_LIMIT; 
     224        }, 
     225 
     226        hasSettingsUI: function(){ 
     227                return false; 
     228        }, 
     229         
     230        showSettingsUI: function(){ 
     231                dojo.raise(this.getType() + " does not support a storage settings user-interface"); 
     232        }, 
     233         
     234        hideSettingsUI: function(){ 
     235                dojo.raise(this.getType() + " does not support a storage settings user-interface"); 
     236        }, 
     237         
     238        getType: function(){ 
     239                return "dojo.storage.browser.FileStorageProvider"; 
     240        }, 
     241         
     242        _save: function(key, value, updateKeyIndex){ 
     243                if(typeof updateKeyIndex == "undefined"){ 
     244                        updateKeyIndex = true;   
     245                } 
     246                 
     247                // serialize the value; 
     248                // handle strings differently so they have better performance; 
     249                // add a comment at the top of the JSON so we can differentiate 
     250                // JSONed script blocks from a giant string for better 
     251                // performance 
     252                if(dojo.lang.isString(value) == false){ 
     253                        value = dojo.json.serialize(value); 
     254                        value = "/* JavaScript */\n" + value + "\n\n"; 
     255                } 
     256                 
     257                // the filename for this key/value entry 
     258                var fullPath = this._getPagePath() + key + ".txt"; 
     259                 
     260                if(this._isAvailableXPCOM()){ 
     261                        this._saveFileXPCOM(fullPath, value);    
     262                }else if(this._isAvailableActiveX()){ 
     263                        this._saveFileActiveX(fullPath, value); 
     264                } /*else if(this._isAvailableJava()){ 
     265                        this._saveFileJava(fullPath, value); 
     266                }*/ 
     267                 
     268                if(updateKeyIndex){ 
     269                        this._updateKeyIndex(key); 
     270                } 
     271        }, 
     272         
     273        _load: function(key){ 
     274                // the filename for this key/value entry 
     275                var fullPath = this._getPagePath() + key + ".txt"; 
     276                 
     277                var results = null; 
     278                if(this._isAvailableXPCOM()){ 
     279                        results = this._loadFileXPCOM(fullPath);         
     280                }else if(this._isAvailableActiveX()){ 
     281                        results = this._loadFileActiveX(fullPath); 
     282                }else if(this._isAvailableJava()){ 
     283                        results = this._loadFileJava(fullPath); 
     284                } 
     285                 
     286                if(results == null){ 
     287                        return null; 
     288                } 
     289                 
     290                // destringify the content back into a  
     291                // real JavaScript object; 
     292                // handle strings differently so they have better performance 
     293                if(!dojo.lang.isUndefined(results) && results != null  
     294                         && /^\/\* JavaScript \*\//.test(results)){ 
     295                        results = dojo.json.evalJson(results); 
     296                } 
     297                 
     298                return results; 
     299        }, 
     300         
     301        _updateKeyIndex: function(key){ 
     302                // reload the key index, in case someone 
     303                // has changed it by hand on disk 
     304                this._loadKeyIndex(); 
     305                 
     306                // now add our new key if it's not there yet 
     307                var alreadyAdded = false; 
     308                for(var i = 0; i < this._keyIndex.length; i++){ 
     309                        if(this._keyIndex[i] == key){ 
     310                                alreadyAdded = true; 
     311                                break; 
     312                        } 
     313                } 
     314                 
     315                if(alreadyAdded == false){ 
     316                        this._keyIndex[this._keyIndex.length] = key; 
     317                } 
     318                 
     319                this._save(dojo.storage.browser.FileStorageProvider._KEY_INDEX_FILENAME, 
     320                                        this._keyIndex, 
     321                                        false); 
     322        }, 
     323         
     324        _loadKeyIndex: function(){ 
     325                var indexContents = this._load( 
     326                                                                dojo.storage.browser.FileStorageProvider._KEY_INDEX_FILENAME); 
     327                 
     328                // turn the file from a JSONed array back into 
     329                // a real object 
     330                if(indexContents == null){ 
     331                        this._keyIndex = new Array(); 
     332                }else{ 
     333                        this._keyIndex = indexContents; 
     334                } 
     335        }, 
     336         
     337        _saveFileXPCOM: function(filename, value){ // Mozilla 
     338                try{ 
     339                        // indicate we are a privileged code block 
     340                        netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); 
     341                         
     342                        // get the file to work with 
     343                        var f = Components.classes["@mozilla.org/file/local;1"] 
     344                                                .createInstance(Components.interfaces.nsILocalFile); 
     345                        f.initWithPath(filename); 
     346                         
     347                        // open an output stream 
     348                        var ouputStream = Components.classes["@mozilla.org/network/file-output-stream;1"] 
     349                                                .createInstance(Components.interfaces.nsIFileOutputStream); 
     350                        // void init(nsIFile file, int ioFlags, int perm, int behaviorFlags) 
     351                        //              Arguments: 
     352                        //                      file: - file to write to 
     353                        //                  ioFlags: - file open flags  
     354                        //                              0x20 = PR_TRUNCATE - If the file exists, its length is truncated to 0 
     355                        //                              0x04 = R_RDWR - Read and Write access 
     356                        //                              0x08 = PR_CREATE_FILE - Create file if it doesn't exist 
     357                        //                  perm: - file mode bits (Applied on Unix only) 
     358                        //                              00400   Read by owner. 
     359                        //                              00200   Write by owner. 
     360                        //                  behaviorFlags: flags specifying various behaviors of the  
     361                        //                              class (currently none supported) 
     362                          
     363                        // FIXME: BUG: We don't work on Parallels when we are on Windows trying 
     364                        // to update a SMB share on a Mac; .allKeys.txt gets zeroed out 
     365                         
     366                        ouputStream.init(f, 0x20 | 0x04 | 0x08, 00400 + 00200, null); 
     367                        ouputStream.write(value, value.length); 
     368                        ouputStream.close(); 
     369                }catch(e){ 
     370                        var msg = e.toString(); 
     371                        if(e.name && e.message){ 
     372                                msg = e.name + ": " + e.message; 
     373                        } 
     374                        dojo.raise("dojo.storage.browser.FileStorageProvider._saveFileXPCOM(): " + msg); 
     375                } 
     376        }, 
     377         
     378        _loadFileXPCOM: function(filename){ // Mozilla 
     379                try{ 
     380                        // indicate we are a privileged code block 
     381                        netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); 
     382                 
     383                        // get the file to work with 
     384                        var f = Components.classes["@mozilla.org/file/local;1"] 
     385                                                .createInstance(Components.interfaces.nsILocalFile); 
     386                        f.initWithPath(filename); 
     387                        if(f.exists() == false){ 
     388                                return null; 
     389                        } 
     390                 
     391                        var inp = Components.classes["@mozilla.org/network/file-input-stream;1"] 
     392                                                .createInstance(Components.interfaces.nsIFileInputStream); 
     393                        // void init(nsIFile file, int ioFlags, int perm, int behaviorFlags) 
     394                        //      Arguments: 
     395                        //          file: file to read from 
     396                        //          ioFlags: file open flags 
     397                        //                      0x01 = PR_RDONLY - Open for reading only 
     398                        //          perm: file mode bits 
     399                        //                      00004 = Read by others (applied on UNIX only) 
     400                        //          behaviorFlags: flags specifying various behaviors of the class 
     401                        inp.init(f, 0x01, 00004, null); 
     402                        var inputStream = Components.classes["@mozilla.org/scriptableinputstream;1"] 
     403                                                                .createInstance(Components.interfaces.nsIScriptableInputStream); 
     404                        inputStream.init(inp); 
     405                        var results = inputStream.read(inputStream.available()); 
     406                        return results; 
     407                }catch(e){ 
     408                        var msg = e.toString(); 
     409                        if(e.name && e.message){ 
     410                                msg = e.name + ": " + e.message; 
     411                        } 
     412                        dojo.raise("dojo.storage.browser.FileStorageProvider._loadFileXPCOM(): " + msg); 
     413                } 
     414                 
     415                return null; 
     416        }, 
     417         
     418        _saveFileActiveX: function(filename, value){ // Internet Explorer 
     419                try{ 
     420                        var fileSystem = new ActiveXObject("Scripting.FileSystemObject"); 
     421                         
     422                        // object.OpenTextFile(filename[, iomode[, create[, format]]]) 
     423                        // 
     424                        //      Arguments 
     425                        //              filename - String expression that identifies the file to open. 
     426                        //              iomode - Can be one of three constants: ForReading (1),  
     427                        //                              ForWriting (2), or ForAppending (8). 
     428                        //              create - Boolean value that indicates whether a new file  
     429                        //                              can be created if the specified filename doesn't exist.  
     430                        //                              The value is True if a new file is created, False if it isn't created.  
     431                        //                              If omitted, a new file isn't created. 
     432                        //              format - One of three Tristate values used to indicate the format of the  
     433                        //              opened file. If omitted, the file is opened as ASCII.  
     434                         
     435                        var f = fileSystem.OpenTextFile(filename, 2 /* ForWriting */,  
     436                                                                                        true /* Create File */); 
     437                        f.Write(value); 
     438                        f.Close(); 
     439                }catch(e){ 
     440                        var msg = e.toString(); 
     441                        if(e.name && e.message){ 
     442                                msg = e.name + ": " + e.message; 
     443                        } 
     444                        dojo.raise("dojo.storage.browser.FileStorageProvider._saveFileActiveX(): " + msg); 
     445                } 
     446        }, 
     447         
     448        _loadFileActiveX: function(filename){ // Internet Explorer 
     449                try{ 
     450                        var fileSystem = new ActiveXObject("Scripting.FileSystemObject"); 
     451                         
     452                        if(fileSystem.FileExists(filename) == false){ 
     453                                return null; 
     454                        } 
     455                         
     456                        // object.OpenTextFile(filename[, iomode[, create[, format]]]) 
     457                        // 
     458                        //      Arguments 
     459                        //              filename - String expression that identifies the file to open. 
     460                        //              iomode - Can be one of three constants: ForReading (1),  
     461                        //                              ForWriting (2), or ForAppending (8). 
     462                        //              create - Boolean value that indicates whether a new file  
     463                        //                              can be created if the specified filename doesn't exist.  
     464                        //                              The value is True if a new file is created, False if it isn't created.  
     465                        //                              If omitted, a new file isn't created. 
     466                        //              format - One of three Tristate values used to indicate the format of the  
     467                        //              opened file. If omitted, the file is opened as ASCII.  
     468                         
     469                        var f = fileSystem.OpenTextFile(filename, 1 /* ForReading */); 
     470                        var results = f.ReadAll(); 
     471                        f.Close(); 
     472                         
     473                        return results; 
     474                }catch(e){ 
     475                        var msg = e.toString(); 
     476                        if(e.name && e.message){ 
     477                                msg = e.name + ": " + e.message; 
     478                        } 
     479                        dojo.raise("dojo.storage.browser.FileStorageProvider._loadFileActiveX(): " + msg); 
     480                } 
     481        }, 
     482         
     483        _saveFileJava: function(filename, value){ // Safari and Opera 
     484                //dojo.debug("saveFileJava, filename="+filename+", value="+value); 
     485                try{ 
     486                        var applet = dojo.byId(dojo.storage.browser.FileStorageProvider._APPLET_ID); 
     487                        applet.save(filename, value); 
     488                }catch(e){ 
     489                        var msg = e.toString(); 
     490                        if(e.name && e.message){ 
     491                                msg = e.name + ": " + e.message; 
     492                        } 
     493                        dojo.raise("dojo.storage.browser.FileStorageProvider._saveFileJava(): " + msg); 
     494                } 
     495        }, 
     496         
     497        _loadFileJava: function(filename){ // Safari and Opera 
     498                //dojo.debug("loadFileJava, filename="+filename); 
     499                try{ 
     500                        var applet = dojo.byId(dojo.storage.browser.FileStorageProvider._APPLET_ID); 
     501                        var results = applet.load(filename); 
     502                        return results; 
     503                }catch(e){ 
     504                        var msg = e.toString(); 
     505                        if(e.name && e.message){ 
     506                                msg = e.name + ": " + e.message; 
     507                        } 
     508                        dojo.raise("dojo.storage.browser.FileStorageProvider._loadFileJava(): " + msg); 
     509                } 
     510        }, 
     511         
     512        _isAvailableActiveX: function(){ // Internet Explorer 
     513                try{ 
     514                        if(window.ActiveXObject){ 
     515                                var fileSystem = new window.ActiveXObject("Scripting.FileSystemObject"); 
     516                                return true; 
     517                &n