blob: 0bc4217ded2dec02b5a1b7aeea37f8dbff1caf9c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2017 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
* Genady Beryozkin <eclipse@genady.org> - [misc] Display values for constant fields in the Javadoc view - https://bugs.eclipse.org/bugs/show_bug.cgi?id=204914
* Brock Janiczak <brockj@tpg.com.au> - [implementation] Streams not being closed in Javadoc views - https://bugs.eclipse.org/bugs/show_bug.cgi?id=214854
* Benjamin Muskalla <bmuskalla@innoopract.com> - [javadoc view] NPE on enumerations - https://bugs.eclipse.org/bugs/show_bug.cgi?id=223586
* Stephan Herrmann - Contribution for Bug 403917 - [1.8] Render TYPE_USE annotations in Javadoc hover/view
*******************************************************************************/
package org.eclipse.jdt.internal.ui.infoviews;
import java.io.Reader;
import java.io.StringReader;
import java.net.URL;
import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTError;
import org.eclipse.swt.browser.Browser;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.resources.IFile;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.MessageDialogWithToggle;
import org.eclipse.jface.internal.text.html.BrowserInput;
import org.eclipse.jface.internal.text.html.HTMLPrinter;
import org.eclipse.jface.internal.text.html.HTMLTextPresenter;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.window.Window;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.BadPartitioningException;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentExtension3;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.ITypedRegion;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.TextPresentation;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.jface.text.TextUtilities;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.IWorkbenchCommandConstants;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPartSite;
import org.eclipse.ui.IWorkbenchSite;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.texteditor.AbstractDecoratedTextEditor;
import org.eclipse.ui.texteditor.IAbstractTextEditorHelpContextIds;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.ui.texteditor.ITextEditor;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IJarEntryResource;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.ILocalVariable;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IModularClassFile;
import org.eclipse.jdt.core.IOrdinaryClassFile;
import org.eclipse.jdt.core.IPackageDeclaration;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.ISourceReference;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeParameter;
import org.eclipse.jdt.core.ITypeRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.SourceRange;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.NodeFinder;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.core.manipulation.SharedASTProviderCore;
import org.eclipse.jdt.internal.core.manipulation.util.BasicElementLabels;
import org.eclipse.jdt.internal.corext.dom.IASTSharedValues;
import org.eclipse.jdt.internal.corext.javadoc.JavaDocLocations;
import org.eclipse.jdt.internal.corext.refactoring.structure.ASTNodeSearchUtil;
import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
import org.eclipse.jdt.internal.corext.util.Messages;
import org.eclipse.jdt.ui.IContextMenuConstants;
import org.eclipse.jdt.ui.JavaElementLabels;
import org.eclipse.jdt.ui.JavaUI;
import org.eclipse.jdt.ui.PreferenceConstants;
import org.eclipse.jdt.ui.actions.IJavaEditorActionDefinitionIds;
import org.eclipse.jdt.ui.actions.JdtActionConstants;
import org.eclipse.jdt.ui.actions.OpenAttachedJavadocAction;
import org.eclipse.jdt.ui.text.IJavaPartitions;
import org.eclipse.jdt.internal.ui.IJavaHelpContextIds;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.JavaPluginImages;
import org.eclipse.jdt.internal.ui.actions.ActionMessages;
import org.eclipse.jdt.internal.ui.actions.SimpleSelectionProvider;
import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor;
import org.eclipse.jdt.internal.ui.text.java.hover.JavadocHover;
import org.eclipse.jdt.internal.ui.text.java.hover.JavadocHover.FallbackInformationPresenter;
import org.eclipse.jdt.internal.ui.text.javadoc.JavadocContentAccess2;
import org.eclipse.jdt.internal.ui.viewsupport.BindingLinkedLabelComposer;
import org.eclipse.jdt.internal.ui.viewsupport.JavaElementLinks;
/**
* View which shows Javadoc for a given Java element.
*
* FIXME: As of 3.0 selectAll() and getSelection() is not working
* see https://bugs.eclipse.org/bugs/show_bug.cgi?id=63022
*
* @since 3.0
*/
public class JavadocView extends AbstractInfoView {
/**
* Implementation of a {@link BrowserInput} using
* a {@link IJavaElement} as input.
*
* @since 3.4
*/
private static final class JavaElementBrowserInput extends BrowserInput {
private final IJavaElement fInput;
public JavaElementBrowserInput(BrowserInput previous, IJavaElement inputElement) {
super(previous);
Assert.isNotNull(inputElement);
fInput= inputElement;
}
@Override
public Object getInputElement() {
return fInput;
}
@Override
public String getInputName() {
return fInput.getElementName();
}
}
/**
* Implementation of a {@link BrowserInput} using an
* {@link URL} as input.
*
* @since 3.4
*/
private static class URLBrowserInput extends BrowserInput {
private final URL fURL;
public URLBrowserInput(BrowserInput previous, URL url) {
super(previous);
Assert.isNotNull(url);
fURL= url;
}
@Override
public Object getInputElement() {
return fURL;
}
@Override
public String getInputName() {
return fURL.toExternalForm();
}
}
/**
* Action to go forward in the history.
*
* @since 3.4
*/
private final class ForthAction extends Action {
public ForthAction() {
setText(InfoViewMessages.JavadocView_action_forward_name);
ISharedImages images= PlatformUI.getWorkbench().getSharedImages();
setImageDescriptor(images.getImageDescriptor(ISharedImages.IMG_TOOL_FORWARD));
setDisabledImageDescriptor(images.getImageDescriptor(ISharedImages.IMG_TOOL_FORWARD_DISABLED));
update();
}
public void update() {
if (fCurrent != null && fCurrent.getNext() != null) {
BrowserInput element= fCurrent.getNext();
setToolTipText(Messages.format(InfoViewMessages.JavadocView_action_forward_enabledTooltip, BasicElementLabels.getJavaElementName(element.getInputName())));
setEnabled(true);
} else {
setToolTipText(InfoViewMessages.JavadocView_action_forward_disabledTooltip);
setEnabled(false);
}
}
@Override
public void run() {
setInput(fCurrent.getNext());
}
}
/**
* Action to go backwards in the history.
*
* @since 3.4
*/
private final class BackAction extends Action {
public BackAction() {
setText(InfoViewMessages.JavadocView_action_back_name);
ISharedImages images= PlatformUI.getWorkbench().getSharedImages();
setImageDescriptor(images.getImageDescriptor(ISharedImages.IMG_TOOL_BACK));
setDisabledImageDescriptor(images.getImageDescriptor(ISharedImages.IMG_TOOL_BACK_DISABLED));
update();
}
private void update() {
if (fCurrent != null && fCurrent.getPrevious() != null) {
BrowserInput element= fCurrent.getPrevious();
setToolTipText(Messages.format(InfoViewMessages.JavadocView_action_back_enabledTooltip, BasicElementLabels.getJavaElementName(element.getInputName())));
setEnabled(true);
} else {
setToolTipText(InfoViewMessages.JavadocView_action_back_disabledTooltip);
setEnabled(false);
}
}
@Override
public void run() {
setInput(fCurrent.getPrevious());
}
}
/**
* Action to open the selection in an external browser. If the selection is a java element its
* corresponding javadoc is shown if possible. If it is an URL the URL's content is shown.
*
* The action is disabled if the selection cannot be opened.
*
* @since 3.6
*/
private static class OpenInBrowserAction extends OpenAttachedJavadocAction {
/**
* Create a new ShowExternalJavadocAction
*
* @param site the site
*/
public OpenInBrowserAction(IWorkbenchSite site) {
super(site);
}
@Override
public void selectionChanged(IStructuredSelection structuredSelection) {
super.selectionChanged(structuredSelection);
Object element= structuredSelection.getFirstElement();
if (element instanceof URL) {
setText(InfoViewMessages.OpenInBrowserAction_url_label);
setToolTipText(InfoViewMessages.OpenInBrowserAction_url_toolTip);
} else {
setText(ActionMessages.OpenAttachedJavadocAction_label);
setToolTipText(ActionMessages.OpenAttachedJavadocAction_tooltip);
}
}
@Override
public void run(IStructuredSelection selection) {
if (!canEnableFor(selection))
return;
Object element= selection.getFirstElement();
if (element instanceof IJavaElement)
super.run(selection);
else
open((URL)element);
}
/*
* @see org.eclipse.jdt.ui.actions.OpenAttachedJavadocAction#canEnableFor(org.eclipse.jface.viewers.IStructuredSelection)
*/
@Override
protected boolean canEnableFor(IStructuredSelection selection) {
if (selection.size() != 1)
return false;
Object element= selection.getFirstElement();
return element instanceof URL || super.canEnableFor(selection);
}
}
/**
* Preference key for the preference whether to show a dialog
* when the SWT Browser widget is not available.
* @since 3.0
*/
private static final String DO_NOT_WARN_PREFERENCE_KEY= "JavadocView.error.doNotWarn"; //$NON-NLS-1$
// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=73558
private static final boolean WARNING_DIALOG_ENABLED= false;
/** The HTML widget. */
private Browser fBrowser;
/** The text widget. */
private StyledText fText;
/** The information presenter. */
private HTMLTextPresenter fPresenter;
/** The text presentation. */
private final TextPresentation fPresentation= new TextPresentation();
/** The select all action */
private SelectAllAction fSelectAllAction;
/** The style sheet (css) */
private static String fgStyleSheet;
/**
* <code>true</code> once the style sheet has been loaded.
* @since 3.3
*/
private static boolean fgStyleSheetLoaded= false;
/** The Browser widget */
private boolean fIsUsingBrowserWidget;
private RGB fBackgroundColorRGB;
private RGB fForegroundColorRGB;
/**
* The font listener.
* @since 3.3
*/
private IPropertyChangeListener fFontListener;
/**
* Holds original Javadoc input string.
* @since 3.4
*/
private String fOriginalInput;
/**
* The current input element if any
* @since 3.4
*/
private BrowserInput fCurrent;
/**
* Action to go back in the link history.
* @since 3.4
*/
private BackAction fBackAction;
/**
* Action to go forth in the link history.
* @since 3.4
*/
private ForthAction fForthAction;
/**
* Action to open the attached Javadoc.
* @since 3.4
*/
private OpenInBrowserAction fOpenBrowserAction;
/**
* A selection provider providing the current
* Java element input of this view as selection.
* @since 3.4
*/
private ISelectionProvider fInputSelectionProvider;
/**
* The Javadoc view's select all action.
*/
private class SelectAllAction extends Action {
/** The control. */
private final Control fControl;
/** The selection provider. */
private final SelectionProvider fSelectionProvider;
/**
* Creates the action.
*
* @param control the widget
* @param selectionProvider the selection provider
*/
public SelectAllAction(Control control, SelectionProvider selectionProvider) {
super("selectAll"); //$NON-NLS-1$
Assert.isNotNull(control);
Assert.isNotNull(selectionProvider);
fControl= control;
fSelectionProvider= selectionProvider;
// FIXME: see https://bugs.eclipse.org/bugs/show_bug.cgi?id=63022
setEnabled(!fIsUsingBrowserWidget);
setText(InfoViewMessages.SelectAllAction_label);
setToolTipText(InfoViewMessages.SelectAllAction_tooltip);
setDescription(InfoViewMessages.SelectAllAction_description);
PlatformUI.getWorkbench().getHelpSystem().setHelp(this, IAbstractTextEditorHelpContextIds.SELECT_ALL_ACTION);
}
/**
* Selects all in the view.
*/
@Override
public void run() {
if (fControl instanceof StyledText)
((StyledText)fControl).selectAll();
else {
// FIXME: see https://bugs.eclipse.org/bugs/show_bug.cgi?id=63022
// ((Browser)fControl).selectAll();
if (fSelectionProvider != null)
fSelectionProvider.fireSelectionChanged();
}
}
}
/**
* The Javadoc view's selection provider.
*/
private static class SelectionProvider implements ISelectionProvider {
/** The selection changed listeners. */
private final ListenerList<ISelectionChangedListener> fListeners= new ListenerList<>(ListenerList.IDENTITY);
/** The widget. */
private final Control fControl;
/**
* Creates a new selection provider.
*
* @param control the widget
*/
public SelectionProvider(Control control) {
Assert.isNotNull(control);
fControl= control;
if (fControl instanceof StyledText) {
((StyledText)fControl).addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
fireSelectionChanged();
}
});
} else {
// FIXME: see https://bugs.eclipse.org/bugs/show_bug.cgi?id=63022
// ((Browser)fControl).addSelectionListener(new SelectionAdapter() {
// public void widgetSelected(SelectionEvent e) {
// fireSelectionChanged();
// }
// });
}
}
/**
* Sends a selection changed event to all listeners.
*/
public void fireSelectionChanged() {
ISelection selection= getSelection();
SelectionChangedEvent event= new SelectionChangedEvent(this, selection);
for (ISelectionChangedListener selectionChangedListener : fListeners) {
selectionChangedListener.selectionChanged(event);
}
}
/*
* @see org.eclipse.jface.viewers.ISelectionProvider#addSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
*/
@Override
public void addSelectionChangedListener(ISelectionChangedListener listener) {
fListeners.add(listener);
}
/*
* @see org.eclipse.jface.viewers.ISelectionProvider#getSelection()
*/
@Override
public ISelection getSelection() {
if (fControl instanceof StyledText) {
IDocument document= new Document(((StyledText)fControl).getSelectionText());
return new TextSelection(document, 0, document.getLength());
} else {
// FIXME: see https://bugs.eclipse.org/bugs/show_bug.cgi?id=63022
return StructuredSelection.EMPTY;
}
}
/*
* @see org.eclipse.jface.viewers.ISelectionProvider#removeSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
*/
@Override
public void removeSelectionChangedListener(ISelectionChangedListener listener) {
fListeners.remove(listener);
}
/*
* @see org.eclipse.jface.viewers.ISelectionProvider#setSelection(org.eclipse.jface.viewers.ISelection)
*/
@Override
public void setSelection(ISelection selection) {
// not supported
}
}
/*
* @see AbstractInfoView#internalCreatePartControl(Composite)
*/
@Override
protected void internalCreatePartControl(Composite parent) {
try {
fBrowser= new Browser(parent, SWT.NONE);
fBrowser.setJavascriptEnabled(false);
fIsUsingBrowserWidget= true;
addLinkListener(fBrowser);
fBrowser.addOpenWindowListener(event -> event.required= true);
} catch (SWTError er) {
/* The Browser widget throws an SWTError if it fails to
* instantiate properly. Application code should catch
* this SWTError and disable any feature requiring the
* Browser widget.
* Platform requirements for the SWT Browser widget are available
* from the SWT FAQ web site.
*/
IPreferenceStore store= JavaPlugin.getDefault().getPreferenceStore();
boolean doNotWarn= store.getBoolean(DO_NOT_WARN_PREFERENCE_KEY);
if (WARNING_DIALOG_ENABLED) {
if (!doNotWarn) {
String title= InfoViewMessages.JavadocView_error_noBrowser_title;
String message= InfoViewMessages.JavadocView_error_noBrowser_message;
String toggleMessage= InfoViewMessages.JavadocView_error_noBrowser_doNotWarn;
MessageDialogWithToggle dialog= MessageDialogWithToggle.openError(parent.getShell(), title, message, toggleMessage, false, null, null);
if (dialog.getReturnCode() == Window.OK)
store.setValue(DO_NOT_WARN_PREFERENCE_KEY, dialog.getToggleState());
}
}
fIsUsingBrowserWidget= false;
}
if (!fIsUsingBrowserWidget) {
fText= new StyledText(parent, SWT.V_SCROLL | SWT.H_SCROLL);
fText.setEditable(false);
fPresenter= new FallbackInformationPresenter();
fText.addControlListener(new ControlAdapter() {
/*
* @see org.eclipse.swt.events.ControlAdapter#controlResized(org.eclipse.swt.events.ControlEvent)
*/
@Override
public void controlResized(ControlEvent e) {
doSetInput(fOriginalInput);
}
});
}
initStyleSheet();
listenForFontChanges();
getViewSite().setSelectionProvider(new SelectionProvider(getControl()));
}
/**
* Registers a listener for the Java editor font.
*
* @since 3.3
*/
private void listenForFontChanges() {
fFontListener= event -> {
if (PreferenceConstants.APPEARANCE_JAVADOC_FONT.equals(event.getProperty())) {
fgStyleSheetLoaded= false;
// trigger reloading, but make sure other listeners have already run, so that
// the style sheet gets reloaded only once.
final Display display= getSite().getPage().getWorkbenchWindow().getWorkbench().getDisplay();
if (!display.isDisposed()) {
display.asyncExec(() -> {
if (!display.isDisposed()) {
initStyleSheet();
refresh();
}
});
}
}
};
JFaceResources.getFontRegistry().addListener(fFontListener);
}
private static void initStyleSheet() {
if (fgStyleSheetLoaded)
return;
fgStyleSheetLoaded= true;
fgStyleSheet= JavadocHover.loadStyleSheet("/JavadocViewStyleSheet.css"); //$NON-NLS-1$
}
/*
* @see AbstractInfoView#createActions()
*/
@Override
protected void createActions() {
super.createActions();
fSelectAllAction= new SelectAllAction(getControl(), (SelectionProvider) getSelectionProvider());
fBackAction= new BackAction();
fBackAction.setActionDefinitionId(IWorkbenchCommandConstants.NAVIGATE_BACK);
fForthAction= new ForthAction();
fForthAction.setActionDefinitionId(IWorkbenchCommandConstants.NAVIGATE_FORWARD);
fInputSelectionProvider= new SimpleSelectionProvider();
fOpenBrowserAction= new OpenInBrowserAction(getSite());
fOpenBrowserAction.setSpecialSelectionProvider(fInputSelectionProvider);
fOpenBrowserAction.setImageDescriptor(JavaPluginImages.DESC_ELCL_OPEN_BROWSER);
fOpenBrowserAction.setDisabledImageDescriptor(JavaPluginImages.DESC_DLCL_OPEN_BROWSER);
fOpenBrowserAction.setActionDefinitionId(IJavaEditorActionDefinitionIds.OPEN_ATTACHED_JAVADOC);
fInputSelectionProvider.addSelectionChangedListener(fOpenBrowserAction);
IJavaElement input= getOrignalInput();
StructuredSelection selection;
if (input != null) {
selection= new StructuredSelection(input);
} else {
selection= new StructuredSelection();
}
fInputSelectionProvider.setSelection(selection);
}
@Override
protected void fillActionBars(final IActionBars actionBars) {
super.fillActionBars(actionBars);
actionBars.setGlobalActionHandler(ActionFactory.BACK.getId(), fBackAction);
actionBars.setGlobalActionHandler(ActionFactory.FORWARD.getId(), fForthAction);
fInputSelectionProvider.addSelectionChangedListener(event -> actionBars.setGlobalActionHandler(JdtActionConstants.OPEN_ATTACHED_JAVA_DOC, fOpenBrowserAction));
}
@Override
protected void fillToolBar(IToolBarManager tbm) {
tbm.add(fBackAction);
tbm.add(fForthAction);
tbm.add(new Separator());
super.fillToolBar(tbm);
tbm.add(fOpenBrowserAction);
}
@Override
public void menuAboutToShow(IMenuManager menu) {
super.menuAboutToShow(menu);
menu.appendToGroup(IContextMenuConstants.GROUP_GOTO, fBackAction);
menu.appendToGroup(IContextMenuConstants.GROUP_GOTO, fForthAction);
menu.appendToGroup(IContextMenuConstants.GROUP_OPEN, fOpenBrowserAction);
}
/*
* @see org.eclipse.jdt.internal.ui.infoviews.AbstractInfoView#getSelectAllAction()
* @since 3.0
*/
@Override
protected IAction getSelectAllAction() {
// FIXME: see https://bugs.eclipse.org/bugs/show_bug.cgi?id=63022
if (fIsUsingBrowserWidget)
return null;
return fSelectAllAction;
}
/*
* @see org.eclipse.jdt.internal.ui.infoviews.AbstractInfoView#getCopyToClipboardAction()
* @since 3.0
*/
@Override
protected IAction getCopyToClipboardAction() {
// FIXME: see https://bugs.eclipse.org/bugs/show_bug.cgi?id=63022
if (fIsUsingBrowserWidget)
return null;
return super.getCopyToClipboardAction();
}
/*
* @see AbstractInfoView#setForeground(Color)
*/
@Override
protected void setForeground(Color color) {
getControl().setForeground(color);
fForegroundColorRGB= color.getRGB();
refresh();
}
/*
* @see AbstractInfoView#setBackground(Color)
*/
@Override
protected void setBackground(Color color) {
getControl().setBackground(color);
fBackgroundColorRGB= color.getRGB();
refresh();
}
/**
* Refreshes the view.
*
* @since 3.3
*/
private void refresh() {
doSetInput(computeInput(getOrignalInput()));
}
/*
* @see org.eclipse.jdt.internal.ui.infoviews.AbstractInfoView#getBackgroundColorKey()
* @since 3.2
*/
@Override
protected String getBackgroundColorKey() {
return "org.eclipse.jdt.ui.Javadoc.backgroundColor"; //$NON-NLS-1$
}
@Override
protected String getForegroundColorKey() {
return "org.eclipse.jdt.ui.Javadoc.foregroundColor"; //$NON-NLS-1$
}
/*
* @see AbstractInfoView#internalDispose()
*/
@Override
protected void internalDispose() {
fText= null;
fBrowser= null;
if (fFontListener != null) {
JFaceResources.getFontRegistry().removeListener(fFontListener);
fFontListener= null;
}
if (fOpenBrowserAction != null) {
fInputSelectionProvider.removeSelectionChangedListener(fOpenBrowserAction);
fOpenBrowserAction= null;
}
}
/*
* @see org.eclipse.ui.part.WorkbenchPart#setFocus()
*/
@Override
public void setFocus() {
getControl().setFocus();
}
/*
* @see AbstractInfoView#computeInput(Object)
*/
@Override
protected Object computeInput(Object input) {
if (getControl() == null || ! (input instanceof IJavaElement))
return null;
IWorkbenchPart part= null;
IWorkbenchWindow window= PlatformUI.getWorkbench().getActiveWorkbenchWindow();
if (window != null) {
IWorkbenchPage page= window.getActivePage();
if (page != null) {
part= page.getActivePart();
}
}
ISelection selection= null;
if (part != null) {
IWorkbenchPartSite site= part.getSite();
if (site != null) {
ISelectionProvider provider= site.getSelectionProvider();
if (provider != null) {
selection= provider.getSelection();
}
}
}
return computeInput(part, selection, (IJavaElement) input, new NullProgressMonitor());
}
@Override
protected Object computeInput(IWorkbenchPart part, ISelection selection, IJavaElement input, IProgressMonitor monitor) {
if (getControl() == null || input == null)
return null;
String javadocHtml;
switch (input.getElementType()) {
case IJavaElement.COMPILATION_UNIT:
try {
IType[] types= ((ICompilationUnit) input).getTypes();
if (types.length == 0 && JavaModelUtil.isPackageInfo((ICompilationUnit) input)) {
javadocHtml= getJavadocHtml(new IJavaElement[] { input.getParent() }, part, selection, monitor);
} else {
javadocHtml= getJavadocHtml(types, part, selection, monitor);
}
} catch (JavaModelException ex) {
javadocHtml= null;
}
break;
case IJavaElement.CLASS_FILE:
if (JavaModelUtil.PACKAGE_INFO_CLASS.equals(input.getElementName())) {
javadocHtml= getJavadocHtml(new IJavaElement[] { input.getParent() }, part, selection, monitor);
} else if (input instanceof IModularClassFile) {
try {
javadocHtml= getJavadocHtml(new IJavaElement[] { ((IModularClassFile) input).getModule() }, part, selection, monitor);
} catch (JavaModelException e) {
return null;
}
} else {
javadocHtml= getJavadocHtml(new IJavaElement[] { ((IOrdinaryClassFile) input).getType() }, part, selection, monitor);
}
break;
default:
javadocHtml= getJavadocHtml(new IJavaElement[] { input }, part, selection, monitor);
}
return javadocHtml;
}
/*
* @see AbstractInfoView#computeDescription(org.eclipse.ui.IWorkbenchPart, org.eclipse.jface.viewers.ISelection, org.eclipse.jdt.core.IJavaElement, org.eclipse.core.runtime.IProgressMonitor)
* @since 3.4
*/
@Override
protected String computeDescription(IWorkbenchPart part, ISelection selection, IJavaElement inputElement, IProgressMonitor monitor) {
return ""; //$NON-NLS-1$
}
/**
* Set input to the given input.
*
* @param input the input for the view
* @since 3.4
*/
public void setInput(BrowserInput input) {
fCurrent= input;
Object inputElement= input.getInputElement();
if (inputElement instanceof IJavaElement) {
setInput((IJavaElement) inputElement);
} else if (inputElement instanceof URL) {
fBrowser.setUrl(((URL) inputElement).toExternalForm());
if (fInputSelectionProvider != null)
fInputSelectionProvider.setSelection(new StructuredSelection(inputElement));
}
fForthAction.update();
fBackAction.update();
}
/**
* {@inheritDoc}
*
* @param input a String containing the HTML to be shown in the view, or <code>null</code>
*/
@Override
protected void doSetInput(Object input) {
String javadocHtml;
if (input instanceof String) {
javadocHtml= (String) input;
} else {
StringBuilder buffer= new StringBuilder();
HTMLPrinter.insertPageProlog(buffer, 0, fForegroundColorRGB, fBackgroundColorRGB, fgStyleSheet);
HTMLPrinter.addPageEpilog(buffer);
javadocHtml= buffer.toString();
}
fOriginalInput= javadocHtml;
if (fInputSelectionProvider != null) {
IJavaElement inputElement= getOrignalInput();
StructuredSelection selection= inputElement == null ? StructuredSelection.EMPTY : new StructuredSelection(inputElement);
fInputSelectionProvider.setSelection(selection);
}
if (fOpenBrowserAction != null)
fOpenBrowserAction.setEnabled(input != null);
if (fIsUsingBrowserWidget) {
if (javadocHtml != null && javadocHtml.length() > 0) {
boolean RTL= (getSite().getShell().getStyle() & SWT.RIGHT_TO_LEFT) != 0;
if (RTL) {
StringBuilder buffer= new StringBuilder(javadocHtml);
HTMLPrinter.insertStyles(buffer, new String[] { "direction:rtl" } ); //$NON-NLS-1$
javadocHtml= buffer.toString();
}
}
fBrowser.setText(javadocHtml);
} else {
fPresentation.clear();
Rectangle size= fText.getClientArea();
try {
javadocHtml= fPresenter.updatePresentation(fText, javadocHtml, fPresentation, size.width, size.height);
} catch (IllegalArgumentException ex) {
// the javadoc might no longer be valid
return;
}
fText.setText(javadocHtml);
TextPresentation.applyTextPresentation(fPresentation, fText);
}
}
/**
* Returns the Javadoc of the Java element in HTML format.
*
* @param result the Java elements for which to get the Javadoc
* @param activePart the active part if any
* @param selection the selection of the active site if any
* @param monitor a monitor to report progress to
* @return a string with the Javadoc in HTML format, or <code>null</code> if none
*/
private String getJavadocHtml(IJavaElement[] result, IWorkbenchPart activePart, ISelection selection, IProgressMonitor monitor) {
StringBuilder buffer= new StringBuilder();
int nResults= result.length;
if (nResults == 0)
return null;
String base= null;
if (nResults > 1) {
for (IJavaElement r : result) {
HTMLPrinter.startBulletList(buffer);
IJavaElement curr= r;
if (curr instanceof IMember || curr instanceof IPackageFragment || curr instanceof IPackageDeclaration || curr.getElementType() == IJavaElement.LOCAL_VARIABLE) {
HTMLPrinter.addBullet(buffer, getInfoText(curr, null, null, false));
HTMLPrinter.endBulletList(buffer);
}
}
} else {
IJavaElement curr= result[0];
if (curr instanceof IPackageDeclaration || curr instanceof IPackageFragment) {
HTMLPrinter.addSmallHeader(buffer, getInfoText(curr, null, null, true));
buffer.append("<br>"); //$NON-NLS-1$
Reader reader= null;
String content= null;
try {
if (curr instanceof IPackageDeclaration) {
try {
ISourceRange nameRange= ((IPackageDeclaration) curr).getNameRange();
if (SourceRange.isAvailable(nameRange)) {
ITypeRoot typeRoot= (ITypeRoot) ((IPackageDeclaration) curr).getParent();
Region hoverRegion= new Region(nameRange.getOffset(), nameRange.getLength());
JavadocHover.addAnnotations(buffer, typeRoot.getParent(), typeRoot, hoverRegion);
}
} catch (JavaModelException e) {
// no annotations this time...
}
content= JavadocContentAccess2.getHTMLContent((IPackageDeclaration) curr);
} else if (curr instanceof IPackageFragment) {
JavadocHover.addAnnotations(buffer, curr, null, null);
content= JavadocContentAccess2.getHTMLContent((IPackageFragment) curr);
}
} catch (CoreException e) {
reader= new StringReader(JavaDocLocations.handleFailedJavadocFetch(e));
}
IPackageFragmentRoot root= (IPackageFragmentRoot) curr.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
try {
boolean isBinary= root.getKind() == IPackageFragmentRoot.K_BINARY;
if (content != null) {
base= JavadocContentAccess2.extractBaseURL(content);
if (base == null) {
base= JavaDocLocations.getBaseURL(curr, isBinary);
}
reader= new StringReader(content);
} else if (reader == null) {
String explanationForMissingJavadoc= JavaDocLocations.getExplanationForMissingJavadoc(curr, root);
if (explanationForMissingJavadoc != null) {
reader= new StringReader(explanationForMissingJavadoc);
}
}
} catch (JavaModelException e) {
reader= new StringReader(InfoViewMessages.JavadocView_error_gettingJavadoc);
JavaPlugin.log(e);
}
if (reader != null) {
HTMLPrinter.addParagraph(buffer, reader);
}
} else if (curr instanceof IMember || curr instanceof ILocalVariable || curr instanceof ITypeParameter) {
final IJavaElement element= curr;
ITypeRoot typeRoot= null;
Region hoverRegion= null;
try {
ISourceRange nameRange= ((ISourceReference) curr).getNameRange();
if (SourceRange.isAvailable(nameRange)) {
if (element instanceof ILocalVariable) {
typeRoot= ((ILocalVariable) curr).getTypeRoot();
} else if (element instanceof ITypeParameter) {
typeRoot= ((ITypeParameter) curr).getTypeRoot();
} else {
typeRoot= ((IMember) curr).getTypeRoot();
}
hoverRegion= new Region(nameRange.getOffset(), nameRange.getLength());
}
} catch (JavaModelException e) {
// no annotations this time...
}
String constantValue= null;
if (element instanceof IField) {
constantValue= computeFieldConstant(activePart, selection, (IField) element, monitor);
if (constantValue != null)
constantValue= HTMLPrinter.convertToHTMLContentWithWhitespace(constantValue);
}
String defaultValue= null;
if (element instanceof IMethod) {
try {
defaultValue= JavadocHover.getAnnotationMemberDefaultValue((IMethod) element, typeRoot, hoverRegion);
if (defaultValue != null) {
defaultValue= HTMLPrinter.convertToHTMLContentWithWhitespace(defaultValue);
}
} catch (JavaModelException e) {
// no default value
}
}
HTMLPrinter.addSmallHeader(buffer, getInfoText(element, constantValue, defaultValue, true));
if (typeRoot != null && hoverRegion != null) {
buffer.append("<br>"); //$NON-NLS-1$
JavadocHover.addAnnotations(buffer, curr, typeRoot, hoverRegion);
}
Reader reader= null;
try {
String content= JavadocContentAccess2.getHTMLContent(element, true);
IPackageFragmentRoot root= (IPackageFragmentRoot) element.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
if (content != null) {
IMember member;
if (element instanceof ILocalVariable) {
member= ((ILocalVariable) element).getDeclaringMember();
} else if (element instanceof ITypeParameter) {
member= ((ITypeParameter) element).getDeclaringMember();
} else {
member= (IMember) element;
}
base= JavadocContentAccess2.extractBaseURL(content);
if (base == null) {
base= JavaDocLocations.getBaseURL(member, member.isBinary());
}
reader= new StringReader(content);
} else {
String explanationForMissingJavadoc= JavaDocLocations.getExplanationForMissingJavadoc(element, root);
if (explanationForMissingJavadoc != null) {
reader= new StringReader(explanationForMissingJavadoc);
}
}
} catch (CoreException ex) {
reader= new StringReader(JavaDocLocations.handleFailedJavadocFetch(ex));
}
if (reader != null) {
HTMLPrinter.addParagraph(buffer, reader);
}
}
}
if (buffer.length() == 0)
return null;
HTMLPrinter.insertPageProlog(buffer, 0, fForegroundColorRGB, fBackgroundColorRGB, fgStyleSheet);
if (base != null) {
int endHeadIdx= buffer.indexOf("</head>"); //$NON-NLS-1$
buffer.insert(endHeadIdx, "\n<base href='" + base + "'>\n"); //$NON-NLS-1$ //$NON-NLS-2$
}
HTMLPrinter.addPageEpilog(buffer);
return buffer.toString();
}
/**
* Gets the label for the given member.
*
* @param member the Java member
* @param constantValue the constant value if any
* @param defaultValue the default value of the annotation type member, if any
* @param allowImage <code>true</code> if the Java element image should be shown
* @return a string containing the member's label
*/
private String getInfoText(IJavaElement member, String constantValue, String defaultValue, boolean allowImage) {
long flags= JavadocHover.getHeaderFlags(member);
IBinding binding= JavadocHover.getHoverBinding(member, null);
StringBuffer label;
if (binding != null) {
label= new StringBuffer();
// setting haveSource to false lets the JavadocView *always* show qualified type names,
// would need to track the source of our input to distinguish classfile/compilationUnit:
boolean haveSource= false;
new BindingLinkedLabelComposer(member, label, haveSource).appendBindingLabel(binding, flags);
} else {
label= new StringBuffer(JavaElementLinks.getElementLabel(member, flags));
}
if (member.getElementType() == IJavaElement.FIELD && constantValue != null) {
label.append(constantValue);
} else if (member.getElementType() == IJavaElement.METHOD && defaultValue != null) {
label.append(JavadocHover.CONSTANT_VALUE_SEPARATOR);
label.append(defaultValue);
}
return JavadocHover.getImageAndLabel(member, allowImage, label.toString());
}
/*
* @see org.eclipse.jdt.internal.ui.infoviews.AbstractInfoView#isIgnoringNewInput(org.eclipse.jdt.core.IJavaElement, org.eclipse.jface.viewers.ISelection)
* @since 3.2
*/
@Override
protected boolean isIgnoringNewInput(IJavaElement je, IWorkbenchPart part, ISelection selection) {
if (fCurrent != null && fCurrent.getInputElement() instanceof URL)
return false;
if (super.isIgnoringNewInput(je, part, selection)
&& part instanceof ITextEditor
&& selection instanceof ITextSelection) {
ITextEditor editor= (ITextEditor)part;
IDocumentProvider docProvider= editor.getDocumentProvider();
if (docProvider == null)
return false;
IDocument document= docProvider.getDocument(editor.getEditorInput());
if (!(document instanceof IDocumentExtension3))
return false;
try {
int offset= ((ITextSelection)selection).getOffset();
String partition= ((IDocumentExtension3)document).getContentType(IJavaPartitions.JAVA_PARTITIONING, offset, false);
return !IJavaPartitions.JAVA_DOC.equals(partition);
} catch (BadPartitioningException | BadLocationException ex) {
return false;
}
}
return false;
}
/*
* @see AbstractInfoView#findSelectedJavaElement(IWorkbenchPart)
*/
@Override
protected IJavaElement findSelectedJavaElement(IWorkbenchPart part, ISelection selection) {
IJavaElement element= super.findSelectedJavaElement(part, selection);
try {
//update the Javadoc view when package.html is selected in project explorer view
if (element == null && selection instanceof IStructuredSelection) {
Object selectedElement= ((IStructuredSelection) selection).getFirstElement();
if (selectedElement instanceof IFile) {
IFile selectedFile= (IFile) selectedElement;
if (JavaModelUtil.PACKAGE_HTML.equals(selectedFile.getName())) {
element= JavaCore.create(selectedFile.getParent());
}
} else if (selectedElement instanceof IJarEntryResource) {
IJarEntryResource jarEntryResource= (IJarEntryResource) selectedElement;
if (JavaModelUtil.PACKAGE_HTML.equals(jarEntryResource.getName())) {
Object parent= jarEntryResource.getParent();
if (parent instanceof IJavaElement) {
element= (IJavaElement) parent;
}
}
}
}
if (element == null && selection instanceof ITextSelection) {
ITextSelection textSelection= (ITextSelection) selection;
if (part instanceof AbstractDecoratedTextEditor) {
AbstractDecoratedTextEditor editor= (AbstractDecoratedTextEditor) part;
IDocumentProvider documentProvider= editor.getDocumentProvider();
if (documentProvider != null) {
IEditorInput editorInput= editor.getEditorInput();
IDocument document= documentProvider.getDocument(editorInput);
if (document != null) {
ITypedRegion typedRegion= TextUtilities.getPartition(document, IJavaPartitions.JAVA_PARTITIONING, textSelection.getOffset(), false);
if (IJavaPartitions.JAVA_DOC.equals(typedRegion.getType())){
element= TextSelectionConverter.getElementAtOffset((JavaEditor) part, textSelection);
}
else if (editorInput instanceof IFileEditorInput) {
IFile file= ((IFileEditorInput) editorInput).getFile();
//update the Javadoc view when the content of the package.html is modified in the editor
if (JavaModelUtil.PACKAGE_HTML.equals(file.getName())) {
element= JavaCore.create(file.getParent());
}
}
}
}
}
}
} catch (JavaModelException | BadLocationException e) {
return null;
}
return element;
}
/*
* @see AbstractInfoView#getControl()
*/
@Override
protected Control getControl() {
if (fIsUsingBrowserWidget)
return fBrowser;
else
return fText;
}
/*
* @see org.eclipse.jdt.internal.ui.infoviews.AbstractInfoView#getHelpContextId()
* @since 3.1
*/
@Override
protected String getHelpContextId() {
return IJavaHelpContextIds.JAVADOC_VIEW;
}
/**
* Compute the textual representation of a 'static' 'final' field's constant initializer value.
*
* @param activePart the part that triggered the computation, or <code>null</code>
* @param selection the selection that references the field, or <code>null</code>
* @param resolvedField the field whose constant value will be computed
* @param monitor the progress monitor
*
* @return the textual representation of the constant, or <code>null</code> if the
* field is not a constant field, the initializer value could not be computed, or
* the progress monitor was cancelled
* @since 3.4
*/
private String computeFieldConstant(IWorkbenchPart activePart, ISelection selection, IField resolvedField, IProgressMonitor monitor) {
if (!JavadocHover.isStaticFinal(resolvedField))
return null;
Object constantValue;
if (selection instanceof ITextSelection && activePart instanceof JavaEditor) {
IEditorPart editor= (IEditorPart) activePart;
ITypeRoot activeType= JavaUI.getEditorInputTypeRoot(editor.getEditorInput());
constantValue= getConstantValueFromActiveEditor(activeType, resolvedField, (ITextSelection) selection, monitor);
if (constantValue == null) // fall back - e.g. when selection is inside Javadoc of the element
constantValue= computeFieldConstantFromTypeAST(resolvedField, monitor);
} else {
constantValue= computeFieldConstantFromTypeAST(resolvedField, monitor);
}
if (constantValue != null)
return JavadocHover.CONSTANT_VALUE_SEPARATOR + formatCompilerConstantValue(constantValue);
return null;
}
/**
* Retrieve a constant initializer value of a field by (AST) parsing field's type.
*
* @param constantField the constant field
* @param monitor the progress monitor or null
* @return the constant value of the field, or <code>null</code> if it could not be computed
* (or if the progress was cancelled).
* @since 3.4
*/
public static Object computeFieldConstantFromTypeAST(IField constantField, IProgressMonitor monitor) {
if (monitor != null && monitor.isCanceled())
return null;
CompilationUnit ast= SharedASTProviderCore.getAST(constantField.getTypeRoot(), SharedASTProviderCore.WAIT_NO, monitor);
if (ast != null) {
try {
if (constantField.isEnumConstant())
return null;
VariableDeclarationFragment fieldDecl= ASTNodeSearchUtil.getFieldDeclarationFragmentNode(constantField, ast);
if (fieldDecl == null)
return null;
Expression initializer= fieldDecl.getInitializer();
if (initializer == null)
return null;
return initializer.resolveConstantExpressionValue();
} catch (JavaModelException e) {
// ignore the exception and try the next method
}
}
if (monitor != null && monitor.isCanceled())
return null;
ASTParser p= ASTParser.newParser(IASTSharedValues.SHARED_AST_LEVEL);
p.setProject(constantField.getJavaProject());
IBinding[] createBindings;
try {
createBindings= p.createBindings(new IJavaElement[] { constantField }, monitor);
} catch (OperationCanceledException e) {
return null;
}
IVariableBinding variableBinding= (IVariableBinding) createBindings[0];
if (variableBinding != null)
return variableBinding.getConstantValue();
return null;
}
/**
* Returns the constant value for a field that is referenced by the currently active type. This
* method does may not run in the main UI thread.
*
* @param activeType the type that is currently active
* @param field the field that is being referenced (usually not declared in
* <code>activeType</code>)
* @param selection the region in <code>activeType</code> that contains the field reference
* @param monitor a progress monitor
*
* @return the constant value for the given field or <code>null</code> if none
* @since 3.4
*/
private static Object getConstantValueFromActiveEditor(ITypeRoot activeType, IField field, ITextSelection selection, IProgressMonitor monitor) {
CompilationUnit unit= SharedASTProviderCore.getAST(activeType, SharedASTProviderCore.WAIT_ACTIVE_ONLY, monitor);
if (unit == null)
return null;
ASTNode node= NodeFinder.perform(unit, selection.getOffset(), selection.getLength());
return JavadocHover.getVariableBindingConstValue(node, field);
}
/**
* Returns the string representation of the given constant value.
*
* @param constantValue the constant value
* @return the string representation of the given constant value.
* @since 3.4
*/
private static String formatCompilerConstantValue(Object constantValue) {
if (constantValue instanceof String) {
StringBuilder result= new StringBuilder();
result.append('"');
String stringConstant= (String)constantValue;
if (stringConstant.length() > 80) {
result.append(stringConstant.substring(0, 80));
result.append(JavaElementLabels.ELLIPSIS_STRING);
} else {
result.append(stringConstant);
}
result.append('"');
return result.toString();
} else {
return JavadocHover.getHexConstantValue(constantValue);
}
}
/**
* see also org.eclipse.jdt.internal.ui.text.java.hover.JavadocHover.addLinkListener(BrowserInformationControl)
*
* Add link listener to the given browser
* @param browser the browser to add a listener to
* @since 3.4
*/
private void addLinkListener(Browser browser) {
browser.addLocationListener(JavaElementLinks.createLocationListener(new JavaElementLinks.ILinkHandler() {
@Override
public void handleDeclarationLink(IJavaElement target) {
try {
JavadocHover.openDeclaration(target);
} catch (PartInitException | JavaModelException e) {
JavaPlugin.log(e);
}
}
@Override
public boolean handleExternalLink(final URL url, Display display) {
if (fCurrent == null ||
!(fCurrent.getInputElement() instanceof URL)
|| !url.toExternalForm().equals(((URL) fCurrent.getInputElement()).toExternalForm())) {
fCurrent= new URLBrowserInput(fCurrent, url);
if (fBackAction != null) {
fBackAction.update();
fForthAction.update();
}
if (fInputSelectionProvider != null)
fInputSelectionProvider.setSelection(new StructuredSelection(url));
}
return false;
}
@Override
public void handleInlineJavadocLink(IJavaElement target) {
JavaElementBrowserInput newInput= new JavaElementBrowserInput(fCurrent, target);
JavadocView.this.setInput(newInput);
}
@Override
public void handleJavadocViewLink(IJavaElement target) {
handleInlineJavadocLink(target);
}
@Override
public void handleTextSet() {
IJavaElement input= getOrignalInput();
if (input == null)
return;
if (fCurrent == null || !fCurrent.getInputElement().equals(input)) {
fCurrent= new JavaElementBrowserInput(null, input);
if (fBackAction != null) {
fBackAction.update();
fForthAction.update();
}
}
}
}));
}
}