From 3e0a9ff2c708891a15a681e5af549caf0b18ff60 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 26 Oct 2021 12:06:36 -0400
Subject: when document is in lightbox view and a link is followed to its
 sidebar, we no longer reopen the document in its context.  fixed selecting
 annotations on web/pdf.  don't change document height when shown in a
 linkPreview.  webBox fixes for pages with scripts. fixed range bounds when
 clicking on web text

several fixes to web pages on server and client.
client webbox allows clicks on divs with onclick instead of doing selection
       also hacky fix so that google search url doesn't keep expanding by removing 'q=' regions
       also added prevent/allow script menu item
server grabs all html and hides id="google.." which are ads.
      also rewrites hrefs starting with http to route through corsProxy
      also removes target=_blank tags to prevent pages from opening outside of dash.
     also cleaned up routes and comments
     also when not logged in, references to anything in dash domain route to /home
---
 src/client/views/nodes/WebBox.tsx | 77 +++++++++++++++++++++++++++++++--------
 1 file changed, 61 insertions(+), 16 deletions(-)

(limited to 'src/client/views/nodes/WebBox.tsx')

diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 37d716993..9956cc36b 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -58,8 +58,10 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     private _sidebarRef = React.createRef<SidebarAnnos>();
     private _searchRef = React.createRef<HTMLInputElement>();
     private _searchString = "";
+    @observable private _webUrl = "";  // url of the src parameter of the embedded iframe but not necessarily the rendered page - eg, when following a link, the rendered page changes but we don't wan the src parameter to also change as that would cause an unnecessary re-render.  
+    @observable private _hackHide = false;  // apparently changing the value of the 'sandbox' prop doesn't necessarily apply it to the active iframe.  so thisforces the ifrmae to be rebuilt when allowScripts is toggled
     @observable private _searching: boolean = false;
-    @observable _showSidebar = false;
+    @observable private _showSidebar = false;
     @observable private _scrollTimer: any;
     @observable private _overlayAnnoInfo: Opt<Doc>;
     @observable private _marqueeing: number[] | undefined;
@@ -79,6 +81,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         super(props);
         Doc.SetNativeWidth(this.dataDoc, Doc.NativeWidth(this.dataDoc) || 850);
         Doc.SetNativeHeight(this.dataDoc, Doc.NativeHeight(this.dataDoc) || this.Document[HeightSym]() / this.Document[WidthSym]() * 850);
+        runInAction(() => this._webUrl = this._url);  // setting the weburl will change the src parameter of the embedded iframe and force a navigation to it.
     }
 
     @action
@@ -239,7 +242,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
         this._marqueeing = [e.clientX * scale + mainContBounds.translateX,
         e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._scrollTop) * scale];
