/* * 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.io.IOException; import javax.microedition.io.HttpConnection; import javax.microedition.io.InputConnection; import net.rim.blackberry.api.browser.Browser; import net.rim.blackberry.api.browser.BrowserSession; import net.rim.device.api.browser.field2.BrowserField; import net.rim.device.api.browser.field2.BrowserFieldRequest; import net.rim.device.api.browser.field2.BrowserFieldResponse; import net.rim.device.api.browser.field2.ProtocolController; import net.rim.device.api.io.MIMETypeAssociations; import net.rim.device.api.io.URI; import net.rim.device.api.web.WidgetAccess; import net.rim.device.api.web.WidgetConfig; import blackberry.common.util.StringUtilities; import blackberry.core.IJSExtension; import blackberry.core.JSExtensionRequest; import blackberry.core.JSExtensionResponse; import blackberry.web.widget.MemoryMaid; import blackberry.web.widget.auth.Authenticator; import blackberry.web.widget.device.DeviceInfo; import blackberry.web.widget.exception.MediaHandledException; import blackberry.web.widget.impl.WidgetConfigImpl; import blackberry.web.widget.impl.WidgetException; import blackberry.web.widget.policy.WidgetPolicy; import blackberry.web.widget.policy.WidgetPolicyFactory; import blackberry.web.widget.bf.HTTPResponseStatus; /** * */ public class WidgetRequestController extends ProtocolController { private WidgetConfig _widgetConfig; private WidgetPolicy _widgetPolicy; private boolean _hasMultiAccess; private BrowserField _browserField; /** * Constructor. */ public WidgetRequestController( BrowserField bf, WidgetConfig config ) { super( bf ); _widgetConfig = config; _widgetPolicy = WidgetPolicyFactory.getPolicy(); // Set BF handle. _browserField = bf; // Support for * if( _widgetConfig instanceof WidgetConfigImpl ) { _hasMultiAccess = ( (WidgetConfigImpl) _widgetConfig ).allowMultiAccess(); } } /** * @see net.rim.device.api.browser.field2.BrowserFieldController */ public void handleNavigationRequest( BrowserFieldRequest request ) throws Exception { WidgetAccess access = _widgetPolicy.getElement( request.getURL(), _widgetConfig.getAccessList() ); if( access == null && !_hasMultiAccess ) { if( !DeviceInfo.isBlackBerry6() ) { _browserField.getHistory().go( -1 ); } throw new WidgetException( WidgetException.ERROR_WHITELIST_FAIL, request.getURL() ); } BrowserFieldScreen bfScreen = ( (BrowserFieldScreen) _browserField.getScreen() ); // Launch the browser if BF2 cannot handle the mime type if( !openWithBrowser( request ) ) { // Determine the MIME type of the url String contentType = MIMETypeAssociations.getMIMEType( request.getURL() ); // Normalize and strip off parameters. String normalizedContentType = MIMETypeAssociations.getNormalizedType( contentType ); // Determine protocol String protocol = request.getProtocol(); bfScreen.getPageManager().setGoingBackSafe( false ); InputConnection ic = null; try { if( bfScreen.getCacheManager() != null && bfScreen.getCacheManager().isRequestCacheable( request ) ) { if( bfScreen.getCacheManager().hasCache( request.getURL() ) && !bfScreen.getCacheManager().hasCacheExpired( request.getURL() ) ) { ic = bfScreen.getCacheManager().getCache( request.getURL() ); } else { ic = _browserField.getConnectionManager().makeRequest( request ); if( ic instanceof HttpConnection ) { HttpConnection response = (HttpConnection) ic; if( bfScreen.getCacheManager().isResponseCacheable( response ) ) { ic = bfScreen.getCacheManager().createCache( request.getURL(), response ); } } } ic = processAuthentication( ic, request ); _browserField.displayContent( ic, request.getURL() ); } else { // Check whether authentication is required if( isHttpProtocol( request ) ) { // Only HTTP/HTTPS can use the API to receive the response ic = _browserField.getConnectionManager().makeRequest( request ); ic = processAuthentication( ic, request ); _browserField.displayContent( ic, request.getURL() ); } else { super.handleNavigationRequest( request ); } } if( !bfScreen.getPageManager().isRedirectableNavigation( protocol ) ) { // The navigation won't redirect bfScreen.getPageManager().clearFlags(); if( !bfScreen.getPageManager().isLocalTextHtml( normalizedContentType ) ) { if( bfScreen.getPageManager().isLoadingScreenDisplayed() ) { bfScreen.getPageManager().hideLoadingScreen(); } } } } catch( Exception e ) { bfScreen.getPageManager().hideLoadingScreen(); bfScreen.getPageManager().clearFlags(); // Rethrow the Exception throw e; } } MemoryMaid mm = MemoryMaid.getInstance(); if( mm != null ) { if( mm.isAlive() ) { mm.flagGC(); } else { // Start the memory manager after our first page has been loaded mm.start(); } } } /** * @see net.rim.device.api.browser.field2.BrowserFieldController */ public InputConnection handleResourceRequest( BrowserFieldRequest request ) throws Exception { if( this._browserField == null ) { return new HTTPResponseStatus( HTTPResponseStatus.SC_SERVER_ERROR, request ).getResponse(); } if( request.getURL().startsWith( "http://localhost:8472/" ) ) { URI requestURI = URI.create( request.getURL() ); String[] splitPath = StringUtilities.split( requestURI.getPath(), "/" ); String featureID = ""; for( int i = 0; i < splitPath.length - 1; i++ ) { if( featureID == "" ) { featureID = featureID + splitPath[ i ]; } else { featureID = featureID + "." + splitPath[ i ]; } } Object ext = ( (WidgetConfigImpl) _widgetConfig ).getExtensionObjectForFeature( featureID ); if( ext != null && ext instanceof IJSExtension ) { JSExtensionRequest req = new JSExtensionRequest( request.getURL(), request.getPostData(), request.getHeaders(), ( (WidgetConfigImpl) _widgetConfig ).getFeatureTable() ); JSExtensionResponse res = new JSExtensionResponse( request.getURL(), null, request.getHeaders() ); try { ( (IJSExtension) ext ).invoke( req, res ); return new BrowserFieldResponse( res.getURL(), res.getPostData(), res.getHeaders() ); } catch( net.rim.device.api.web.WidgetException e ) { // this block is reached if the method cannot be found within the extension return new HTTPResponseStatus( HTTPResponseStatus.SC_NOT_IMPLEMENTED, request ).getResponse(); } } else { if( ext == null ) { return new HTTPResponseStatus( HTTPResponseStatus.SC_NOT_FOUND, request ).getResponse(); } else if( !( ext instanceof IJSExtension ) ) { return new HTTPResponseStatus( HTTPResponseStatus.SC_NOT_IMPLEMENTED, request ).getResponse(); } } } WidgetAccess access = _widgetPolicy.getElement( request.getURL(), _widgetConfig.getAccessList() ); if( access == null && !_hasMultiAccess ) { throw new WidgetException( WidgetException.ERROR_WHITELIST_FAIL, request.getURL() ); } // In this event, only rtsp link needs to open browser if( request.getProtocol().equalsIgnoreCase( "rtsp" ) ) { openWithBrowser( request ); return null; } BrowserFieldScreen bfScreen = ( (BrowserFieldScreen) _browserField.getScreen() ); InputConnection ic = null; if( bfScreen.getCacheManager() != null && bfScreen.getCacheManager().isRequestCacheable( request ) ) { if( bfScreen.getCacheManager().hasCache( request.getURL() ) && !bfScreen.getCacheManager().hasCacheExpired( request.getURL() ) ) { ic = bfScreen.getCacheManager().getCache( request.getURL() ); } else { ic = super.handleResourceRequest( request ); if( ic instanceof HttpConnection ) { HttpConnection response = (HttpConnection) ic; if( bfScreen.getCacheManager().isResponseCacheable( response ) ) { ic = bfScreen.getCacheManager().createCache( request.getURL(), response ); } } } } else { ic = super.handleResourceRequest( request ); } ic = processAuthentication( ic, request ); return ic; } /** * Performs authentication checks and requests credentials when needed */ private InputConnection processAuthentication( InputConnection ic, BrowserFieldRequest request ) throws Exception { // Check if Basic authentication is required to access the resource boolean authenticationIsNeeded = false; while( isAuthenticationNeeded( ic, request ) ) { authenticationIsNeeded = true; BrowserFieldRequest authenticationReq = Authenticator.getAuthenticationRequest( (HttpConnection) ic, request ); if( authenticationReq == request ) { break; } ic = super.handleResourceRequest( authenticationReq ); } // If credentials are needed and they are correct, set them verified if( authenticationIsNeeded && !isHTTPError( ic ) ) { Authenticator.verifyCredential( (HttpConnection) ic ); } return ic; } /** * Check if authentication is needed by checking the response code. * * @param icResponse * @param request * @return */ private static boolean isAuthenticationNeeded( InputConnection icResponse, BrowserFieldRequest request ) { if( icResponse instanceof HttpConnection && isHttpProtocol( request ) ) { HttpConnection hcResponse = (HttpConnection) icResponse; try { int responseCode = hcResponse.getResponseCode(); return HttpConnection.HTTP_UNAUTHORIZED == responseCode; } catch( IOException e ) { return false; } } else { return false; } } /** * Check if the request uses the HTTP protocol * * @param request * @return */ private static boolean isHttpProtocol( BrowserFieldRequest request ) { String protocol = request.getProtocol(); return ( protocol.equalsIgnoreCase( "http" ) || protocol.equalsIgnoreCase( "https" ) ); } /** * Check the connection for an error response code * * @param ic * @return */ private static boolean isHTTPError( InputConnection ic ) { if( ic instanceof HttpConnection ) { try { HttpConnection hcResponse = (HttpConnection) ic; int responseCode = hcResponse.getResponseCode(); return ( responseCode >= 400 && responseCode < 600 ); } catch( IOException e ) { return true; } } else { return true; } } // Method to check if the browser needs to be launched to handle the file. private boolean openWithBrowser( BrowserFieldRequest request ) throws Exception { BrowserFieldScreen bfScreen = ( (BrowserFieldScreen) _browserField.getScreen() ); // Determine the MIME type of the url String contentType = MIMETypeAssociations.getMIMEType( request.getURL() ); // Normalize and strip off parameters. String normalizedContentType = MIMETypeAssociations.getNormalizedType( contentType ); // Determine protocol String protocol = request.getProtocol(); // Launch the browser if BF2 cannot handle the mime type if( openWithBrowser( normalizedContentType, protocol, request.getURL() ) ) { invokeBrowser( request.getURL() ); // Reset the loading screen flags to ensure that the back button is enabled after the invoke bfScreen.getPageManager().clearFlags(); if( DeviceInfo.isBlackBerry6() ) { // Throw a special type of exception that will prevent the history from being updated throw new MediaHandledException(); } else { // On 5.0 devices we can simply go back once to counter the history updating before this. _browserField.getHistory().go( -1 ); } return true; } else { return false; } } // Method to check if the browser needs to be launched to handle the file. private boolean openWithBrowser( String mimeType, String protocol, String url ) { // rtsp links should open in the browser. �BF2 does not support the protocol if( protocol.equalsIgnoreCase( "rtsp" ) ) { return true; } if( mimeType != null ) { // Determine media type. int mediaType = MIMETypeAssociations.getMediaTypeFromMIMEType( mimeType ); // Allow all local media to be handled by BF2. // Even if it is not supported by BF2 yet. if( protocol.equalsIgnoreCase( "local" ) ) { return false; } // List of types we don't want BF2 to handle. if( ( mediaType == MIMETypeAssociations.MEDIA_TYPE_AUDIO ) || ( mediaType == MIMETypeAssociations.MEDIA_TYPE_VIDEO ) || ( mediaType == MIMETypeAssociations.MEDIA_TYPE_PLAY_LIST ) || ( mediaType == MIMETypeAssociations.MEDIA_TYPE_APPLICATION ) || ( mediaType == MIMETypeAssociations.MEDIA_TYPE_UNKNOWN ) ) { return true; } } // If the type was null, check the file extension for .zip or .exe // Mark .zip and .exe to be unsupported by BF2 so that the browser will // be launched // Local files must be opened by BF2 all the time since the browser has // no access to those internal files if( checkFileExtension( url ) && !protocol.equalsIgnoreCase( "local" ) ) { return true; } else { return false; } } // Checks the file extension for special types. private boolean checkFileExtension( String url ) { boolean isMarked = false; // Check for .zip if( url.endsWith( ".zip" ) ) { isMarked = true; } // Check for .exe else if( url.endsWith( ".exe" ) ) { isMarked = true; } // Check for .wpd else if( url.endsWith( ".wpd" ) ) { isMarked = true; } // Return the value. False means it is not a special extension return isMarked; } // Invokes the browser on the given URL private void invokeBrowser( String url ) { BrowserSession bs = null; bs = Browser.getDefaultSession(); bs.displayPage( url ); } }