Browse Source

Gui: Strip `blank:` from links [#799]

desb42 4 years ago
parent
commit
567e03dc1d

+ 28 - 21
150_gfui/src/gplx/gfui/kits/swts/Swt_html.java

@@ -185,11 +185,24 @@ public class Swt_html implements Gxw_html, Swt_control, FocusListener, Gfo_evt_m
 	, Browser_tid_mozilla 	= SWT.MOZILLA
 	, Browser_tid_mozilla 	= SWT.MOZILLA
 	, Browser_tid_webkit	= SWT.WEBKIT
 	, Browser_tid_webkit	= SWT.WEBKIT
 	;
 	;
-	private static final String URL_ABOUT_PREFIX = "about:";
-	public static String StripAboutFromUrl(String url) {
-		return String_.Has_at_bgn(url, URL_ABOUT_PREFIX)
-			? String_.Mid(url, URL_ABOUT_PREFIX.length())
-			: url;
+
+	private static final String URL_PREFIX_ABOUT = "about:";
+	private static final String URL_PREFIX_BLANK = "blank";
+	public static String NormalizeSwtUrl(String url) {
+		String rv = url;
+
+		// 2020-09-19|ISSUE#:799|strip "about:" from url due to SWT 4.16
+		rv = String_.Has_at_bgn(rv, URL_PREFIX_ABOUT)
+			? String_.Mid(rv, URL_PREFIX_ABOUT.length())
+			: rv;
+
+		// 2015-06-09|webkit prefixes "about:blank" to anchors; causes TOC to fail when clicking on links; EX:about:blank#TOC1
+		// 2020-09-22|removed webkit check due to SWT 4.16; `html_box.Browser_tid() == Swt_html.Browser_tid_webkit`
+		// still strip "blank"; note that SWT 4.16 changes anchors from "file:///#anchor" to "en.w/wiki/page/#anchor"
+		rv = String_.Has_at_bgn(rv, URL_PREFIX_BLANK)
+			? String_.Mid(rv, URL_PREFIX_BLANK.length())
+			: rv;
+		return rv;
 	}
 	}
 }
 }
 class Swt_core_cmds_html extends Swt_core__basic {
 class Swt_core_cmds_html extends Swt_core__basic {
@@ -238,8 +251,10 @@ class Swt_html_lnr_status implements StatusTextListener {
 	public void Host_set(Gfo_evt_itm host) {this.host = host;} Gfo_evt_itm host;
 	public void Host_set(Gfo_evt_itm host) {this.host = host;} Gfo_evt_itm host;
 	@Override public void changed(StatusTextEvent ev) {
 	@Override public void changed(StatusTextEvent ev) {
 		if (html_box.Kit().Kit_mode__term()) return;	// shutting down raises status changed events; ignore, else SWT exception thrown; DATE:2014-05-29 
 		if (html_box.Kit().Kit_mode__term()) return;	// shutting down raises status changed events; ignore, else SWT exception thrown; DATE:2014-05-29 
-		String ev_text = ev.text;
-		ev_text = Swt_html.StripAboutFromUrl(ev_text); // 2020-09-19|ISSUE#:799|strip "about:/" from url due to SWT 4.16
+
+		// 2020-09-22|ISSUE#:799|normalize URL due to SWT 4.16
+		String ev_text = Swt_html.NormalizeSwtUrl(ev.text);
+
 		String load_by_url_path = html_box.Load_by_url_path();
 		String load_by_url_path = html_box.Load_by_url_path();
 		if (load_by_url_path != null) ev_text = String_.Replace(ev_text, load_by_url_path, "");	// remove "C:/xowa/tab_1.html"
 		if (load_by_url_path != null) ev_text = String_.Replace(ev_text, load_by_url_path, "");	// remove "C:/xowa/tab_1.html"
 //		if (String_.Has(ev_text, "Loading [MathJax]")) return;	// suppress MathJax messages; // NOTE: disabled for 2.1 (which no longer outputs messages to status); DATE:2013-05-03
 //		if (String_.Has(ev_text, "Loading [MathJax]")) return;	// suppress MathJax messages; // NOTE: disabled for 2.1 (which no longer outputs messages to status); DATE:2013-05-03
@@ -259,24 +274,16 @@ class Swt_html_lnr_location implements LocationListener {
 	public void Host_set(Gfo_evt_itm host) {this.host = host;} private Gfo_evt_itm host;
 	public void Host_set(Gfo_evt_itm host) {this.host = host;} private Gfo_evt_itm host;
 	@Override public void changed(LocationEvent arg) 	{Pub_evt(arg, Gfui_html.Evt_location_changed);}
 	@Override public void changed(LocationEvent arg) 	{Pub_evt(arg, Gfui_html.Evt_location_changed);}
 	@Override public void changing(LocationEvent arg) 	{Pub_evt(arg, Gfui_html.Evt_location_changing);}
 	@Override public void changing(LocationEvent arg) 	{Pub_evt(arg, Gfui_html.Evt_location_changing);}
-	private void Pub_evt(LocationEvent arg, String evt) {		
-		String location = arg.location;
+	private void Pub_evt(LocationEvent arg, String evt) {
+		// 2020-09-22|ISSUE#:799|normalize URL due to SWT 4.16
+		String location = Swt_html.NormalizeSwtUrl(arg.location);
 
 
-		// location_changing fires once when page is loaded; ignore
-		if (String_.Eq(location, "about:blank")) {
+		// location_changing fires once when page is loaded -> ignore
+		if (String_.Eq(location, String_.Empty)) {
 			return;
 			return;
 		}
 		}
 
 
-		// webkit prefixes "about:blank" to anchors; causes TOC to fail when clicking on links; EX:about:blank#TOC1; DATE:2015-06-09
-		if (html_box.Browser_tid() == Swt_html.Browser_tid_webkit
-			&&	String_.Has_at_bgn(location, "about:blank")) {
-			location = String_.Mid(location, 11);	// 11 = "about:blank".length 
-		}
-
-		// 2020-09-19|ISSUE#:799|strip "about:/" from url due to SWT 4.16
-		location = Swt_html.StripAboutFromUrl(location);
-
-		// navigating to file://page.html will fire location event; ignore if url mode
+		// navigating to file://page.html will fire location event; ignore if url mode (loading pages from file)
 		if (html_box.Html_doc_html_load_tid() == Gxw_html_load_tid_.Tid_url
 		if (html_box.Html_doc_html_load_tid() == Gxw_html_load_tid_.Tid_url
 			&& 	String_.Has_at_bgn(location, "file:")
 			&& 	String_.Has_at_bgn(location, "file:")
 			&& 	String_.Has_at_end(location, ".html")
 			&& 	String_.Has_at_end(location, ".html")

+ 87 - 61
400_xowa/src/gplx/xowa/guis/history/Xog_history_mgr.java

@@ -1,6 +1,6 @@
 /*
 /*
 XOWA: the XOWA Offline Wiki Application
 XOWA: the XOWA Offline Wiki Application
-Copyright (C) 2012-2017 gnosygnu@gmail.com
+Copyright (C) 2012-2020 gnosygnu@gmail.com
 
 
 XOWA is licensed under the terms of the General Public License (GPL) Version 3,
 XOWA is licensed under the terms of the General Public License (GPL) Version 3,
 or alternatively under the terms of the Apache License Version 2.0.
 or alternatively under the terms of the Apache License Version 2.0.
@@ -13,63 +13,89 @@ The terms of each license can be found in the source code repository:
 GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
 GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
 Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
 Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
 */
 */
-package gplx.xowa.guis.history; import gplx.*; import gplx.xowa.*; import gplx.xowa.guis.*;
-public class Xog_history_mgr {
-	private final    Ordered_hash hash = Ordered_hash_.New_bry(); private final    Xog_history_stack stack = new Xog_history_stack();
-	public int Count() {return hash.Count();}
-	public Xoae_page Cur_page(Xowe_wiki wiki) {return Get_or_fetch(wiki, stack.Cur_itm());}
-	public Xoae_page Go_bwd(Xowe_wiki wiki) {return Go_by_dir(wiki, Bool_.N);}
-	public Xoae_page Go_fwd(Xowe_wiki wiki) {return Go_by_dir(wiki, Bool_.Y);}
-	public Xoae_page Go_by_dir(Xowe_wiki wiki, boolean fwd) {
-		Xog_history_itm itm = fwd ? stack.Go_fwd() : stack.Go_bwd();
-		if (itm == Xog_history_itm.Null) return Xoae_page.Empty;
-		Xoae_page rv = Get_or_fetch(wiki, itm);
-		byte[] anch_key = itm.Anch();
-		rv.Url().Anch_bry_(anch_key); // must override anchor as it may be different for cached page
-		rv.Html_data().Bmk_pos_(itm.Bmk_pos());
-		return rv;
-	}
-	public void Add(Xoae_page page) {
-		Xog_history_itm new_itm = Xog_history_mgr.new_(page);
-		stack.Add(new_itm);
-		byte[] page_key = Build_page_key(page);
-		if (!hash.Has(page_key))
-			hash.Add(page_key, page);
-	}
-	public void Update_html_doc_pos(Xoae_page page, byte history_nav_type) {
-		Xog_history_itm itm = Get_recent(page, history_nav_type);
-		if (itm != null) itm.Bmk_pos_(page.Html_data().Bmk_pos());
-	}
-	private Xog_history_itm Get_recent(Xoae_page page, byte history_nav_type) {
-		int pos = -1;
-		int list_pos = stack.Cur_pos();
-		switch (history_nav_type) {
-			case Xog_history_stack.Nav_fwd:			pos = list_pos - 1; break;
-			case Xog_history_stack.Nav_bwd:			pos = list_pos + 1; break;
-			case Xog_history_stack.Nav_by_anchor:	pos = list_pos; break;
-		}
-		if (pos < 0 || pos >= stack.Len()) return null;
-		Xog_history_itm recent = stack.Get_at(pos);
-		Xog_history_itm page_itm = Xog_history_mgr.new_(page);
-		return page_itm.Eq_wo_bmk_pos(recent) ? recent : null;	// check that recent page actually matches current; DATE:2014-05-10
-	}
-	private Xoae_page Get_or_fetch(Xowe_wiki wiki, Xog_history_itm itm) {
-		byte[] page_key = Build_page_key(itm.Wiki(), itm.Page(), itm.Qarg());
-		Xoae_page rv = (Xoae_page)hash.Get_by(page_key);
-		if (rv != null) return rv;
-		Xoa_ttl ttl = Xoa_ttl.Parse(wiki, itm.Page());
-		return wiki.Data_mgr().Load_page_by_ttl(ttl);
-	}
-	private static byte[] Build_page_key(Xoae_page page) {return Build_page_key(page.Wiki().Domain_bry(), page.Ttl().Full_url(), page.Url().Qargs_mgr().To_bry());}
-	private static byte[] Build_page_key(byte[] wiki_key, byte[] page_key, byte[] args_key) {return Bry_.Add_w_dlm(Byte_ascii.Pipe, wiki_key, page_key, args_key);}
-	public static Xog_history_itm new_(Xoae_page pg) {
-		byte[] wiki = pg.Wiki().Domain_bry();
-		byte[] page = pg.Ttl().Full_url();		// get page_name only (no anchor; no query args)
-		byte[] anch = pg.Url().Anch_bry();
-		byte[] qarg = pg.Url().Qargs_mgr().To_bry();
-		boolean redirect_force = pg.Url().Qargs_mgr().Match(Xoa_url_.Qarg__redirect, Xoa_url_.Qarg__redirect__no);
-		String bmk_pos = pg.Html_data().Bmk_pos();
-		if (bmk_pos == null) bmk_pos = Xog_history_itm.Html_doc_pos_toc;	// never allow null doc_pos; set to top
-		return new Xog_history_itm(wiki, page, anch, qarg, redirect_force, bmk_pos);
-	}
-}
+package gplx.xowa.guis.history;
+
+import gplx.Bool_;
+import gplx.Bry_;
+import gplx.Byte_ascii;
+import gplx.Ordered_hash;
+import gplx.Ordered_hash_;
+import gplx.xowa.Xoa_ttl;
+import gplx.xowa.Xoa_url;
+import gplx.xowa.Xoa_url_;
+import gplx.xowa.Xoae_page;
+import gplx.xowa.Xowe_wiki;
+
+public class Xog_history_mgr {
+	private final    Ordered_hash hash = Ordered_hash_.New_bry(); private final    Xog_history_stack stack = new Xog_history_stack();
+	public int Count() {return hash.Count();}
+	public Xoae_page Cur_page(Xowe_wiki wiki) {return Get_or_fetch(wiki, stack.Cur_itm());}
+	public Xoae_page Go_bwd(Xowe_wiki wiki) {return Go_by_dir(wiki, Bool_.N);}
+	public Xoae_page Go_fwd(Xowe_wiki wiki) {return Go_by_dir(wiki, Bool_.Y);}
+	public Xoae_page Go_by_dir(Xowe_wiki wiki, boolean fwd) {
+		Xog_history_itm itm = fwd ? stack.Go_fwd() : stack.Go_bwd();
+		if (itm == Xog_history_itm.Null) return Xoae_page.Empty;
+		Xoae_page rv = Get_or_fetch(wiki, itm);
+		byte[] anch_key = itm.Anch();
+		rv.Url().Anch_bry_(anch_key); // must override anchor as it may be different for cached page
+		rv.Html_data().Bmk_pos_(itm.Bmk_pos());
+		return rv;
+	}
+	public void Add(Xoae_page page) {
+		this.Add(page, Xog_history_mgr.new_(page));
+	}
+	public void Add(Xoae_page page, Xoa_url url) {
+		this.Add(page, Xog_history_mgr.new_(url, page.Html_data().Bmk_pos()));
+	}
+	private void Add(Xoae_page page, Xog_history_itm new_itm) {
+		stack.Add(new_itm);
+		byte[] page_key = Build_page_key(page);
+		if (!hash.Has(page_key))
+			hash.Add(page_key, page);
+	}
+	public void Update_html_doc_pos(Xoae_page page, byte history_nav_type) {
+		Xog_history_itm itm = Get_recent(page, history_nav_type);
+		if (itm != null) itm.Bmk_pos_(page.Html_data().Bmk_pos());
+	}
+	private Xog_history_itm Get_recent(Xoae_page page, byte history_nav_type) {
+		int pos = -1;
+		int list_pos = stack.Cur_pos();
+		switch (history_nav_type) {
+			case Xog_history_stack.Nav_fwd:			pos = list_pos - 1; break;
+			case Xog_history_stack.Nav_bwd:			pos = list_pos + 1; break;
+			case Xog_history_stack.Nav_by_anchor:	pos = list_pos; break;
+		}
+		if (pos < 0 || pos >= stack.Len()) return null;
+		Xog_history_itm recent = stack.Get_at(pos);
+		Xog_history_itm page_itm = Xog_history_mgr.new_(page);
+		return page_itm.Eq_wo_bmk_pos(recent) ? recent : null;	// check that recent page actually matches current; DATE:2014-05-10
+	}
+	private Xoae_page Get_or_fetch(Xowe_wiki wiki, Xog_history_itm itm) {
+		byte[] page_key = Build_page_key(itm.Wiki(), itm.Page(), itm.Qarg());
+		Xoae_page rv = (Xoae_page)hash.Get_by(page_key);
+		if (rv != null) return rv;
+		Xoa_ttl ttl = Xoa_ttl.Parse(wiki, itm.Page());
+		return wiki.Data_mgr().Load_page_by_ttl(ttl);
+	}
+	private static byte[] Build_page_key(Xoae_page page) {return Build_page_key(page.Wiki().Domain_bry(), page.Ttl().Full_url(), page.Url().Qargs_mgr().To_bry());}
+	private static byte[] Build_page_key(byte[] wiki_key, byte[] page_key, byte[] args_key) {return Bry_.Add_w_dlm(Byte_ascii.Pipe, wiki_key, page_key, args_key);}
+	public static Xog_history_itm new_(Xoae_page pg) {
+		byte[] wiki = pg.Wiki().Domain_bry();
+		byte[] page = pg.Ttl().Full_url();		// get page_name only (no anchor; no query args)
+		byte[] anch = pg.Url().Anch_bry();
+		byte[] qarg = pg.Url().Qargs_mgr().To_bry();
+		boolean redirect_force = pg.Url().Qargs_mgr().Match(Xoa_url_.Qarg__redirect, Xoa_url_.Qarg__redirect__no);
+		String bmk_pos = pg.Html_data().Bmk_pos();
+		if (bmk_pos == null) bmk_pos = Xog_history_itm.Html_doc_pos_toc;	// never allow null doc_pos; set to top
+		return new Xog_history_itm(wiki, page, anch, qarg, redirect_force, bmk_pos);
+	}
+	public static Xog_history_itm new_(Xoa_url url, String bmk_pos) {
+		byte[] wiki = url.Wiki_bry();
+		byte[] page = url.Page_bry();
+		byte[] anch = url.Anch_bry();
+		byte[] qarg = url.Qargs_mgr().To_bry();
+		boolean redirect_force = url.Qargs_mgr().Match(Xoa_url_.Qarg__redirect, Xoa_url_.Qarg__redirect__no);
+		if (bmk_pos == null) bmk_pos = Xog_history_itm.Html_doc_pos_toc;	// never allow null doc_pos; set to top
+		return new Xog_history_itm(wiki, page, anch, qarg, redirect_force, bmk_pos);
+	}
+}

+ 84 - 65
400_xowa/src/gplx/xowa/guis/urls/Xog_url_wkr.java

@@ -1,6 +1,6 @@
 /*
 /*
 XOWA: the XOWA Offline Wiki Application
 XOWA: the XOWA Offline Wiki Application
-Copyright (C) 2012-2017 gnosygnu@gmail.com
+Copyright (C) 2012-2020 gnosygnu@gmail.com
 
 
 XOWA is licensed under the terms of the General Public License (GPL) Version 3,
 XOWA is licensed under the terms of the General Public License (GPL) Version 3,
 or alternatively under the terms of the Apache License Version 2.0.
 or alternatively under the terms of the Apache License Version 2.0.
@@ -13,67 +13,86 @@ The terms of each license can be found in the source code repository:
 GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
 GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
 Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
 Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
 */
 */
-package gplx.xowa.guis.urls; import gplx.*; import gplx.xowa.*; import gplx.xowa.guis.*;
-import gplx.core.net.*; import gplx.core.net.qargs.*; import gplx.core.envs.*;
-import gplx.gfui.controls.standards.*;
-import gplx.xowa.files.*; import gplx.xowa.files.repos.*; import gplx.xowa.files.origs.*;
-import gplx.langs.htmls.encoders.*; import gplx.xowa.htmls.hrefs.*; import gplx.xowa.htmls.doms.*;
-import gplx.xowa.guis.views.*;
-public class Xog_url_wkr {
-	private final    Xoa_url tmp_url = Xoa_url.blank();
-	private Xoae_app app; private Xog_win_itm win; private Xowe_wiki wiki; private Xoae_page page;
-	public Xoa_url Parse(Xog_win_itm win, String href_str) {
-		if (href_str == null) return tmp_url;	// text is not link; return;
-		byte[] href_bry = Bry_.new_u8(href_str);
-		this.win = win; this.app = win.App(); 
-		this.page = win.Active_page();
-		this.wiki = win.Active_tab().Wiki();
-		app.Html__href_parser().Parse_as_url(tmp_url, href_bry, wiki, page.Ttl().Page_url());
-		return tmp_url;
-	}
-	public void Init(Xowe_wiki wiki) {	// TEST:
-		this.wiki = wiki;
-	}
-	public Xoa_url Exec_url(Xoa_url url) {
-		switch (url.Tid()) {
-			case Xoa_url_.Tid_unknown:		return Xoa_url.Null;										// unknown; return null which will become a noop
-			case Xoa_url_.Tid_inet:			return Exec_url_http(app);									// http://site.org
-			case Xoa_url_.Tid_anch:			return Exec_url_anchor(win);								// #anchor
-			case Xoa_url_.Tid_xcmd:			return Exec_url_xowa(app);									// xowa:app.version or /xcmd/app.version
-			case Xoa_url_.Tid_file:			return Exec_url_file(app, wiki, page, win, url.Raw());		// file:///xowa/A.png
-			case Xoa_url_.Tid_page:			return Exec_url_page(wiki, url.Orig());						// /wiki/Page
-			default:						throw Err_.new_unhandled(url.Tid());
-		}
-	}
-	private Xoa_url Exec_url_xowa(Xoae_app app) {		// EX: xowa:app.version
-		// NOTE: must catch exception else it will bubble to SWT browser and raise secondary exception of xowa is not a registered protocol
-		try {app.Gfs_mgr().Run_str(String_.new_u8(tmp_url.Page_bry()));}
-		catch (Exception e) {app.Gui_mgr().Kit().Ask_ok("", "", Err_.Message_gplx_full(e));}
-		return Rslt_handled;
-	}
-	private Xoa_url Exec_url_http(Xoae_app app) {		// EX: http://a.org
-		app.Prog_mgr().Exec_view_web(tmp_url.Raw());
-		return Rslt_handled;
-	}
-	private Xoa_url Exec_url_anchor(Xog_win_itm win) {	// EX: #anchor
-		win.Active_html_itm().Scroll_page_by_id_gui(tmp_url.Anch_str());	// NOTE: was originally directly; changed to call on thread; DATE:2014-05-03
-		return Rslt_handled;
-	}
-	private Xoa_url Exec_url_file(Xoae_app app, Xowe_wiki cur_wiki, Xoae_page page, Xog_win_itm win, byte[] href_bry) {	// EX: file:///xowa/A.png
-		Xog_url_wkr__file wkr = new Xog_url_wkr__file(app, cur_wiki, page);
-		wkr.Extract_data(win.Active_html_box().Html_js_eval_proc_as_str(Xog_js_procs.Doc__root_html_get), href_bry);
-		wkr.Download_and_run();
-		return Rslt_handled;
-	}
-	private Xoa_url Exec_url_page(Xowe_wiki wiki, byte[] href_bry) {	// EX: "Page"; "/wiki/Page"; // rewritten; DATE:2014-01-19
-		return wiki.Utl__url_parser().Parse(href_bry);
-	}
-	public static Xoa_url Rslt_handled = null;
-	public static Xoa_url Exec_url(Xog_win_itm win, String href_str) {
-		Xog_url_wkr url_wkr = new Xog_url_wkr();
-		Xoa_url url = url_wkr.Parse(win, href_str);
-		return url_wkr.Exec_url(url);
-	}
-	public static void Get_href_url() {
-	}
-}
+package gplx.xowa.guis.urls;
+
+import gplx.Bry_;
+import gplx.Err_;
+import gplx.String_;
+import gplx.xowa.Xoa_ttl;
+import gplx.xowa.Xoa_url;
+import gplx.xowa.Xoa_url_;
+import gplx.xowa.Xoae_app;
+import gplx.xowa.Xoae_page;
+import gplx.xowa.Xowe_wiki;
+import gplx.xowa.guis.views.Xog_js_procs;
+import gplx.xowa.guis.views.Xog_win_itm;
+
+public class Xog_url_wkr {
+	private final    Xoa_url tmp_url = Xoa_url.blank();
+	private Xoae_app app; private Xog_win_itm win; private Xowe_wiki wiki; private Xoae_page page;
+	public Xoa_url Parse(Xog_win_itm win, String href_str) {
+		if (href_str == null) return tmp_url;	// text is not link; return;
+		byte[] href_bry = Bry_.new_u8(href_str);
+		this.win = win; this.app = win.App(); 
+		this.page = win.Active_page();
+		this.wiki = win.Active_tab().Wiki();
+		app.Html__href_parser().Parse_as_url(tmp_url, href_bry, wiki, page.Ttl().Page_url());
+		return tmp_url;
+	}
+	public void Init(Xowe_wiki wiki) {	// TEST:
+		this.wiki = wiki;
+	}
+	public Xoa_url Exec_url(Xoa_url url) {
+		switch (url.Tid()) {
+			case Xoa_url_.Tid_unknown:		return Xoa_url.Null;										// unknown; return null which will become a noop
+			case Xoa_url_.Tid_inet:			return Exec_url_http(app);									// http://site.org
+			case Xoa_url_.Tid_anch:			return Exec_url_anchor(win);								// #anchor
+			case Xoa_url_.Tid_xcmd:			return Exec_url_xowa(app);									// xowa:app.version or /xcmd/app.version
+			case Xoa_url_.Tid_file:			return Exec_url_file(app, wiki, page, win, url.Raw());		// file:///xowa/A.png
+			case Xoa_url_.Tid_page:			return Exec_url_page(wiki, url.Orig());						// /wiki/Page
+			default:						throw Err_.new_unhandled(url.Tid());
+		}
+	}
+	private Xoa_url Exec_url_xowa(Xoae_app app) {		// EX: xowa:app.version
+		// NOTE: must catch exception else it will bubble to SWT browser and raise secondary exception of xowa is not a registered protocol
+		try {app.Gfs_mgr().Run_str(String_.new_u8(tmp_url.Page_bry()));}
+		catch (Exception e) {app.Gui_mgr().Kit().Ask_ok("", "", Err_.Message_gplx_full(e));}
+		return Rslt_handled;
+	}
+	private Xoa_url Exec_url_http(Xoae_app app) {		// EX: http://a.org
+		app.Prog_mgr().Exec_view_web(tmp_url.Raw());
+		return Rslt_handled;
+	}
+	private Xoa_url Exec_url_anchor(Xog_win_itm win) {	// EX: #anchor
+		// 2014-05-03|was originally called directly; changed to call on thread
+		win.Active_html_itm().Scroll_page_by_id_gui(tmp_url.Anch_str());
+
+		// 2020-09-22|ISSUE#:799|SWT 4.16 changes anchors from "file:///#anchor" to "en.w/wiki/page/#anchor"
+		if (app.Mode().Tid_is_gui()) {
+			// manually update url box
+			win.Gui_mgr().Browser_win().Url_box().Text_(tmp_url.To_str());
+
+			// manually register url; note that tmp_url needs to be passed b/c page.Url() doesn't have #anchor info
+			win.Tab_mgr().Active_tab().History_mgr().Add(page, tmp_url);
+		}
+
+		return Rslt_handled;
+	}
+	private Xoa_url Exec_url_file(Xoae_app app, Xowe_wiki cur_wiki, Xoae_page page, Xog_win_itm win, byte[] href_bry) {	// EX: file:///xowa/A.png
+		Xog_url_wkr__file wkr = new Xog_url_wkr__file(app, cur_wiki, page);
+		wkr.Extract_data(win.Active_html_box().Html_js_eval_proc_as_str(Xog_js_procs.Doc__root_html_get), href_bry);
+		wkr.Download_and_run();
+		return Rslt_handled;
+	}
+	private Xoa_url Exec_url_page(Xowe_wiki wiki, byte[] href_bry) {	// EX: "Page"; "/wiki/Page"; // rewritten; DATE:2014-01-19
+		return wiki.Utl__url_parser().Parse(href_bry);
+	}
+	public static Xoa_url Rslt_handled = null;
+	public static Xoa_url Exec_url(Xog_win_itm win, String href_str) {
+		Xog_url_wkr url_wkr = new Xog_url_wkr();
+		Xoa_url url = url_wkr.Parse(win, href_str);
+		return url_wkr.Exec_url(url);
+	}
+	public static void Get_href_url() {
+	}
+}