|
430 | 430 | return (this._tabLess ||= { requests: new Map() }); |
431 | 431 | }, |
432 | 432 |
|
| 433 | + async checkTabLessRequest(request, candidate) { |
| 434 | + if (request.tabId !== -1) { |
| 435 | + // belts and suspenders |
| 436 | + return; |
| 437 | + } |
| 438 | + // called from an onBeforeSendHeaders listener, with request headers |
| 439 | + const tabLess = await this.getTabLess(); |
| 440 | + if (request.frameId == 0 && request.type == "main_frame") { |
| 441 | + if (request.documentUrl) { |
| 442 | + // main_frame request from service worker |
| 443 | + return; |
| 444 | + } |
| 445 | + const { url } = request; |
| 446 | + if (tabLess.mainUrl == url) { |
| 447 | + // same chatbot as before, probably closed & reopened, bailout |
| 448 | + return; |
| 449 | + } |
| 450 | + // filter out requests with wrong headers, e.g. prefetches |
| 451 | + for (let h of request.requestHeaders) { |
| 452 | + switch(h.name) { |
| 453 | + case 'Sec-Fetch-Dest': |
| 454 | + if (h.value !== "document") { |
| 455 | + return; |
| 456 | + } |
| 457 | + break; |
| 458 | + case 'Sec-Fetch-Mode': |
| 459 | + if (h.value !== "navigate") { |
| 460 | + return; |
| 461 | + } |
| 462 | + break; |
| 463 | + case 'Sec-Fetch-Site': |
| 464 | + if (h.value !== "cross-site") { |
| 465 | + return; |
| 466 | + } |
| 467 | + break; |
| 468 | + case 'Sec-Purpose': |
| 469 | + // prefetch |
| 470 | + return; |
| 471 | + } |
| 472 | + } |
| 473 | + await include("/nscl/service/SidebarUtil.js"); |
| 474 | + const sidebarWidth = await SidebarUtil.guessSidebarWidth(); |
| 475 | + if (sidebarWidth < 400) { |
| 476 | + // sidebar is closed, bailout |
| 477 | + return; |
| 478 | + } |
| 479 | + tabLess.sidebarWidth = sidebarWidth; |
| 480 | + tabLess.requests.clear(); |
| 481 | + tabLess.mainUrl = url; |
| 482 | + } else if ( |
| 483 | + !tabLess?.requests?.size || |
| 484 | + tabLess.mainUrl !== |
| 485 | + (request?.frameAncestors?.length |
| 486 | + ? request.frameAncestors[ |
| 487 | + request.frameAncestors?.length - 1]?.url |
| 488 | + : request.documentUrl) |
| 489 | + ) { |
| 490 | + // at least one top document needs to be loaded and it must match |
| 491 | + return; |
| 492 | + } |
| 493 | + tabLess.requests.set(candidate.request.key, candidate); |
| 494 | + this._session.save(); |
| 495 | + }, |
| 496 | + |
433 | 497 | async reportTo(originalRequest, allowed, policyType) { |
434 | | - let { requestId, tabId, frameId, type, url, documentUrl, originUrl } = |
| 498 | + const { requestId, tabId, frameId, type, url, documentUrl, originUrl } = |
435 | 499 | originalRequest; |
436 | | - let pending = pendingRequests.get(requestId); // null if from a CSP report |
| 500 | + const pending = pendingRequests.get(requestId); // null if from a CSP report |
437 | 501 |
|
438 | | - let request = { |
| 502 | + const request = { |
439 | 503 | key: Policy.requestKey( |
440 | 504 | url, |
441 | 505 | type, |
|
455 | 519 | if ( |
456 | 520 | (policyType === "script" || policyType === "fetch") && |
457 | 521 | url.startsWith("https://") && |
458 | | - documentUrl && |
459 | | - documentUrl.startsWith("https://") |
| 522 | + documentUrl?.startsWith("https://") |
460 | 523 | ) { |
461 | 524 | // service worker request ? |
462 | | - let payload = { |
| 525 | + const payload = { |
463 | 526 | request, |
464 | 527 | allowed, |
465 | 528 | policyType, |
466 | 529 | serviceWorker: Sites.origin(documentUrl), |
467 | 530 | }; |
468 | | - let recipient = { frameId: 0 }; |
469 | | - for (let tabId of TabStatus.findTabsByOrigin(payload.serviceWorker)) { |
| 531 | + const recipient = { frameId: 0 }; |
| 532 | + for (const tabId of TabStatus.findTabsByOrigin(payload.serviceWorker)) { |
470 | 533 | recipient.tabId = tabId; |
471 | 534 | try { |
472 | 535 | Messages.send("seen", payload, recipient); |
|
479 | 542 | return; |
480 | 543 | } |
481 | 544 | } |
482 | | - if (!(pending && UA.isMozilla)) { |
483 | | - // we don't support tab-less / sidebars outside Firefox |
484 | | - return; |
485 | | - } |
486 | | - |
487 | | - // no tab, record as tabLess |
488 | | - const tabLess = await this.getTabLess(); |
489 | | - if (frameId == 0 && type == "main_frame") { |
490 | | - if (documentUrl) { |
491 | | - // main_frame request from service worker |
492 | | - return; |
493 | | - } |
494 | | - const { url } = request; |
495 | | - if (tabLess.mainUrl == url) { |
496 | | - // same chatbot as before, probably closed & reopened, bailout |
497 | | - return; |
498 | | - } |
499 | | - await include("/nscl/service/SidebarUtil.js"); |
500 | | - const sidebarWidth = await SidebarUtil.guessSidebarWidth(); |
501 | | - if (sidebarWidth < 400) { |
502 | | - // sidebar is closed, bailout |
503 | | - return; |
504 | | - } |
505 | | - tabLess.sidebarWidth = sidebarWidth; |
506 | | - tabLess.requests.clear(); |
507 | | - tabLess.mainUrl = url; |
508 | | - } else if ( |
509 | | - !tabLess?.requests?.size || |
510 | | - tabLess.mainUrl !== |
511 | | - (originalRequest?.frameAncestors?.length |
512 | | - ? originalRequest.frameAncestors[frameAncestors?.length - 1]?.url |
513 | | - : documentUrl) |
514 | | - ) { |
515 | | - // at least one top document needs to be loaded and it must match |
516 | | - return; |
| 545 | + if ((pending && UA.isMozilla)) { |
| 546 | + // we only support tab-less / sidebars in Firefox |
| 547 | + pending.tabLessCandidate = { |
| 548 | + request, |
| 549 | + allowed, |
| 550 | + policyType, |
| 551 | + tabLess: true, |
| 552 | + }; |
517 | 553 | } |
518 | | - tabLess.requests.set(request.key, { |
519 | | - request, |
520 | | - allowed, |
521 | | - policyType, |
522 | | - tabLess: true, |
523 | | - }); |
524 | | - this._session.save(); |
525 | 554 | return; |
526 | 555 | } |
527 | 556 | if (pending) request.initialUrl = pending.initialUrl; |
|
795 | 824 | return ALLOW; |
796 | 825 | } |
797 | 826 |
|
| 827 | + |
798 | 828 | const listeners = { |
799 | 829 | onBeforeRequest(request) { |
800 | 830 | try { |
|
821 | 851 | return lanRes; |
822 | 852 | } |
823 | 853 | if (lanRes === ABORT) return ABORT; |
824 | | - // redirection loop test |
| 854 | + |
825 | 855 | const pending = pendingRequests.get(request.requestId); |
| 856 | + // redirection loop test |
826 | 857 | if (pending?.redirected?.url === request.url) { |
827 | 858 | return lanRes; // don't go on stripping cookies if we're in a redirection loop |
828 | 859 | } |
829 | | - const chainNext = r => r === ABORT ? r : TabGuard.onSend(request); |
| 860 | + const chainNext = r => |
| 861 | + r === ABORT |
| 862 | + ? r |
| 863 | + : pending.tabLessCandidate |
| 864 | + ? Content.checkTabLessRequest(request, pending.tabLessCandidate) |
| 865 | + : TabGuard.onSend(request); |
830 | 866 | return lanRes instanceof Promise ? lanRes.then(chainNext) : chainNext(lanRes); |
831 | 867 | }, |
832 | 868 |
|
|
0 commit comments