Browse Source

feat(inIframe/isEmbedded) add ability to detect generic embedding

On web we detect if we run on an iframe, and on mobile we detect if the
app is one of ours.
j25
Saúl Ibarra Corretgé 7 months ago
parent
commit
1e8cc9d085

+ 2
- 2
index.html View File

@@ -36,7 +36,7 @@
36 36
                 Component: JitsiMeetJS.app.entryPoints.APP
37 37
             })
38 38
 
39
-            const inIframe = () => {
39
+            const isEmbedded = () => {
40 40
                 try {
41 41
                     return window.self !== window.top;
42 42
                 } catch (e) {
@@ -45,7 +45,7 @@
45 45
             };
46 46
 
47 47
             const isElectron = navigator.userAgent.includes('Electron');
48
-            const shouldRegisterWorker = !isElectron && !inIframe() && 'serviceWorker' in navigator;
48
+            const shouldRegisterWorker = !isElectron && !isEmbedded() && 'serviceWorker' in navigator;
49 49
 
50 50
             if (shouldRegisterWorker) {
51 51
                 navigator.serviceWorker

+ 2
- 2
react/features/analytics/functions.ts View File

@@ -12,8 +12,8 @@ import JitsiMeetJS, {
12 12
     browser
13 13
 } from '../base/lib-jitsi-meet';
14 14
 import { isAnalyticsEnabled } from '../base/lib-jitsi-meet/functions.any';
15
+import { isEmbedded } from '../base/util/embedUtils';
15 16
 import { getJitsiMeetGlobalNS } from '../base/util/helpers';
16
-import { inIframe } from '../base/util/iframeUtils';
17 17
 import { loadScript } from '../base/util/loadScript';
18 18
 import { parseURLParams } from '../base/util/parseURLParams';
19 19
 import { parseURIString } from '../base/util/uri';
@@ -213,7 +213,7 @@ export function initAnalytics(store: IStore, handlers: Array<Object>): boolean {
213 213
     permanentProperties.externalApi = typeof API_ID === 'number';
214 214
 
215 215
     // Report if we are loaded in iframe
216
-    permanentProperties.inIframe = inIframe();
216
+    permanentProperties.inIframe = isEmbedded();
217 217
 
218 218
     // Report the tenant from the URL.
219 219
     permanentProperties.tenant = tenant || '/';

+ 2
- 2
react/features/app/actions.web.ts View File

@@ -7,7 +7,7 @@ import {
7 7
 } from '../base/config/actions';
8 8
 import { setLocationURL } from '../base/connection/actions.web';
9 9
 import { loadConfig } from '../base/lib-jitsi-meet/functions.web';
10
-import { inIframe } from '../base/util/iframeUtils';
10
+import { isEmbedded } from '../base/util/embedUtils';
11 11
 import { parseURIString } from '../base/util/uri';
12 12
 import { isVpaasMeeting } from '../jaas/functions';
13 13
 import { clearNotifications, showNotification } from '../notifications/actions';
@@ -102,7 +102,7 @@ export function maybeRedirectToWelcomePage(options: { feedbackSubmitted?: boolea
102 102
         // if close page is enabled redirect to it, without further action
103 103
         if (enableClosePage) {
104 104
             if (isVpaasMeeting(getState())) {
105
-                const isOpenedInIframe = inIframe();
105
+                const isOpenedInIframe = isEmbedded();
106 106
 
107 107
                 if (isOpenedInIframe) {
108 108
                     // @ts-ignore

+ 2
- 1
react/features/app/components/App.native.tsx View File

@@ -12,6 +12,7 @@ import { clientResized, setSafeAreaInsets } from '../../base/responsive-ui/actio
12 12
 import DimensionsDetector from '../../base/responsive-ui/components/DimensionsDetector.native';
13 13
 import { updateSettings } from '../../base/settings/actions';
14 14
 import JitsiThemePaperProvider from '../../base/ui/components/JitsiThemeProvider.native';
15
+import { isEmbedded } from '../../base/util/embedUtils.native';
15 16
 import { _getRouteToRender } from '../getRouteToRender.native';
16 17
 import logger from '../logger';
17 18
 
@@ -87,7 +88,7 @@ export class App extends AbstractApp<IProps> {
87 88
 
88 89
         const liteTxt = AppInfo.isLiteSDK ? ' (lite)' : '';
89 90
 
90
-        logger.info(`Loaded SDK ${AppInfo.sdkVersion}${liteTxt}`);
91
+        logger.info(`Loaded SDK ${AppInfo.sdkVersion}${liteTxt} isEmbedded=${isEmbedded()}`);
91 92
     }
92 93
 
93 94
     /**

+ 2
- 2
react/features/app/middleware.ts View File

@@ -7,7 +7,7 @@ import { SET_ROOM } from '../base/conference/actionTypes';
7 7
 import { CONNECTION_ESTABLISHED, CONNECTION_FAILED } from '../base/connection/actionTypes';
8 8
 import { getURLWithoutParams } from '../base/connection/utils';
9 9
 import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
10
-import { inIframe } from '../base/util/iframeUtils';
10
+import { isEmbedded } from '../base/util/embedUtils';
11 11
 
12 12
 import { reloadNow } from './actions';
13 13
 import { _getRouteToRender } from './getRouteToRender';
@@ -52,7 +52,7 @@ function _connectionEstablished(store: IStore, next: Function, action: AnyAction
52 52
     // @ts-ignore
53 53
     const { history, location } = window;
54 54
 
55
-    if (inIframe()) {
55
+    if (isEmbedded()) {
56 56
         return;
57 57
     }
58 58
 

+ 2
- 2
react/features/base/app/middleware.web.ts View File

@@ -1,7 +1,7 @@
1 1
 import { AnyAction } from 'redux';
2 2
 
3 3
 import MiddlewareRegistry from '../redux/MiddlewareRegistry';
4
-import { inIframe } from '../util/iframeUtils';
4
+import { isEmbedded } from '../util/embedUtils';
5 5
 
6 6
 import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from './actionTypes';
7 7
 import logger from './logger';
@@ -24,7 +24,7 @@ MiddlewareRegistry.register(() => (next: Function) => (action: AnyAction) => {
24 24
     case APP_WILL_MOUNT: {
25 25
         // Disable it inside an iframe until Google fixes the origin trial for 3rd party sources:
26 26
         // https://bugs.chromium.org/p/chromium/issues/detail?id=1504167
27
-        if (!inIframe() && 'PressureObserver' in globalThis) {
27
+        if (!isEmbedded() && 'PressureObserver' in globalThis) {
28 28
             pressureObserver = new window.PressureObserver(
29 29
                     (records: typeof window.PressureRecord) => {
30 30
                         logger.info('Compute pressure state changed:', JSON.stringify(records));

+ 3
- 3
react/features/base/config/configWhitelist.ts View File

@@ -1,7 +1,7 @@
1
-import { inIframe } from '../util/iframeUtils';
1
+import { isEmbedded } from '../util/embedUtils';
2 2
 
3 3
 import extraConfigWhitelist from './extraConfigWhitelist';
4
-import inIframeConfigWhitelist from './inIframeConfigWhitelist';
4
+import isEmbeddedConfigWhitelist from './isEmbeddedConfigWhitelist';
5 5
 
6 6
 /**
7 7
  * The config keys to whitelist, the keys that can be overridden.
@@ -249,4 +249,4 @@ export default [
249 249
     'webrtcIceTcpDisable',
250 250
     'webrtcIceUdpDisable',
251 251
     'whiteboard.enabled'
252
-].concat(extraConfigWhitelist).concat(inIframe() ? inIframeConfigWhitelist : []);
252
+].concat(extraConfigWhitelist).concat(isEmbedded() ? isEmbeddedConfigWhitelist : []);

+ 0
- 4
react/features/base/config/inIframeConfigWhitelist.ts View File

@@ -1,4 +0,0 @@
1
-/**
2
- * Additional config whitelist extending the original whitelist in the case where jitsi-meet is loaded in an iframe.
3
- */
4
-export default [];

+ 0
- 5
react/features/base/config/inIframeInterfaceConfigWhitelist.ts View File

@@ -1,5 +0,0 @@
1
-/**
2
- * Additional interface config whitelist extending the original whitelist in the case where jitsi-meet is loaded in an
3
- * iframe.
4
- */
5
-export default [];

+ 3
- 3
react/features/base/config/interfaceConfigWhitelist.ts View File

@@ -1,7 +1,7 @@
1
-import { inIframe } from '../util/iframeUtils';
1
+import { isEmbedded } from '../util/embedUtils';
2 2
 
3 3
 import extraInterfaceConfigWhitelistCopy from './extraInterfaceConfigWhitelist';
4
-import inIframeInterfaceConfigWhitelist from './inIframeInterfaceConfigWhitelist';
4
+import isEmbeddedInterfaceConfigWhitelist from './isEmbeddedInterfaceConfigWhitelist';
5 5
 
6 6
 /**
7 7
  * The interface config keys to whitelist, the keys that can be overridden.
@@ -54,4 +54,4 @@ export default [
54 54
     'VERTICAL_FILMSTRIP',
55 55
     'VIDEO_LAYOUT_FIT',
56 56
     'VIDEO_QUALITY_LABEL_DISABLED'
57
-].concat(extraInterfaceConfigWhitelistCopy).concat(inIframe() ? inIframeInterfaceConfigWhitelist : []);
57
+].concat(extraInterfaceConfigWhitelistCopy).concat(isEmbedded() ? isEmbeddedInterfaceConfigWhitelist : []);

+ 11
- 0
react/features/base/config/isEmbeddedConfigWhitelist.ts View File

@@ -0,0 +1,11 @@
1
+/**
2
+ * Additional config whitelist extending the original whitelist applied when Jitsi Meet is embedded
3
+ * in another app be that with an iframe or a mobile SDK.
4
+ */
5
+export default [
6
+    'customToolbarButtons',
7
+    'defaultLogoUrl',
8
+    'deploymentUrls',
9
+    'liveStreaming',
10
+    'salesforceUrl'
11
+];

+ 6
- 0
react/features/base/config/isEmbeddedInterfaceConfigWhitelist.ts View File

@@ -0,0 +1,6 @@
1
+/**
2
+ * Additional interface config whitelist extending the original whitelist applied when Jitsi Meet is embedded
3
+ * in another app be that with an iframe or a mobile SDK.
4
+ */
5
+export default [
6
+];

+ 2
- 2
react/features/base/connection/actions.any.ts View File

@@ -5,7 +5,7 @@ import { conferenceLeft, conferenceWillLeave, redirect } from '../conference/act
5 5
 import { getCurrentConference } from '../conference/functions';
6 6
 import { IConfigState } from '../config/reducer';
7 7
 import JitsiMeetJS, { JitsiConnectionEvents } from '../lib-jitsi-meet';
8
-import { inIframe } from '../util/iframeUtils';
8
+import { isEmbedded } from '../util/embedUtils';
9 9
 import { parseURLParams } from '../util/parseURLParams';
10 10
 import {
11 11
     appendURLParam,
@@ -121,7 +121,7 @@ export function constructOptions(state: IReduxState) {
121 121
     const iceServersOverride = params['iceServers.replace'];
122 122
 
123 123
     // Allow iceServersOverride only when jitsi-meet is in an iframe.
124
-    if (inIframe() && iceServersOverride) {
124
+    if (isEmbedded() && iceServersOverride) {
125 125
         options.iceServersOverride = iceServersOverride;
126 126
     }
127 127
 

+ 2
- 2
react/features/base/jitsi-local-storage/setup.web.ts View File

@@ -5,7 +5,7 @@ import { jitsiLocalStorage } from '@jitsi/js-utils/jitsi-local-storage';
5 5
 import { safeJsonParse } from '@jitsi/js-utils/json';
6 6
 
7 7
 import { browser } from '../lib-jitsi-meet';
8
-import { inIframe } from '../util/iframeUtils';
8
+import { isEmbedded } from '../util/embedUtils';
9 9
 import { parseURLParams } from '../util/parseURLParams';
10 10
 
11 11
 import logger from './logger';
@@ -41,7 +41,7 @@ function shouldUseHostPageLocalStorage(urlParams: { 'config.useHostPageLocalStor
41 41
         return true;
42 42
     }
43 43
 
44
-    if (browser.isWebKitBased() && inIframe()) {
44
+    if (browser.isWebKitBased() && isEmbedded()) {
45 45
         // WebKit browsers don't persist local storage for third-party iframes.
46 46
 
47 47
         return true;

+ 32
- 0
react/features/base/util/embedUtils.native.ts View File

@@ -0,0 +1,32 @@
1
+import { getBundleId } from 'react-native-device-info';
2
+
3
+/**
4
+ * BUndle ids for the Jitsi Meet apps.
5
+ */
6
+const JITSI_MEET_APPS = [
7
+
8
+    // iOS app.
9
+    'com.atlassian.JitsiMeet.ios',
10
+
11
+    // Android + iOS (testing) app.
12
+    'org.jitsi.meet',
13
+
14
+    // Android debug app.
15
+    'org.jitsi.meet.debug',
16
+
17
+    // 8x8 Work (Android).
18
+    'org.vom8x8.sipua',
19
+
20
+    // 8x8 Work (iOS).
21
+    'com.yourcompany.Virtual-Office'
22
+];
23
+
24
+/**
25
+ * Checks whether we are loaded in iframe. In the mobile case we treat SDK
26
+ * consumers as the web treats iframes.
27
+ *
28
+ * @returns {boolean} Whether the current app is a Jitsi Meet app.
29
+ */
30
+export function isEmbedded(): boolean {
31
+    return !JITSI_MEET_APPS.includes(getBundleId());
32
+}

react/features/base/util/iframeUtils.ts → react/features/base/util/embedUtils.web.ts View File

@@ -3,11 +3,7 @@
3 3
  *
4 4
  * @returns {boolean} Whether the current page is loaded in an iframe.
5 5
  */
6
-export function inIframe(): boolean {
7
-    if (navigator.product === 'ReactNative') {
8
-        return false;
9
-    }
10
-
6
+export function isEmbedded(): boolean {
11 7
     try {
12 8
         return window.self !== window.top;
13 9
     } catch (e) {

+ 4
- 3
react/features/conference/middleware.any.ts View File

@@ -27,7 +27,7 @@ import StateListenerRegistry from '../base/redux/StateListenerRegistry';
27 27
 import { SET_REDUCED_UI } from '../base/responsive-ui/actionTypes';
28 28
 import { LOWER_HAND_MESSAGE } from '../base/tracks/constants';
29 29
 import { BUTTON_TYPES } from '../base/ui/constants.any';
30
-import { inIframe } from '../base/util/iframeUtils';
30
+import { isEmbedded } from '../base/util/embedUtils';
31 31
 import { isCalendarEnabled } from '../calendar-sync/functions';
32 32
 import FeedbackDialog from '../feedback/components/FeedbackDialog';
33 33
 import { setFilmstripEnabled } from '../filmstrip/actions.any';
@@ -191,8 +191,9 @@ function _checkIframe(state: IReduxState, dispatch: IStore['dispatch']) {
191 191
         }
192 192
     }
193 193
 
194
-    if (inIframe() && state['features/base/config'].disableIframeAPI && !browser.isElectron()
195
-        && !isVpaasMeeting(state) && !allowIframe) {
194
+    // TODO: enable for mobile too?
195
+    if (isEmbedded() && state['features/base/config'].disableIframeAPI && !browser.isElectron()
196
+            && !browser.isReactNative() && !isVpaasMeeting(state) && !allowIframe) {
196 197
         // show sticky notification and redirect in 5 minutes
197 198
         const { locationURL } = state['features/base/connection'];
198 199
         let translationKey = 'notify.disabledIframe';

+ 3
- 3
react/features/recent-list/middleware.ts View File

@@ -6,7 +6,7 @@ import { CONFERENCE_WILL_LEAVE, SET_ROOM } from '../base/conference/actionTypes'
6 6
 import { JITSI_CONFERENCE_URL_KEY } from '../base/conference/constants';
7 7
 import { addKnownDomains } from '../base/known-domains/actions';
8 8
 import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
9
-import { inIframe } from '../base/util/iframeUtils';
9
+import { isEmbedded } from '../base/util/embedUtils';
10 10
 import { parseURIString } from '../base/util/uri';
11 11
 
12 12
 import { _storeCurrentConference, _updateConferenceDuration } from './actions';
@@ -86,7 +86,7 @@ function _conferenceWillLeave({ dispatch, getState }: IStore, next: Function, ac
86 86
     const state = getState();
87 87
     const { doNotStoreRoom } = state['features/base/config'];
88 88
 
89
-    if (!doNotStoreRoom && !inIframe()) {
89
+    if (!doNotStoreRoom && !isEmbedded()) {
90 90
         let locationURL;
91 91
 
92 92
         /**
@@ -130,7 +130,7 @@ function _conferenceWillLeave({ dispatch, getState }: IStore, next: Function, ac
130 130
 function _setRoom({ dispatch, getState }: IStore, next: Function, action: AnyAction) {
131 131
     const { doNotStoreRoom } = getState()['features/base/config'];
132 132
 
133
-    if (!doNotStoreRoom && !inIframe() && action.room) {
133
+    if (!doNotStoreRoom && !isEmbedded() && action.room) {
134 134
         const { locationURL } = getState()['features/base/connection'];
135 135
 
136 136
         if (locationURL) {

+ 2
- 2
react/features/recording/components/Recording/LocalRecordingManager.web.ts View File

@@ -6,7 +6,7 @@ import { IStore } from '../../../app/types';
6 6
 import { getRoomName } from '../../../base/conference/functions';
7 7
 import { MEDIA_TYPE } from '../../../base/media/constants';
8 8
 import { getLocalTrack, getTrackState } from '../../../base/tracks/functions';
9
-import { inIframe } from '../../../base/util/iframeUtils';
9
+import { isEmbedded } from '../../../base/util/embedUtils';
10 10
 import { stopLocalVideoRecording } from '../../actions.any';
11 11
 
12 12
 interface ISelfRecording {
@@ -180,7 +180,7 @@ const LocalRecordingManager: ILocalRecordingManager = {
180 180
         const { dispatch, getState } = store;
181 181
 
182 182
         // @ts-ignore
183
-        const supportsCaptureHandle = Boolean(navigator.mediaDevices.setCaptureHandleConfig) && !inIframe();
183
+        const supportsCaptureHandle = Boolean(navigator.mediaDevices.setCaptureHandleConfig) && !isEmbedded();
184 184
         const tabId = uuidV4();
185 185
 
186 186
         this.selfRecording.on = onlySelf;

Loading…
Cancel
Save