Aller au contenu
Lilo
Esc
↑↓naviguer↵ouvrir⌘Japerçu
Sur cette page

Compatibilité web

Les fonctions web qui marchent autrement dans Lilo que dans d'autres navigateurs, et les limites connues à garder en tête quand un site se comporte mal.

Lilo affiche chaque page avec WebKit, le moteur de Safari. Cette page répertorie les fonctions web pour lesquelles Lilo diffère, la manière dont Lilo gère chacune d’elles, et les limites de WebKit et de macOS que Lilo ne peut pas contourner. Quand un site se comporte mal, consultez d’abord cette page, puis reportez-vous à Dépannage.

Le texte ci-dessous provient de l’outil de suivi de compatibilité du projet, COMPATIBILITY.md. Les API d’extension sont traitées dans Compatibilité des extensions.

API web

navigator.serial, window.EyeDropper et window.documentPictureInPicture n’existent que dans les contextes sécurisés, comme dans Chrome, mais Safari n’en a aucune : une page qui teste leur présence peut distinguer un onglet Lilo de Safari, dont Lilo affiche l’agent utilisateur.

API Web Serial (navigator.serial)

  • État : implémenté (Lilo/Engine/SerialBridge.swift)
  • Prise en charge par WebKit : aucune - Chrome/Edge uniquement ; Lilo fournit un polyfill
  • Fonctionnement : un polyfill dans le monde de la page pour les documents sécurisés de premier niveau (requestPort avec filtres USB de fabricant et de produit, getPorts, open avec débit en bauds, bits de données, bits d’arrêt, parité et contrôle de flux matériel, flux readable/writable, setSignals, getSignals, forget) au-dessus d’un pont natif vers les périphériques série que liste IOKit. requestPort affiche un sélecteur natif ; un port n’atteint que l’origine pour laquelle l’utilisateur l’a choisi, pour la durée de la session. Une page rechargée ou fermée libère ses ports.
  • Limites : aucun événement connect lorsqu’un périphérique est branché (disconnect se déclenche lorsqu’il disparaît) ; les iframes d’origine différente n’obtiennent pas navigator.serial ; les lectures ne sont pas soumises à un contrôle de flux par la page.

API EyeDropper (window.EyeDropper)

  • État : implémenté (Lilo/Engine/EyeDropperBridge.swift)
  • Prise en charge par WebKit : aucune - Chrome/Edge uniquement ; Lilo fournit un polyfill dans les contextes sécurisés
  • Fonctionnement : open() affiche la pipette de couleurs du système (la loupe du panneau de couleurs de macOS) et se résout avec { sRGBHex } ; Échap rejette avec AbortError.

API File System Access (showOpenFilePicker, showSaveFilePicker, showDirectoryPicker)

  • État : non implémenté, volontairement pour l’instant
  • Prise en charge par WebKit : aucune - Chrome/Edge uniquement (WebKit ne fournit que le système de fichiers privé de l’origine)
  • Conséquences : les applications détectent sa présence et se rabattent sur les champs de fichier et les téléchargements, qui fonctionnent. Un polyfill partiel ferait basculer ces applications sur un chemin auquel il manque des éléments (des handles qui ne survivent pas à IndexedDB, les écritures dans des dossiers), ce qui est pire que la solution de repli.

Gestionnaires de protocole (navigator.registerProtocolHandler)

  • État : non implémenté, volontairement
  • Prise en charge par WebKit : aucune sur macOS - 'registerProtocolHandler' in navigator vaut false dans une WKWebView (vérifié sur macOS 27) ; Chrome permet à un site de gérer un schéma, comme Gmail pour les liens mailto:
  • Conséquences : les sites détectent sa présence et renoncent à le proposer. Un lien mailto: que suit l’utilisateur ouvre aussitôt l’app que macOS attribue au schéma ; tout autre lien non web l’ouvre après une demande de Lilo. Les pages voient Lilo comme Safari, à qui l’API manque aussi : l’ajouter distinguerait Lilo de Safari, et une trace visible par la page signale Lilo comme un robot.

Clics sur les notifications de service worker

  • État : partiel - un clic exécute le notificationclick du worker, et client.focus() ramène une page qu’il a ouverte
  • Conséquences : une notification sur laquelle on clique après le redémarrage de Lilo n’atteint aucun worker : le clic ne fait que ramener Lilo au premier plan. clients.openWindow() ouvre son onglet dans un espace du profil du worker, celui qui est à l’écran lorsqu’il a ce profil. Les notifications propres à une page (new Notification) ramènent leur onglet comme dans Chrome.

