/*
* Copyright 2011 Google Inc.
*
* 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 com.google.gwt.useragent.rebind;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.linker.ConfigurationProperty;
import com.google.gwt.core.ext.linker.PropertyProviderGenerator;
import com.google.gwt.user.rebind.SourceWriter;
import com.google.gwt.user.rebind.StringSourceWriter;
import java.util.HashSet;
import java.util.Set;
import java.util.SortedSet;
/**
* Generator which writes out the JavaScript for determining the value of the
* <code>user.agent</code> selection property.
*/
public class UserAgentPropertyGenerator implements PropertyProviderGenerator {
/**
* The list of {@code user.agent} values listed here should be kept in sync with
* {@code UserAgent.gwt.xml}.
* <p>Note that the order of enums matter as the script selection is based on running
* these predicates in order and matching the first one that returns {@code true}.
* <p> Also note that, {@code docMode < 11} in predicates for older IEs exists to
* ensures we never choose them for IE11 (we know that they will not work for IE11).
*/
private enum UserAgent {
safari("return (ua.indexOf('webkit') != -1);"),
ie10("return (ua.indexOf('msie') != -1 && (docMode >= 10 && docMode < 11));"),
ie9("return (ua.indexOf('msie') != -1 && (docMode >= 9 && docMode < 11));"),
ie8("return (ua.indexOf('msie') != -1 && (docMode >= 8 && docMode < 11));"),
gecko1_8("return (ua.indexOf('gecko') != -1 || docMode >= 11);");
private final String predicateBlock;
private UserAgent(String predicateBlock) {
this.predicateBlock = predicateBlock;
}
private static Set<String> getKnownAgents() {
HashSet<String> userAgents = new HashSet<String>();
for (UserAgent userAgent : values()) {
userAgents.add(userAgent.name());
}
return userAgents;
}
}
/**
* Writes out the JavaScript function body for determining the value of the
* <code>user.agent</code> selection property. This method is used to create
* the selection script and by {@link UserAgentGenerator} to assert at runtime
* that the correct user agent permutation is executing.
*/
static void writeUserAgentPropertyJavaScript(SourceWriter body,
SortedSet<String> possibleValues, String fallback) {
// write preamble
body.println("var ua = navigator.userAgent.toLowerCase();");
body.println("var docMode = $doc.documentMode;");
for (UserAgent userAgent : UserAgent.values()) {
// write only selected user agents
if (possibleValues.contains(userAgent.name())) {
body.println("if ((function() { ");
body.indentln(userAgent.predicateBlock);
body.println("})()) return '%s';", userAgent.name());
}
}
// default return
if (fallback == null) {
fallback = "unknown";
}
body.println("return '" + fallback + "';");
}
@Override
public String generate(TreeLogger logger, SortedSet<String> possibleValues, String fallback,
SortedSet<ConfigurationProperty> configProperties) {
assertUserAgents(logger, possibleValues);
StringSourceWriter body = new StringSourceWriter();
body.println("{");
body.indent();
writeUserAgentPropertyJavaScript(body, possibleValues, fallback);
body.outdent();
body.println("}");
return body.toString();
}
private static void assertUserAgents(TreeLogger logger, SortedSet<String> possibleValues) {
HashSet<String> unknownValues = new HashSet<String>(possibleValues);
unknownValues.removeAll(UserAgent.getKnownAgents());
if (!unknownValues.isEmpty()) {
logger.log(TreeLogger.WARN, "Unrecognized " + UserAgentGenerator.PROPERTY_USER_AGENT
+ " values " + unknownValues + ", possibly due to UserAgent.gwt.xml and "
+ UserAgentPropertyGenerator.class.getName() + " being out of sync.");
}
}
}