/* * 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."); } } }