6fc07d82b2e50fb8fc499453c231d8e098f2aa12
[phpeclipse.git] /
1 /**
2  * Copyright (c) 2003 IBM Corporation and others. All rights reserved. � This
3  * program and the accompanying materials are made available under the terms of
4  * the Common Public License v1.0 which accompanies this distribution, and is
5  * available at http://www.eclipse.org/legal/cpl-v10.html �* Contributors: IBM -
6  * Initial API and implementation
7  */
8
9 // TODO 1. Handle the sizing of a popup running in shelled out secondary window.
10 // TODO 2. Support printing: waiting on eclipse bug 47937/44823.
11 package net.sourceforge.phpeclipse.webbrowser.internal;
12
13 import java.util.Iterator;
14
15 import net.sourceforge.phpeclipse.webbrowser.IURLMap;
16
17 import org.eclipse.swt.SWT;
18 import org.eclipse.swt.browser.Browser;
19 import org.eclipse.swt.browser.CloseWindowListener;
20 import org.eclipse.swt.browser.LocationEvent;
21 import org.eclipse.swt.browser.LocationListener;
22 import org.eclipse.swt.browser.OpenWindowListener;
23 import org.eclipse.swt.browser.ProgressEvent;
24 import org.eclipse.swt.browser.ProgressListener;
25 import org.eclipse.swt.browser.StatusTextEvent;
26 import org.eclipse.swt.browser.StatusTextListener;
27 import org.eclipse.swt.browser.TitleEvent;
28 import org.eclipse.swt.browser.TitleListener;
29 import org.eclipse.swt.browser.WindowEvent;
30 import org.eclipse.swt.dnd.Clipboard;
31 import org.eclipse.swt.events.SelectionAdapter;
32 import org.eclipse.swt.events.SelectionEvent;
33 import org.eclipse.swt.graphics.Point;
34 import org.eclipse.swt.graphics.Rectangle;
35 import org.eclipse.swt.layout.FillLayout;
36 import org.eclipse.swt.layout.GridData;
37 import org.eclipse.swt.layout.GridLayout;
38 import org.eclipse.swt.widgets.Combo;
39 import org.eclipse.swt.widgets.Composite;
40 import org.eclipse.swt.widgets.Control;
41 import org.eclipse.swt.widgets.Event;
42 import org.eclipse.swt.widgets.Label;
43 import org.eclipse.swt.widgets.Listener;
44 import org.eclipse.swt.widgets.Menu;
45 import org.eclipse.swt.widgets.MenuItem;
46 import org.eclipse.swt.widgets.ProgressBar;
47 import org.eclipse.swt.widgets.Shell;
48 import org.eclipse.swt.widgets.ToolBar;
49 import org.eclipse.swt.widgets.ToolItem;
50 import org.eclipse.ui.PlatformUI;
51
52 public class WebBrowser extends Composite {
53         protected Composite toolbarComp;
54
55         protected Composite statusComp;
56
57         protected Combo combo;
58
59         protected Clipboard clipboard;
60
61         protected boolean showToolbar;
62
63         protected ToolItem back;
64
65         protected ToolItem forward;
66
67         protected ToolItem stop;
68
69         protected ToolItem favorites;
70
71         protected ToolItem refresh;
72
73         protected BusyIndicator busy;
74
75         protected boolean showStatusbar;
76
77         protected ProgressBar progress;
78
79         protected Label status;
80
81         private static int MAX_HISTORY = 50;
82
83         protected static java.util.List history;
84
85         protected Browser browser;
86
87         protected Shell shell;
88
89         protected WebBrowserEditor editor;
90
91         protected String title;
92
93         public WebBrowser(Composite parent, final boolean showToolbar,
94                         final boolean showStatusbar) {
95                 super(parent, SWT.NONE);
96
97                 this.showToolbar = showToolbar;
98                 this.showStatusbar = showStatusbar;
99
100                 GridLayout layout = new GridLayout();
101                 layout.marginHeight = 3;
102                 layout.marginWidth = 3;
103                 layout.horizontalSpacing = 3;
104                 layout.verticalSpacing = 3;
105                 layout.numColumns = 1;
106                 setLayout(layout);
107                 setLayoutData(new GridData(GridData.FILL_BOTH));
108                 clipboard = new Clipboard(parent.getDisplay());
109                 PlatformUI.getWorkbench().getHelpSystem().setHelp(this,
110                                 ContextIds.WEB_BROWSER);
111
112                 if (showToolbar) {
113                         toolbarComp = new Composite(this, SWT.NONE);
114                         GridLayout outerLayout = new GridLayout();
115                         outerLayout.numColumns = 2;
116                         outerLayout.marginWidth = 0;
117                         outerLayout.marginHeight = 0;
118                         toolbarComp.setLayout(outerLayout);
119                         toolbarComp.setLayoutData(new GridData(
120                                         GridData.VERTICAL_ALIGN_BEGINNING
121                                                         | GridData.FILL_HORIZONTAL));
122
123                         // create the top line, with a combo box for history and a "go"
124                         // button
125                         Composite top = new Composite(toolbarComp, SWT.NONE);
126                         GridLayout topLayout = new GridLayout();
127                         topLayout.numColumns = 2;
128                         topLayout.marginWidth = 0;
129                         topLayout.marginHeight = 0;
130                         top.setLayout(topLayout);
131                         top.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_CENTER
132                                         | GridData.FILL_HORIZONTAL));
133
134                         combo = new Combo(top, SWT.DROP_DOWN);
135
136                         updateHistory();
137
138                         combo.addSelectionListener(new SelectionAdapter() {
139                                 public void widgetSelected(SelectionEvent we) {
140                                         try {
141                                                 if (combo.getSelectionIndex() != -1)
142                                                         setURL(combo.getItem(combo.getSelectionIndex()),
143                                                                         false);
144                                         } catch (Exception e) {
145                                         }
146                                 }
147                         });
148                         combo.addListener(SWT.DefaultSelection, new Listener() {
149                                 public void handleEvent(Event e) {
150                                         setURL(combo.getText());
151                                 }
152                         });
153                         combo.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
154                         PlatformUI.getWorkbench().getHelpSystem().setHelp(combo,
155                                         ContextIds.WEB_BROWSER_URL);
156
157                         ToolBar toolbar = new ToolBar(top, SWT.FLAT);
158                         fillToolBar(toolbar);
159
160                         new ToolItem(toolbar, SWT.SEPARATOR);
161
162                         busy = new BusyIndicator(toolbarComp, SWT.NONE);
163                         busy.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_END));
164                 }
165
166                 // create a new SWT Web browser widget, checking once again to make sure
167                 // we can use it in this environment
168                 // if (WebBrowserUtil.canUseInternalWebBrowser() &
169                 // WebBrowserUtil.isInternalBrowserOperational())
170                 if (WebBrowserUtil.isInternalBrowserOperational())
171                         this.browser = new Browser(this, SWT.NONE);
172                 else {
173                         WebBrowserUtil.openError(WebBrowserUIPlugin
174                                         .getResource("%errorCouldNotLaunchInternalWebBrowser"));
175                         return;
176                 }
177
178                 if (showToolbar) {
179                         back.setEnabled(browser.isBackEnabled());
180                         forward.setEnabled(browser.isForwardEnabled());
181                 }
182
183                 PlatformUI.getWorkbench().getHelpSystem().setHelp(browser,
184                                 ContextIds.WEB_BROWSER_WEB);
185                 GridData data = new GridData();
186                 data.horizontalAlignment = GridData.FILL;
187                 data.verticalAlignment = GridData.FILL;
188                 data.horizontalSpan = 3;
189                 data.grabExcessHorizontalSpace = true;
190                 data.grabExcessVerticalSpace = true;
191                 browser.setLayoutData(data);
192
193                 if (showStatusbar)
194                         createStatusArea(this);
195
196                 addBrowserListeners();
197         }
198
199         /**
200          * 
201          */
202         protected void addBrowserListeners() {
203                 if (showStatusbar) {
204                         // respond to Browser StatusTextEvents events by updating the status
205                         // Text label
206                         browser.addStatusTextListener(new StatusTextListener() {
207                                 public void changed(StatusTextEvent event) {
208                                         status.setText(event.text);
209                                 }
210                         });
211                 }
212
213                 /**
214                  * Add listener for new window creation so that we can instead of
215                  * opening a separate new window in which the session is lost, we can
216                  * instead open a new window in a new shell within the browser area
217                  * thereby maintaining the session.
218                  */
219                 browser.addOpenWindowListener(new OpenWindowListener() {
220                         public void open(WindowEvent event) {
221                                 Shell shell2 = new Shell(getDisplay());
222                                 shell2.setLayout(new FillLayout());
223                                 shell2.setText(WebBrowserUIPlugin
224                                                 .getResource("%viewWebBrowserTitle"));
225                                 shell2.setImage(getShell().getImage());
226                                 WebBrowser browser2 = new WebBrowser(shell2, showToolbar,
227                                                 showStatusbar);
228                                 browser2.shell = shell2;
229                                 event.browser = browser2.browser;
230                                 shell2.open();
231                         }
232                 });
233
234                 browser.addCloseWindowListener(new CloseWindowListener() {
235                         public void close(WindowEvent event) {
236                                 // if shell is not null, it must be a secondary popup window,
237                                 // else its an editor window
238                                 if (shell != null)
239                                         shell.dispose();
240                                 else {
241                                         // #1365431 (toshihiro) editor.closeEditor(); causes NPE
242                                         if (editor != null)
243                                                 editor.closeEditor();
244                                 }
245                         }
246                 });
247
248                 browser.addProgressListener(new ProgressListener() {
249                         public void changed(ProgressEvent event) {
250                                 if (event.total == 0)
251                                         return;
252
253                                 boolean done = (event.current == event.total);
254
255                                 int percentProgress = event.current * 100 / event.total;
256                                 if (showStatusbar) {
257                                         if (done)
258                                                 progress.setSelection(0);
259                                         else
260                                                 progress.setSelection(percentProgress);
261                                 }
262
263                                 if (showToolbar) {
264                                         if (!busy.isBusy()
265                                                         && (percentProgress > 0 && percentProgress < 100)) {
266                                                 busy.setBusy(true);
267                                         }
268                                         // Once the progress hits 100 percent, done, set busy to
269                                         // false
270                                         else if (busy.isBusy() && done) {
271                                                 busy.setBusy(false);
272                                         }
273                                 }
274                         }
275
276                         public void completed(ProgressEvent event) {
277                                 if (showStatusbar)
278                                         progress.setSelection(0);
279                                 if (showToolbar) {
280                                         busy.setBusy(false);
281                                         back.setEnabled(browser.isBackEnabled());
282                                         forward.setEnabled(browser.isForwardEnabled());
283                                 }
284                         }
285                 });
286
287                 if (showToolbar) {
288                         browser.addLocationListener(new LocationListener() {
289                                 public void changed(LocationEvent event) {
290                                         if (!event.top)
291                                                 return;
292                                         if (!isHome()) {
293                                                 combo.setText(event.location);
294                                                 addToHistory(event.location);
295                                                 updateHistory();
296                                         } else
297                                                 combo.setText("");
298                                 }
299
300                                 public void changing(LocationEvent event) {
301                                 }
302                         });
303                 }
304
305                 browser.addTitleListener(new TitleListener() {
306                         public void changed(TitleEvent event) {
307                                 title = event.title;
308                         }
309                 });
310         }
311
312         /**
313          * Return the underlying browser control.
314          * 
315          * @return org.eclipse.swt.browser.Browser
316          */
317         public Browser getBrowser() {
318                 return browser;
319         }
320
321         /**
322          * 
323          */
324         protected void forward() {
325                 browser.forward();
326         }
327
328         /**
329          * 
330          */
331         protected void back() {
332                 browser.back();
333         }
334
335         /**
336          * 
337          */
338         protected void stop() {
339                 browser.stop();
340         }
341
342         /**
343          * 
344          */
345         protected void navigate(String url) {
346                 Trace.trace(Trace.FINER, "Navigate: " + url);
347                 if (url != null && url.equals(getURL())) {
348                         refresh();
349                         return;
350                 }
351                 browser.setUrl(url);
352         }
353
354         /**
355          * Refresh the currently viewed page.
356          */
357         public void refresh() {
358                 browser.refresh();
359         }
360
361         protected void setURL(String url, boolean browse) {
362                 Trace.trace(Trace.FINEST, "setURL: " + url + " " + browse);
363                 if (url == null) {
364                         home();
365                         return;
366                 }
367
368                 if (url.endsWith(WebBrowserPreference.getHomePageURL().substring(9)))
369                         return;
370
371                 // check URL maps
372                 Iterator iterator = WebBrowserUtil.getURLMaps().iterator();
373                 String newURL = null;
374                 while (iterator.hasNext() && newURL == null) {
375                         try {
376                                 IURLMap map = (IURLMap) iterator.next();
377                                 newURL = map.getMappedURL(url);
378                         } catch (Exception e) {
379                         }
380                 }
381                 if (newURL != null)
382                         url = newURL;
383
384                 if (browse) {
385                         navigate(url);
386
387                         addToHistory(url);
388                         updateHistory();
389                 }
390         }
391
392         protected void addToHistory(String url) {
393                 if (history == null)
394                         history = WebBrowserPreference.getInternalWebBrowserHistory();
395                 int found = -1;
396                 int size = history.size();
397                 for (int i = 0; i < size; i++) {
398                         String s = (String) history.get(i);
399                         if (s.equals(url)) {
400                                 found = i;
401                                 break;
402                         }
403                 }
404
405                 if (found == -1) {
406                         if (size >= MAX_HISTORY)
407                                 history.remove(size - 1);
408                         history.add(0, url);
409                         WebBrowserPreference.setInternalWebBrowserHistory(history);
410                 } else if (found != 0) {
411                         history.remove(found);
412                         history.add(0, url);
413                         WebBrowserPreference.setInternalWebBrowserHistory(history);
414                 }
415         }
416
417         public void setURL(String url) {
418                 setURL(url, true);
419         }
420
421         /**
422          * Creates the Web browser status area.
423          */
424         private void createStatusArea(Composite parent) {
425                 Composite composite = new Composite(parent, SWT.NONE);
426                 GridLayout layout = new GridLayout();
427                 layout.numColumns = 2;
428                 layout.marginHeight = 0;
429                 layout.marginWidth = 0;
430                 layout.horizontalSpacing = 4;
431                 layout.verticalSpacing = 0;
432                 composite.setLayout(layout);
433                 composite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
434
435                 // Add a label for displaying status messages as they are received from
436                 // the control
437                 status = new Label(composite, SWT.SINGLE | SWT.READ_ONLY);
438                 GridData gridData = new GridData(GridData.FILL_HORIZONTAL
439                                 | GridData.VERTICAL_ALIGN_FILL);
440                 gridData.horizontalIndent = 2;
441                 status.setLayoutData(gridData);
442
443                 // Add a progress bar to display downloading progress information
444                 progress = new ProgressBar(composite, SWT.BORDER);
445                 gridData = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING
446                                 | GridData.VERTICAL_ALIGN_FILL);
447                 gridData.widthHint = 100;
448                 gridData.heightHint = 10;
449                 progress.setLayoutData(gridData);
450         }
451
452         /**
453          * 
454          */
455         public void dispose() {
456                 super.dispose();
457
458                 showStatusbar = false;
459                 showToolbar = false;
460
461                 if (busy != null)
462                         busy.dispose();
463                 busy = null;
464
465                 browser = null;
466         }
467
468         /**
469          * Populate the toolbar.
470          * 
471          * @param toolbar
472          *            org.eclipse.swt.widgets.ToolBar
473          */
474         private void fillToolBar(final ToolBar toolbar) {
475                 ToolItem go = new ToolItem(toolbar, SWT.NONE);
476                 go.setImage(ImageResource.getImage(ImageResource.IMG_ELCL_NAV_GO));
477                 go.setHotImage(ImageResource.getImage(ImageResource.IMG_CLCL_NAV_GO));
478                 go.setDisabledImage(ImageResource
479                                 .getImage(ImageResource.IMG_DLCL_NAV_GO));
480                 go
481                                 .setToolTipText(WebBrowserUIPlugin
482                                                 .getResource("%actionWebBrowserGo"));
483                 go.addSelectionListener(new SelectionAdapter() {
484                         public void widgetSelected(SelectionEvent event) {
485                                 setURL(combo.getText());
486                         }
487                 });
488
489                 new ToolItem(toolbar, SWT.SEPARATOR);
490
491                 favorites = new ToolItem(toolbar, SWT.DROP_DOWN);
492                 favorites.setImage(ImageResource
493                                 .getImage(ImageResource.IMG_ELCL_NAV_FAVORITES));
494                 favorites.setHotImage(ImageResource
495                                 .getImage(ImageResource.IMG_CLCL_NAV_FAVORITES));
496                 favorites.setDisabledImage(ImageResource
497                                 .getImage(ImageResource.IMG_DLCL_NAV_FAVORITES));
498                 favorites.setToolTipText(WebBrowserUIPlugin
499                                 .getResource("%actionWebBrowserFavorites"));
500
501                 favorites.addSelectionListener(new SelectionAdapter() {
502                         public void widgetSelected(SelectionEvent event) {
503                                 if (event.detail == SWT.ARROW) {
504                                         Rectangle r = favorites.getBounds();
505                                         showFavorites(toolbar, toolbar.toDisplay(r.x, r.y
506                                                         + r.height));
507                                 } else
508                                         addFavorite();
509                         }
510                 });
511
512                 // create back and forward actions
513                 back = new ToolItem(toolbar, SWT.NONE);
514                 back.setImage(ImageResource
515                                 .getImage(ImageResource.IMG_ELCL_NAV_BACKWARD));
516                 back.setHotImage(ImageResource
517                                 .getImage(ImageResource.IMG_CLCL_NAV_BACKWARD));
518                 back.setDisabledImage(ImageResource
519                                 .getImage(ImageResource.IMG_DLCL_NAV_BACKWARD));
520                 back.setToolTipText(WebBrowserUIPlugin
521                                 .getResource("%actionWebBrowserBack"));
522                 back.addSelectionListener(new SelectionAdapter() {
523                         public void widgetSelected(SelectionEvent event) {
524                                 back();
525                         }
526                 });
527
528                 forward = new ToolItem(toolbar, SWT.NONE);
529                 forward.setImage(ImageResource
530                                 .getImage(ImageResource.IMG_ELCL_NAV_FORWARD));
531                 forward.setHotImage(ImageResource
532                                 .getImage(ImageResource.IMG_CLCL_NAV_FORWARD));
533                 forward.setDisabledImage(ImageResource
534                                 .getImage(ImageResource.IMG_DLCL_NAV_FORWARD));
535                 forward.setToolTipText(WebBrowserUIPlugin
536                                 .getResource("%actionWebBrowserForward"));
537                 forward.addSelectionListener(new SelectionAdapter() {
538                         public void widgetSelected(SelectionEvent event) {
539                                 forward();
540                         }
541                 });
542
543                 // create refresh, stop, and print actions
544                 stop = new ToolItem(toolbar, SWT.NONE);
545                 stop.setImage(ImageResource.getImage(ImageResource.IMG_ELCL_NAV_STOP));
546                 stop.setHotImage(ImageResource
547                                 .getImage(ImageResource.IMG_CLCL_NAV_STOP));
548                 stop.setDisabledImage(ImageResource
549                                 .getImage(ImageResource.IMG_DLCL_NAV_STOP));
550                 stop.setToolTipText(WebBrowserUIPlugin
551                                 .getResource("%actionWebBrowserStop"));
552                 stop.addSelectionListener(new SelectionAdapter() {
553                         public void widgetSelected(SelectionEvent event) {
554                                 stop();
555                         }
556                 });
557
558                 refresh = new ToolItem(toolbar, SWT.NONE);
559                 refresh.setImage(ImageResource
560                                 .getImage(ImageResource.IMG_ELCL_NAV_REFRESH));
561                 refresh.setHotImage(ImageResource
562                                 .getImage(ImageResource.IMG_CLCL_NAV_REFRESH));
563                 refresh.setDisabledImage(ImageResource
564                                 .getImage(ImageResource.IMG_DLCL_NAV_REFRESH));
565                 refresh.setToolTipText(WebBrowserUIPlugin
566                                 .getResource("%actionWebBrowserRefresh"));
567                 refresh.addSelectionListener(new SelectionAdapter() {
568                         public void widgetSelected(SelectionEvent event) {
569                                 refresh();
570                         }
571                 });
572         }
573
574         protected void addFavorite() {
575                 java.util.List list = WebBrowserPreference
576                                 .getInternalWebBrowserFavorites();
577                 Favorite f = new Favorite(title, browser.getUrl());
578                 if (!list.contains(f)) {
579                         list.add(f);
580                         WebBrowserPreference.setInternalWebBrowserFavorites(list);
581                 }
582         }
583
584         protected void showFavorites(Control parent, Point p) {
585                 Menu perspectiveBarMenu = null;
586                 if (perspectiveBarMenu == null) {
587                         Menu menu = new Menu(parent);
588
589                         // locked favorites
590                         Iterator iterator = WebBrowserUtil.getLockedFavorites().iterator();
591                         if (iterator.hasNext()) {
592                                 while (iterator.hasNext()) {
593                                         final Favorite f = (Favorite) iterator.next();
594                                         MenuItem item = new MenuItem(menu, SWT.NONE);
595                                         item.setText(f.getName());
596                                         item.setImage(ImageResource
597                                                         .getImage(ImageResource.IMG_FAVORITE));
598                                         item.addSelectionListener(new SelectionAdapter() {
599                                                 public void widgetSelected(SelectionEvent event) {
600                                                         setURL(f.getURL());
601                                                 }
602                                         });
603                                 }
604
605                                 new MenuItem(menu, SWT.SEPARATOR);
606                         }
607
608                         iterator = WebBrowserPreference.getInternalWebBrowserFavorites()
609                                         .iterator();
610                         if (!iterator.hasNext()) {
611                                 MenuItem item = new MenuItem(menu, SWT.NONE);
612                                 item.setText(WebBrowserUIPlugin
613                                                 .getResource("%actionWebBrowserNoFavorites"));
614                         }
615                         while (iterator.hasNext()) {
616                                 final Favorite f = (Favorite) iterator.next();
617                                 MenuItem item = new MenuItem(menu, SWT.NONE);
618                                 item.setText(f.getName());
619                                 item.setImage(ImageResource
620                                                 .getImage(ImageResource.IMG_FAVORITE));
621                                 item.addSelectionListener(new SelectionAdapter() {
622                                         public void widgetSelected(SelectionEvent event) {
623                                                 setURL(f.getURL());
624                                         }
625                                 });
626                         }
627
628                         new MenuItem(menu, SWT.SEPARATOR);
629
630                         MenuItem item = new MenuItem(menu, SWT.NONE);
631                         item.setText(WebBrowserUIPlugin
632                                         .getResource("%actionWebBrowserOrganizeFavorites"));
633                         item.addSelectionListener(new SelectionAdapter() {
634                                 public void widgetSelected(SelectionEvent event) {
635                                         OrganizeFavoritesDialog dialog = new OrganizeFavoritesDialog(
636                                                         shell);
637                                         dialog.open();
638                                 }
639                         });
640
641                         perspectiveBarMenu = menu;
642                 }
643
644                 if (perspectiveBarMenu != null) {
645                         perspectiveBarMenu.setLocation(p.x, p.y);
646                         perspectiveBarMenu.setVisible(true);
647                 }
648         }
649
650         public void home() {
651                 navigate(WebBrowserPreference.getHomePageURL());
652         }
653
654         /**
655          * Returns true if the homepage is currently being displayed.
656          * 
657          * @return boolean
658          */
659         protected boolean isHome() {
660                 return getURL() != null
661                                 && getURL().endsWith(
662                                                 WebBrowserPreference.getHomePageURL().substring(9));
663         }
664
665         public String getURL() {
666                 return browser.getUrl();
667         }
668
669         /**
670          * Update the history list to the global copy.
671          */
672         protected void updateHistory() {
673                 if (combo == null)
674                         return;
675
676                 String temp = combo.getText();
677                 if (history == null)
678                         history = WebBrowserPreference.getInternalWebBrowserHistory();
679
680                 String[] historyList = new String[history.size()];
681                 history.toArray(historyList);
682                 combo.setItems(historyList);
683
684                 combo.setText(temp);
685         }
686
687         public void addProgressListener(ProgressListener listener) {
688                 browser.addProgressListener(listener);
689         }
690
691         public void addStatusTextListener(StatusTextListener listener) {
692                 browser.addStatusTextListener(listener);
693         }
694
695         public void addTitleListener(TitleListener listener) {
696                 browser.addTitleListener(listener);
697         }
698 }