mirror of
https://github.com/alexbelgium/hassio-addons.git
synced 2026-10-01 14:02:41 +02:00
* fix(filebrowser_quantum): keep the no-preview Download and Open file links inside the ingress panel 1.5.3.2 fixed the download anchor api/resources.js builds and clicks itself, but the 'no preview available' screen -- what a .zip or .bin gets -- offers its own Download and 'Open file' buttons as target="_blank" links, and so does the share list in settings. The companion app has no navigationAction policy delegate, so every new-window request reaches createWebViewWith and is handed to an external browser, which carries no ingress session cookie: 401. Replaces the wrapper around HTMLAnchorElement.prototype.click with a single capturing click listener. It reaches the hidden anchor exactly as before -- a programmatic .click() dispatches through the document like a real one -- and also the two the user clicks, which the wrapper never saw. Downloads gain the attribute everywhere; dropping target="_blank" is limited to the companion app, identified by the Mobile/HomeAssistant marker it appends to the user agent, because in a real browser a new tab is the better behaviour. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(filebrowser_quantum): leave modified clicks alone, and correct the comments Review of #3033 found that the listener ran for every click, so a cmd/ctrl/shift-click on the visible Download link -- an explicit request for a separate context -- was turned into a download instead. It now only touches unmodified primary clicks, which is also what a programmatic .click() reports (button 0, no modifiers), so the hidden anchor is unaffected. Also corrects three overclaims: the listener reaches connected anchors only (both shipped download paths append theirs first); absolute sidebar links go through window.open rather than an anchor and are not covered; and links inside the pdf, srcdoc-preview and OnlyOffice iframes are a separate document this listener never sees. Records that an error response is now saved as a file. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs(filebrowser_quantum): say why the download branch also drops target Review of #3033 read the unconditional target removal as contradicting the comment above it. The removal is deliberate: with the download attribute set, a same-origin link downloads and never opens a tab, so it changes nothing in a browser (measured), but it stops the companion app from taking its new-window path before it considers the download -- an ordering not testable from outside iOS. The comment now says so. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
113 lines
7.8 KiB
Plaintext
113 lines
7.8 KiB
Plaintext
server {
|
|
listen %%interface%%:%%port%% default_server;
|
|
|
|
include /etc/nginx/includes/server_params.conf;
|
|
include /etc/nginx/includes/proxy_params.conf;
|
|
|
|
client_max_body_size 0;
|
|
|
|
location / {
|
|
add_header Access-Control-Allow-Origin *;
|
|
proxy_connect_timeout 30m;
|
|
proxy_send_timeout 30m;
|
|
proxy_read_timeout 30m;
|
|
proxy_pass %%protocol%%://backend%%subpath%%;
|
|
|
|
# Two things the ingress panel needs that a plain browser tab does not.
|
|
# Both are injected into the page's existing nonce-carrying inline script
|
|
# rather than next to <div id="app">: FileBrowser sends
|
|
# script-src 'self' 'nonce-<random>', so a standalone inline <script>
|
|
# would be blocked. If upstream ever drops that
|
|
# window.__pwaDeferredPrompt line the filter stops matching and both
|
|
# behaviours revert, which is the state before either fix.
|
|
#
|
|
# 1. Opening a folder. The tool views (Tools -> File Size Analyzer and
|
|
# the others) always set the context menu's showLimitedOptions flag, and
|
|
# openParentFolder() hands that same flag to goToItem() as its newTab
|
|
# argument, so the folder is opened with window.open(<url>, '_blank').
|
|
# Behind ingress that popup lands on the raw /api/hassio_ingress/<token>/
|
|
# url with no Home Assistant frontend around it to keep the ingress
|
|
# session alive, so the new tab answers 401 instead of showing the
|
|
# folder. Turn it into a navigation of the panel itself. The context
|
|
# menu's "go to item" action, offered by search results and the tool
|
|
# views, hardcodes the same new tab and is fixed with it.
|
|
#
|
|
# Scoped to the two prefixes goToItem() builds, "files/" and
|
|
# "public/share/", so the window.open calls that download or preview a
|
|
# file (they go to api/resources/download) keep their own tab: sending
|
|
# an inline raw file to location.assign would replace the whole app. A
|
|
# link out of the add-on keeps its own tab as well, unless a user points
|
|
# a sidebar link -- or a link inside a file open in the editor -- at this
|
|
# same instance's files/ or public/share/ route. Same shape as the komga
|
|
# add-on's ingress filter.
|
|
#
|
|
# 2. Saving and opening a file. FileBrowser downloads by clicking an <a>
|
|
# that carries no download attribute -- a hidden one it builds itself
|
|
# (api/resources.js), and two visible ones in the "no preview available"
|
|
# fallback (views/files/Preview.vue), which are also target="_blank".
|
|
# Neither works in the Home Assistant companion app:
|
|
#
|
|
# - A download happens in its WKWebView only when WebKit turns a
|
|
# navigation *action* into a WKDownload, which is what the download
|
|
# attribute does; the app hands that to its own download manager
|
|
# (WebViewController+WebKitDelegates.swift, navigationAction:didBecome
|
|
# download:, iOS 17+). Its response policy delegate returns .allow for
|
|
# every sub-frame and never returns .download, so a plain attachment
|
|
# navigation in the ingress panel is just rendered: the file opens and
|
|
# shows its content with no way to save it.
|
|
# - target="_blank" is worse. The app has no navigationAction policy
|
|
# delegate, so every new-window request reaches createWebViewWith,
|
|
# which hands the url to an external browser. That browser carries no
|
|
# ingress session cookie, so Home Assistant answers 401.
|
|
#
|
|
# One capturing click listener covers both, and covers the hidden anchor
|
|
# too: a programmatic .click() on an anchor that is in the document
|
|
# dispatches through it exactly like a real one -- same event, button 0,
|
|
# no modifiers -- and the capture phase runs before the link is followed.
|
|
# It replaces an earlier wrapper around HTMLAnchorElement.prototype.click,
|
|
# which reached the hidden anchor but not the two the user clicks. The
|
|
# wrapper would also have caught a click on a *detached* anchor, which
|
|
# this cannot; both of FileBrowser's download paths append theirs to the
|
|
# body first (api/resources.js), so nothing is lost today.
|
|
#
|
|
# Only unmodified primary clicks are touched. A cmd/ctrl/shift-click is
|
|
# an explicit request for a separate context and is left alone, which
|
|
# matters on desktop and in the Mac Catalyst app.
|
|
#
|
|
# Downloads gain the attribute everywhere -- desktop browsers already
|
|
# saved these on a plain click, and an empty value keeps the filename the
|
|
# server sends in Content-Disposition. One behaviour does change there:
|
|
# if the endpoint answers with an error the body is saved as a file
|
|
# instead of being shown, because the decision is made before the
|
|
# response exists. Matched on the two exact download endpoints minus
|
|
# inline=true, which is the fallback's "Open file" link and is meant to
|
|
# open rather than save. That branch drops target="_blank" in every
|
|
# browser too: with the attribute present a same-origin link downloads
|
|
# and never opens a tab (measured), so it changes nothing here, but it
|
|
# stops the companion app from taking its new-window path before it
|
|
# considers the download -- an ordering that cannot be tested from
|
|
# outside iOS.
|
|
#
|
|
# Dropping target="_blank" from links that are *not* downloads is limited
|
|
# to the companion app, identified by the Mobile/HomeAssistant marker it
|
|
# appends to the user agent
|
|
# (HAAPI.swift, applicationNameForUserAgent -- it covers iPhone, iPad and
|
|
# Mac Catalyst). In a real browser a new tab is the better behaviour and
|
|
# is left alone; in the app it is a 401. It applies to any same-origin
|
|
# link below the add-on's own base path, which in practice is the
|
|
# "Open file" button and the share links in settings
|
|
# (views/settings/Shares.vue). Links to other origins keep their new tab,
|
|
# and only an exact target="_blank" is matched, so named windows, _parent
|
|
# and _top are untouched.
|
|
#
|
|
# Not covered, and still 401 in the app: anything the app opens with
|
|
# window.open() rather than an anchor, which is the public-share sidebar
|
|
# download and absolute sidebar links (components/sidebar/Links.vue); and
|
|
# links inside a document FileBrowser renders in its own iframe -- the
|
|
# pdf viewer, the srcdoc markdown/html preview, OnlyOffice -- because a
|
|
# listener on this document never sees another document's clicks.
|
|
sub_filter "window.__pwaDeferredPrompt = null;" "window.__pwaDeferredPrompt = null;(function(){var o=window.open;window.open=function(u,n,f){try{var b=(window.globalVars||{}).baseURL;if(u&&n==='_blank'&&!f&&b){if(b.slice(-1)!=='/')b+='/';var t=new URL(u,location.href);if((t.protocol==='http:'||t.protocol==='https:')&&t.origin===location.origin&&(t.pathname.indexOf(b+'files/')===0||t.pathname.indexOf(b+'public/share/')===0)){location.assign(t.href);return window}}}catch(e){}return o.apply(window,arguments)};document.addEventListener('click',function(e){try{if(e.button||e.metaKey||e.ctrlKey||e.shiftKey||e.altKey)return;var a=e.target&&e.target.closest?e.target.closest('a'):null;if(!a||!a.href)return;var b=(window.globalVars||{}).baseURL;if(!b)return;if(b.slice(-1)!=='/')b+='/';var t=new URL(a.href,location.href);if(t.origin!==location.origin)return;if(!a.hasAttribute('download')&&t.searchParams.get('inline')!=='true'&&(t.pathname===b+'api/resources/download'||t.pathname===b+'public/api/resources/download')){a.download='';a.removeAttribute('target');return}if(a.target==='_blank'&&t.pathname.indexOf(b)===0&&navigator.userAgent.indexOf('Mobile/HomeAssistant')!==-1){a.removeAttribute('target')}}catch(err){}},true)})();";
|
|
}
|
|
|
|
}
|