Clés d’accès / WebAuthn

  • État : nécessite l’habilitation com.apple.developer.web-browser.public-key-credential, qu’Apple accorde par identifiant d’app. Sans elle, la vérification getCanCurrentProcessAccessPasskeysForRelyingParty de WebKit échoue : PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable() vaut false et navigator.credentials.create() / .get() rejettent aussitôt avec NotAllowedError (vérifié sur macOS 27). Apple ne l’a pas encore accordée, donc les versions publiées ne la portent pas : le WebAuthn propre à WebKit n’y fonctionne pas, clés d’accès comme clés de sécurité USB/NFC. Une requête que traite WebKit échoue aussitôt avec NotAllowedError, sans aucune invite, et ressemble à une annulation par l’utilisateur ; un compte dont le seul second facteur est une clé matérielle ne peut pas se connecter. Une extension de gestionnaire de mots de passe qui répond elle-même aux requêtes de clés d’accès, par son crochet sur navigator.credentials, ne passe pas par la vérification de WebKit, et Lilo maintient ce crochet en place (PageWorld) ; le fonctionnement des clés d’accès d’un gestionnaire donné dans Lilo n’a pas été testé. Une fois l’habilitation accordée, les builds de publication la portent par l’intermédiaire d’un profil d’approvisionnement, voir docs/RELEASING.md ; les builds ad hoc et Debug ne la portent jamais.
  • Pistes écartées : une intégration sur mesure de ASAuthorizationPlatformPublicKeyCredentialProvider ne ferait que dupliquer WebKit. com.apple.developer.associated-domains (webcredentials:) régit les domaines propres à l’app et ne peut pas couvrir des sites web arbitraires - ce n’est pas ainsi qu’un navigateur héberge WebAuthn.

DRM - FairPlay (fonctionne) et Widevine/PlayReady (impossible)

  • FairPlay Streaming (com.apple.fps, 1_0, 2_0, 3_0) : pris en charge, aucune configuration nécessaire. Mesuré dans une WKWebView configurée comme les onglets de Lilo : requestMediaKeySystemAccess est accordé pour les données d’initialisation cenc, sinf et skd avec H.264 et HEVC 10 bits, et createMediaKeys fonctionne. L’ancien window.WebKitMediaKeys est également présent. ClearKey est accordé.
  • Aucune session à licence persistante : les téléchargements hors ligne dans les applications web de streaming ne sont pas disponibles.
  • Widevine / PlayReady : rejetés (NotSupportedError), sans correction possible. WebKit sur les plateformes Apple n’implémente EME que pour FairPlay et n’expose aucune API permettant d’enregistrer un autre CDM ; libwidevinecdm ne se branche que sur l’hôte CDM de Chromium. Obtenir Widevine supposerait d’embarquer un second moteur pour les origines à DRM, ce qui est délibérément hors des objectifs.
  • Codecs (MSE et ManagedMediaSource) : H.264, HEVC Main/Main10, Dolby Vision, VP9 profils 0 et 2 (10 bits), AV1, Opus et Vorbis dans WebM, AAC, E-AC-3. Le décodage 4K HDR est fluide.
  • Agent utilisateur : une simple identité Safari (sans jeton Lilo), si bien que les services servent leur variante Safari/FairPlay (voir Agent utilisateur et Chrome Web Store).
  • Services de streaming (d’après leurs pages d’assistance publiques pour Safari, pas encore testés à la main dans Lilo) :
    • Devraient fonctionner : Netflix (4K non vérifiée), Prime Video, Disney+, Max, Apple TV+, lecteur web Spotify, Apple Music web, Crunchyroll, YouTube (4K HDR), YouTube Music, Twitch.
    • Qualité réduite : Canal+ (720p signalé dans Safari), Tidal (pas de lossless, qui nécessite Widevine).
    • Inconnu : Deezer, films achetés et loués sur YouTube.

