/* ********************************************************************** **
** Copyright notice **
** **
** (c) 2005-2009 RSSOwl Development Team **
** http://www.rssowl.org/ **
** **
** All rights reserved **
** **
** This program and the accompanying materials are made available under **
** the terms of the Eclipse Public License v1.0 which accompanies this **
** distribution, and is available at: **
** http://www.rssowl.org/legal/epl-v10.html **
** **
** A copy is found in the file epl-v10.html and important notices to the **
** license from the team is found in the textfile LICENSE.txt distributed **
** in this package. **
** **
** This copyright notice MUST APPEAR in all copies of the file! **
** **
** Contributors: **
** RSSOwl Development Team - initial API and implementation **
** **
** ********************************************************************** */
package org.rssowl.ui.internal.util;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.osgi.service.datalocation.Location;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTError;
import org.eclipse.swt.browser.Browser;
import org.eclipse.swt.browser.LocationEvent;
import org.eclipse.swt.browser.LocationListener;
import org.eclipse.swt.browser.OpenWindowListener;
import org.eclipse.swt.browser.WindowEvent;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.ui.PlatformUI;
import org.rssowl.core.Owl;
import org.rssowl.core.internal.persist.pref.DefaultPreferences;
import org.rssowl.core.persist.pref.IPreferenceScope;
import org.rssowl.core.util.StringUtils;
import org.rssowl.core.util.URIUtils;
import org.rssowl.ui.internal.Activator;
import org.rssowl.ui.internal.Application;
import org.rssowl.ui.internal.ApplicationServer;
import org.rssowl.ui.internal.ApplicationWorkbenchWindowAdvisor;
import org.rssowl.ui.internal.ILinkHandler;
import org.rssowl.ui.internal.OwlUI;
import org.rssowl.ui.internal.editors.browser.WebBrowserContext;
import org.rssowl.ui.internal.editors.browser.WebBrowserView;
import java.io.File;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Instances of <code>CBrowser</code> wrap around the Browser-Widget and enhance
* it by some means.
*
* @author bpasero
*/
public class CBrowser {
/* Delay before a URL is set in a IE Browser (guess) */
private static final int IE_BROWSER_URL_DELAY = 800;
/* Delay before a URL is set in a Mozilla Browser (guess) */
private static final int MOZILLA_BROWSER_URL_DELAY = 2000;
/* JavaScript: print() Method */
private static final String JAVA_SCRIPT_PRINT = "window.print();"; //$NON-NLS-1$
/* System Properties to configure proxy with XULRunner */
private static final String XULRUNNER_PROXY_HOST = "network.proxy_host"; //$NON-NLS-1$
private static final String XULRUNNER_PROXY_PORT = "network.proxy_port"; //$NON-NLS-1$
/* System Property to force disable XULRunner even if registered */
private static final String DISABLE_XULRUNNER = "noXulrunner"; //$NON-NLS-1$
/* Local XULRunner Runtime Constants */
private static final String XULRUNNER_PATH_PROPERTY = "org.eclipse.swt.browser.XULRunnerPath"; //$NON-NLS-1$
private static final String XULRUNNER_DIR = "xulrunner"; //$NON-NLS-1$
private static boolean fgXulrunnerRuntimeTested = false;
/* Delay in millies after a refresh until to allow ext. navigation again (see Bug 1429) */
private static final long REFRESH_NAVIGATION_DELAY = 3000;
/* Mac only: Delay in millies after a URL change until to allow ext. navigation again (see Bug 1350) */
private static final long URL_CHANGE_NAVIGATION_DELAY = 1000;
/* Flag to check if Mozilla is available on Windows */
private static boolean fgMozillaAvailable = true;
/* Flag to check if Mozilla is running as Browser */
private static boolean fgMozillaRunningOnWindows;
/* Flag to check if RSSOwl failed to set a Feature in IE */
private static boolean fgCoInternetSetFeatureError;
/* Track if the Navigation Sound Disable Feature was set */
private static boolean fgNavigationSoundsDisabled;
/* Track if the Popup Blocker Feature was set */
private static boolean fgPopupBlockerEnabled;
/* Windows IE Only Features (CoInternetSetFeatureEnabled) */
private static final int FEATURE_WEBOC_POPUPMANAGEMENT = 5;
private static final int FEATURE_SECURITYBAND = 9;
private static final int FEATURE_DISABLE_NAVIGATION_SOUNDS = 21;
private static final int SET_FEATURE_ON_PROCESS = 0x2;
/* Blacklist of common iframes that cause external window opening */
private static final List<String> EXTERNAL_BLACKLIST = Arrays.asList("ipv6/exp/iframe.html", "/plugins/like.php", "scribd.com/embeds", "youtube.com/embed/"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
/* Count failing JS executions and log as appropiate */
private static final AtomicInteger FAILING_JS_COUNTER = new AtomicInteger();
private static final int MAX_FAILING_JS_LOGS = 5;
private static final int MAX_FAILING_JS_LOG_LENGTH = 500;
private Browser fBrowser;
private boolean fAllowExternalNavigation;
private boolean fDisableXulrunner;
private boolean fCanOpenLinksInTabs;
private long fLastRefresh;
private long fLastUrlChange;
private IPreferenceScope fPreferences;
private IPreferenceScope fEclipsePreferences;
private Map<String, ILinkHandler> fLinkHandler;
private String fLastSetUrl;
/**
* @param parent The Parent Composite of this Browser.
* @param style The Style to use for the Browser-
*/
public CBrowser(Composite parent, int style) {
fPreferences = Owl.getPreferenceService().getGlobalScope();
fEclipsePreferences = Owl.getPreferenceService().getEclipseScope();
fDisableXulrunner = System.getProperty(DISABLE_XULRUNNER) != null;
try {
fBrowser = createBrowser(parent, style);
} catch (SWTError e) {
MessageBox box = new MessageBox(parent.getShell(), SWT.ICON_ERROR | SWT.OK | SWT.CANCEL);
box.setText(Messages.CBrowser_ERROR_CREATE_BROWSER);
box.setMessage(Messages.CBrowser_ERROR_CREATE_BROWSER_MSG);
if (box.open() == SWT.OK)
BrowserUtils.openLinkExternal("http://www.rssowl.org/help#item_6j"); //$NON-NLS-1$
throw e;
}
fLinkHandler = new HashMap<String, ILinkHandler>();
hookListeners();
/* Add custom Context Menu on OS where this is not supported */
if (Application.IS_LINUX || (useMozillaOnWindows()))
hookMenu();
}
private boolean useMozillaOnWindows() {
return Application.IS_WINDOWS && fgMozillaAvailable && !fDisableXulrunner;
}
/**
* @param canOpenLinksInTabs if <code>true</code>, enables links selected from
* the user to open in new tabs instead of the same browser instance or
* externally.
*/
public void setCanOpenLinksInTabs(boolean canOpenLinksInTabs) {
fCanOpenLinksInTabs = canOpenLinksInTabs;
}
/**
* Adds the given Handler to this instance responsible for the given Command.
*
* @param commandId The ID of the Command the provided Handler is responsible
* for.
* @param handler The Handler responsible for the fiven ID.
*/
public void addLinkHandler(String commandId, ILinkHandler handler) {
fLinkHandler.put(commandId, handler);
}
private Browser createBrowser(Composite parent, int style) {
Browser browser = null;
/* Properly configure Proxy for Firefox/XULRunner if required */
if (Application.IS_LINUX || (useMozillaOnWindows())) {
String proxyHost = fEclipsePreferences.getString(DefaultPreferences.ECLIPSE_PROXY_HOST);
String proxyPort = fEclipsePreferences.getString(DefaultPreferences.ECLIPSE_PROXY_PORT);
if (useProxy() && StringUtils.isSet(proxyHost) && StringUtils.isSet(proxyPort)) {
System.setProperty(XULRUNNER_PROXY_HOST, proxyHost);
System.setProperty(XULRUNNER_PROXY_PORT, proxyPort);
}
}
/* Try Mozilla over IE on Windows */
if (useMozillaOnWindows()) {
try {
browser = new Browser(parent, style | SWT.MOZILLA);
fgMozillaRunningOnWindows = true;
} catch (SWTError e) {
fgMozillaAvailable = false;
if (!"No more handles [Could not detect registered XULRunner to use]".equals(e.getMessage())) //This happens too often to log it //$NON-NLS-1$
Activator.getDefault().getLog().log(Activator.getDefault().createInfoStatus(e.getMessage(), null));
}
}
/* Linux: Multiple approaches to get XULRunner right */
if (Application.IS_LINUX && !fgXulrunnerRuntimeTested) {
boolean xulrunnerPathSpecified = (System.getProperty(XULRUNNER_PATH_PROPERTY) != null);
/* 1.) User has XULRunner path explicitly set, try it first */
if (xulrunnerPathSpecified) {
try {
browser = new Browser(parent, styleForLinux(style));
} catch (SWTError e) {
Activator.safeLogInfo(NLS.bind("Error loading XULRunner from system property (''{0}'')", System.getProperty(XULRUNNER_PATH_PROPERTY))); //Ignored (we continue trying) //$NON-NLS-1$
}
}
/* 2.) Fallback to default OS XULRunner runtime */
if (browser == null) {
if (xulrunnerPathSpecified)
System.clearProperty(XULRUNNER_PATH_PROPERTY);
try {
browser = new Browser(parent, styleForLinux(style));
} catch (SWTError e) {
Activator.safeLogInfo("Error loading system default XULRunner"); //Ignored (we continue trying) //$NON-NLS-1$
}
}
/* 3.) Try with deployed XULRunner runtime */
if (browser == null) {
File xulRunnerRuntimeDir = getXULRunnerRuntimeDir();
if (xulRunnerRuntimeDir != null) {
System.setProperty(XULRUNNER_PATH_PROPERTY, xulRunnerRuntimeDir.toString());
try {
browser = new Browser(parent, styleForLinux(style));
} catch (SWTError e) {
Activator.safeLogInfo("Error loading XULRunner from bundled version"); //Ignored (we continue trying) //$NON-NLS-1$
}
}
}
fgXulrunnerRuntimeTested = true; //Avoid redundant lookup if XULRunner runtime found
}
/* Any other OS, or Mozilla unavailable, use default */
if (browser == null)
browser = new Browser(parent, styleForLinux(style));
/* Add Focusless Scroll Hook on Windows */
if (Application.IS_WINDOWS)
browser.setData(ApplicationWorkbenchWindowAdvisor.FOCUSLESS_SCROLL_HOOK, true);
/* Disable IE Navigation Sound (Windows Only) */
Method method = null;
if (!fgNavigationSoundsDisabled) {
method = callCoInternetSetFeatureEnabled(method, FEATURE_DISABLE_NAVIGATION_SOUNDS, SET_FEATURE_ON_PROCESS, true);
fgNavigationSoundsDisabled = true;
}
/* Set Popupblocker if necessary */
if (Application.IS_WINDOWS) {
boolean prefEnablePopupBlocker = fPreferences.getBoolean(DefaultPreferences.ENABLE_IE_POPUP_BLOCKER);
if (prefEnablePopupBlocker != fgPopupBlockerEnabled) {
method = callCoInternetSetFeatureEnabled(method, FEATURE_WEBOC_POPUPMANAGEMENT, SET_FEATURE_ON_PROCESS, prefEnablePopupBlocker);
callCoInternetSetFeatureEnabled(method, FEATURE_SECURITYBAND, SET_FEATURE_ON_PROCESS, prefEnablePopupBlocker);
fgPopupBlockerEnabled = prefEnablePopupBlocker;
}
}
/* Clear all Link Handlers upon disposal */
browser.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
fLinkHandler.clear();
}
});
return browser;
}
private File getXULRunnerRuntimeDir() {
/* Retrieve Install Location */
Location installLocation = Platform.getInstallLocation();
if (installLocation == null || installLocation.getURL() == null)
return null;
/* Retrieve Program Dir as File Object */
File programDir = toFile(installLocation.getURL());
if (programDir == null || !programDir.isDirectory() || !programDir.exists())
return null;
/* Retrieve the XULRunner Directory */
File xulrunnerDir = new File(programDir, XULRUNNER_DIR);
if (!xulrunnerDir.exists() || !xulrunnerDir.isDirectory())
return null;
return xulrunnerDir;
}
private File toFile(URL url) {
try {
return new File(url.toURI());
} catch (URISyntaxException e) {
return new File(url.getPath());
}
}
/* If RSSOwl runs with SWT 3.7, force use of Mozilla over WebKit */
private int styleForLinux(int style) {
if (Application.IS_LINUX && SWT.getVersion() >= 3700)
return style | SWT.MOZILLA;
return style;
}
private Method callCoInternetSetFeatureEnabled(Method method, int feature, int scope, boolean enable) {
if (!fgCoInternetSetFeatureError && Application.IS_WINDOWS && !useMozillaOnWindows()) {
try {
Class<?> clazz = Class.forName("org.eclipse.swt.internal.win32.OS"); //$NON-NLS-1$
if (method == null)
method = clazz.getMethod("CoInternetSetFeatureEnabled", int.class, int.class, boolean.class); //$NON-NLS-1$
method.invoke(clazz, feature, scope, enable);
return method;
} catch (Throwable t) {
Activator.getDefault().logError(t.getMessage(), t);
fgCoInternetSetFeatureError = true;
}
}
return null;
}
private boolean useProxy() {
boolean useProxy = fEclipsePreferences.getBoolean(DefaultPreferences.ECLIPSE_USE_PROXY);
boolean useSystemProxy = fEclipsePreferences.getBoolean(DefaultPreferences.ECLIPSE_USE_SYSTEM_PROXY);
return useProxy && !useSystemProxy;
}
private void hookMenu() {
MenuManager manager = new MenuManager();
manager.setRemoveAllWhenShown(true);
manager.addMenuListener(new IMenuListener() {
public void menuAboutToShow(IMenuManager manager) {
/* Back */
manager.add(new Action(Messages.CBrowser_BACK) {
@Override
public void run() {
fBrowser.back();
}
@Override
public boolean isEnabled() {
return fBrowser.isBackEnabled();
}
@Override
public ImageDescriptor getImageDescriptor() {
return OwlUI.getImageDescriptor("icons/etool16/backward.gif"); //$NON-NLS-1$
}
});
/* Forward */
manager.add(new Action(Messages.CBrowser_FORWARD) {
@Override
public void run() {
fBrowser.forward();
}
@Override
public boolean isEnabled() {
return fBrowser.isForwardEnabled();
}
@Override
public ImageDescriptor getImageDescriptor() {
return OwlUI.getImageDescriptor("icons/etool16/forward.gif"); //$NON-NLS-1$
}
});
/* Reload */
manager.add(new Separator());
manager.add(new Action(Messages.CBrowser_RELOAD) {
@Override
public void run() {
refresh();
}
@Override
public ImageDescriptor getImageDescriptor() {
return OwlUI.getImageDescriptor("icons/elcl16/reload.gif"); //$NON-NLS-1$
}
});
/* Stop */
manager.add(new Action(Messages.CBrowser_STOP) {
@Override
public void run() {
fBrowser.stop();
}
@Override
public ImageDescriptor getImageDescriptor() {
return OwlUI.getImageDescriptor("icons/etool16/cancel.gif"); //$NON-NLS-1$
}
});
}
});
Menu menu = manager.createContextMenu(fBrowser);
fBrowser.setMenu(menu);
}
/**
* Returns the Browser-Widget this class is wrapping.
*
* @return The Browser-Widget this class is wrapping.
*/
public Browser getControl() {
return fBrowser;
}
/**
* Refresh the Browser.
*/
public void refresh() {
fLastRefresh = System.currentTimeMillis();
fBrowser.refresh();
}
/**
* Browse to the given URL.
*
* @param url The URL to browse to.
*/
public void setUrl(String url) {
setUrl(url, false);
}
private boolean supportExternalNavigation() {
return OwlUI.useExternalBrowser() || (fCanOpenLinksInTabs && fPreferences.getBoolean(DefaultPreferences.OPEN_LINKS_IN_NEW_TAB));
}
/**
* Browse to the given URL.
*
* @param url The URL to browse to.
* @param allowExternalNavigation <code>true</code> to allow external
* navigation and <code>false</code> otherwise.
*/
public void setUrl(String url, boolean allowExternalNavigation) {
fLastSetUrl = url;
/* Caller wants to allow external navigation - validate first */
if (allowExternalNavigation) {
/*
* The only reason to allow external navigation is when the URL is
* actually external (non local) and the user has choosen to open
* links in the external Browser.
*/
if (StringUtils.isSet(url) && !URIUtils.ABOUT_BLANK.equals(url) && !ApplicationServer.getDefault().isNewsServerUrl(url) && supportExternalNavigation())
fAllowExternalNavigation = true;
else
fLastUrlChange = System.currentTimeMillis();
}
/* Normal situation: External navigation blocked until page is loaded */
else {
fAllowExternalNavigation = false;
fLastUrlChange = System.currentTimeMillis();
}
fBrowser.setUrl(url);
}
/**
* Execute some code while external navigation is blocked.
*
* @param runnable the code to execute while external navigation should be
* blocked.
*/
public void blockExternalNavigationWhile(Runnable runnable) {
fAllowExternalNavigation = false;
try {
runnable.run();
} finally {
fAllowExternalNavigation = true;
}
}
/**
* Navigate to the previous session history item.
*
* @return <code>true</code> if the operation was successful and
* <code>false</code> otherwise
*/
public boolean back() {
fAllowExternalNavigation = false;
fLastUrlChange = System.currentTimeMillis();
return fBrowser.back();
}
/**
* Navigate to the next session history item.
*
* @return <code>true</code> if the operation was successful and
* <code>false</code> otherwise
*/
public boolean forward() {
fAllowExternalNavigation = false;
fLastUrlChange = System.currentTimeMillis();
return fBrowser.forward();
}
/**
* Print the Browser using the JavaScript print() method
*
* @return <code>TRUE</code> in case of success, <code>FALSE</code> otherwise
*/
public boolean print() {
return execute(JAVA_SCRIPT_PRINT, null);
}
/**
* Executes JavaScript in this Browser instance.
*
* @param js the JavaScript to execute in the browser.
* @param context the context from where the call is made, will be added to
* the log in case execution fails.
* @return <code>true</code> if the JavaScript was successfully executed and
* <code>false</code> otherwise.
*/
public boolean execute(String js, String context) {
return execute(js, true, context);
}
/**
* Executes JavaScript in this Browser instance.
*
* @param js the JavaScript to execute in the browser.
* @param handleJSEnablement if <code>true</code> make sure that JS becomes
* enabled if not and <code>false</code> otherwise
* @param context the context from where the call is made, will be added to
* the log in case execution fails.
* @return <code>true</code> if the JavaScript was successfully executed and
* <code>false</code> otherwise.
*/
public boolean execute(String js, boolean handleJSEnablement, String context) {
if (fBrowser.isDisposed())
return false;
if (handleJSEnablement && shouldDisableScript())
setScriptDisabled(false);
try {
return internalExecute(js, context);
} finally {
if (handleJSEnablement && shouldDisableScript())
setScriptDisabled(true);
}
}
private boolean internalExecute(String js, String context) {
boolean res = fBrowser.execute(js);
if (!res) {
if (FAILING_JS_COUNTER.incrementAndGet() < MAX_FAILING_JS_LOGS) {
if (!StringUtils.isSet(context))
context = "Unknown Context"; //$NON-NLS-1$
if (js.length() > MAX_FAILING_JS_LOG_LENGTH)
js = js.substring(0, MAX_FAILING_JS_LOG_LENGTH);
boolean isMozilla = isMozillaRunningOnWindows();
if (!isMozilla)
Activator.getDefault().logError(NLS.bind("Failed to execute JavaScript ({0}): {1}", context, js), null); //$NON-NLS-1$
else
Activator.getDefault().logError(NLS.bind("Failed to execute JavaScript ({0}, XULRunner): {1}", context, js), null); //$NON-NLS-1$
}
}
return res;
}
/* Special handling of opened websites on Windows (IE and Mozilla) */
private OpenWindowListener getOpenWindowListener() {
return new OpenWindowListener() {
public void open(WindowEvent event) {
/* Special handle external Browser */
boolean useMozilla = useMozillaOnWindows();
if (OwlUI.useExternalBrowser()) {
/* Avoid IE being loaded from SWT on Windows */
final Browser tempBrowser = new Browser(fBrowser.getShell(), useMozilla ? SWT.MOZILLA : styleForLinux(SWT.NONE));
tempBrowser.setVisible(false);
event.browser = tempBrowser;
tempBrowser.getDisplay().timerExec(useMozilla ? MOZILLA_BROWSER_URL_DELAY : IE_BROWSER_URL_DELAY, new Runnable() {
public void run() {
if (!tempBrowser.isDisposed() && PlatformUI.isWorkbenchRunning()) {
String url = tempBrowser.getUrl();
tempBrowser.dispose();
if (StringUtils.isSet(url))
BrowserUtils.openLinkExternal(URIUtils.toUnManaged(url));
}
}
});
return;
}
/* Open internal Browser in a new Tab */
if (fEclipsePreferences.getBoolean(DefaultPreferences.ECLIPSE_MULTIPLE_TABS)) {
WebBrowserView browserView = BrowserUtils.openLinkInternal(null, WebBrowserContext.createFrom(Messages.CBrowser_LOADING));
if (browserView != null)
event.browser = browserView.getBrowser().getControl();
}
/* Open internal Browser in same Browser */
else {
final Browser tempBrowser = new Browser(fBrowser.getShell(), useMozilla ? SWT.MOZILLA : styleForLinux(SWT.NONE));
tempBrowser.setVisible(false);
event.browser = tempBrowser;
tempBrowser.getDisplay().timerExec(useMozilla ? MOZILLA_BROWSER_URL_DELAY : IE_BROWSER_URL_DELAY, new Runnable() {
public void run() {
if (!tempBrowser.isDisposed() && PlatformUI.isWorkbenchRunning()) {
String url = tempBrowser.getUrl();
tempBrowser.dispose();
if (StringUtils.isSet(url))
setUrl(url);
}
}
});
}
}
};
}
/**
* @return <code>true</code> if this browser is IE and <code>false</code>
* otherwise.
*/
public boolean isIE() {
return Application.IS_WINDOWS && !fBrowser.isDisposed() && (fBrowser.getStyle() & SWT.MOZILLA) == 0;
}
/**
* @return <code>true</code> if this browser is Mozilla on Windows and
* <code>false</code> otherwise. Note that this will always return
* <code>false</code> as long as a Browser has not been created.
*/
public static boolean isMozillaRunningOnWindows() {
return Application.IS_WINDOWS && fgMozillaRunningOnWindows;
}
/**
* @param disabled <code>true</code> to disable JavaScript and
* <code>false</code> otherwise.
*/
public void setScriptDisabled(Boolean disabled) {
/* Not Supported on Windows XULRunner */
if (isMozillaRunningOnWindows())
return;
/* Windows */
if (Application.IS_WINDOWS) {
try {
Method method = fBrowser.getClass().getMethod("setScriptDisabled", Boolean.class); //$NON-NLS-1$
if (method != null)
method.invoke(fBrowser, disabled);
} catch (Exception e) {
/* Ignore Silently */
}
}
/* Linux / Mac */
else {
try {
Method method = fBrowser.getClass().getMethod("setJavascriptEnabled", boolean.class); //$NON-NLS-1$
if (method != null)
method.invoke(fBrowser, !disabled);
} catch (Exception e) {
/* Ignore Silently */
}
}
}
/**
* @return <code>true</code> if the configuration is to disable JavaScript and
* <code>false</code> otherwise.
*/
public boolean shouldDisableScript() {
return fPreferences.getBoolean(DefaultPreferences.DISABLE_JAVASCRIPT);
}
private boolean shouldDisableScript(String location) {
if (!shouldDisableScript())
return false;
if (!StringUtils.isSet(location))
return true;
String[] websites = fPreferences.getStrings(DefaultPreferences.DISABLE_JAVASCRIPT_EXCEPTIONS);
if (websites != null) {
for (String website : websites) {
if (location.contains(website))
return false;
}
}
return true;
}
private void hookListeners() {
/* Listen to Open-Window-Changes */
fBrowser.addOpenWindowListener(getOpenWindowListener());
/* Listen to Location-Changes */
fBrowser.addLocationListener(new LocationListener() {
public void changed(LocationEvent event) {
/* The website is fully loaded and external navigation is supported from now on. */
if (event.top) {
/*
* Bug: It is possible that a changed() event is triggered for a previously visited
* site although another site was already browsed to. In this case, we check if the
* URL that was last set to the browser equals the event location to make sure the
* changed() event is captured for the right moment.
*/
if (StringUtils.isSet(event.location) && event.location.equals(fLastSetUrl))
fAllowExternalNavigation = true;
}
}
public void changing(LocationEvent event) {
/* Update JavaScript enabled state */
if (StringUtils.isSet(event.location) && !URIUtils.ABOUT_BLANK.equals(event.location) && !event.location.startsWith(URIUtils.JS_IDENTIFIER))
setScriptDisabled(shouldDisableScript(event.location));
/* Handle Application Protocol */
if (event.location != null && event.location.contains(ILinkHandler.HANDLER_PROTOCOL)) {
try {
final URI link = new URI(event.location);
final String host = URIUtils.safeGetHost(link);
if (StringUtils.isSet(host) && fLinkHandler.containsKey(host)) {
try {
fLinkHandler.get(host).handle(host, link);
} finally {
event.doit = false;
}
return;
}
} catch (URISyntaxException e) {
Activator.getDefault().getLog().log(Activator.getDefault().createErrorStatus(e.getMessage(), e));
}
}
/* Support opening Links in External Browser */
if (supportExternalNavigation()) {
/* The URL must not be empty or about:blank (Problem on Linux) */
if (!StringUtils.isSet(event.location) || URIUtils.ABOUT_BLANK.equals(event.location))
return;
/* Do not Let local ApplicationServer URLs open */
if (ApplicationServer.getDefault().isNewsServerUrl(event.location))
return;
/* Find out if the Link is Managed (under our control) */
boolean isManaged = URIUtils.isManaged(event.location);
/* Only proceed if external navigation should not be blocked */
if (!fAllowExternalNavigation) {
if (!isManaged) //Workaround for Bug 1347: External Browser not used if page still loading
return;
}
/* See Bug 1429: Potential external link opening from Browser.refresh() */
long currentTimeMillis = System.currentTimeMillis();
if (!isManaged && currentTimeMillis - fLastRefresh < REFRESH_NAVIGATION_DELAY)
return;
/* See Bug 1350: Potential browser popup when reading articles */
if (Application.IS_MAC && !isManaged && currentTimeMillis - fLastUrlChange < URL_CHANGE_NAVIGATION_DELAY)
return;
/* Check for common URLs that are blacklisted */
for (String blacklistItem : EXTERNAL_BLACKLIST) {
if (event.location.contains(blacklistItem))
return;
}
/* Finally, cancel event and open URL external */
event.doit = false;
if (fCanOpenLinksInTabs && fPreferences.getBoolean(DefaultPreferences.OPEN_LINKS_IN_NEW_TAB))
BrowserUtils.openLinkInternal(URIUtils.toUnManaged(event.location), null);
else
BrowserUtils.openLinkExternal(URIUtils.toUnManaged(event.location));
}
}
});
}
}