/* * Copyright 2010-2011 Research In Motion Limited. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package blackberry.web.widget.bf; import java.util.Enumeration; import net.rim.device.api.browser.field.RenderingOptions; import net.rim.device.api.browser.field2.BrowserField; import net.rim.device.api.browser.field2.BrowserFieldConfig; import net.rim.device.api.browser.field2.BrowserFieldRequest; import net.rim.device.api.io.transport.ConnectionFactory; import net.rim.device.api.io.transport.TransportInfo; import net.rim.device.api.io.transport.options.BisBOptions; import net.rim.device.api.system.Application; import net.rim.device.api.ui.Graphics; import net.rim.device.api.ui.Manager; import net.rim.device.api.ui.Trackball; import net.rim.device.api.ui.container.VerticalFieldManager; import net.rim.device.api.ui.decor.Background; import net.rim.device.api.ui.decor.BackgroundFactory; import net.rim.device.api.web.WidgetExtension; import blackberry.web.widget.Widget; import blackberry.web.widget.WidgetScreen; import blackberry.web.widget.bf.navigationcontroller.NavigationController; import blackberry.web.widget.bf.navigationcontroller.NavigationExtension; import blackberry.web.widget.caching.CacheManager; import blackberry.web.widget.caching.WidgetCacheNamespace; import blackberry.web.widget.device.DeviceInfo; import blackberry.web.widget.html5.GearsHTML5Extension; import blackberry.web.widget.impl.WidgetConfigImpl; import blackberry.web.widget.loadingScreen.PageManager; import blackberry.web.widget.util.WidgetUtil; public final class BrowserFieldScreen extends WidgetScreen { // Set our preferred transport order. // Order is: MDS, BIS-B, TCP_WIFI, TCP_CELLULAR, WAP2, WAP. public static int[] PREFERRED_TRANSPORTS = { TransportInfo.TRANSPORT_MDS, TransportInfo.TRANSPORT_BIS_B, TransportInfo.TRANSPORT_TCP_WIFI, TransportInfo.TRANSPORT_TCP_CELLULAR, TransportInfo.TRANSPORT_WAP2, TransportInfo.TRANSPORT_WAP }; private GearsHTML5Extension _HTML5ToGearsExtension; private BrowserField _browserField; private BrowserFieldConfig _bfConfig; private Manager _manager; private boolean _attached; private int _bgColor; private static BrowserField _browserFieldReference; private NavigationExtension _navigationJS; private NavigationController _navigationController; private NavigationNamespace _navigationExtension; private PageManager _pageManager; private CacheManager _cacheManager; private WidgetCacheNamespace _widgetCacheExtension; private String _locationURI; /** * <description> * * @param wConfig * <description> */ public BrowserFieldScreen( Widget widget, PageManager pageManager, String locationURI ) { super( widget, Manager.NO_HORIZONTAL_SCROLL | Manager.NO_VERTICAL_SCROLL ); _locationURI = locationURI; _pageManager = pageManager; initialize(); } /** Override */ protected void onUiEngineAttached( boolean attached ) { // If an error occurs on the content page that requires // a dialog box, an exception was thrown because we tried to use // invokeAndWait which is a problem since while we process the first screen // we still haven't entered the application's event queue. // This puts the request for the first page on the event queue, so // it can be safely run. _attached = attached; if( _attached ) { Application.getApplication().invokeLater( new Thread() { public void run() { _browserField.requestContent( createRequest( _locationURI ) ); } } ); } } public void setLocation( String url ) { _browserField.requestContent( createRequest( url ) ); _browserField.getHistory().clearHistory(); } /** * Overrides parent method to reset navigation controller after screen is closed. * * @see net.rim.device.api.ui.Screen#close() */ public void close() { if( getAppNavigationMode() ) { getNavigationController().clearEventQueue(); } super.close(); } private void initialize() { _bfConfig = new BrowserFieldConfig(); // POINTER mode is used for for normal WebWorks Applications. // NONE mode is used for navigation mode WebWorks Applications. if( getAppNavigationMode() ) { _bfConfig.setProperty( BrowserFieldConfig.NAVIGATION_MODE, BrowserFieldConfig.NAVIGATION_MODE_NONE ); } else { _bfConfig.setProperty( BrowserFieldConfig.NAVIGATION_MODE, BrowserFieldConfig.NAVIGATION_MODE_POINTER ); } // Enable Google Gears. _bfConfig.setProperty( BrowserFieldConfig.ENABLE_GEARS, Boolean.TRUE ); // Enable Cross-Site XHR by default. _bfConfig.setProperty( BrowserFieldConfig.ALLOW_CS_XHR, Boolean.TRUE ); // Disable MDS transcoding since it interferes with whitelist on external sites. _bfConfig.setProperty( BrowserFieldConfig.MDS_TRANSCODING_ENABLED, Boolean.FALSE ); // Enable web inspector debugging if required if( _wConfig instanceof WidgetConfigImpl ) { WidgetConfigImpl configObj = (WidgetConfigImpl) _wConfig; if( DeviceInfo.isCompatibleVersion( 7 ) && configObj.isDebugEnabled() ) { _bfConfig.setProperty( "ENABLE_WEB_INSPECTOR", Boolean.TRUE ); } } // Check for our Config type and cast. if( _wConfig instanceof WidgetConfigImpl ) { // Update the transport order. updateConnectionFactory(); } // Create Browser field. _browserField = new BrowserField( _bfConfig ); _browserField.addListener( new WidgetBrowserFieldListener( _wConfig ) ); // Remove animation max value. _browserField.getRenderingOptions().setProperty( RenderingOptions.CORE_OPTIONS_GUID, RenderingOptions.ANIMATION_COUNT_VALUE, Integer.MAX_VALUE ); // Enable blackberry.location by default. _browserField.getRenderingOptions().setProperty( RenderingOptions.CORE_OPTIONS_GUID, RenderingOptions.JAVASCRIPT_LOCATION_ENABLED, true ); // Add a custom controller to handle requests. _browserField.getConfig().setProperty( BrowserFieldConfig.CONTROLLER, new WidgetRequestController( _browserField, _wConfig ) ); // Add a custom error handler. _browserField.getConfig().setProperty( BrowserFieldConfig.ERROR_HANDLER, new BrowserFieldCustomErrorHandler( _browserField, _wConfig ) ); // Add custom headers. if( _wConfig.getCustomHeaders().size() > 0 ) { _browserField.getConfig().setProperty( BrowserFieldConfig.HTTP_HEADERS, _wConfig.getCustomHeaders() ); } // Create field manager. if( getAppNavigationMode() ) { _manager = new WidgetFieldManager( Manager.VERTICAL_SCROLL | Manager.VERTICAL_SCROLLBAR | Manager.HORIZONTAL_SCROLL | Manager.HORIZONTAL_SCROLLBAR ); // navController depends on navExtension. Initialize navExtension first _navigationJS = new NavigationExtension(); _navigationExtension = new NavigationNamespace( this, (WidgetFieldManager) _manager ); _navigationController = new NavigationController( this ); } else { _manager = new VerticalFieldManager( Manager.VERTICAL_SCROLL | Manager.VERTICAL_SCROLLBAR | Manager.HORIZONTAL_SCROLL | Manager.HORIZONTAL_SCROLLBAR ); } // Add BrowserField/Manager to the Screen. _manager.add( _browserField ); add( _manager ); _bgColor = processColorString( _wConfig.getLoadingScreenColor() ); // Set background color of the browserfield. // -1 denotes an invalid color. if( _bgColor != -1 ) { Background color = BackgroundFactory.createSolidBackground( _bgColor ); _browserField.setBackground( color ); _manager.setBackground( color ); this.setBackground( color ); this.getMainManager().setBackground( color ); } // Register extensions. Enumeration ext = _wConfig.getExtensions(); while(ext.hasMoreElements()) { Object extension = ext.nextElement(); if( extension instanceof WidgetExtension ){ ((WidgetExtension) extension ).register( _wConfig, _browserField ); } } // Update the static reference of browser field. _browserFieldReference = _browserField; // Create the CacheManager to handle caching functions. _cacheManager = null; if( _wConfig instanceof WidgetConfigImpl ) { WidgetConfigImpl wConfigImpl = (WidgetConfigImpl) _wConfig; if( wConfigImpl.isCacheEnabled() ) { _cacheManager = new CacheManager( wConfigImpl ); } _widgetCacheExtension = new WidgetCacheNamespace( this ); } if( DeviceInfo.isBlackBerry5() ) { _HTML5ToGearsExtension = new GearsHTML5Extension(); } } /** * <description> Obtain a handle to the BrowserField that resides in this Screen. * * @return <description> */ public static BrowserField getBrowserField() { return _browserFieldReference; } private BrowserFieldRequest createRequest( String url ) { BrowserFieldRequest result = new BrowserFieldRequest( WidgetUtil.getLocalPath( url ) ); return result; } /* Paints a bgcolor if the page is not loaded yet. */ protected void paint( Graphics graphics ) { super.paint( graphics ); } /* * Process a string in the format "#000000" and return the hex int value. -1 denotes an invalid color. */ private int processColorString( String colorString ) { // Remove leading # String str = colorString; if( str != null && str.startsWith( "#" ) && str.length() == 7 ) { str = str.substring( 1 ); // Attempt to convert string to hex. try { return Integer.parseInt( str, 16 ); } catch( Exception e ) { return -1; } } else { // Failed to determine color. return -1; } } /** * Sets the ConnectionFactory to have transport settings from the WidgetConfigImpl. */ private void updateConnectionFactory() { // Cast the WidgetConfig to WidgetConfigImpl version. WidgetConfigImpl wConfigImpl = (WidgetConfigImpl) _wConfig; ConnectionFactory connFact = new ConnectionFactory(); // Set default transport order. connFact.setPreferredTransportTypes( PREFERRED_TRANSPORTS ); // Set default time out for all connections to 30 seconds. connFact.setTimeLimit( 30000L ); // Set transports if specified in config.xml. if( wConfigImpl.getPreferredTransports() != null ) { connFact.setPreferredTransportTypes( wConfigImpl.getPreferredTransports() ); // Set the timeout for transports. if( wConfigImpl.getTransportTimeout() != null && wConfigImpl.getTransportTimeout().intValue() >= 0 ) { // setTimeLimit sets the max time limit for making a connection. connFact.setTimeLimit( wConfigImpl.getTransportTimeout().longValue() ); } } // Set options. connFact.setTransportTypeOptions( TransportInfo.TRANSPORT_BIS_B, new BisBOptions( "mds-public" ) ); connFact.setTimeoutSupported( true ); // Set BrowserFieldConfig. _bfConfig.setProperty( BrowserFieldConfig.CONNECTION_FACTORY, connFact ); } public GearsHTML5Extension getHTML5Extension() { return _HTML5ToGearsExtension; } public NavigationExtension getNavigationJS() { return _navigationJS; } public NavigationNamespace getNavigationExtension() { return _navigationExtension; } public NavigationController getNavigationController() { return _navigationController; } public WidgetFieldManager getWidgetFieldManager() { if( _manager instanceof WidgetFieldManager ) { return (WidgetFieldManager) _manager; } return null; } public BrowserField getWidgetBrowserField() { return _browserField; } public boolean getAppNavigationMode() { if( _wConfig instanceof WidgetConfigImpl ) { return ( ( (WidgetConfigImpl) _wConfig ).getNavigationMode() && Trackball.isSupported() ); } return false; } public PageManager getPageManager() { return _pageManager; } public void suppressLoadingScreenForGoingBack() { _pageManager.setSuppressLoadingScreen(); } public CacheManager getCacheManager() { return _cacheManager; } public WidgetCacheNamespace getWidgetCacheExtension() { return _widgetCacheExtension; } protected boolean onBackButton() { boolean result = false; // If the behaviour is 'exit', close instead of checking history. String backButtonSetting = ( (WidgetConfigImpl) _wConfig ).getBackButtonBehaviour(); if( backButtonSetting != null && backButtonSetting.equalsIgnoreCase( "exit" ) ) { result = onClose(); } // Default behaviour: Check history, go back if possible. // Do not allow going back if the page is in transition process. // This may cause conflicting threads to deadlock. else if( _browserField.getHistory().canGoBack() ) { // If the back button is pressed during page transitions, stop the transition. if( _pageManager.isGoingBackSafe() ) { _browserField.back(); } else { // Do nothing for now. } result = true; } else { // Close the app if there is no previous history result = onClose(); } return result; } }