Image dans l’image - PiP <video> et Document PiP (shim Lilo)

  • Image dans l’image pour une seule <video> : pris en charge. Le PiP d’élément de WebKit fonctionne (video.webkitSetPresentationMode('picture-in-picture') / requestPictureInPicture()), et Lilo y entre automatiquement lorsqu’un onglet dont un média est actif passe à l’arrière-plan (PiPController). Cela détache une seule vidéo dans la fenêtre PiP du système (sans les commandes de la page) - ce qui convient au PiP à flux unique du type YouTube/Netflix.
  • Document Picture-in-Picture (window.documentPictureInPicture) : pris en charge par un shim Lilo. WebKit ne fournit aucune implémentation. Un module dans le monde de la page (DocumentPiPBridge) définit l’API dans les documents sécurisés de premier niveau des onglets ordinaires ; requestWindow fait du prochain window.open de l’onglet une fenêtre contextuelle de même origine qu’héberge DocumentPiPController : ancrée à sa propre taille dans la barre latérale tant que Lilo est au premier plan et que son onglet n’est pas affiché, flottante sinon (toujours au premier plan, dans tous les espaces, exclue du partage d’écran, non activante, calée dans un coin mémorisé pour chaque écran, boutons de retour et de fermeture au survol). La fenêtre se ferme lorsque sa page navigue ou que son onglet se ferme. Un onglet d’appel dont la page enregistre l’action Media Session enterpictureinpicture obtient sa fenêtre PiP lorsqu’on quitte l’onglet (elle se referme au retour) ; sans fenêtre dans un délai de 1,5 s, la page entière passe dans le dock ou le panneau flottant comme auparavant. Les onglets d’agent reçoivent une NotAllowedError.
  • Google Meet : Meet ne propose son Document PiP qu’à Chrome, d’après la lecture de navigator.userAgent (il ne lit jamais userAgentData), et fixe au chargement l’identité que suit sa pile WebRTC. Lilo n’affiche un agent utilisateur Chrome sur meet.google.com que dans la microtâche qui suit une lecture de documentPictureInPicture : les vérifications PiP de Meet réussissent, son appel reste sur le chemin multimédia de Safari. Une identité Chrome complète casse la caméra et le micro (createEncodedStreams et MediaStreamTrackGenerator audio, propres à Chromium). defaults write <bundle id> LiloMeetDocumentPiPDisabled -bool true désactive la partie Meet (nouveaux onglets).

Partage d’écran - capture d’onglet (navette d’images Lilo)

  • Le partage d’onglet est pris en charge par un shim Lilo. Le pipeline de capture de WebKit n’énumère que les écrans et les fenêtres du système (la capture d’onglet est une surface getDisplayMedia propre à Chromium), donc Lilo intercepte getDisplayMedia dans le monde de la page : un sélecteur de l’interface du navigateur liste les onglets ouverts, et en choisir un renvoie un véritable MediaStream construit côté page à partir d’un canvas alimenté par une navette d’images native (TabShareSessionController : instantanés à 30 ips du moteur de l’onglet partagé, encodés en H.264 par le matériel et décodés côté page avec WebCodecs ; les images identiques sont ignorées, si bien qu’un contenu statique ne coûte presque rien). Un onglet partagé passé à l’arrière-plan reste attaché sous la vue active à la taille complète du contenu (le principe de rétention utilisé pour l’inspecteur), de sorte qu’il conserve sa mise en page de bureau et continue d’être rendu ; l’indicateur de capture de la barre latérale le signale comme partagé.
  • Réserves par rapport à Chrome : le flux transporté est uniquement vidéo (pas d’audio de l’onglet : WebKit n’expose aucune prise audio par page) et plafonné à 30 ips / 1920 pixels de large.
  • Choisir « partager plutôt un écran ou une fenêtre » exécute le getDisplayMedia propre à WebKit et son sélecteur système (le choix lui-même vaut consentement ; aucune autorisation Enregistrement de l’écran), invoqué par le code natif avec un nouveau geste utilisateur, puisque le sélecteur d’onglets asynchrone a quitté celui de la page.
  • Les extensions (chrome.tabCapture, chrome.desktopCapture) empruntent la même navette (voir tabCapture et desktopCapture sous Différences d’API). Un choix de bureau utilise directement le SCContentSharingPicker du système, et un SCStream alimente la navette tant que le flux vit.
  • Remarque : les utilitaires qui maintiennent une fenêtre de superposition transparente en plein écran (LanguageTool for Desktop, certains outils de capture ou de gestion de fenêtres) interceptent les clics destinés au sélecteur de fenêtres du système : le sélecteur s’ouvre mais aucune fenêtre ne peut être sélectionnée, dans tous les navigateurs, Safari compris. Quittez l’app de superposition pendant le partage.

