blob: 5b46a44bdcba1d4c0805a50859ba5f69111997d7 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.web.browser.api;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.prefs.PreferenceChangeEvent;
import java.util.prefs.PreferenceChangeListener;
import org.netbeans.core.IDESettings;
import org.netbeans.modules.extbrowser.ExtWebBrowser;
import org.netbeans.modules.extbrowser.PrivateBrowserFamilyId;
import org.netbeans.modules.web.browser.spi.EnhancedBrowserFactory;
import org.openide.awt.HtmlBrowser;
import org.openide.cookies.InstanceCookie;
import org.openide.filesystems.*;
import org.openide.loaders.DataFolder;
import org.openide.loaders.DataObject;
import org.openide.util.Exceptions;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.openide.util.NbPreferences;
import org.openide.util.lookup.Lookups;
/**
* Access to browsers available in the IDE.
* @see WebBrowserSupport
*/
public final class WebBrowsers {
/**
* Property fired when list of browsers has changed.
*/
public static final String PROP_BROWSERS = "browsers"; // NOI18N
/**
* Property fired when default browser has changed.
*/
public static final String PROP_DEFAULT_BROWSER = "browser"; // NOI18N
private static final WebBrowsers INST = new WebBrowsers();
private static final String BROWSERS_FOLDER = "Services/Browsers"; // NOI18N
private static final String BROWSERS2_FOLDER = "Services/Browsers2"; // NOI18N
//private WebBrowserFactories fact;
private PropertyChangeSupport sup = new PropertyChangeSupport(this);
private PropertyChangeListener l;
private FileChangeListener lis;
private PreferenceChangeListener lis2;
static final String DEFAULT = "default"; // NOI18N
private WebBrowsers() {
sup = new PropertyChangeSupport(this);
FileObject servicesBrowsers = getConfigFolder();
if (servicesBrowsers != null) {
lis = new FileChangeListener() {
@Override
public void fileFolderCreated(FileEvent fe) {
}
@Override
public void fileDataCreated(FileEvent fe) {
fireBrowsersChange();
}
@Override
public void fileChanged(FileEvent fe) {
fireBrowsersChange();
}
@Override
public void fileDeleted(FileEvent fe) {
fireBrowsersChange();
}
@Override
public void fileRenamed(FileRenameEvent fe) {
fireBrowsersChange();
}
@Override
public void fileAttributeChanged(FileAttributeEvent fe) {
fireBrowsersChange();
}
};
servicesBrowsers.addRecursiveListener(lis);
}
lis2 = new PreferenceChangeListener() {
@Override
public void preferenceChange(PreferenceChangeEvent evt) {
if (IDESettings.PROP_WWWBROWSER.equals(evt.getKey())) {
fireDefaultBrowserChange();
}
}
};
NbPreferences.forModule(IDESettings.class).addPreferenceChangeListener(lis2);
}
/**
* Singleton instance of WebBrowsers class
*/
public static WebBrowsers getInstance() {
return INST;
}
/**
* Returns browser corresponding to user's choice in IDE options.
*/
public WebBrowser getPreferred() {
WebBrowserFactoryDescriptor someFactory = null;
for (WebBrowserFactoryDescriptor desc : getFactories(true)) {
if (someFactory == null) {
someFactory = desc;
}
if (!desc.isDefault()) {
continue;
}
return new WebBrowser(desc);
}
// if none of the browsers is marked as default one then simply use first one:
if (someFactory != null) {
return new WebBrowser(someFactory);
}
assert false : "there are no browsers registered on your classpath. can you fix that"; // NOI18N
return null;
}
/**
* Get default embedded browser.
* @return can be null if none installed
*/
public WebBrowser getEmbedded() {
for (WebBrowserFactoryDescriptor desc : getFactories(true)) {
if (desc.getBrowserFamily() != BrowserFamilyId.JAVAFX_WEBVIEW) {
continue;
}
return new WebBrowser(desc);
}
return null;
}
/**
* Returns all browsers registered in the IDE.
*/
public List<WebBrowser> getAll(boolean includeSystemDefaultBrowser,
boolean includeIDEGlobalBrowserOption,
boolean sortBrowsers) {
return getAll(includeSystemDefaultBrowser,
includeIDEGlobalBrowserOption, false, sortBrowsers);
}
/**
* Returns all browsers registered in the IDE.
*/
public List<WebBrowser> getAll(boolean includeSystemDefaultBrowser,
boolean includeIDEGlobalBrowserOption,
boolean includePhoneGap,
boolean sortBrowsers) {
if (sortBrowsers) {
return getSortedBrowsers(includeSystemDefaultBrowser,
includeIDEGlobalBrowserOption, includePhoneGap);
} else {
return getUnsortedBrowsers(includeSystemDefaultBrowser,
includeIDEGlobalBrowserOption, includePhoneGap);
}
}
private List<WebBrowser> getSortedBrowsers(boolean includeSystemDefaultBrowser,
boolean includeIDEGlobalBrowserOption,
boolean includePhoneGap) {
List<BrowserWrapper> browsers = new ArrayList<BrowserWrapper>();
int chrome = 200;
int chromium = 300;
int others = 1000;
int android = 1400;
int ios = 1500;
int phonegap = 1600;
for (WebBrowserFactoryDescriptor desc : getFactories(includeSystemDefaultBrowser)) {
if (desc.getBrowserFamily().equals(BrowserFamilyId.PHONEGAP) && !includePhoneGap) {
continue;
}
WebBrowser browser = new WebBrowser(desc);
if (browser.getBrowserFamily() == BrowserFamilyId.JAVAFX_WEBVIEW) {
browsers.add(new BrowserWrapper(browser, 100));
} else if (browser.getBrowserFamily() == BrowserFamilyId.CHROME || browser.getId().endsWith("ChromeBrowser")) { // NOI18N
BrowserWrapper wrapper = new BrowserWrapper(browser, chrome++);
browsers.add(wrapper);
} else if (browser.getBrowserFamily() == BrowserFamilyId.CHROMIUM || browser.getId().endsWith("ChromiumBrowser")) { // NOI18N
BrowserWrapper wrapper = new BrowserWrapper(browser, chromium++);
browsers.add(wrapper);
} else if (browser.getBrowserFamily() == BrowserFamilyId.ANDROID) { // NOI18N
browsers.add(new BrowserWrapper(browser, android++));
} else if (browser.getBrowserFamily() == BrowserFamilyId.IOS) { // NOI18N
browsers.add(new BrowserWrapper(browser, ios++));
} else if (browser.getBrowserFamily() == BrowserFamilyId.PHONEGAP) { // NOI18N
browsers.add(new BrowserWrapper(browser, phonegap++));
} else {
browsers.add(new BrowserWrapper(browser, others++));
}
}
Collections.sort(browsers, new Comparator<BrowserWrapper>() {
@Override
public int compare(BrowserWrapper o1, BrowserWrapper o2) {
return o1.getOrder() - o2.getOrder();
}
});
List<WebBrowser> result = new ArrayList<WebBrowser>();
if (includeIDEGlobalBrowserOption) {
result.add(createIDEGlobalDelegate());
}
for (BrowserWrapper bw : browsers) {
result.add(bw.getBrowser());
}
return result;
}
@NbBundle.Messages({
"WebBrowsers.idebrowser=IDE's default browser"
})
private WebBrowser createIDEGlobalDelegate() {
return WebBrowser.createIDEGlobalDelegate();
}
private List<WebBrowser> getUnsortedBrowsers(boolean includeSystemDefaultBrowser,
boolean includeIDEGlobalBrowserOption,
boolean includeMobileBrowsers) {
List<WebBrowser> browsers = new ArrayList<WebBrowser>();
if (includeIDEGlobalBrowserOption) {
browsers.add(createIDEGlobalDelegate());
}
for (WebBrowserFactoryDescriptor desc : getFactories(includeSystemDefaultBrowser)) {
if (desc.getBrowserFamily().isMobile() && !includeMobileBrowsers) {
continue;
}
WebBrowser browser = new WebBrowser(desc);
browsers.add(browser);
}
return browsers;
}
public void addPropertyChangeListener(PropertyChangeListener l) {
sup.addPropertyChangeListener(l);
}
public void removePropertyChangeListener(PropertyChangeListener l) {
sup.removePropertyChangeListener(l);
}
private void fireBrowsersChange() {
sup.firePropertyChange(PROP_BROWSERS, null, null);
}
private void fireDefaultBrowserChange() {
sup.firePropertyChange(PROP_DEFAULT_BROWSER, null, null);
}
private FileObject getConfigFolder() {
return FileUtil.getConfigFile(BROWSERS_FOLDER);
}
private List<WebBrowserFactoryDescriptor> getFactories(boolean includeSystemDefaultBrowser) {
List<WebBrowserFactoryDescriptor> browsers = new ArrayList<>();
List<WebBrowserFactoryDescriptor> browsersToUpdate = new ArrayList<>();
WebBrowserFactoryDescriptor advancedChrome = null;
WebBrowserFactoryDescriptor advancedChromium = null;
Lookup l = Lookups.forPath(BROWSERS2_FOLDER);
for (HtmlBrowser.Factory f : l.lookupAll(HtmlBrowser.Factory.class)) {
if (!(f instanceof EnhancedBrowserFactory)) {
continue;
}
EnhancedBrowserFactory fact = (EnhancedBrowserFactory)f;
if (!fact.canCreateHtmlBrowserImpl()) {
continue;
}
String browserId = fact.getId();
if (browserId == null) {
// fallback:
browserId = fact.getBrowserFamilyId().toString();
}
WebBrowserFactoryDescriptor desc = new WebBrowserFactoryDescriptor(
browserId,
null,
false,
f);
browsers.add(desc);
if (fact.getBrowserFamilyId() == BrowserFamilyId.CHROME && !fact.hasNetBeansIntegration()) {
advancedChrome = desc;
}
if (fact.getBrowserFamilyId() == BrowserFamilyId.CHROMIUM && !fact.hasNetBeansIntegration()) {
advancedChromium = desc;
}
browsersToUpdate.add(desc);
}
FileObject servicesBrowsers = getConfigFolder();
if (servicesBrowsers == null) {
return browsers;
}
DataFolder folder = DataFolder.findFolder(servicesBrowsers);
// force object creation:
Lookup.getDefault ().lookupAll(HtmlBrowser.Factory.class).toArray();
for (DataObject browserSetting : folder.getChildren()) {
if (Boolean.TRUE.equals(browserSetting.getPrimaryFile().getAttribute("hidden"))) {
continue;
}
if (!includeSystemDefaultBrowser && isSystemDefaultBrowser(browserSetting)) {
continue;
}
InstanceCookie cookie = browserSetting.getCookie(InstanceCookie.class);
if (cookie == null) {
continue;
}
HtmlBrowser.Factory fact;
try {
fact = (HtmlBrowser.Factory) cookie.instanceCreate();
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
continue;
} catch (ClassNotFoundException ex) {
Exceptions.printStackTrace(ex);
continue;
}
Lookup.Item<HtmlBrowser.Factory> item =
Lookup.getDefault ().lookupItem (
new Lookup.Template<HtmlBrowser.Factory> (
HtmlBrowser.Factory.class, null, fact));
if (item == null) {
continue;
}
ExtWebBrowser extFact = fact instanceof ExtWebBrowser ? (ExtWebBrowser)fact : null;
boolean isDefault;
if (IDESettings.getWWWBrowser() == null){
isDefault = false;
}
else {
isDefault = IDESettings.getWWWBrowser().equals(fact);
}
if (extFact != null && !isSystemDefaultBrowser(browserSetting)) {
// if this is Chrome browser instance which is configurable in
// the Options UI then its browser executable configuration should be
// used by "Chrome with NB integration"; similarly for Chromium
if (extFact.getPrivateBrowserFamilyId() == PrivateBrowserFamilyId.CHROME) {
setBrowserExecutableDelegate(extFact, browsersToUpdate, BrowserFamilyId.CHROME);
} else if (extFact.getPrivateBrowserFamilyId() == PrivateBrowserFamilyId.CHROMIUM) {
setBrowserExecutableDelegate(extFact, browsersToUpdate, BrowserFamilyId.CHROMIUM);
}
}
// ignore default Chrome browser if advanced version is available;
// this will also cover case when "Default System Browser" is configured
// in the IDE Options and Chrome is OS's default browser - even in this case
// the SystemDefaultBrowser instance is going to be replaced with advanced
// version of Chrome otherwise some features would not work (eg. saving changes in CDT)
// if replaced browser was a default one then advanced browser version
// is marked as default;
if (advancedChrome != null && extFact != null &&
extFact.getPrivateBrowserFamilyId() == PrivateBrowserFamilyId.CHROME) {
if (isDefault) {
advancedChrome.setDefault(true);
}
continue;
}
if (advancedChromium != null && extFact != null &&
extFact.getPrivateBrowserFamilyId() == PrivateBrowserFamilyId.CHROMIUM) {
if (isDefault) {
advancedChromium.setDefault(true);
}
continue;
}
browsers.add(
new WebBrowserFactoryDescriptor(
item.getId(),
browserSetting,
isDefault,
fact));
}
return browsers;
}
static BrowserFamilyId getIDEOptionsBrowserFamily() {
HtmlBrowser.Factory factory = IDESettings.getWWWBrowser();
if (factory != null && factory instanceof ExtWebBrowser) {
return WebBrowserFactoryDescriptor.convertBrowserFamilyId(((ExtWebBrowser)factory).getPrivateBrowserFamilyId());
} else if (factory != null && factory instanceof EnhancedBrowserFactory) {
return ((EnhancedBrowserFactory)factory).getBrowserFamilyId();
}
return BrowserFamilyId.UNKNOWN;
}
private boolean isSystemDefaultBrowser(DataObject browserSetting) {
return browserSetting.getPrimaryFile().getName().startsWith("SystemDefaultBrowser");
}
private void setBrowserExecutableDelegate(ExtWebBrowser fact, List<WebBrowserFactoryDescriptor> browsersToUpdate, BrowserFamilyId family) {
for (WebBrowserFactoryDescriptor desc : browsersToUpdate) {
if (desc.getBrowserFamily() == family && desc.getFactory() instanceof ExtWebBrowser) {
((ExtWebBrowser)desc.getFactory()).useBrowserExecutableDelegate(fact);
}
}
}
/**
* Wrapper class for {@link WebBrowser}.
* <p>
* This class is thread-safe.
*/
private static final class BrowserWrapper {
private final WebBrowser browser;
private final int order;
public BrowserWrapper(WebBrowser browser, int order) {
this.browser = browser;
this.order = order;
}
public WebBrowser getBrowser() {
return browser;
}
int getOrder() {
return order;
}
}
}