Skip to content

Commit affcb0a

Browse files
author
Alessandro Borile
committed
catch context close if framenavigated
1 parent c018e2d commit affcb0a

File tree

1 file changed

+152
-138
lines changed

1 file changed

+152
-138
lines changed

src/Client.js

Lines changed: 152 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -93,168 +93,182 @@ class Client extends EventEmitter {
9393
* Private function
9494
*/
9595
async inject() {
96-
await this.pupPage.waitForFunction('window.Debug?.VERSION != undefined', {timeout: this.options.authTimeoutMs});
96+
let hasReloaded = false;
97+
const reloadHandler = async () => {
98+
hasReloaded = true;
99+
};
100+
try{
101+
this.pupPage.on('framenavigated', reloadHandler);
97102

98-
const version = await this.getWWebVersion();
99-
const isCometOrAbove = parseInt(version.split('.')?.[1]) >= 3000;
103+
await this.pupPage.waitForFunction('window.Debug?.VERSION != undefined', {timeout: this.options.authTimeoutMs});
100104

101-
if (isCometOrAbove) {
102-
await this.pupPage.evaluate(ExposeAuthStore);
103-
} else {
104-
await this.pupPage.evaluate(ExposeLegacyAuthStore, moduleRaid.toString());
105-
}
105+
const version = await this.getWWebVersion();
106+
const isCometOrAbove = parseInt(version.split('.')?.[1]) >= 3000;
106107

107-
const needAuthentication = await this.pupPage.evaluate(async () => {
108-
let state = window.AuthStore.AppState.state;
109-
110-
if (state === 'OPENING' || state === 'UNLAUNCHED' || state === 'PAIRING') {
111-
// wait till state changes
112-
await new Promise(r => {
113-
window.AuthStore.AppState.on('change:state', function waitTillInit(_AppState, state) {
114-
if (state !== 'OPENING' && state !== 'UNLAUNCHED' && state !== 'PAIRING') {
115-
window.AuthStore.AppState.off('change:state', waitTillInit);
116-
r();
117-
}
118-
});
119-
});
108+
if (isCometOrAbove) {
109+
await this.pupPage.evaluate(ExposeAuthStore);
110+
} else {
111+
await this.pupPage.evaluate(ExposeLegacyAuthStore, moduleRaid.toString());
120112
}
121-
state = window.AuthStore.AppState.state;
122-
return state == 'UNPAIRED' || state == 'UNPAIRED_IDLE';
123-
});
124113

125-
if (needAuthentication) {
126-
const { failed, failureEventPayload, restart } = await this.authStrategy.onAuthenticationNeeded();
114+
const needAuthentication = await this.pupPage.evaluate(async () => {
115+
let state = window.AuthStore.AppState.state;
116+
117+
if (state === 'OPENING' || state === 'UNLAUNCHED' || state === 'PAIRING') {
118+
// wait till state changes
119+
await new Promise(r => {
120+
window.AuthStore.AppState.on('change:state', function waitTillInit(_AppState, state) {
121+
if (state !== 'OPENING' && state !== 'UNLAUNCHED' && state !== 'PAIRING') {
122+
window.AuthStore.AppState.off('change:state', waitTillInit);
123+
r();
124+
}
125+
});
126+
});
127+
}
128+
state = window.AuthStore.AppState.state;
129+
return state == 'UNPAIRED' || state == 'UNPAIRED_IDLE';
130+
});
127131

128-
if(failed) {
129-
/**
130-
* Emitted when there has been an error while trying to restore an existing session
131-
* @event Client#auth_failure
132-
* @param {string} message
133-
*/
134-
this.emit(Events.AUTHENTICATION_FAILURE, failureEventPayload);
135-
await this.destroy();
136-
if (restart) {
137-
// session restore failed so try again but without session to force new authentication
138-
return this.initialize();
132+
if (needAuthentication) {
133+
const { failed, failureEventPayload, restart } = await this.authStrategy.onAuthenticationNeeded();
134+
135+
if(failed) {
136+
/**
137+
* Emitted when there has been an error while trying to restore an existing session
138+
* @event Client#auth_failure
139+
* @param {string} message
140+
*/
141+
this.emit(Events.AUTHENTICATION_FAILURE, failureEventPayload);
142+
await this.destroy();
143+
if (restart) {
144+
// session restore failed so try again but without session to force new authentication
145+
return this.initialize();
146+
}
147+
return;
139148
}
140-
return;
141-
}
142149

143-
// Register qr events
144-
let qrRetries = 0;
145-
await exposeFunctionIfAbsent(this.pupPage, 'onQRChangedEvent', async (qr) => {
146-
/**
147-
* Emitted when a QR code is received
148-
* @event Client#qr
149-
* @param {string} qr QR Code
150-
*/
151-
this.emit(Events.QR_RECEIVED, qr);
152-
if (this.options.qrMaxRetries > 0) {
153-
qrRetries++;
154-
if (qrRetries > this.options.qrMaxRetries) {
155-
this.emit(Events.DISCONNECTED, 'Max qrcode retries reached');
156-
await this.destroy();
150+
// Register qr events
151+
let qrRetries = 0;
152+
await exposeFunctionIfAbsent(this.pupPage, 'onQRChangedEvent', async (qr) => {
153+
/**
154+
* Emitted when a QR code is received
155+
* @event Client#qr
156+
* @param {string} qr QR Code
157+
*/
158+
this.emit(Events.QR_RECEIVED, qr);
159+
if (this.options.qrMaxRetries > 0) {
160+
qrRetries++;
161+
if (qrRetries > this.options.qrMaxRetries) {
162+
this.emit(Events.DISCONNECTED, 'Max qrcode retries reached');
163+
await this.destroy();
164+
}
157165
}
166+
});
167+
168+
169+
await this.pupPage.evaluate(async () => {
170+
const registrationInfo = await window.AuthStore.RegistrationUtils.waSignalStore.getRegistrationInfo();
171+
const noiseKeyPair = await window.AuthStore.RegistrationUtils.waNoiseInfo.get();
172+
const staticKeyB64 = window.AuthStore.Base64Tools.encodeB64(noiseKeyPair.staticKeyPair.pubKey);
173+
const identityKeyB64 = window.AuthStore.Base64Tools.encodeB64(registrationInfo.identityKeyPair.pubKey);
174+
const advSecretKey = await window.AuthStore.RegistrationUtils.getADVSecretKey();
175+
const platform = window.AuthStore.RegistrationUtils.DEVICE_PLATFORM;
176+
const getQR = (ref) => ref + ',' + staticKeyB64 + ',' + identityKeyB64 + ',' + advSecretKey + ',' + platform;
177+
178+
window.onQRChangedEvent(getQR(window.AuthStore.Conn.ref)); // initial qr
179+
window.AuthStore.Conn.on('change:ref', (_, ref) => { window.onQRChangedEvent(getQR(ref)); }); // future QR changes
180+
});
181+
}
182+
183+
await exposeFunctionIfAbsent(this.pupPage, 'onAuthAppStateChangedEvent', async (state) => {
184+
if (state == 'UNPAIRED_IDLE') {
185+
// refresh qr code
186+
window.Store.Cmd.refreshQR();
158187
}
159188
});
160189

190+
await exposeFunctionIfAbsent(this.pupPage, 'onAppStateHasSyncedEvent', async () => {
191+
const authEventPayload = await this.authStrategy.getAuthEventPayload();
192+
/**
193+
* Emitted when authentication is successful
194+
* @event Client#authenticated
195+
*/
196+
this.emit(Events.AUTHENTICATED, authEventPayload);
161197

162-
await this.pupPage.evaluate(async () => {
163-
const registrationInfo = await window.AuthStore.RegistrationUtils.waSignalStore.getRegistrationInfo();
164-
const noiseKeyPair = await window.AuthStore.RegistrationUtils.waNoiseInfo.get();
165-
const staticKeyB64 = window.AuthStore.Base64Tools.encodeB64(noiseKeyPair.staticKeyPair.pubKey);
166-
const identityKeyB64 = window.AuthStore.Base64Tools.encodeB64(registrationInfo.identityKeyPair.pubKey);
167-
const advSecretKey = await window.AuthStore.RegistrationUtils.getADVSecretKey();
168-
const platform = window.AuthStore.RegistrationUtils.DEVICE_PLATFORM;
169-
const getQR = (ref) => ref + ',' + staticKeyB64 + ',' + identityKeyB64 + ',' + advSecretKey + ',' + platform;
198+
const injected = await this.pupPage.evaluate(async () => {
199+
return typeof window.Store !== 'undefined' && typeof window.WWebJS !== 'undefined';
200+
});
201+
202+
if (!injected) {
203+
if (this.options.webVersionCache.type === 'local' && this.currentIndexHtml) {
204+
const { type: webCacheType, ...webCacheOptions } = this.options.webVersionCache;
205+
const webCache = WebCacheFactory.createWebCache(webCacheType, webCacheOptions);
170206

171-
window.onQRChangedEvent(getQR(window.AuthStore.Conn.ref)); // initial qr
172-
window.AuthStore.Conn.on('change:ref', (_, ref) => { window.onQRChangedEvent(getQR(ref)); }); // future QR changes
173-
});
174-
}
207+
await webCache.persist(this.currentIndexHtml, version);
208+
}
175209

176-
await exposeFunctionIfAbsent(this.pupPage, 'onAuthAppStateChangedEvent', async (state) => {
177-
if (state == 'UNPAIRED_IDLE') {
178-
// refresh qr code
179-
window.Store.Cmd.refreshQR();
180-
}
181-
});
210+
if (isCometOrAbove) {
211+
await this.pupPage.evaluate(ExposeStore);
212+
} else {
213+
// make sure all modules are ready before injection
214+
// 2 second delay after authentication makes sense and does not need to be made dyanmic or removed
215+
await new Promise(r => setTimeout(r, 2000));
216+
await this.pupPage.evaluate(ExposeLegacyStore);
217+
}
182218

183-
await exposeFunctionIfAbsent(this.pupPage, 'onAppStateHasSyncedEvent', async () => {
184-
const authEventPayload = await this.authStrategy.getAuthEventPayload();
185-
/**
186-
* Emitted when authentication is successful
187-
* @event Client#authenticated
188-
*/
189-
this.emit(Events.AUTHENTICATED, authEventPayload);
219+
// Check window.Store Injection
220+
await this.pupPage.waitForFunction('window.Store != undefined');
221+
222+
/**
223+
* Current connection information
224+
* @type {ClientInfo}
225+
*/
226+
this.info = new ClientInfo(this, await this.pupPage.evaluate(() => {
227+
return { ...window.Store.Conn.serialize(), wid: window.Store.User.getMeUser() };
228+
}));
190229

191-
const injected = await this.pupPage.evaluate(async () => {
192-
return typeof window.Store !== 'undefined' && typeof window.WWebJS !== 'undefined';
193-
});
230+
this.interface = new InterfaceController(this);
194231

195-
if (!injected) {
196-
if (this.options.webVersionCache.type === 'local' && this.currentIndexHtml) {
197-
const { type: webCacheType, ...webCacheOptions } = this.options.webVersionCache;
198-
const webCache = WebCacheFactory.createWebCache(webCacheType, webCacheOptions);
199-
200-
await webCache.persist(this.currentIndexHtml, version);
201-
}
232+
//Load util functions (serializers, helper functions)
233+
await this.pupPage.evaluate(LoadUtils);
202234

203-
if (isCometOrAbove) {
204-
await this.pupPage.evaluate(ExposeStore);
205-
} else {
206-
// make sure all modules are ready before injection
207-
// 2 second delay after authentication makes sense and does not need to be made dyanmic or removed
208-
await new Promise(r => setTimeout(r, 2000));
209-
await this.pupPage.evaluate(ExposeLegacyStore);
235+
await this.attachEventListeners();
210236
}
211-
212-
// Check window.Store Injection
213-
await this.pupPage.waitForFunction('window.Store != undefined');
214-
215237
/**
216-
* Current connection information
217-
* @type {ClientInfo}
238+
* Emitted when the client has initialized and is ready to receive messages.
239+
* @event Client#ready
218240
*/
219-
this.info = new ClientInfo(this, await this.pupPage.evaluate(() => {
220-
return { ...window.Store.Conn.serialize(), wid: window.Store.User.getMeUser() };
221-
}));
222-
223-
this.interface = new InterfaceController(this);
224-
225-
//Load util functions (serializers, helper functions)
226-
await this.pupPage.evaluate(LoadUtils);
227-
228-
await this.attachEventListeners();
229-
}
230-
/**
231-
* Emitted when the client has initialized and is ready to receive messages.
232-
* @event Client#ready
233-
*/
234-
this.emit(Events.READY);
235-
this.authStrategy.afterAuthReady();
236-
});
237-
let lastPercent = null;
238-
await exposeFunctionIfAbsent(this.pupPage, 'onOfflineProgressUpdateEvent', async (percent) => {
239-
if (lastPercent !== percent) {
240-
lastPercent = percent;
241-
this.emit(Events.LOADING_SCREEN, percent, 'WhatsApp'); // Message is hardcoded as "WhatsApp" for now
242-
}
243-
});
244-
await exposeFunctionIfAbsent(this.pupPage, 'onLogoutEvent', async () => {
245-
this.lastLoggedOut = true;
246-
await this.pupPage.waitForNavigation({waitUntil: 'load', timeout: 5000}).catch((_) => _);
247-
});
248-
await this.pupPage.evaluate(() => {
249-
window.AuthStore.AppState.on('change:state', (_AppState, state) => { window.onAuthAppStateChangedEvent(state); });
250-
window.AuthStore.AppState.on('change:hasSynced', () => { window.onAppStateHasSyncedEvent(); });
251-
window.AuthStore.Cmd.on('offline_progress_update', () => {
252-
window.onOfflineProgressUpdateEvent(window.AuthStore.OfflineMessageHandler.getOfflineDeliveryProgress());
241+
this.emit(Events.READY);
242+
this.authStrategy.afterAuthReady();
243+
});
244+
let lastPercent = null;
245+
await exposeFunctionIfAbsent(this.pupPage, 'onOfflineProgressUpdateEvent', async (percent) => {
246+
if (lastPercent !== percent) {
247+
lastPercent = percent;
248+
this.emit(Events.LOADING_SCREEN, percent, 'WhatsApp'); // Message is hardcoded as "WhatsApp" for now
249+
}
253250
});
254-
window.AuthStore.Cmd.on('logout', async () => {
255-
await window.onLogoutEvent();
251+
await exposeFunctionIfAbsent(this.pupPage, 'onLogoutEvent', async () => {
252+
this.lastLoggedOut = true;
253+
await this.pupPage.waitForNavigation({waitUntil: 'load', timeout: 5000}).catch((_) => _);
256254
});
257-
});
255+
await this.pupPage.evaluate(() => {
256+
window.AuthStore.AppState.on('change:state', (_AppState, state) => { window.onAuthAppStateChangedEvent(state); });
257+
window.AuthStore.AppState.on('change:hasSynced', () => { window.onAppStateHasSyncedEvent(); });
258+
window.AuthStore.Cmd.on('offline_progress_update', () => {
259+
window.onOfflineProgressUpdateEvent(window.AuthStore.OfflineMessageHandler.getOfflineDeliveryProgress());
260+
});
261+
window.AuthStore.Cmd.on('logout', async () => {
262+
await window.onLogoutEvent();
263+
});
264+
});
265+
266+
}catch(err){
267+
if(!hasReloaded)
268+
throw err;
269+
}finally{
270+
this.pupPage.off('framenavigated', reloadHandler);
271+
}
258272
}
259273

260274
/**

0 commit comments

Comments
 (0)