Gel des onglets inactifs

  • Épargnés comme dans la politique de gel de Chrome : les onglets qui lisent ou capturent un média, qui chargent, qui sont autorisés à notifier, qui détiennent une RTCPeerConnection connectée (y compris avec de simples canaux de données) ou un Web Lock.
  • Réserve : WebKit ne signale à l’app ni les pairs ni les verrous, donc Lilo suit les pairs dans le monde de la page du document principal et demande au document principal ses verrous lorsque l’onglet doit être gelé : un pair ou un verrou détenu par un cadre ou un worker n’empêche pas son onglet d’être gelé.

Limitations connues (niveau WebKit/macOS - aucune correction possible dans Lilo)

Extension Mots de passe iCloud

  • Symptôme : l’extension Chrome « Mots de passe iCloud » ne parvient pas à se connecter à son assistant natif dans Lilo.
  • Cause profonde : l’hôte de messagerie native com.apple.passwordmanager (/System/Cryptexes/App/System/Library/CoreServices/PasswordManagerBrowserExtensionHelper.app) porte des contraintes de lancement sur le parent dans sa signature de code, indépendantes des allowed_origins du manifeste. Son parent doit soit correspondre à une liste fixe de paires identifiant de signature et identifiant d’équipe de navigateurs connus, soit détenir l’habilitation com.apple.developer.web-browser.public-key-credential. Lilo n’a ni l’un ni l’autre, donc l’extension ne peut pas joindre son assistant.
  • Correction : impossible dans le code de Lilo : elle nécessite cette habilitation, demandée à Apple (demande « macOS Browsers Passkeys »).

Médiation conditionnelle des clés d’accès (remplissage automatique)

  • Symptôme : navigator.credentials.get({ mediation: "conditional" }) / PublicKeyCredential.isConditionalMediationAvailable() (suggestions de clés d’accès dans les champs de nom d’utilisateur) peuvent s’annoncer indisponibles depuis un hôte WKWebView tiers.
  • Cause profonde : la prise en charge de la médiation conditionnelle par WebKit en dehors de Safari. Il n’existe aucun remède côté app. Les flux de clés d’accès explicites (non conditionnels) nécessitent eux aussi l’habilitation décrite dans Clés d’accès / WebAuthn, que les versions publiées ne portent pas encore : aucune requête que traite WebKit n’y fonctionne.

Apple Pay sur le web

  • Symptôme : window.PaymentRequest est indéfini et ApplePaySession, lorsqu’il est présent, est le substitut du SDK Apple Pay JS, qui paie au moyen d’un code scanné avec un iPhone (vérifié sur macOS 27).
  • Cause profonde : WebKit n’expose les deux qu’avec le réglage privé _applePayEnabled, désactivé sur macOS en dehors de Safari. Une fois activé, canMakePayments() vaut true mais la feuille système se ferme aussitôt : PassKit refuse un hôte dépourvu des habilitations privées de Safari com.apple.private.in-app-payments et com.apple.payment.all-access (« not entitled for merchant session »), qu’Apple n’accorde à aucune app tierce. com.apple.developer.in-app-payments couvre les identifiants de marchand propres à une app, pas ceux d’un site web.
  • Traitement par Lilo : le réglage reste désactivé : un ApplePaySession natif prendrait la place du substitut du SDK, puis échouerait.

Web Push

  • Symptôme : PushManager et registration.pushManager sont indéfinis, si bien qu’un site ne peut pas notifier lorsqu’aucune de ses pages n’est ouverte. Les notifications qu’affiche une page ou son service worker (new Notification, registration.showNotification) fonctionnent.
  • Cause profonde : WebKit n’active Web Push que pour une app détenant l’habilitation web push d’Apple, que Lilo n’a pas.
  • Traitement par Lilo : aucun.

Notification.permission précoce

  • Symptôme : les scripts d’une page qui s’exécutent avant DOMContentLoaded peuvent lire Notification.permission comme 'default' sur un site que vous avez autorisé ou bloqué.
  • Cause profonde : WebKit ne donne à un processus web les décisions du magasin de données que lorsqu’il le lance pour ce magasin ; un processus préchauffé, que prennent souvent les nouveaux onglets et les navigations intersites, démarre sans aucune. Il lit aussi un site bloqué comme 'default' tant que le site n’a pas demandé, pour contrer le pistage par empreinte.
  • Traitement par Lilo : à DOMContentLoaded, la page apprend la décision enregistrée. Un processus lancé pour son magasin, ainsi que tout service worker, la connaît dès le premier script.