-        if (word) {
+        if (word || (e.target as any || "").className.includes("rangeslider") || (e.target as any)?.onclick || (e.target as any)?.parentNode?.onclick) {
             setTimeout(action(() => this._marqueeing = undefined), 100); // bcz: hack .. anchor menu is setup within MarqueeAnnotator so we need to at least create the marqueeAnnotator even though we aren't using it.
         } else {
             this._iframeClick = this._iframe ?? undefined;
@@ -269,9 +272,24 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @action
     iframeLoaded = (e: any) => {
         const iframe = this._iframe;
-        this._iframe?.contentDocument?.addEventListener("pointerup", this.iframeUp);
+        let requrlraw = decodeURIComponent(iframe?.contentWindow?.location.href.replace(Utils.prepend("") + "/corsProxy/", "") ?? this._url.toString());
+        if (requrlraw !== this._url.toString()) {
+            if (requrlraw.match(/q=.*&/)?.length && this._url.toString().match(/q=.*&/)?.length) {
+                const matches = requrlraw.match(/[^a-zA-z]q=[^&]*/g);
+                const newsearch = matches?.lastElement()!;
+                if (matches) {
+                    requrlraw = requrlraw.substring(0, requrlraw.indexOf(newsearch));
+                    for (let i = 1; i < Array.from(matches)?.length; i++) {
+                        requrlraw = requrlraw.replace(matches[i], "");
+                    }
+                }
+                requrlraw = requrlraw.replace(/q=[^&]*/, newsearch.substring(1)).replace("search&", "search?").replace("?gbv=1", "");
+            }
+            this.submitURL(requrlraw, undefined, true);
+        }
         if (iframe?.contentDocument) {
-            iframe?.contentDocument.addEventListener("pointerdown", this.iframeDown);
+            iframe.contentDocument.addEventListener("pointerup", this.iframeUp);
+            iframe.contentDocument.addEventListener("pointerdown", this.iframeDown);
             this._scrollHeight = Math.max(this.scrollHeight, iframe?.contentDocument.body.scrollHeight);
             setTimeout(action(() => this._scrollHeight = Math.max(this.scrollHeight, iframe?.contentDocument?.body.scrollHeight || 0)), 5000);
             const initialScroll = this._initialScroll;
@@ -281,13 +299,15 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 this._initialScroll = undefined;
             }
             iframe.setAttribute("enable-annotation", "true");
-            iframe.contentDocument.addEventListener("click", undoBatch(action(e => {
+            iframe.contentDocument.addEventListener("click", undoBatch(action((e: MouseEvent) => {
                 let href = "";
-                for (let ele = e.target; ele; ele = ele.parentElement) {
+                for (let ele = e.target as any; ele; ele = ele.parentElement) {
                     href = (typeof (ele.href) === "string" ? ele.href : ele.href?.baseVal) || ele.parentElement?.href || href;
                 }
-                if (href && this.webField?.origin) {
-                    this.submitURL(href.replace(Utils.prepend(""), this.webField?.origin));
+                const origin = this.webField?.origin;
+                if (href && origin) {
+                    e.stopPropagation();
+                    setTimeout(() => this.submitURL(href.replace(Utils.prepend(""), origin)));
                     if (this._outerRef.current) {
                         this._outerRef.current.scrollTop = NumCast(this.layoutDoc._scrollTop);
                         this._outerRef.current.scrollLeft = 0;
@@ -338,8 +358,15 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         const future = Cast(this.dataDoc[this.fieldKey + "-future"], listSpec("string"), []);
         const history = Cast(this.dataDoc[this.fieldKey + "-history"], listSpec("string"), []);
         if (future.length) {
+            const curUrl = this._url;
             this.dataDoc[this.fieldKey + "-history"] = new List<string>([...history, this._url]);
             this.dataDoc[this.fieldKey] = new WebField(new URL(future.pop()!));
+            if (this._webUrl === this._url) {
+                this._webUrl = curUrl;
+                setTimeout(action(() => this._webUrl = this._url));
+            } else {
+                this._webUrl = this._url;
+            }
             return true;
         }
         return false;
@@ -350,10 +377,16 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         const future = Cast(this.dataDoc[this.fieldKey + "-future"], listSpec("string"));
         const history = Cast(this.dataDoc[this.fieldKey + "-history"], listSpec("string"), []);
         if (history.length) {
+            const curUrl = this._url;
             if (future === undefined) this.dataDoc[this.fieldKey + "-future"] = new List<string>([this._url]);
             else this.dataDoc[this.fieldKey + "-future"] = new List<string>([...future, this._url]);
             this.dataDoc[this.fieldKey] = new WebField(new URL(history.pop()!));
-            console.log(this._urlHash);
+            if (this._webUrl === this._url) {
+                this._webUrl = curUrl;
+                setTimeout(action(() => this._webUrl = this._url));
+            } else {
+                this._webUrl = this._url;
+            }
             return true;
         }
         return false;
@@ -362,9 +395,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     static urlHash = (s: string) => {
         return Math.abs(s.split('').reduce((a: any, b: any) => { a = ((a << 5) - a) + b.charCodeAt(0); return a & a; }, 0));
     }
-
     @action
-    submitURL = (newUrl?: string, preview?: boolean) => {
+    submitURL = (newUrl?: string, preview?: boolean, dontUpdateIframe?: boolean) => {
         if (!newUrl) return;
         if (!newUrl.startsWith("http")) newUrl = "http://" + newUrl;
         try {
@@ -376,7 +408,10 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 this.layoutDoc._scrollTop = 0;
                 future && (future.length = 0);
             }
-            if (!preview) this.dataDoc[this.fieldKey] = new WebField(new URL(newUrl));
+            if (!preview) {
+                this.dataDoc[this.fieldKey] = new WebField(new URL(newUrl));
+                !dontUpdateIframe && (this._webUrl = this._url);
+            }
         } catch (e) {
             console.log("WebBox URL error:" + this._url);
         }
@@ -429,6 +464,15 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         const funcs: ContextMenuProps[] = [];
         if (!cm.findByDescription("Options...")) {
             !Doc.UserDoc().noviceMode && funcs.push({ description: (this.layoutDoc.useCors ? "Don't Use" : "Use") + " Cors", event: () => this.layoutDoc.useCors = !this.layoutDoc.useCors, icon: "snowflake" });
+            funcs.push({
+                description: (this.layoutDoc.allowScripts ? "Prevent" : "Allow") + " Scripts", event: () => {
+                    this.layoutDoc.allowScripts = !this.layoutDoc.allowScripts;
+                    if (this._iframe) {
+                        runInAction(() => this._hackHide = true);
+                        setTimeout(action(() => this._hackHide = false));
+                    }
+                }, icon: "snowflake"
+            });
             funcs.push({
                 description: (!this.layoutDoc.forceReflow ? "Force" : "Prevent") + " Reflow", event: () => {
                     const nw = !this.layoutDoc.forceReflow ? undefined : Doc.NativeWidth(this.layoutDoc) - this.sidebarWidth() / (this.props.scaling?.() || 1);
@@ -468,18 +512,19 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     }
 
     @computed get urlContent() {
+        if (this._hackHide) return (null);
         const field = this.dataDoc[this.props.fieldKey];
         let view;
         if (field instanceof HtmlField) {
             view = <span className="webBox-htmlSpan" dangerouslySetInnerHTML={{ __html: field.html }} />;
         } else if (field instanceof WebField) {
-            const url = this.layoutDoc.useCors ? Utils.CorsProxy(this._url) : this._url;
+            const url = this.layoutDoc.useCors ? Utils.CorsProxy(this._webUrl) : this._webUrl;
             view = <iframe className="webBox-iframe" enable-annotation={"true"}
                 style={{ pointerEvents: this._scrollTimer ? "none" : undefined }}
                 ref={action((r: HTMLIFrameElement | null) => this._iframe = r)} src={url} onLoad={this.iframeLoaded}
                 // the 'allow-top-navigation' and 'allow-top-navigation-by-user-activation' attributes are left out to prevent iframes from redirecting the top-level Dash page
                 // sandbox={"allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-presentation allow-same-origin allow-scripts"} />;
-                sandbox={"allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-presentation allow-same-origin"} />;
+                sandbox={`${this.layoutDoc.allowScripts ? "allow-scripts" : ""} allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-presentation allow-same-origin`} />;
         } else {
             view = <iframe className="webBox-iframe" enable-annotation={"true"}
                 style={{ pointerEvents: this._scrollTimer ? "none" : undefined }} // if we allow pointer events when scrolling is on, then reversing direction does not work smoothly
@@ -615,12 +660,12 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 moveDocument={this.moveDocument}
                 addDocument={this.sidebarAddDocument}
                 styleProvider={this.childStyleProvider}
-                pointerEvents={CurrentUserUtils.SelectedTool !== InkTool.None || this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none"} />;
+                childPointerEvents={this.props.isContentActive() ? "all" : undefined}
+                pointerEvents={this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none"} />;
         return (
             <div className="webBox" ref={this._mainCont}
                 style={{ pointerEvents: this.pointerEvents() }} >
                 <div className={`webBox-container`} style={{ pointerEvents }} onContextMenu={this.specificContextMenu}>
-                    <base target="_blank" />
                     <div className={"webBox-outerContent"} ref={this._outerRef}
                         style={{
                             width: `calc(${100 / scale}% - ${this.sidebarWidth() / scale * (this._previewWidth ? scale : 1)}px)`,
-- 
cgit v1.2.3-70-g09d2

