Procházet zdrojové kódy

Merge pull request #255 from AtkinsSJ/appconnection-close

Notify apps when a child app closes, and let them close their children manually
Eric Dubé před 1 rokem
rodič
revize
92dc0a7671
3 změnil soubory, kde provedl 77 přidání a 6 odebrání
  1. 46 5
      src/IPC.js
  2. 27 1
      src/UI/UIWindow.js
  3. 4 0
      src/helpers.js

+ 46 - 5
src/IPC.js

@@ -74,11 +74,15 @@ window.addEventListener('message', async (event) => {
         return;
     }
 
-    const iframe_for_app_instance = (instanceID) => {
-        return $(`.window[data-element_uuid="${instanceID}"]`).find('.window-app-iframe').get(0)
+    const window_for_app_instance = (instance_id) => {
+        return $(`.window[data-element_uuid="${instance_id}"]`).get(0);
     };
 
-    const $el_parent_window = $(`.window[data-element_uuid="${event.data.appInstanceID}"]`);
+    const iframe_for_app_instance = (instance_id) => {
+        return $(window_for_app_instance(instance_id)).find('.window-app-iframe').get(0);
+    };
+
+    const $el_parent_window = $(window_for_app_instance(event.data.appInstanceID));
     const parent_window_id = $el_parent_window.attr('data-id');
     const $el_parent_disable_mask = $el_parent_window.find('.window-disable-mask');
     const target_iframe = iframe_for_app_instance(event.data.appInstanceID);
@@ -357,7 +361,7 @@ window.addEventListener('message', async (event) => {
     // setWindowTitle
     //--------------------------------------------------------
     else if(event.data.msg === 'setWindowTitle' && event.data.new_title !== undefined){
-        const el_window = $(`.window[data-element_uuid="${event.data.appInstanceID}"]`).get(0);
+        const el_window = window_for_app_instance(event.data.appInstanceID);
         // set window title
         $(el_window).find(`.window-head-title`).html(html_encode(event.data.new_title));
         // send confirmation to requester window
@@ -1099,11 +1103,48 @@ window.addEventListener('message', async (event) => {
             contents,
         }, targetAppOrigin);
     }
+    //--------------------------------------------------------
+    // closeApp
+    //--------------------------------------------------------
+    else if (event.data.msg === 'closeApp') {
+        const { appInstanceID, targetAppInstanceID } = event.data;
+
+        const target_window = window_for_app_instance(targetAppInstanceID);
+        if (!target_window) {
+            console.warn(`Failed to close non-existent app ${targetAppInstanceID}`);
+            return;
+        }
+
+        // Check permissions
+        const allowed = (() => {
+            // Parents can close their children
+            if (target_window.dataset['parent_instance_id']) {
+                console.log(`⚠️ Allowing app ${appInstanceID} to close child app ${targetAppInstanceID}`);
+                return true;
+            }
+
+            // God-mode apps can close anything
+            const app_info = await get_apps(app_name);
+            if (app_info.godmode === 1) {
+                console.log(`⚠️ Allowing GODMODE app ${appInstanceID} to close app ${targetAppInstanceID}`);
+                return true;
+            }
+
+            // TODO: What other situations should we allow?
+            return false;
+        })();
+
+        if (allowed) {
+            $(target_window).close();
+        } else {
+            console.warn(`⚠️ App ${appInstanceID} is not permitted to close app ${targetAppInstanceID}`);
+        }
+    }
 
     //--------------------------------------------------------
     // exit
     //--------------------------------------------------------
     else if(event.data.msg === 'exit'){
-        $(`.window[data-element_uuid="${event.data.appInstanceID}"]`).close({bypass_iframe_messaging: true});
+        $(window_for_app_instance(event.data.appInstanceID)).close({bypass_iframe_messaging: true});
     }
 });

+ 27 - 1
src/UI/UIWindow.js

@@ -185,6 +185,7 @@ async function UIWindow(options) {
                 data-uid ="${options.uid}"
                 data-element_uuid="${options.element_uuid}"
                 data-parent_uuid="${options.parent_uuid}"
+                ${options.parent_instance_id ? `data-parent_instance_id="${options.parent_instance_id}"` : ''}
                 data-id ="${win_id}"
                 data-iframe_msg_uid ="${options.iframe_msg_uid}"
                 data-is_dir ="${options.is_dir}"
@@ -2745,8 +2746,9 @@ $.fn.close = async function(options) {
     options = options || {};
     $(this).each(async function() {
         const el_iframe = $(this).find('.window-app-iframe');
+        const app_uses_sdk = el_iframe.length > 0 && el_iframe.attr('data-appUsesSDK') === 'true';
         // tell child app that this window is about to close, get its response
-        if(el_iframe.length > 0 && el_iframe.attr('data-appUsesSDK') === 'true'){
+        if(app_uses_sdk){
             if(!options.bypass_iframe_messaging){
                 const resp = await sendWindowWillCloseMsg(el_iframe.get(0));
                 if(!resp.msg){
@@ -2819,6 +2821,30 @@ $.fn.close = async function(options) {
             }
             // close child windows
             $(`.window[data-parent_uuid="${window_uuid}"]`).close();
+
+            // notify other apps that we're closing
+            if (app_uses_sdk) {
+                // notify parent app, if we have one, that we're closing
+                const parent_id = this.dataset['parent_instance_id'];
+                const parent = $(`.window[data-element_uuid="${parent_id}"] .window-app-iframe`).get(0);
+                if (parent) {
+                    parent.contentWindow.postMessage({
+                        msg: 'appClosed',
+                        appInstanceID: window_uuid,
+                    }, '*');
+                }
+
+                // notify child apps, if we have them, that we're closing
+                const children = $(`.window[data-parent_instance_id="${window_uuid}"] .window-app-iframe`);
+                children.each((_, child) => {
+                    child.contentWindow.postMessage({
+                        msg: 'appClosed',
+                        appInstanceID: window_uuid,
+                    }, '*');
+                });
+                // TODO: Once other AppConnections exist, those will need notifying too.
+            }
+
             // remove backdrop
             $(this).closest('.window-backdrop').remove();
             // remove DOM element

+ 4 - 0
src/helpers.js

@@ -1860,6 +1860,10 @@ window.launch_app = async (options)=>{
     let icon, title, file_signature;
     const window_options = options.window_options ?? {};
 
+    if (options.parent_instance_id) {
+        window_options.parent_instance_id = options.parent_instance_id;
+    }
+
     // try to get 3rd-party app info
     let app_info = options.app_obj ?? await get_apps(options.name);