Qualité du micro pendant les appels

  • Symptôme : le micro d’un appel sonne plus terne et plus traité que dans Chrome : rien au-dessus de 12 kHz n’atteint la page.
  • Cause profonde : avec l’annulation d’écho activée (la valeur par défaut, et ce que demandent les sites d’appel), WebKit fait passer le micro par le traitement de la voix d’Apple, qui le plafonne à 12 kHz ; Chrome annule l’écho, supprime le bruit et règle le gain de façon logicielle sur le signal brut. WebKit n’expose séparément ni noiseSuppression ni autoGainControl, et sa capture avec echoCancellation: false sort de 14 à 26 dB sous le niveau du périphérique, avec des coupures, si bien que la désactiver n’est pas un remède. La liaison d’un casque Bluetooth plafonne son micro à 12 kHz dans tous les navigateurs.
  • Traitement par Lilo : aucun ; égaler Chrome exigerait une pile de traitement audio propre à Lilo.

Favicons qu’une page modifie par script

  • Symptôme : un onglet conserve l’icône avec laquelle sa page s’est chargée lorsque la page remplace ensuite son <link rel=icon> (une pastille de non-lus, un état de build).
  • Cause profonde : WebKit propose à l’app les icônes de lien d’un document une seule fois, au chargement du document, et plus jamais pour un <link rel=icon> ajouté ou modifié par la suite (vérifié sur macOS 27). Chrome suit ces changements.
  • Traitement par Lilo : aucun ; les suivre exigerait un script surveillant le <head> de chaque page dans un monde isolé, un chargement d’icône propre à Lilo en dehors de celui de WebKit, et une icône par onglet distincte de celle par site que Lilo met en cache.

Référent d’un téléchargement depuis le menu contextuel

  • Symptôme : « Télécharger l’image » ou « Télécharger le fichier lié » depuis un cadre isolé en bac à sable sans allow-popups envoie l’origine du cadre (aucune de https vers http), quelle que soit la politique de référent du cadre ; avec « Empêcher le suivi entre sites » activé, ou dans un espace privé, un téléchargement vers un autre site envoie au plus l’origine de la page, même sous unsafe-url.
  • Cause profonde : les éléments de téléchargement propres à WebKit n’envoient aucun Referer, donc Lilo fait ouvrir l’adresse par WebKit dans une nouvelle fenêtre depuis le cadre cliqué, comme le ferait un lien, et prend le Referer dans la décision de politique de cette requête avant de l’annuler ; un cadre en bac à sable sans allow-popups ne peut pas en ouvrir. La prévention du suivi de WebKit réduit tout Referer intersite à son origine dans le processus réseau, y compris celui d’un téléchargement. Chrome applique la politique du cadre dans les deux cas.
  • Traitement par Lilo : le cadre en bac à sable obtient strict-origin-when-cross-origin à partir de son adresse ; la réduction intersite est celle de WebKit, comme pour toute requête.

Erreurs de certificat sur les sites qui définissent eux-mêmes HSTS

  • Symptôme : une erreur de certificat sur un site qui a envoyé un en-tête Strict-Transport-Security lors d’une visite antérieure propose toujours « Continuer quand même », et l’exception charge la page. Chrome n’offre aucun moyen de passer outre. Les hôtes de la liste de préchargement HSTS (tout .app et .dev, de nombreux grands sites) n’obtiennent pas « Continuer quand même », comme dans Chrome.
  • Cause profonde : les hôtes HSTS qu’enregistrent les pages d’un profil résident dans le processus réseau de WebKit. La seule vue que WebKit donne à l’app, ses enregistrements de données de sites web _WKWebsiteDataTypeHSTSCache, nomme chacun par domaine enregistrable (example.com pour www.example.com), sans l’hôte ni includeSubDomains, si bien qu’elle ne peut pas distinguer un hôte HSTS d’un hôte voisin sans HSTS (vérifié sur macOS 27). CFNetwork respecte bel et bien l’exception sur un hôte HSTS.
  • Traitement par Lilo : la liste de préchargement provient de la recherche de CFNetwork dans le processus propre à Lilo ; une règle par site interdirait aussi aux utilisateurs l’accès à un hôte voisin doté d’un certificat auto-signé (un serveur domestique sous un domaine personnel), donc Lilo continue d’y proposer l’exception.