Search code examples
javascriptoffice-jsoutlook-web-addinsoffice-web-app

Is it possible to utilize sessionStorage on desktop outlook-js addins?


TL;DR

I use sessionStorage to communicate between different parts of the addin, code executed when pressing the ribbon button in the compose window, and a dialog which it opens. This works in browser, but on desktop outlook sessionStorage in the dialog is empty. What gives?

//TL;DR

I've built a js addin that works fine in the browser on the web, but is horribly broken on the desktop. I would prefer to disable it completely on the desktop since there's a VSTO addin that does much more then the js api is even capable of, but since i have not found a way to do that so, my only option now seems to be: make the js version work globally.

I found out the desktop is running IE of all "browsers" and I've gotten pretty far debugging the addin using IE on outlook.com, however i have run out of syntax errors and i'm now faced with a problem that only presents itself on desktop outlook.

The basic idea is: When the user launches this action, i want to create an object into sessionStorage unless it exists. The dialog will then read the object and present the user with a form, upon submit that object is updated, settings are applied and the dialog closes. For some reason on desktop outlook, when the dialog launches, sessionStorage for that key is null, whereas in the browser (even with IE) it is correctly initialized.

I've tried opening the dev console using F12Chooser, but since it comes in to play after the code i want to debug has already been run, i dont know what i can do to catch the dialog as it is executed.

code time:

the dialog button is defined in the manifest as

<ExtensionPoint xsi:type="MessageComposeCommandSurface">
...
    <Action xsi:type="ExecuteFunction">
        <FunctionName>showMessageDialog</FunctionName>
    </Action>

which runs:

function showMessageDialog(event) {
    addinLogic.initializeMessage().done(function(messageItem) {
        addinSessionStorage.setItem('CurrentAccount', Office.context.mailbox.userProfile.emailAddress);
        addinSessionStorage.setItem(addinLogic.CurrentMessageItems, messageItem);
        $.when(openDialogAsIframe('/dialogfile.html')).always(function(){ event.completed(); });
    });
}
function openDialogAsIframe(dialogPage) {
    var def = $.Deferred();
    Office.context.ui.displayDialogAsync(
        window.location.protocol + '//' + window.location.host + dialogPage,
        { height: 50, width: 75, displayInIframe: true }, dialogCallback.bind(def));
    return def.promise();
}

At this point sessionstorage should contain the object, however, when the dialog is launched, it returns telling me the messageItem object was not there.

(function () {
    var _messageItem = null;
    ...
    Office.onReady( function addinLevelsControllerInit(reason) {
        app.initialize();
        $(function () {
            ...
            _messageItem = addinSessionStorage.getItem(addinLogic.CurrentMessageItems);
            if (!_messageItem) {
                Office.context.ui.messageParent('messageItem isnt there!');
                return;
            }

addinSessionStorage.getItem basically does window.sessionStorage || window.opener.sessionStorage || parent.window.opener.sessionStorage

So far the only way to get any information has been to open the F12 window for something else and tell it to halt on any exception, which has given me like IE6 level of error messages without any ability to investigate the error state deeper. Painstakingly i've been able to narrow the issue down to here, and now the best way to debug further, that i can think of, is guess work and the return message through ui.messageParent. If only there was a way to redirect console.log to a file..

--

edit: So i've been getting somewhere even though i've not found any good debugging avenues. If i write this object into localStorage, the addin works fine, but as there is sensitive data being handled it is not an option to leave it on disk for all of eternity.

How then should i go about fixing this since it seems the ribbon button and the opened dialog are running in separate sessions from the point of view of Outlook/IE?


Solution

  • As sessionStorage is the only storage location that fulfills the requirements of auto clearing, up to date and shareable between addin functions; it is the only viable choice i know of.

    On desktop outlook however the sessions are not shared between all parts as IE, which outlook uses to run the js addins, behaves as if it is closed after each addin function has been executed, thus clearing sessionStorage.

    I believe i found the relevant pieces of documentation as well -- https://learn.microsoft.com/en-us/office/dev/add-ins/develop/dialog-api-in-office-add-ins#take-advantage-of-a-performance-option-in-office-online : "If the add-in is not running in Office Online, the displayInIframe is ignored." Which makes desktop outlook open my dialog in a separate window, thus https://learn.microsoft.com/en-us/office/dev/add-ins/develop/dialog-api-in-office-add-ins#use-the-office-dialog-api-with-single-page-applications-and-client-side-routing "Important! The dialog box is in a new window with its own execution context ... Similarly, the dialog window has its own session storage, which is not accessible from code in the task pane."

    As a result, until i find a better method of inter-add-in communication, I've had to resort to one of the ugliest hacks in a very long time:

    Office.onReady(function(){
        addinLocalStorage = new addinStorage('localStorage');
        try { addinLocalStorage.setItem('_useSessionStorage', Office.context.mailbox.diagnostics.hostName != 'Outlook'); 
        } catch (e) {}
    
        if (addinLocalStorage.getItem('_useSessionStorage')) {
            addinSessionStorage = new addinStorage('sessionStorage');
        } else {
            addinSessionStorage = new addinStorage('localStorage', 'ss_');
        }
        ...
    });
    
    

    Basically, the ribbon button has access to Office.context.mailbox, thus can set the platform into localStorage. The dialog does not have access to it, which btw is the whole reason why I'm forced to use storage for communication at all, it can read localStorage and based on it, decide whether or not we can use sessionStorage or not.

    Now all that remains is to figure out which objects i can delete from localStorage and which i should leave, so that the data wont remain on disk forever.

    Oh, and the short answer to the question in thread title: NO