/*******************************************************************************
* Copyright (c) 2002, 2015 Innoopract Informationssysteme GmbH and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Innoopract Informationssysteme GmbH - initial API and implementation
* EclipseSource - ongoing development
******************************************************************************/
package org.eclipse.swt.internal.browser.browserkit;
import static org.eclipse.rap.rwt.internal.lifecycle.DisplayUtil.getAdapter;
import static org.eclipse.rap.rwt.internal.lifecycle.WidgetLCAUtil.getStyles;
import static org.eclipse.rap.rwt.internal.lifecycle.WidgetLCAUtil.preserveListener;
import static org.eclipse.rap.rwt.internal.lifecycle.WidgetLCAUtil.renderListener;
import static org.eclipse.rap.rwt.internal.lifecycle.WidgetUtil.getId;
import static org.eclipse.rap.rwt.internal.protocol.JsonUtil.createJsonArray;
import static org.eclipse.rap.rwt.internal.protocol.RemoteObjectFactory.createRemoteObject;
import static org.eclipse.rap.rwt.internal.protocol.RemoteObjectFactory.getRemoteObject;
import static org.eclipse.swt.internal.events.EventLCAUtil.isListening;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.eclipse.rap.json.JsonArray;
import org.eclipse.rap.json.JsonObject;
import org.eclipse.rap.json.JsonValue;
import org.eclipse.rap.rwt.RWT;
import org.eclipse.rap.rwt.internal.lifecycle.WidgetLCA;
import org.eclipse.rap.rwt.internal.lifecycle.ControlLCAUtil;
import org.eclipse.rap.rwt.internal.lifecycle.WidgetLCAUtil;
import org.eclipse.rap.rwt.internal.lifecycle.WidgetUtil;
import org.eclipse.rap.rwt.internal.protocol.JsonUtil;
import org.eclipse.rap.rwt.internal.service.ContextProvider;
import org.eclipse.rap.rwt.internal.service.ServiceStore;
import org.eclipse.rap.rwt.remote.RemoteObject;
import org.eclipse.rap.rwt.service.ResourceManager;
import org.eclipse.swt.browser.Browser;
import org.eclipse.swt.internal.events.EventTypes;
import org.eclipse.swt.internal.widgets.IBrowserAdapter;
import org.eclipse.swt.internal.widgets.WidgetRemoteAdapter;
import org.eclipse.swt.widgets.Display;
public final class BrowserLCA extends WidgetLCA<Browser> {
public static final BrowserLCA INSTANCE = new BrowserLCA();
private static final String TYPE = "rwt.widgets.Browser";
private static final String[] ALLOWED_STYLES = { "BORDER" };
// Background color is set due to bug 401300 and 401278
static final String BLANK_HTML
= "<html><script></script><body style=\"background-color: transparent;\"></body></html>";
public static final String EVENT_PROGRESS = "Progress";
private static final String PARAM_PROGRESS_LISTENER = "Progress";
private static final String PARAM_SCRIPT = "script";
private static final String METHOD_EVALUATE = "evaluate";
private static final String PARAM_FUNCTIONS = "functions";
private static final String METHOD_CREATE_FUNCTIONS = "createFunctions";
private static final String METHOD_DESTROY_FUNCTIONS = "destroyFunctions";
private static final String PARAM_FUNCTION_RESULT = "functionResult";
private static final String PREFIX = Browser.class.getName();
static final String EXECUTED_FUNCTION_NAME = PREFIX.concat( "#executedFunctionName." );
static final String EXECUTED_FUNCTION_RESULT = PREFIX.concat( "#executedFunctionResult." );
static final String EXECUTED_FUNCTION_ERROR = PREFIX.concat( "#executedFunctionError." );
private static final String FUNCTIONS_TO_CREATE = PREFIX.concat( "#functionsToCreate." );
private static final String FUNCTIONS_TO_DESTROY = PREFIX.concat( "#functionsToDestroy." );
@Override
public void preserveValues( Browser browser ) {
preserveListener( browser, PARAM_PROGRESS_LISTENER, hasProgressListener( browser ) );
}
@Override
public void renderInitialization( Browser browser ) throws IOException {
RemoteObject remoteObject = createRemoteObject( browser, TYPE );
remoteObject.setHandler( new BrowserOperationHandler( browser ) );
remoteObject.set( "parent", getId( browser.getParent() ) );
remoteObject.set( "style", createJsonArray( getStyles( browser, ALLOWED_STYLES ) ) );
}
@Override
public void renderChanges( final Browser browser ) throws IOException {
ControlLCAUtil.renderChanges( browser );
WidgetLCAUtil.renderCustomVariant( browser );
destroyBrowserFunctions( browser );
renderUrl( browser );
createBrowserFunctions( browser );
renderAfterAll( browser.getDisplay(), new Runnable() {
@Override
public void run() {
renderEvaluate( browser );
}
} );
renderFunctionResult( browser );
renderListener( browser, PARAM_PROGRESS_LISTENER, hasProgressListener( browser ), false );
}
private static void renderUrl( Browser browser ) throws IOException {
if( hasUrlChanged( browser ) ) {
getRemoteObject( browser ).set( "url", getUrl( browser ) );
browser.getAdapter( IBrowserAdapter.class ).resetUrlChanged();
}
}
static boolean hasUrlChanged( Browser browser ) {
boolean initialized = WidgetUtil.getAdapter( browser ).isInitialized();
return !initialized || browser.getAdapter( IBrowserAdapter.class ).hasUrlChanged();
}
static String getUrl( Browser browser ) throws IOException {
String text = getText( browser );
String url = browser.getUrl();
String result;
if( !"".equals( text.trim() ) ) {
result = registerHtml( text );
} else if( !"".equals( url.trim() ) ) {
result = url;
} else {
result = registerHtml( BLANK_HTML );
}
return result;
}
private static void renderAfterAll( Display display, Runnable runnable ) {
// [if] Put the execution to the end of the rendered message. This is very
// important when Browser#execute is called from within a BrowserFunction,
// because then we have a synchronous requests.
( ( WidgetRemoteAdapter )getAdapter( display ) ).addRenderRunnable( runnable );
}
private static void renderEvaluate( Browser browser ) {
IBrowserAdapter adapter = browser.getAdapter( IBrowserAdapter.class );
final String executeScript = adapter.getExecuteScript();
boolean executePending = adapter.getExecutePending();
if( executeScript != null && !executePending ) {
JsonObject parameters = new JsonObject().add( PARAM_SCRIPT, executeScript );
getRemoteObject( browser ).call( METHOD_EVALUATE, parameters );
adapter.setExecutePending( true );
}
}
private static String registerHtml( String html ) throws IOException {
String name = createUrlFromHtml( html );
byte[] bytes = html.getBytes( "UTF-8" );
InputStream inputStream = new ByteArrayInputStream( bytes );
ResourceManager resourceManager = RWT.getResourceManager();
resourceManager.register( name, inputStream );
return resourceManager.getLocation( name );
}
private static String createUrlFromHtml( String html ) {
StringBuilder result = new StringBuilder();
result.append( "org.eclipse.swt.browser/text" );
result.append( String.valueOf( html.hashCode() ) );
result.append( ".html" );
return result.toString();
}
private static String getText( Browser browser ) {
Object adapter = browser.getAdapter( IBrowserAdapter.class );
IBrowserAdapter browserAdapter = ( IBrowserAdapter )adapter;
return browserAdapter.getText();
}
//////////////////////////////////////
// Helping methods for BrowserFunction
private static void createBrowserFunctions( Browser browser ) {
ServiceStore serviceStore = ContextProvider.getServiceStore();
String id = WidgetUtil.getId( browser );
String[] functions = ( String[] )serviceStore.getAttribute( FUNCTIONS_TO_CREATE + id );
if( functions != null ) {
JsonObject parameters = new JsonObject()
.add( PARAM_FUNCTIONS, JsonUtil.createJsonArray( functions ) );
getRemoteObject( browser ).call( METHOD_CREATE_FUNCTIONS, parameters );
}
}
private static void destroyBrowserFunctions( Browser browser ) {
ServiceStore serviceStore = ContextProvider.getServiceStore();
String id = WidgetUtil.getId( browser );
String[] functions = ( String[] )serviceStore.getAttribute( FUNCTIONS_TO_DESTROY + id );
if( functions != null ) {
JsonObject parameters = new JsonObject()
.add( PARAM_FUNCTIONS, JsonUtil.createJsonArray( functions ) );
getRemoteObject( browser ).call( METHOD_DESTROY_FUNCTIONS, parameters );
}
}
private static void renderFunctionResult( Browser browser ) {
ServiceStore serviceStore = ContextProvider.getServiceStore();
String id = getId( browser );
String name = ( String )serviceStore.getAttribute( EXECUTED_FUNCTION_NAME + id );
if( name != null ) {
JsonValue result = ( JsonValue )serviceStore.getAttribute( EXECUTED_FUNCTION_RESULT + id );
if( result == null ) {
result = JsonValue.NULL;
}
String error = ( String )serviceStore.getAttribute( EXECUTED_FUNCTION_ERROR + id );
JsonArray array = new JsonArray().add( name ).add( result ).add( error );
getRemoteObject( browser ).set( PARAM_FUNCTION_RESULT, array );
}
}
private static boolean hasProgressListener( Browser browser ) {
return isListening( browser, EventTypes.PROGRESS_CHANGED )
|| isListening( browser, EventTypes.PROGRESS_COMPLETED );
}
private BrowserLCA() {
// prevent instantiation
}
}