View Javadoc

1   /*
2    *  soapUI, copyright (C) 2004-2010 eviware.com 
3    *
4    *  soapUI is free software; you can redistribute it and/or modify it under the 
5    *  terms of version 2.1 of the GNU Lesser General Public License as published by 
6    *  the Free Software Foundation.
7    *
8    *  soapUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without 
9    *  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
10   *  See the GNU Lesser General Public License for more details at gnu.org.
11   */
12  
13  package com.eviware.soapui.support.components;
14  
15  import java.awt.BorderLayout;
16  import java.awt.Component;
17  import java.awt.Dimension;
18  import java.awt.Toolkit;
19  import java.awt.event.ActionEvent;
20  import java.beans.PropertyChangeListener;
21  import java.beans.PropertyChangeSupport;
22  import java.lang.reflect.InvocationTargetException;
23  import java.net.MalformedURLException;
24  import java.net.URL;
25  import java.util.ArrayList;
26  import java.util.HashMap;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.Set;
30  
31  import javax.swing.AbstractAction;
32  import javax.swing.Action;
33  import javax.swing.JEditorPane;
34  import javax.swing.JLabel;
35  import javax.swing.JPanel;
36  import javax.swing.SwingUtilities;
37  
38  import org.mozilla.interfaces.nsIBinaryInputStream;
39  import org.mozilla.interfaces.nsIDOMWindow;
40  import org.mozilla.interfaces.nsIHttpChannel;
41  import org.mozilla.interfaces.nsIHttpHeaderVisitor;
42  import org.mozilla.interfaces.nsIInputStream;
43  import org.mozilla.interfaces.nsIInterfaceRequestor;
44  import org.mozilla.interfaces.nsIObserver;
45  import org.mozilla.interfaces.nsIObserverService;
46  import org.mozilla.interfaces.nsIRequest;
47  import org.mozilla.interfaces.nsISeekableStream;
48  import org.mozilla.interfaces.nsIServiceManager;
49  import org.mozilla.interfaces.nsISupports;
50  import org.mozilla.interfaces.nsIURI;
51  import org.mozilla.interfaces.nsIUploadChannel;
52  import org.mozilla.interfaces.nsIWeakReference;
53  import org.mozilla.interfaces.nsIWebProgress;
54  import org.mozilla.interfaces.nsIWebProgressListener;
55  import org.mozilla.xpcom.Mozilla;
56  import org.mozilla.xpcom.XPCOMException;
57  
58  import com.eviware.soapui.SoapUI;
59  import com.eviware.soapui.impl.rest.panels.request.views.html.HttpHtmlResponseView;
60  import com.eviware.soapui.impl.rest.support.RestUtils;
61  import com.eviware.soapui.impl.wsdl.testcase.WsdlTestCase;
62  import com.eviware.soapui.impl.wsdl.teststeps.HttpTestRequest;
63  import com.eviware.soapui.impl.wsdl.teststeps.HttpTestRequestStep;
64  import com.eviware.soapui.impl.wsdl.teststeps.registry.HttpRequestStepFactory;
65  import com.eviware.soapui.model.propertyexpansion.PropertyExpander;
66  import com.eviware.soapui.model.propertyexpansion.PropertyExpansionContext;
67  import com.eviware.soapui.model.settings.Settings;
68  import com.eviware.soapui.settings.ProxySettings;
69  import com.eviware.soapui.settings.WebRecordingSettings;
70  import com.eviware.soapui.support.StringUtils;
71  import com.eviware.soapui.support.Tools;
72  import com.eviware.soapui.support.UISupport;
73  import com.eviware.soapui.support.types.StringList;
74  import com.eviware.soapui.support.types.StringToStringsMap;
75  import com.eviware.soapui.support.xml.XmlUtils;
76  import com.teamdev.jxbrowser.Browser;
77  import com.teamdev.jxbrowser.BrowserFactory;
78  import com.teamdev.jxbrowser.BrowserType;
79  import com.teamdev.jxbrowser.NewWindowContainer;
80  import com.teamdev.jxbrowser.NewWindowManager;
81  import com.teamdev.jxbrowser.NewWindowParams;
82  import com.teamdev.jxbrowser.events.NavigationAdapter;
83  import com.teamdev.jxbrowser.events.NavigationEvent;
84  import com.teamdev.jxbrowser.events.NavigationFinishedEvent;
85  import com.teamdev.jxbrowser.events.NavigationListener;
86  import com.teamdev.jxbrowser.events.NavigationStatusCode;
87  import com.teamdev.jxbrowser.events.StatusChangedEvent;
88  import com.teamdev.jxbrowser.events.StatusListener;
89  import com.teamdev.jxbrowser.mozilla.MozillaBrowser;
90  import com.teamdev.jxbrowser.prompt.DefaultPromptService;
91  import com.teamdev.jxbrowser.security.HttpSecurityAction;
92  import com.teamdev.jxbrowser.security.HttpSecurityHandler;
93  import com.teamdev.jxbrowser.security.SecurityProblem;
94  import com.teamdev.jxbrowser1.mozilla.MozillaWebBrowser;
95  import com.teamdev.xpcom.PoxyAuthenticationHandler;
96  import com.teamdev.xpcom.ProxyConfiguration;
97  import com.teamdev.xpcom.ProxyServerAuthInfo;
98  import com.teamdev.xpcom.ProxyServerType;
99  import com.teamdev.xpcom.Services;
100 import com.teamdev.xpcom.Xpcom;
101 import com.teamdev.xpcom.util.XPCOMManager;
102 
103 public class BrowserComponent implements nsIWebProgressListener, nsIWeakReference, StatusListener
104 {
105 	private static final String CONTENT_TYPE_FORM_URLENCODED = "application/x-www-form-urlencoded";
106 	private MozillaBrowser browser;
107 	private JPanel panel = new JPanel( new BorderLayout() );
108 	private JPanel statusBar;
109 	private JLabel statusLabel;
110 	private String errorPage;
111 	private boolean showingErrorPage;
112 	public String url;
113 	private Boolean possibleError = false;
114 	@SuppressWarnings( "unused" )
115 	private boolean disposed;
116 	// private static boolean disabled;
117 	private NavigationListener internalNavigationListener;
118 	private HttpHtmlResponseView httpHtmlResponseView;
119 	private static Boolean initialized = false;
120 	private static SoapUINewWindowManager newWindowManager;
121 	private static Map<nsIDOMWindow, BrowserComponent> browserMap = new HashMap<nsIDOMWindow, BrowserComponent>();
122 	private static Map<BrowserComponent, Map<String, RecordedRequest>> browserRecordingMap = new HashMap<BrowserComponent, Map<String, RecordedRequest>>();
123 	private final boolean addStatusBar;
124 
125 	static
126 	{
127 		initialize();
128 	}
129 
130 	public BrowserComponent( boolean addToolbar, boolean addStatusBar )
131 	{
132 		this.addStatusBar = addStatusBar;
133 	}
134 
135 	public synchronized static void initialize()
136 	{
137 		if( initialized )
138 			return;
139 
140 		try
141 		{
142 			if( !SoapUI.isJXBrowserDisabled() )
143 			{
144 				Xpcom.initialize();
145 			}
146 
147 			initialized = true;
148 		}
149 		catch( Throwable t )
150 		{
151 			t.printStackTrace();
152 		}
153 	}
154 
155 	// public static void setDisabled( boolean disabled )
156 	// {
157 	// BrowserComponent.disabled = disabled;
158 	// }
159 
160 	/***
161 	 * @deprecated
162 	 */
163 
164 	public static boolean isJXBrowserDisabled()
165 	{
166 		return SoapUI.isJXBrowserDisabled();
167 	}
168 
169 	public Component getComponent()
170 	{
171 		if( SoapUI.isJXBrowserDisabled() )
172 		{
173 			JEditorPane jxbrowserDisabledPanel = new JEditorPane();
174 			jxbrowserDisabledPanel.setText( "browser component disabled" );
175 			panel.add( jxbrowserDisabledPanel );
176 		}
177 		else
178 		{
179 			if( browser == null )
180 			{
181 				if( addStatusBar )
182 				{
183 					statusBar = new JPanel( new BorderLayout() );
184 					statusLabel = new JLabel();
185 					UISupport.setFixedSize( statusBar, new Dimension( 20, 20 ) );
186 					statusBar.add( statusLabel, BorderLayout.WEST );
187 					panel.add( statusBar, BorderLayout.SOUTH );
188 				}
189 
190 				// if( addToolbar )
191 				// panel.add( buildToolbar(), BorderLayout.NORTH );
192 
193 				initBrowser();
194 
195 				browser.navigate( "about:blank" );
196 			}
197 		}
198 		return panel;
199 	}
200 
201 	@SuppressWarnings( "unused" )
202 	private Component buildToolbar()
203 	{
204 		JXToolBar toolbar = UISupport.createToolbar();
205 
206 		toolbar.addFixed( UISupport.createToolbarButton( new BackAction() ) );
207 		toolbar.addRelatedGap();
208 		toolbar.addFixed( UISupport.createToolbarButton( new ForwardAction() ) );
209 
210 		toolbar.addGlue();
211 
212 		return toolbar;
213 	}
214 
215 	public void setRecordingHttpHtmlResponseView( HttpHtmlResponseView httpHtmlResponseView )
216 	{
217 		this.httpHtmlResponseView = httpHtmlResponseView;
218 		if( httpHtmlResponseView != null )
219 		{
220 			if( !browserRecordingMap.containsKey( BrowserComponent.this ) )
221 			{
222 				browserRecordingMap.put( BrowserComponent.this, new HashMap<String, RecordedRequest>() );
223 			}
224 		}
225 		else
226 		{
227 			browserRecordingMap.remove( BrowserComponent.this );
228 		}
229 	}
230 
231 	private static final class RecordingHttpListener implements Runnable
232 	{
233 		public void run()
234 		{
235 			final Mozilla mozilla = Mozilla.getInstance();
236 			nsIServiceManager serviceManager = mozilla.getServiceManager();
237 			nsIObserverService observerService = ( nsIObserverService )serviceManager.getServiceByContractID(
238 					"@mozilla.org/observer-service;1", nsIObserverService.NS_IOBSERVERSERVICE_IID );
239 
240 			final nsIBinaryInputStream in = XPCOMManager.getInstance().newComponent( "@mozilla.org/binaryinputstream;1",
241 					nsIBinaryInputStream.class );
242 
243 			nsIObserver httpObserver = new nsIObserver()
244 			{
245 				public void observe( nsISupports subject, String sTopic, String sData )
246 				{
247 					try
248 					{
249 						if( EVENT_HTTP_ON_MODIFY_REQUEST.equals( sTopic ) )
250 						{
251 							nsIHttpChannel httpChannel = ( nsIHttpChannel )subject
252 									.queryInterface( nsIHttpChannel.NS_IHTTPCHANNEL_IID );
253 
254 							if( httpChannel.getNotificationCallbacks() == null )
255 								return;
256 
257 							nsIInterfaceRequestor interfaceRequestor = ( nsIInterfaceRequestor )httpChannel
258 									.getNotificationCallbacks()
259 									.queryInterface( nsIInterfaceRequestor.NS_IINTERFACEREQUESTOR_IID );
260 
261 							nsIDOMWindow window = ( nsIDOMWindow )interfaceRequestor
262 									.getInterface( nsIDOMWindow.NS_IDOMWINDOW_IID );
263 
264 							BrowserComponent browserComponent = browserMap.get( window );
265 							if( browserComponent != null && browserRecordingMap.containsKey( browserComponent ) )
266 							{
267 								RecordedRequest rr = new RecordedRequest( dumpUri( httpChannel.getURI() ),
268 										httpChannel.getRequestMethod() );
269 
270 								nsIUploadChannel upload = ( nsIUploadChannel )httpChannel
271 										.queryInterface( nsIUploadChannel.NS_IUPLOADCHANNEL_IID );
272 
273 								byte[] requestData = null;
274 								if( upload != null )
275 								{
276 									nsIInputStream uploadStream = upload.getUploadStream();
277 
278 									if( uploadStream != null && uploadStream.available() > 0 )
279 									{
280 										nsISeekableStream seekable = ( nsISeekableStream )uploadStream
281 												.queryInterface( nsISeekableStream.NS_ISEEKABLESTREAM_IID );
282 
283 										long pos = seekable.tell();
284 										long available = uploadStream.available();
285 
286 										if( available > 0 )
287 										{
288 											try
289 											{
290 												synchronized( mozilla )
291 												{
292 													in.setInputStream( uploadStream );
293 													requestData = in.readByteArray( available );
294 													String requestBody = getRequestBody( requestData );
295 													if( requestBody != null && requestBody.length() > 0 )
296 													{
297 														rr.setContent( requestBody );
298 														String contentType = getContentType( requestData );
299 														if( StringUtils.hasContent( contentType ) )
300 															rr.setContentType( contentType );
301 													}
302 												}
303 											}
304 											catch( Throwable e )
305 											{
306 												e.printStackTrace();
307 											}
308 											finally
309 											{
310 												seekable.seek( nsISeekableStream.NS_SEEK_SET, pos );
311 											}
312 										}
313 									}
314 								}
315 
316 								final StringToStringsMap headersMap = new StringToStringsMap();
317 								httpChannel.visitRequestHeaders( new nsIHttpHeaderVisitor()
318 								{
319 
320 									public void visitHeader( String header, String value )
321 									{
322 										if( !isHeaderExcluded( header ) )
323 										{
324 											headersMap.put( header, value );
325 										}
326 									}
327 
328 									public nsISupports queryInterface( String sIID )
329 									{
330 										return Mozilla.queryInterface( this, sIID );
331 									}
332 								} );
333 
334 								rr.setHeaders( headersMap );
335 
336 								browserRecordingMap.get( browserComponent ).put( rr.getUrl(), rr );
337 							}
338 						}
339 						else
340 						{
341 							System.out.println( "HTTPObserver: Unknown event '" + sTopic + "'" );
342 						}
343 					}
344 					catch( Throwable e )
345 					{
346 						// ignore errors related to the querying of unsupported
347 						// interfaces
348 						if( e.getMessage().indexOf( "0x80004002" ) == -1 )
349 							SoapUI.logError( e );
350 					}
351 				}
352 
353 				public nsISupports queryInterface( String sIID )
354 				{
355 					return Mozilla.queryInterface( this, sIID );
356 				}
357 			};
358 
359 			boolean blnObserverIsWeakReference = false;
360 			observerService.addObserver( httpObserver, EVENT_HTTP_ON_MODIFY_REQUEST, blnObserverIsWeakReference );
361 		}
362 
363 	}
364 
365 	public static boolean isHeaderExcluded( String header )
366 	{
367 		String excluded = SoapUI.getSettings().getString( WebRecordingSettings.EXCLUDED_HEADERS, null );
368 		List<String> result = new ArrayList<String>();
369 		if( excluded != null && excluded.trim().length() > 0 )
370 		{
371 			try
372 			{
373 				StringList names = StringList.fromXml( excluded );
374 				for( String name : names )
375 				{
376 					result.add( name );
377 				}
378 			}
379 			catch( Exception e )
380 			{
381 				SoapUI.logError( e );
382 			}
383 		}
384 		return result.contains( header );
385 	}
386 
387 	private static String getContentType( byte[] requestData )
388 	{
389 		String request = new String( requestData );
390 		int ix = request.indexOf( "Content-Type" );
391 		if( ix > 0 && ix < request.length() - 14 )
392 		{
393 			String contentType = request.substring( ix + 14 );
394 			contentType = contentType.substring( 0, contentType.indexOf( "\n" ) - 1 );
395 			return contentType.trim();
396 		}
397 		else
398 			return null;
399 	}
400 
401 	private static String getRequestBody( byte[] requestData )
402 	{
403 		String request = new String( requestData );
404 		int ix = request.indexOf( "\r\n\r\n" );
405 		return ix == -1 ? "" : request.substring( ix + 4 );
406 	}
407 
408 	private static final class SoapUINewWindowManager implements NewWindowManager
409 	{
410 		public NewWindowContainer evaluateWindow( final NewWindowParams params )
411 		{
412 			return new NewWindowContainer()
413 			{
414 				public void insertBrowser( final Browser browser )
415 				{
416 					browser.addNavigationListener( new NavigationListener()
417 					{
418 						public void navigationStarted( final NavigationEvent arg0 )
419 						{
420 							// this is the only event we wanted
421 							arg0.getBrowser().removeNavigationListener( this );
422 
423 							// since there is no way to detect the source browser for
424 							// the new window we just assume it is the recording one.
425 							BrowserComponent browserComponent = browserMap
426 									.get( ( ( ( MozillaWebBrowser )( ( MozillaBrowser )params.getParent() ).getPeer() ) )
427 											.getWebBrowser().getContentDOMWindow() );
428 							if( browserRecordingMap.containsKey( browserComponent ) )
429 							{
430 								browserComponent.replaceBrowser( arg0.getBrowser() );
431 							}
432 							else
433 							{
434 								SwingUtilities.invokeLater( new Runnable()
435 								{
436 									public void run()
437 									{
438 										if( UISupport.confirm( "Open [" + arg0.getUrl() + "] with system Browser?", "Open URL" ) )
439 											Tools.openURL( arg0.getUrl() );
440 									}
441 								} );
442 
443 								arg0.getBrowser().dispose();
444 							}
445 						}
446 
447 						public void navigationFinished( NavigationFinishedEvent arg0 )
448 						{
449 						}
450 					} );
451 				}
452 			};
453 		}
454 	}
455 
456 	private final class InternalBrowserNavigationListener implements NavigationListener
457 	{
458 		public void navigationStarted( NavigationEvent arg0 )
459 		{
460 		}
461 
462 		public void navigationFinished( NavigationFinishedEvent arg0 )
463 		{
464 			if( browserRecordingMap.containsKey( BrowserComponent.this ) )
465 			{
466 				Map<String, RecordedRequest> map = browserRecordingMap.get( BrowserComponent.this );
467 				RecordedRequest recordedRequest = map.get( arg0.getUrl() );
468 				if( recordedRequest != null )
469 				{
470 					if( httpHtmlResponseView != null && httpHtmlResponseView.isRecordHttpTrafic() )
471 					{
472 						HttpTestRequest httpTestRequest = ( HttpTestRequest )( httpHtmlResponseView.getDocument()
473 								.getRequest() );
474 						WsdlTestCase testCase = httpTestRequest.getTestStep().getTestCase();
475 						int count = testCase.getTestStepList().size();
476 
477 						String url2 = recordedRequest.getUrl();
478 						try
479 						{
480 							url2 = new URL( recordedRequest.getUrl() ).getPath();
481 						}
482 						catch( MalformedURLException e )
483 						{
484 
485 						}
486 
487 						HttpTestRequestStep newHttpStep = ( HttpTestRequestStep )testCase.addTestStep(
488 								HttpRequestStepFactory.HTTPREQUEST_TYPE, "Http Test Step " + ++count + " [" + url2 + "]",
489 								recordedRequest.getUrl(), recordedRequest.getMethod() );
490 
491 						newHttpStep.getTestRequest().setRequestHeaders( recordedRequest.getHeaders() );
492 
493 						if( recordedRequest.getContent() != null )
494 						{
495 							newHttpStep.getTestRequest().setMediaType( recordedRequest.getContentType() );
496 							if( newHttpStep.getTestRequest().getMediaType().equals( CONTENT_TYPE_FORM_URLENCODED ) )
497 							{
498 								newHttpStep.getTestRequest().setPostQueryString( true );
499 								newHttpStep.getTestRequest().setMediaType( CONTENT_TYPE_FORM_URLENCODED );
500 								RestUtils.extractParamsFromQueryString( newHttpStep.getTestRequest().getParams(),
501 										recordedRequest.getContent() );
502 							}
503 							else
504 							{
505 								newHttpStep.getTestRequest().setRequestContent( recordedRequest.getContent() );
506 							}
507 						}
508 					}
509 				}
510 			}
511 			// TODO ask Ole why was this removing necessary
512 
513 			// browserRecordingMap.remove( BrowserComponent.this );
514 		}
515 	}
516 
517 	private class BackAction extends AbstractAction
518 	{
519 		public BackAction()
520 		{
521 			putValue( SMALL_ICON, UISupport.createImageIcon( "/arrow_left.png" ) );
522 			putValue( Action.SHORT_DESCRIPTION, "Navigate to previous selection" );
523 		}
524 
525 		public void actionPerformed( ActionEvent e )
526 		{
527 			if( !browser.canGoBack() )
528 				Toolkit.getDefaultToolkit().beep();
529 			else
530 				browser.goBack();
531 		}
532 	}
533 
534 	private class ForwardAction extends AbstractAction
535 	{
536 		public ForwardAction()
537 		{
538 			putValue( SMALL_ICON, UISupport.createImageIcon( "/arrow_right.png" ) );
539 			putValue( Action.SHORT_DESCRIPTION, "Navigate to next selection" );
540 		}
541 
542 		public void actionPerformed( ActionEvent e )
543 		{
544 			if( !browser.canGoForward() )
545 				Toolkit.getDefaultToolkit().beep();
546 			else
547 				browser.goForward();
548 		}
549 	}
550 
551 	public synchronized boolean initBrowser()
552 	{
553 		if( browser != null || SoapUI.isJXBrowserDisabled() )
554 			return false;
555 
556 		browser = ( MozillaBrowser )BrowserFactory.createBrowser( BrowserType.Mozilla );
557 		browserMap.put( ( ( MozillaWebBrowser )browser.getPeer() ).getWebBrowser().getContentDOMWindow(), this );
558 
559 		if( newWindowManager == null )
560 		{
561 			registerHttpListener();
562 
563 			newWindowManager = new SoapUINewWindowManager();
564 			browser.getServices().setNewWindowManager( newWindowManager );
565 			browser.getServices().setPromptService( new DefaultPromptService() );
566 		}
567 
568 		// ignore security errors
569 		browser.setHttpSecurityHandler( new HttpSecurityHandler()
570 		{
571 			@Override
572 			public HttpSecurityAction onSecurityProblem( Set<SecurityProblem> arg0 )
573 			{
574 				return HttpSecurityAction.CONTINUE;
575 			}
576 		} );
577 
578 		internalNavigationListener = new InternalBrowserNavigationListener();
579 		browser.addNavigationListener( internalNavigationListener );
580 		browser.addStatusListener( this );
581 
582 		browser.addNavigationListener( new NavigationAdapter()
583 		{
584 			@Override
585 			public void navigationFinished( NavigationFinishedEvent evt )
586 			{
587 				if( evt.getUrl().equals( SoapUI.PUSH_PAGE_URL ) && !( NavigationStatusCode.OK == evt.getStatusCode() ) )
588 					browser.navigate( SoapUI.PUSH_PAGE_ERROR_URL );
589 			}
590 		} );
591 
592 		panel.add( browser.getComponent(), BorderLayout.CENTER );
593 		return true;
594 	}
595 
596 	protected void replaceBrowser( Browser browser2 )
597 	{
598 		// remove old
599 		browserMap.remove( ( ( MozillaWebBrowser )browser.getPeer() ).getWebBrowser().getContentDOMWindow() );
600 
601 		browser.stop();
602 		browser.removeNavigationListener( internalNavigationListener );
603 		browser.removeStatusListener( this );
604 		panel.remove( browser.getComponent() );
605 		browser.dispose();
606 
607 		// replace
608 		browser = ( MozillaBrowser )browser2;
609 		browserMap.put( ( ( MozillaWebBrowser )browser.getPeer() ).getWebBrowser().getContentDOMWindow(), this );
610 		browser.addNavigationListener( internalNavigationListener );
611 		browser.addStatusListener( this );
612 		panel.add( browser.getComponent(), BorderLayout.CENTER );
613 	}
614 
615 	// public static boolean isRecording()
616 	// {
617 	// return httpHtmlResponseView != null &&
618 	// httpHtmlResponseView.isRecordHttpTrafic();
619 	// }
620 
621 	public void release()
622 	{
623 		if( browser != null )
624 		{
625 			disposed = true;
626 			cleanup();
627 		}
628 
629 		possibleError = false;
630 	}
631 
632 	private synchronized void cleanup()
633 	{
634 		if( browser != null )
635 		{
636 			browserMap.remove( ( ( MozillaWebBrowser )browser.getPeer() ).getWebBrowser().getContentDOMWindow() );
637 			browserRecordingMap.remove( this );
638 			httpHtmlResponseView = null;
639 
640 			browser.stop();
641 			browser.dispose();
642 			browser.removeNavigationListener( internalNavigationListener );
643 			browser.removeStatusListener( this );
644 
645 			panel.removeAll();
646 			browser = null;
647 		}
648 	}
649 
650 	public void setContent( String contentAsString, String contextUri )
651 	{
652 		if( SoapUI.isJXBrowserDisabled() )
653 			return;
654 
655 		if( browser == null )
656 		{
657 			initBrowser();
658 		}
659 
660 		try
661 		{
662 			browser.setContent( contentAsString, contextUri );
663 			pcs.firePropertyChange( "content", null, null );
664 		}
665 		catch( Throwable e )
666 		{
667 			e.printStackTrace();
668 		}
669 
670 	}
671 
672 	public void setContent( String content )
673 	{
674 		if( SoapUI.isJXBrowserDisabled() )
675 			return;
676 
677 		if( browser == null )
678 		{
679 			initBrowser();
680 		}
681 
682 		browser.setContent( content );
683 		pcs.firePropertyChange( "content", null, null );
684 	}
685 
686 	public void navigate( String url, String errorPage )
687 	{
688 		navigate( url, null, errorPage );
689 	}
690 
691 	public String getContent()
692 	{
693 		return browser == null ? null : XmlUtils.serialize( browser.getDocument() );
694 	}
695 
696 	public String getUrl()
697 	{
698 		return url;
699 	}
700 
701 	public void setUrl( String url ) throws InterruptedException, InvocationTargetException
702 	{
703 		navigate( url, null );
704 	}
705 
706 	public nsISupports queryInterface( String uuid )
707 	{
708 		return Mozilla.queryInterface( this, uuid );
709 	}
710 
711 	public nsISupports queryReferent( String uuid )
712 	{
713 		return Mozilla.queryInterface( this, uuid );
714 	}
715 
716 	public void onLocationChange( nsIWebProgress arg0, nsIRequest arg1, nsIURI arg2 )
717 	{
718 		if( getUrl() != null && !getUrl().equals( "about:blank" ) )
719 		{
720 			if( !possibleError )
721 				possibleError = true;
722 			else
723 			{
724 				if( !showingErrorPage )
725 				{
726 					showErrorPage();
727 				}
728 			}
729 		}
730 	}
731 
732 	public void onProgressChange( nsIWebProgress arg0, nsIRequest arg1, int arg2, int arg3, int arg4, int arg5 )
733 	{
734 
735 	}
736 
737 	public void onSecurityChange( nsIWebProgress arg0, nsIRequest arg1, long arg2 )
738 	{
739 	}
740 
741 	public void onStateChange( nsIWebProgress arg0, nsIRequest request, long arg2, long arg3 )
742 	{
743 		try
744 		{
745 			if( getUrl() != null && !getUrl().equals( "about:blank" ) )
746 			{
747 				nsIHttpChannel ch = null;
748 
749 				ch = ( nsIHttpChannel )request.queryInterface( nsIHttpChannel.NS_IHTTPCHANNEL_IID );
750 
751 				if( ch != null )
752 				{
753 					possibleError = false;
754 					showingErrorPage = false;
755 				}
756 			}
757 		}
758 		catch( XPCOMException e )
759 		{
760 			if( possibleError && !showingErrorPage )
761 				showErrorPage();
762 		}
763 
764 	}
765 
766 	private void showErrorPage()
767 	{
768 		if( errorPage != null && !errorPage.equals( getUrl() ) )
769 		{
770 			try
771 			{
772 				showingErrorPage = true;
773 				setUrl( errorPage );
774 			}
775 			catch( Throwable e )
776 			{
777 				e.printStackTrace();
778 			}
779 		}
780 	}
781 
782 	public String getErrorPage()
783 	{
784 		return errorPage;
785 	}
786 
787 	public void setErrorPage( String errorPage )
788 	{
789 		this.errorPage = errorPage;
790 	}
791 
792 	public void onStatusChange( nsIWebProgress arg0, nsIRequest arg1, long arg2, String arg3 )
793 	{
794 		try
795 		{
796 			if( getUrl() != null && !getUrl().equals( "about:blank" ) )
797 			{
798 				nsIHttpChannel ch = null;
799 
800 				ch = ( nsIHttpChannel )arg1.queryInterface( nsIHttpChannel.NS_IHTTPCHANNEL_IID );
801 
802 				if( ch != null )
803 				{
804 					possibleError = false;
805 					showingErrorPage = false;
806 				}
807 			}
808 		}
809 		catch( XPCOMException e )
810 		{
811 			if( possibleError && !showingErrorPage )
812 				showErrorPage();
813 		}
814 	}
815 
816 	public void statusChanged( StatusChangedEvent event )
817 	{
818 		if( statusLabel != null )
819 		{
820 			statusLabel.setText( event.getStatusText() );
821 		}
822 	}
823 
824 	public boolean isBrowserInitialised()
825 	{
826 		return browser != null;
827 	}
828 
829 	/***
830 	 * Setups proxy configuration
831 	 */
832 
833 	private static boolean proxyAuthenticationInitialized = false;
834 
835 	public synchronized static void updateProxy( boolean proxyEnabled )
836 	{
837 		if( SoapUI.isJXBrowserDisabled() )
838 			return;
839 
840 		initialize();
841 
842 		ProxyConfiguration proxyConf = Services.getProxyConfiguration();
843 		if( proxyConf == null )
844 			return;
845 
846 		proxyConf.setType( ProxyConfiguration.MANUAL );
847 
848 		if( !proxyAuthenticationInitialized )
849 		{
850 			proxyConf.setPoxyAuthenticationHandler( ProxyServerType.HTTP, new PoxyAuthenticationHandler()
851 			{
852 				public ProxyServerAuthInfo authenticationRequired()
853 				{
854 					Settings settings = SoapUI.getSettings();
855 					PropertyExpansionContext context = null;
856 
857 					String proxyUsername = PropertyExpander.expandProperties( context,
858 							settings.getString( ProxySettings.USERNAME, null ) );
859 					String proxyPassword = PropertyExpander.expandProperties( context,
860 							settings.getString( ProxySettings.PASSWORD, null ) );
861 
862 					return new ProxyServerAuthInfo( proxyUsername, proxyPassword );
863 				}
864 			} );
865 
866 			proxyAuthenticationInitialized = true;
867 		}
868 
869 		if( proxyEnabled )
870 		{
871 			Settings settings = SoapUI.getSettings();
872 			PropertyExpansionContext context = null;
873 
874 			// check system properties first
875 			String proxyHost = System.getProperty( "http.proxyHost" );
876 			String proxyPort = System.getProperty( "http.proxyPort" );
877 
878 			if( proxyHost == null )
879 				proxyHost = PropertyExpander.expandProperties( context, settings.getString( ProxySettings.HOST, "" ) );
880 
881 			if( proxyPort == null )
882 				proxyPort = PropertyExpander.expandProperties( context, settings.getString( ProxySettings.PORT, "" ) );
883 
884 			proxyConf.setHttpHost( proxyHost );
885 			proxyConf.setHttpPort( Integer.parseInt( proxyPort ) );
886 			// check excludes
887 			String[] excludes = PropertyExpander.expandProperties( context,
888 					settings.getString( ProxySettings.EXCLUDES, "" ) ).split( "," );
889 			for( String url : excludes )
890 			{
891 				proxyConf.setSkipProxyFor( url );
892 			}
893 		}
894 		else
895 		{
896 			proxyConf.setHttpHost( "" );
897 		}
898 
899 	}
900 
901 	private PropertyChangeSupport pcs = new PropertyChangeSupport( this );
902 
903 	public void addPropertyChangeListener( PropertyChangeListener pcl )
904 	{
905 		pcs.addPropertyChangeListener( pcl );
906 	}
907 
908 	public void rempvePropertyChangeListener( PropertyChangeListener pcl )
909 	{
910 		pcs.removePropertyChangeListener( pcl );
911 	}
912 
913 	/***
914 	 * Called after a HTTP response from the server is received.
915 	 * 
916 	 * @see http://developer.mozilla.org/en/Observer_Notifications
917 	 */
918 	public static final String EVENT_HTTP_ON_MODIFY_REQUEST = "http-on-modify-request";
919 
920 	public void registerHttpListener()
921 	{
922 		Xpcom.invokeLater( new RecordingHttpListener() );
923 	}
924 
925 	/***
926 	 * Converts an object implementing the nsIURI interface into a human readable
927 	 * URI.
928 	 * 
929 	 * @param uri
930 	 *           nsIURI object to convert
931 	 * @return String URI result string
932 	 */
933 	public static String dumpUri( nsIURI uri )
934 	{
935 		if( uri == null )
936 		{
937 			return "";
938 		}
939 
940 		return ( ( uri.getUsername() == null || "".equals( uri.getUsername() ) ) ? "" : uri.getUsername() + ":"
941 				+ uri.getUserPass() )
942 				+ uri.getScheme()
943 				+ "://"
944 				+ uri.getHost()
945 				+ ( ( uri.getPort() == -1 ) ? "" : ":" + uri.getPort() )
946 				+ uri.getPath();
947 	}
948 
949 	private static class RecordedRequest
950 	{
951 		private String url;
952 		private String contentType;
953 		private StringToStringsMap headers;
954 		private String method;
955 		private String content;
956 
957 		public RecordedRequest( String url, String method )
958 		{
959 			this.url = url;
960 			this.method = method;
961 		}
962 
963 		public void setContentType( String contentType )
964 		{
965 			this.contentType = contentType;
966 		}
967 
968 		public void setHeaders( StringToStringsMap headersMap )
969 		{
970 			headers = headersMap;
971 		}
972 
973 		public void setContent( String requestBody )
974 		{
975 			content = requestBody;
976 		}
977 
978 		public String getUrl()
979 		{
980 			return url;
981 		}
982 
983 		public String getContentType()
984 		{
985 			return contentType;
986 		}
987 
988 		public StringToStringsMap getHeaders()
989 		{
990 			return headers;
991 		}
992 
993 		public String getMethod()
994 		{
995 			return method;
996 		}
997 
998 		public String getContent()
999 		{
1000 			return content;
1001 		}
1002 	}
1003 
1004 	public void navigate( String url, String postData, String errorPage )
1005 	{
1006 		if( SoapUI.isJXBrowserDisabled() )
1007 			return;
1008 
1009 		if( errorPage != null )
1010 			setErrorPage( errorPage );
1011 
1012 		this.url = url;
1013 
1014 		if( browser == null )
1015 		{
1016 			initBrowser();
1017 		}
1018 
1019 		if( postData != null && postData.length() > 0 )
1020 		{
1021 			browser.navigate( url, postData );
1022 		}
1023 		else
1024 		{
1025 			browser.navigate( url );
1026 		}
1027 
1028 		if( showingErrorPage )
1029 			showingErrorPage = false;
1030 	}
1031 
1032 }