blob: 7cb9275fca9bf4880a248f25aae99a661d9fd644 [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.extbrowser.plugins;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.channels.SelectionKey;
import java.nio.charset.Charset;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.SwingUtilities;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.netbeans.api.debugger.Session;
import org.netbeans.modules.extbrowser.chrome.ChromeBrowserImpl;
import org.netbeans.modules.extbrowser.plugins.chrome.WebKitDebuggingTransport;
import org.netbeans.modules.netserver.api.WebSocketReadHandler;
import org.netbeans.modules.netserver.api.WebSocketServer;
import org.netbeans.modules.web.browser.api.PageInspector;
import org.netbeans.modules.web.browser.api.ResizeOption;
import org.netbeans.modules.web.browser.api.ResizeOptions;
import org.netbeans.modules.web.browser.api.WebBrowserFeatures;
import org.netbeans.modules.web.browser.spi.ExternalModificationsSupport;
import org.netbeans.modules.web.webkit.debugging.api.TransportStateException;
import org.netbeans.modules.web.webkit.debugging.api.WebKitDebugging;
import org.netbeans.modules.web.webkit.debugging.api.WebKitUIManager;
import org.netbeans.modules.web.webkit.debugging.spi.Response;
import org.netbeans.modules.web.webkit.debugging.spi.ResponseCallback;
import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.openide.util.RequestProcessor;
import org.openide.util.lookup.Lookups;
import org.openide.util.lookup.ProxyLookup;
/**
* Support class running a WebSocket server for communication with browser plugins.
*/
public final class ExternalBrowserPlugin {
/** ID of 'reload of save' feature. */
static final String FEATURE_ROS = "RoS"; // NOI18N
public static final String UTF_8 = "UTF-8"; // NOI18N
private static final int PORT = 8008;
private static final Logger LOG = Logger.getLogger(
ExternalBrowserPlugin.class.getCanonicalName());
public static ExternalBrowserPlugin getInstance(){
return INSTANCE;
}
private final List<MessageListener> listeners;
private static final ExternalBrowserPlugin INSTANCE = new ExternalBrowserPlugin();
private static final RequestProcessor RP = new RequestProcessor("ExternalBrowserPlugin", 5); // NOI18N
private static final RequestProcessor BROWSER_CHANGES = new RequestProcessor("Chrome Browser Changes", 1); // NOI18N
@NbBundle.Messages({"# {0} - port", "ServerStartFailed=Internal WebSocket server failed to start "
+ "and communication with the external Chrome browser will not work. Check the IDE log "
+ "for more information. This is likely caused by multiple instances of NetBeans "
+ "running at the same time or some other application using port {0}"})
private ExternalBrowserPlugin() {
listeners = new CopyOnWriteArrayList<MessageListener>();
try {
server = new WebSocketServer(new InetSocketAddress("localhost", PORT), new BrowserPluginHandler()); // NOI18N
server.start();
Thread shutdown = new Thread(){
@Override
public void run() {
for (BrowserTabDescriptor tab : knownBrowserTabs) {
tab.deinitialize();
}
server.stop();
}
};
Runtime.getRuntime().addShutdownHook( shutdown);
}
catch (IOException e) {
LOG.log( Level.INFO , null , e);
DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message(
Bundle.ServerStartFailed(""+PORT), NotifyDescriptor.Message.ERROR_MESSAGE));
}
}
public boolean isServerRunning() {
return server != null;
}
/**
* Register that given URL was opened in external browser and browser
* should confirm it by sending a message back to IDE. This handshake
* will store ID if the browser tab and use it for all consequent external
* browser requests.
*/
public void register(URL tempURL, URL realUrl, ChromeBrowserImpl browserImpl) {
awaitingBrowserResponse.put(urlToString(tempURL), new Pair(browserImpl, realUrl));
}
private String urlToString(URL url) {
try {
// try to 'normalize' the URL
return url.toURI().toASCIIString().toLowerCase();
} catch (URISyntaxException ex) {
return url.toExternalForm();
}
}
/**
* Show URL in browser in given browser tab.
*/
public void showURLInTab(final BrowserTabDescriptor tab, final URL url) {
RP.post(new Runnable() {
@Override
public void run() {
tab.init();
SelectionKey k = tab.keyForFeature(FEATURE_ROS);
if (k != null) {
server.sendMessage(k, createReloadMessage(tab.tabID, url));
}
}
});
}
public void close(final BrowserTabDescriptor tab, final boolean closeTab) {
RP.post(new Runnable() {
@Override
public void run() {
tab.deinitialize();
if (closeTab) {
SelectionKey k = tab.keyForFeature(FEATURE_ROS);
if (k != null) {
server.sendMessage(k, createCloseTabMessage(tab.tabID));
}
}
}
});
}
public void attachWebKitDebugger(BrowserTabDescriptor tab) {
SelectionKey k = tab.keyForFeature(FEATURE_ROS);
if (k != null) {
server.sendMessage(k, createAttachDebuggerMessage(tab.tabID));
}
}
public void detachWebKitDebugger(BrowserTabDescriptor tab) {
if (tab != null) {
SelectionKey k = tab.keyForFeature(FEATURE_ROS);
if (k != null) {
server.sendMessage(k, createDetachDebuggerMessage(tab.tabID));
}
}
}
public void sendWebKitDebuggerCommand(BrowserTabDescriptor tab, JSONObject command) {
SelectionKey k = tab.keyForFeature(FEATURE_ROS);
if (k != null) {
server.sendMessage(k, createDebuggerCommandMessage(tab.tabID, command));
}
}
public void addMessageListener(MessageListener listener){
listeners.add(listener);
}
public void removeMessageListener(MessageListener listener ){
listeners.remove(listener);
}
private void removeKey( SelectionKey key ) {
for (BrowserTabDescriptor browserTab : knownBrowserTabs) {
if (key.equals(browserTab.keyForFeature(FEATURE_ROS))) {
browserTab.deinitialize();
browserTab.browserImpl.wasClosed();
}
browserTab.unregisterKey(key);
if (!browserTab.isAnyKeyRegistered()) {
// SelectionKey of the last feature (that was interested in this tab)
// was removed => we can forget this tab.
knownBrowserTabs.remove(browserTab);
}
}
}
/**
* Notifies {@code MessageDispatcher}(s) that correspond to the given
* {@code SelectionKey} about a new message.
*
* @param message message to dispatch.
* @param key origin of the message.
*/
private void notifyDispatchers(final String message, SelectionKey key) {
for (BrowserTabDescriptor browserTab : knownBrowserTabs) {
String featureId = browserTab.featureForKey(key);
if (featureId != null) {
Lookup lookup = browserTab.browserImpl.getLookup();
final MessageDispatcherImpl dispatcher = lookup.lookup(MessageDispatcherImpl.class);
if (dispatcher != null) {
dispatcher.dispatchMessage(featureId, message);
}
}
}
}
private void fireMessageEvent( Message msg ) {
for (MessageListener listener : listeners) {
listener.messageReceived(msg);
}
}
/**
* Sends a message to the specified feature of the specified web-browser pane.
*
* @param message message to deliver.
* @param impl web-pane where the message should be sent.
* @param featureId ID of the feature the message is related to.
*/
public void sendMessage(String message, ChromeBrowserImpl impl, String featureId) {
for (BrowserTabDescriptor browserTab : knownBrowserTabs) {
if (browserTab.browserImpl == impl) {
SelectionKey key = browserTab.keyForFeature(featureId);
if (key != null) {
server.sendMessage(key, message);
}
}
}
}
private void closeOtherDebuggingSessionsWithPageInspector(int tabId) {
for (BrowserTabDescriptor browserTab : knownBrowserTabs) {
if ( tabId != browserTab.tabID && browserTab.isPageInspectorActive()) {
close(browserTab, false);
}
}
}
class BrowserPluginHandler implements WebSocketReadHandler {
/** Name of the attribute of the INIT message that holds the version information. */
private static final String VERSION = "version"; // NOI18N
private static final String URL = "url"; // NOI18N
/* (non-Javadoc)
* @see org.netbeans.modules.web.common.websocket.WebSocketReadHandler#read(java.nio.channels.SelectionKey, byte[], java.lang.Integer)
*/
@Override
public void read( SelectionKey key, byte[] data, Integer dataType ) {
if ( dataType != null && dataType != 1 ){
return;
}
String message = new String( data , Charset.forName( UTF_8));
Message msg = Message.parse(message);
if (msg == null || (msg.getType() == null)) {
notifyDispatchers(message, key);
return;
}
fireMessageEvent(msg);
Message.MessageType type = msg.getType();
switch (type) {
case INIT:
handleInit(msg, key );
break;
case CLOSE:
handleClose( msg , key );
break;
case URLCHANGE:
handleURLChange(msg, key);
break;
case INSPECT:
handleInspect(msg, key);
break;
case ATTACH_DEBUGGER:
break;
case DETACH_DEBUGGER:
break;
case DEBUGGER_DETACHED:
handleDebuggerDetached(msg);
break;
case DEBUGGER_COMMAND_RESPONSE:
handleDebuggerResponse( msg , key );
break;
case LOAD_RESIZE_OPTIONS:
handleLoadResizeOptions(key);
break;
case SAVE_RESIZE_OPTIONS:
handleSaveResizeOptions(msg.getValue());
break;
case RESOURCE_CHANGED:
handleResourceChanged(msg.getValue(), key);
break;
case READY:
break;
default:
assert false : "Unknown message type: " + type;
}
}
private void handleInit( Message message , SelectionKey key ){
assert key != null : message;
String version = (String)message.getValue().get(VERSION);
String url = (String)message.getValue().get(URL);
int tabId = message.getTabId();
if (version == null || url == null || tabId == -1) {
return;
}
final Pair p;
if (isSupportedVersion(version)) {
p = getAwaitingPair(url);
} else {
p = null;
}
ChromeBrowserImpl browserImpl = p != null ? p.impl : null;
if (browserImpl == null) {
Map map = new HashMap();
map.put( Message.TAB_ID, tabId );
map.put("status","notaccepted"); // NOI18N
map.put("version", getNetBeansVersion()); // NOI18N
Message msg = new Message( Message.MessageType.INIT , map );
server.sendMessage(key, msg.toStringValue());
} else {
final BrowserTabDescriptor tab = new BrowserTabDescriptor(tabId, browserImpl);
tab.registerKeyForFeature(FEATURE_ROS, key);
browserImpl.setBrowserTabDescriptor(tab);
knownBrowserTabs.add(tab);
Map map = new HashMap();
map.put( Message.TAB_ID, tabId );
map.put("status","accepted"); // NOI18N
map.put("version", getNetBeansVersion()); // NOI18N
Message msg = new Message( Message.MessageType.INIT , map );
server.sendMessage(key, msg.toStringValue());
// update temp URL with real one:
assert p.realURL != null;
showURLInTab(tab, p.realURL);
}
}
/**
* Determines whether the specified version of the INIT message/protocol
* is supported or not.
*
* @param version version to check.
* @return {@code true} when the version is supported,
* returns {@code false} otherwise.
*/
private boolean isSupportedVersion(String version) {
return version.startsWith("1."); // NOI18N
}
/**
* Returns the version of NetBeans (sent by IDE to browser extension).
*
* @return version of NetBeans.
*/
private String getNetBeansVersion() {
return "8.1"; // NOI18N
}
private void handleDebuggerDetached(Message message) {
int tabId = message.getTabId();
if ( tabId == -1 ){
return;
}
deinitializeTab(tabId, false);
}
private Pair getAwaitingPair(String url) {
if (url.startsWith("chrome")) {
// ignore internal chrome URLs:
return null;
}
URL u = null;
try {
u = new URL(url);
} catch (MalformedURLException ex) {
LOG.log(Level.WARNING, "cannot parse URL: {0}", url); // NOI18N
}
if (LOG.isLoggable(Level.FINE)) {
LOG.log(Level.FINE, "processing URL: {0}", (u == null) ? null : urlToString(u)); // NOI18N
for (String awaiting : awaitingBrowserResponse.keySet()) {
LOG.log(Level.FINE, "awaiting URL: {0}", awaiting); // NOI18N
}
}
Pair pair = (u == null) ? null : awaitingBrowserResponse.remove(urlToString(u));
ChromeBrowserImpl browserImpl = pair != null ? pair.impl : null;
// XXX: workaround: when Web Project is run it is started as "http:/localhost/aa" but browser URL is
// "http:/localhost/aa/"
if (browserImpl == null && url.endsWith("/")) { // NOI18N
try {
u = new URL(url.substring(0, url.length()-1));
pair = awaitingBrowserResponse.remove(urlToString(u));
browserImpl = pair != null ? pair.impl : null;
} catch (MalformedURLException ex) {
LOG.log(Level.WARNING, "cannot parse URL: {0}", url); // NOI18N
}
}
// XXX: on Mac, file URLs are open with localhost as the host instead of ""
if (browserImpl == null && (u != null) && "file".equals(u.getProtocol()) // NOI18N
&& "localhost".equals(u.getHost())) // NOI18N
{
try {
u = new URL(u.getProtocol(), "", u.getPort(), u.getFile()); // NOI18N
pair = awaitingBrowserResponse.remove(urlToString(u));
} catch (MalformedURLException ex) {
LOG.log(Level.WARNING, "cannot parse URL: {0}", url);// NOI18N
}
}
return pair;
}
private void handleClose( Message message, SelectionKey key ){
int tabId = message.getTabId();
if ( tabId == -1 ){
return;
}
deinitializeTab(tabId, true);
}
private boolean deinitializeTab(int tabId, boolean close) {
for (BrowserTabDescriptor browserTab : knownBrowserTabs) {
if ( tabId == browserTab.tabID ) {
browserTab.deinitialize();
browserTab.disableReInitialization();
if (close) {
knownBrowserTabs.remove(browserTab);
browserTab.browserImpl.wasClosed();
}
return true;
}
}
return false;
}
private void handleDebuggerResponse( Message message, SelectionKey key ){
int tabId = message.getTabId();
JSONObject response = (JSONObject)message.getValue().get("response" );
assert tabId != -1;
assert response != null;
if ( tabId == -1 || response == null) {
return;
}
for(BrowserTabDescriptor browserTab : knownBrowserTabs) {
if (tabId == browserTab.tabID && browserTab.getCallback() != null) {
Response resp;
String error = (String)response.get("error"); // NOI18N
resp = new Response(response, (error == null) ? null : new TransportStateException(error));
browserTab.getCallback().handleResponse(resp);
}
}
}
private void handleURLChange( Message message, SelectionKey key ){
int tabId = message.getTabId();
String url = (String)message.getValue().get(URL);
if ( tabId == -1 ){
return;
}
for (BrowserTabDescriptor browserTab : knownBrowserTabs) {
if ( tabId == browserTab.tabID ) {
browserTab.browserImpl.urlHasChanged(url);
return;
}
}
}
/**
* Handles a request for web-page inspection.
*
* @param message initial message of the inspection.
* @param key origin of the message.
*/
private void handleInspect(Message message, SelectionKey key) {
final PageInspector inspector = PageInspector.getDefault();
if (inspector == null) {
LOG.log(Level.INFO, "No PageInspector found: ignoring the request for page inspection!"); // NOI18N
} else {
int tabId = message.getTabId();
// Find if the tab is known to RoS already
BrowserTabDescriptor browserTab = null;
for (BrowserTabDescriptor descriptor : knownBrowserTabs) {
if (descriptor.tabID == tabId) {
browserTab = descriptor;
}
}
if (browserTab == null) {
// Tab not opened from the IDE => using a dummy ExtBrowserImpl
ChromeBrowserImpl impl = new ChromeBrowserImpl(null, false) {
@Override
public void setURL(URL url) {
throw new UnsupportedOperationException();
}
@Override
public void close(boolean closeTab) {
}
};
browserTab = new BrowserTabDescriptor(tabId, impl);
impl.setBrowserTabDescriptor(browserTab);
impl.initialize(new WebBrowserFeatures());
knownBrowserTabs.add(browserTab);
}
final ChromeBrowserImpl browserImpl = browserTab.browserImpl;
final BrowserTabDescriptor tab = browserTab;
// Do not block WebSocket thread
RP.post(new Runnable() {
@Override
public void run() {
if (!browserImpl.hasEnhancedMode()) {
browserImpl.setTemporaryEnhancedMode(true);
}
tab.reEnableReInitialization();
tab.init();
inspector.inspectPage(new ProxyLookup(browserImpl.getLookup(), browserImpl.getProjectContext()));
}
});
}
}
private void handleLoadResizeOptions(SelectionKey key) {
Map<String, String> map = new HashMap<String, String>();
map.put("resizeOptions", createLoadResizeOptionsMessage(ResizeOptions.getDefault().loadAll())); // NOI18N
Message msg = new Message(Message.MessageType.LOAD_RESIZE_OPTIONS, map);
server.sendMessage(key, msg.toStringValue());
}
@SuppressWarnings("unchecked")
private String createLoadResizeOptionsMessage(List<ResizeOption> resizeOptions) {
JSONArray result = new JSONArray();
for (ResizeOption resizeOption : resizeOptions) {
result.add(mapResizeOption(resizeOption));
}
return result.toJSONString();
}
@SuppressWarnings("unchecked")
private JSONObject mapResizeOption(ResizeOption resizeOption) {
JSONObject mapped = new JSONObject();
mapped.put("type", JSONObject.escape(resizeOption.getType().name())); // NOI18N
mapped.put("displayName", JSONObject.escape(resizeOption.getDisplayName())); // NOI18N
mapped.put("width", resizeOption.getWidth()); // NOI18N
mapped.put("height", resizeOption.getHeight()); // NOI18N
mapped.put("showInToolbar", resizeOption.isShowInToolbar()); // NOI18N
mapped.put("isDefault", resizeOption.isDefault()); // NOI18N
return mapped;
}
private void handleSaveResizeOptions(JSONObject value) {
JSONArray options = (JSONArray) value.get("resizeOptions"); // NOI18N
List<ResizeOption> resizeOptions = new ArrayList<ResizeOption>(options.size());
for (Object item : options) {
JSONObject option = (JSONObject) item;
resizeOptions.add(ResizeOption.create(
ResizeOption.Type.valueOf(String.valueOf(option.get("type"))), // NOI18N
String.valueOf(option.get("displayName")), // NOI18N
Integer.valueOf(String.valueOf(option.get("width"))), // NOI18N
Integer.valueOf(String.valueOf(option.get("height"))), // NOI18N
Boolean.valueOf(String.valueOf(option.get("showInToolbar"))), // NOI18N
Boolean.valueOf(String.valueOf(option.get("isDefault"))))); // NOI18N
}
ResizeOptions.getDefault().saveAll(resizeOptions);
}
@Override
public void accepted(SelectionKey key) {
}
@Override
public void closed(SelectionKey key) {
removeKey( key );
}
private void handleResourceChanged(JSONObject value, SelectionKey key) {
final String content = String.valueOf(value.get("content"));
JSONObject resource = (JSONObject)value.get("resource");
final String url = String.valueOf(resource.get("url"));
final String type = String.valueOf(resource.get("type"));
URL mainDocumentUrl = null;
for (BrowserTabDescriptor browserTab : knownBrowserTabs) {
if (key.equals(browserTab.keyForFeature(FEATURE_ROS))) {
mainDocumentUrl = browserTab.browserImpl.getURL();
}
}
final URL uu = mainDocumentUrl;
BROWSER_CHANGES.post(new Runnable() {
@Override
public void run() {
ExternalModificationsSupport.handle(url, type, content, uu);
}
});
}
}
private String createReloadMessage(int tabId, URL newURL) {
Map params = new HashMap();
params.put( Message.TAB_ID, tabId );
if (newURL != null) {
String url;
try {
url = newURL.toURI().toASCIIString();
} catch (URISyntaxException ex) {
url = newURL.toExternalForm();
}
url = reformatFileURL(url);
params.put("url", url); // NOI18N
}
Message msg = new Message( Message.MessageType.RELOAD, params);
return msg.toStringValue();
}
// changes "file:/some" to "file:///some"
private static String reformatFileURL(String url) {
if (!url.startsWith("file:")) {
return url;
}
url = url.substring(5);
while (url.length() > 0 && url.startsWith("/")) {
url = url.substring(1);
}
return "file:///"+url;
}
private String createCloseTabMessage(int tabId) {
Map<String, Integer> params = new HashMap<>();
params.put( Message.TAB_ID, tabId );
Message msg = new Message( Message.MessageType.CLOSE, params);
return msg.toStringValue();
}
private String createAttachDebuggerMessage(int tabId) {
Map<String, Integer> params = new HashMap<>();
params.put( Message.TAB_ID, tabId );
Message msg = new Message( Message.MessageType.ATTACH_DEBUGGER, params);
return msg.toStringValue();
}
private String createDetachDebuggerMessage(int tabId) {
Map<String, Integer> params = new HashMap<>();
params.put( Message.TAB_ID, tabId );
Message msg = new Message( Message.MessageType.DETACH_DEBUGGER, params);
return msg.toStringValue();
}
private String createDebuggerCommandMessage(int tabId, JSONObject params2) {
JSONObject data = new JSONObject();
data.put(Message.TAB_ID, tabId );
data.put("command", params2);
Message msg = new Message( Message.MessageType.DEBUGGER_COMMAND, data);
return msg.toStringValue();
}
private WebSocketServer server;
private final Map<String,Pair> awaitingBrowserResponse = new HashMap<String,Pair>();
private static class Pair {
ChromeBrowserImpl impl;
URL realURL;
public Pair(ChromeBrowserImpl impl, URL realURL) {
this.impl = impl;
this.realURL = realURL;
}
}
private List<BrowserTabDescriptor> knownBrowserTabs = new CopyOnWriteArrayList<BrowserTabDescriptor>();
/**
* Descriptor of tab opened in the external browser.
*/
public static class BrowserTabDescriptor {
/** Maps IDs of features (related to this tab) to their corresponding sockets. */
private final Map<String,SelectionKey> keyMap = new HashMap<String,SelectionKey>();
private int tabID;
private ChromeBrowserImpl browserImpl;
private ResponseCallback callback;
private boolean initialized;
private boolean doNotInitialize;
private Session session;
private Lookup consoleLogger;
private Lookup networkMonitor;
public BrowserTabDescriptor(int tabID, ChromeBrowserImpl browserImpl) {
this.tabID = tabID;
this.browserImpl = browserImpl;
}
/**
* Registers the given selection key for the specified feature.
*
* @param featureId ID of the feature.
* @param key selection key for the feature.
*/
synchronized void registerKeyForFeature(String featureId, SelectionKey key) {
keyMap.put(featureId, key);
}
/**
* Returns selection key registered for the specified feature.
*
* @param featureId ID of the feature.
* @return selection key registered for the specified feature
* or {@code null} if there is no such feature.
*/
synchronized SelectionKey keyForFeature(String featureId) {
return keyMap.get(featureId);
}
/**
* Unregisters the specified key.
*
* @param key selection key to unregister.
*/
synchronized void unregisterKey(SelectionKey key) {
keyMap.values().removeAll(Collections.singleton(key));
}
/**
* Determines whether any key/feature is registerd for this tab.
*
* @return {@code true} if any key/feature is registered for this tab,
* returns {@code false} otherwise.
*/
synchronized boolean isAnyKeyRegistered() {
return !keyMap.isEmpty();
}
/**
* Returns ID of the feature for which the specified selection key
* is registered.
*
* @param key selection key of the feature we are interested in.
* @return ID of the feature for which the specified selection key
* is registered or {@code null} when there is no such feature.
*/
synchronized String featureForKey(SelectionKey key) {
String featureId = null;
for (Map.Entry<String,SelectionKey> entry : keyMap.entrySet()) {
if (entry.getValue() == key) {
featureId = entry.getKey();
}
}
return featureId;
}
public void setCallback(ResponseCallback callback) {
this.callback = callback;
}
private ResponseCallback getCallback() {
return callback;
}
@NbBundle.Messages({"DebuggerEnableFailed=Browser refused to debug this tab.\n"
+ "Close Chrome Developer Tools (or any other browser debugger) and try again."})
private void init() {
if (initialized || !browserImpl.hasEnhancedMode() || doNotInitialize ||
browserImpl.getBrowserFeatures() == null ||
!browserImpl.getBrowserFeatures().isNetBeansIntegrationEnabled()) {
return;
}
initialized = true;
// perform session closing before creating a new one:
PageInspector inspector = PageInspector.getDefault();
if (inspector != null && browserImpl.getBrowserFeatures().isPageInspectorEnabled()) {
// #219241 - "Web inspection is broken when switching 2 projects with different configuration"
// a solution is to close previous debugging sessions:
ExternalBrowserPlugin.getInstance().closeOtherDebuggingSessionsWithPageInspector(tabID);
}
// lookup which contains Project instance if URL being opened is from a project:
Lookup projectContext = browserImpl.getProjectContext();
WebKitDebuggingTransport transport = browserImpl.getLookup().lookup(WebKitDebuggingTransport.class);
WebKitDebugging webkitDebugger = browserImpl.getLookup().lookup(WebKitDebugging.class);
if (webkitDebugger == null || projectContext == null) {
return;
}
transport.attach();
if (browserImpl.getBrowserFeatures().isLiveHTMLEnabled()) {
webkitDebugger.getDebugger().enableDebuggerInLiveHTMLMode();
} else {
if (!webkitDebugger.getDebugger().enable()) {
initialized = false;
doNotInitialize = true;
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message(
Bundle.DebuggerEnableFailed(), NotifyDescriptor.Message.ERROR_MESSAGE));
}
});
return;
}
}
if (browserImpl.getBrowserFeatures().isJsDebuggerEnabled()) {
session = WebKitUIManager.getDefault().createDebuggingSession(webkitDebugger, projectContext);
}
if (browserImpl.getBrowserFeatures().isConsoleLoggerEnabled()) {
consoleLogger = WebKitUIManager.getDefault().createBrowserConsoleLogger(webkitDebugger, projectContext);
}
if (browserImpl.getBrowserFeatures().isNetworkMonitorEnabled()) {
networkMonitor = WebKitUIManager.getDefault().createNetworkMonitor(webkitDebugger, projectContext);
}
if (inspector != null && browserImpl.getBrowserFeatures().isPageInspectorEnabled()) {
inspector.inspectPage(new ProxyLookup(browserImpl.getLookup(), browserImpl.getProjectContext(), Lookups.fixed(browserImpl)));
}
}
public boolean isInitialized() {
return initialized;
}
private void deinitialize() {
if (!initialized || !browserImpl.hasEnhancedMode()) {
return;
}
initialized = false;
WebKitDebuggingTransport transport = browserImpl.getLookup().lookup(WebKitDebuggingTransport.class);
WebKitDebugging webkitDebugger = browserImpl.getLookup().lookup(WebKitDebugging.class);
if (webkitDebugger == null) {
return;
}
if (session != null) {
WebKitUIManager.getDefault().stopDebuggingSession(session);
}
session = null;
if (consoleLogger != null) {
WebKitUIManager.getDefault().stopBrowserConsoleLogger(consoleLogger);
}
consoleLogger = null;
if (networkMonitor != null) {
WebKitUIManager.getDefault().stopNetworkMonitor(networkMonitor);
}
networkMonitor = null;
MessageDispatcherImpl dispatcher = browserImpl.getLookup().lookup(MessageDispatcherImpl.class);
if (dispatcher != null) {
dispatcher.dispatchMessage(PageInspector.MESSAGE_DISPATCHER_FEATURE_ID, null);
}
if (webkitDebugger.getDebugger().isEnabled()) {
webkitDebugger.getDebugger().disable();
}
webkitDebugger.reset();
transport.detach();
if (browserImpl.hasTemporaryEnhancedMode()) {
browserImpl.setTemporaryEnhancedMode(false);
}
}
/**
* Do not attempt to re-attach when the debugging was canceled
* by the user explicitly.
*/
private void disableReInitialization() {
doNotInitialize = true;
}
public void reEnableReInitialization() {
doNotInitialize = false;
}
public boolean isPageInspectorActive() {
return PageInspector.getDefault() != null &&
browserImpl.getBrowserFeatures() != null &&
browserImpl.getBrowserFeatures().isPageInspectorEnabled();
}
}
}