All Blog Posts  |  Next Post  |  Previous Post

TMS WEB Core embraces ever more powerful PWA support from Google Chrome


Tuesday, November 24, 2020

There is no doubt that Google is and remains the driving force behind the fast evolving web world. And with Microsoft having adopted the Google Chromium engine in its latest and new default Windows 10 operating system browser, it is clear that the browser gets ever more powerful with each release.

While every Chrome update sees a lot of enhancements, the release 86 introduced perhaps another disruptive feature: the file system access API!

Yes, you read this correct, file system access API or in other words, access to the local file system from a PWA (Progressive Web App)! Of course, Google took the necessary measures to look over security. I can imagine you wouldn't want to be directed to some URL and the web application at this URL will suddenly start scanning your local hard drive.
All local file access remains initiated by user interaction and user consent!

User consent prompt to view files in a folder

What the File System Access API actually provides is:
  • open local text or binary files
  • save to a local text or binary files
  • use the operating system file dialog to pick a file
  • use the operating system file dialog to save to a file
  • get access to a folder and its files/subfolders
  • use the operating system to select a folder
  • associate file types with PWA apps

Now, you will understand that as we read the news, we were eager to investigate integration capabilities in a TMS WEB Core PWA and reflect on bringing easy to use Pascal language wrapper classes to take advantage of this new functionality. So, in our labs, we created a support unit for local file handling : WEBLib.LocalFiles. In this unit, three classes are available: TTextFile, TBinaryFile and TFolder.

Working with local text files

TTextFile offers following public interface

  TTextFile = class(TObject)
    procedure OpenFile; overload;
    procedure OpenFile(AOpenFile: TOpenTextFileProc); overload;
    procedure SaveFile; overload;
    procedure SaveFile(ASaveFile: TSaveFileProc); overload;
    procedure SaveAsFile; overload;
    procedure SaveAsFile(ASaveFile: TSaveFileProc); overload;
    property Text: string;
    property FileName: string;

    property OnFileOpen: TNotifyEvent;
    property OnFileSave: TNotifyEvent;

As you can see there are overloads for the OpenFile, SaveFile and SaveAsFile methods. In one version of the methods, there is an anonymouse method that is called when the action on the file completed and the other versioni will signal completion via an event.

To use the class to open a file, we can write:

  ATextFile := TTextFile.Create;
  ATextFile.OpenFile(procedure(AText: string)
      WebMemo1.Lines.Text := AText;      

If the text was modified in the memo control, it can be saved with following code working on the same instance of the TTextFile object:

  ATextFile.Text := WebMemo1.Lines.Text;
      ShowMessage('File succesfully saved');

As you can see, it becomes extremely easy for Pascal developers to take advantage of the file system access API in the browser!

Working with local binary files

For binary files, a similar class TBinaryFile was created with the important different from the TTextFile that here the data read or to be saved is of the type TJSArrayBuffer. TJSArrayBuffer is the Object Pascal wrapper for the JavaScript ArrayBuffer which basically is an array of bytes.

The interface of TBinaryFile is the same as TTextFile except that it exposes public property TBinaryFile.Data: TJSArrayBuffer.

To demonstrate its use, we can open a local image file with the following code:

  base64String: string;
  ABinaryFile.OpenFile(procedure(AValue: TJSArrayBuffer)
     base64string := ArrayBufferToBase64(AValue);
     base64string := 'data:image/jpeg;base64,'+base64String;
     WebImageControl1.URL := base64string;

As you can see, the image file binary data is received via the TJSArrayBuffer and we neeed to convert it to a base64 dataURL in order to visualize it in a TWebImageControl.

Saving an image is equally simple:

  ABinaryFile.Data := Base64ToArrayBuffer(WebImageControl1.Base64Image);

      ShowMessage('File succesfully saved');

Working with folders

Next is working with folders. The File system access API offers a way to show the operating system folder to select a folder. Once access is given after picking the folder, the API enables to retrieve all files and folders in the selected folder. As such, we can get access to all files in this folder or even open a subfolder and query all files in the subfolder. As there is folder access, we have also the capability to get access to all files in the folder.

The TFolder class has following public interface

  TFolder = aclass(TObject)
    procedure Open; overload;
    procedure Open(AOpenFolder: TOpenFolderProc); overload;
    procedure CreateFolder(AName: string);
    procedure CreateFile(AName: string);
    procedure GetFile(AName: string; GetFile: TGetFileProc);
    procedure GetFolder(AName: string; GetFolder: TGetFolderProc);
    property Files: TFileSystemFileHandleArray;
    property OnFolderOpen: TNotifyEvent read FOnFolderOpen write FOnFolderOpen;

So, to let the user select a folder and list all files in the folder, we could do:

  AFolder: TFolder;
  l: integer;
  AFolder := TFolder.Create;
      for l := 0 to Length(AFolder.Files) - 1 do

As a proof of concept, we created three PWA's with TMS WEB Core. One is a local folder browser, the other is a code editor you can use the edit local files (including .PAS files) and the third is a photo editor that allows you to take advantage of web technology to change hue, brightness, contrast, ... of pictures and save the modified version.

You can find the demos here

Folder file browser

Code editor

Image editor

Click image to see the image editor in action

We are further fine-tuning and polishing our easy to use Object Pascal wrappers for the file system API and you can expect this and way bigger things in the next major release of TMS WEB Core. Note that all this functionality will be possible from TMS WEB Core from Delphi, from Visual Studio Code and also from Lazarus. We are eager to learn what great ideas you have for PWAs developed with TMS WEB Core and hear your comments, feedback and wishes for further developments in the comments on this blog.

Bruno Fierens


This blog post has received 4 comments.

1. Wednesday, November 25, 2020 at 1:10:45 AM

This is an exciting new development. Especially on desktop, I think this will go a long way to make web apps feel more like traditional installed software.

It would be nice if the permission granted can extend indefinitely with a PWA. Maybe the service worker can do this for us one day.

What happens if one tries to use WEBLib.LocalFiles on an unsupported browser like FireFox? Should my app check for this and fall back to the old style of uploading and downloading files?

Pienaar Stephen

2. Wednesday, November 25, 2020 at 11:14:13 AM

As far as I see, the grant is at URL level and once granted, it appears to remain granted.
It will for now indeed be needed to check if this is Chromium engine based and offer an alternative way if not.

Bruno Fierens

3. Saturday, March 20, 2021 at 12:30:06 PM

Our Android and IOS apps are extensions of our ERP software, so they do not need the App Store or Google Play for marketing. PWA really seams to be the way to go for us to avoid irritating deployment delays.

Wikipedia says re technical baseline criteria that in order for an app to be considered a PWA by a browser the app must:
"Originate from a secure origin. Served over TLS and green padlock displays (no active mixed content)."

We currently sport only "grey" SSL padlocks and not having EV SSLs installed.

Is this correct?

Christen Morgan

4. Saturday, March 20, 2021 at 3:16:34 PM

I suggest to check your app with Lighthouse, meanwhile integrated in Google Chrome, this will tell you whether everything qualifies to be recognized as PWA or not

Bruno Fierens

Add a new comment

You will receive a confirmation mail with a link to validate your comment, please use a valid email address.
All fields are required.

All Blog Posts  |  Next Post  |  Previous Post