001: /*
002: * This file is part of the Echo Web Application Framework (hereinafter "Echo").
003: * Copyright (C) 2002-2005 NextApp, Inc.
004: *
005: * Version: MPL 1.1/GPL 2.0/LGPL 2.1
006: *
007: * The contents of this file are subject to the Mozilla Public License Version
008: * 1.1 (the "License"); you may not use this file except in compliance with
009: * the License. You may obtain a copy of the License at
010: * http://www.mozilla.org/MPL/
011: *
012: * Software distributed under the License is distributed on an "AS IS" basis,
013: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
014: * for the specific language governing rights and limitations under the
015: * License.
016: *
017: * Alternatively, the contents of this file may be used under the terms of
018: * either the GNU General Public License Version 2 or later (the "GPL"), or
019: * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
020: * in which case the provisions of the GPL or the LGPL are applicable instead
021: * of those above. If you wish to allow use of your version of this file only
022: * under the terms of either the GPL or the LGPL, and not to allow others to
023: * use your version of this file under the terms of the MPL, indicate your
024: * decision by deleting the provisions above and replace them with the notice
025: * and other provisions required by the GPL or the LGPL. If you do not delete
026: * the provisions above, a recipient may use your version of this file under
027: * the terms of any one of the MPL, the GPL or the LGPL.
028: */
029:
030: package nextapp.echo2.webrender;
031:
032: import java.util.ArrayList;
033: import java.util.Collections;
034: import java.util.Enumeration;
035: import java.util.HashSet;
036: import java.util.List;
037: import java.util.Locale;
038: import java.util.Set;
039:
040: import nextapp.echo2.webrender.service.SynchronizeService;
041: import nextapp.echo2.webrender.util.DomUtil;
042:
043: import org.w3c.dom.Element;
044:
045: /**
046: * <code>SynchronizeService.ClientMessagePartProcessor</code> which creates
047: * a <code>ClientProperties</code> object based on the client script's
048: * analysis of its environment.
049: */
050: public class ClientAnalyzerProcessor implements
051: SynchronizeService.ClientMessagePartProcessor {
052:
053: /**
054: * <code>Set</code> containing valid properties which may be received from
055: * the client. Property settings received from the client that are not in
056: * this set are discarded.
057: */
058: private static final Set VALID_PROPERTIES;
059: static {
060: Set set = new HashSet();
061: set.add(ClientProperties.NAVIGATOR_APP_CODE_NAME);
062: set.add(ClientProperties.NAVIGATOR_APP_NAME);
063: set.add(ClientProperties.NAVIGATOR_APP_VERSION);
064: set.add(ClientProperties.NAVIGATOR_COOKIE_ENABLED);
065: set.add(ClientProperties.NAVIGATOR_JAVA_ENABLED);
066: set.add(ClientProperties.NAVIGATOR_LANGUAGE);
067: set.add(ClientProperties.NAVIGATOR_PLATFORM);
068: set.add(ClientProperties.NAVIGATOR_USER_AGENT);
069: set.add(ClientProperties.SCREEN_WIDTH);
070: set.add(ClientProperties.SCREEN_HEIGHT);
071: set.add(ClientProperties.SCREEN_COLOR_DEPTH);
072: set.add(ClientProperties.UTC_OFFSET);
073: VALID_PROPERTIES = Collections.unmodifiableSet(set);
074: }
075:
076: /**
077: * Analyzes the state of <code>ClientProperties</code> and adds additional
078: * inferred data, such as quirk attributes based on browser type.
079: *
080: * @param clientProperties the <code>ClientProperties</code> to analyze
081: * and update
082: */
083: private void analyze(ClientProperties clientProperties) {
084: Connection conn = WebRenderServlet.getActiveConnection();
085:
086: Enumeration localeEnum = conn.getRequest().getLocales();
087: List localeList = new ArrayList();
088: while (localeEnum.hasMoreElements()) {
089: localeList.add(localeEnum.nextElement());
090: }
091: clientProperties.setProperty(ClientProperties.LOCALES,
092: localeList.toArray(new Locale[localeList.size()]));
093:
094: clientProperties.setProperty(ClientProperties.REMOTE_HOST, conn
095: .getRequest().getRemoteHost());
096:
097: String userAgent = clientProperties.getString(
098: ClientProperties.NAVIGATOR_USER_AGENT).toLowerCase();
099:
100: boolean browserOpera = userAgent.indexOf("opera") != -1;
101: boolean browserSafari = userAgent.indexOf("safari") != -1;
102: boolean browserKonqueror = userAgent.indexOf("konqueror") != -1;
103:
104: // Note deceptive user agent fields:
105: // - Konqueror and Safari UA fields contain "like Gecko"
106: // - Opera UA field typically contains "MSIE"
107: boolean deceptiveUserAgent = browserOpera || browserSafari
108: || browserKonqueror;
109:
110: boolean browserMozilla = !deceptiveUserAgent
111: && userAgent.indexOf("gecko") != -1;
112: boolean browserFireFox = userAgent.indexOf("firefox") != -1;
113: boolean browserInternetExplorer = !deceptiveUserAgent
114: && userAgent.indexOf("msie") != -1;
115:
116: int majorVersion = -1, minorVersion = -1;
117:
118: // Store browser information.
119: if (browserOpera) {
120: clientProperties.setProperty(
121: ClientProperties.BROWSER_OPERA, Boolean.TRUE);
122: if (userAgent.indexOf("opera/9") != -1
123: || userAgent.indexOf("opera 9") != -1) {
124: majorVersion = 9;
125: } else if (userAgent.indexOf("opera/8") != -1
126: || userAgent.indexOf("opera 8") != -1) {
127: majorVersion = 8;
128: } else {
129: // Assume future version with Opera 9 compatibility.
130: // (Versions prior to 8 do not provide minimum requirements, i.e., XMLHTTPRequest.)
131: majorVersion = 9;
132: }
133: } else if (browserKonqueror) {
134: clientProperties.setProperty(
135: ClientProperties.BROWSER_KONQUEROR, Boolean.TRUE);
136: } else if (browserSafari) {
137: clientProperties.setProperty(
138: ClientProperties.BROWSER_SAFARI, Boolean.TRUE);
139: } else if (browserMozilla) {
140: clientProperties.setProperty(
141: ClientProperties.BROWSER_MOZILLA, Boolean.TRUE);
142: if (browserFireFox) {
143: clientProperties.setProperty(
144: ClientProperties.BROWSER_MOZILLA_FIREFOX,
145: Boolean.TRUE);
146: }
147: } else if (browserInternetExplorer) {
148: clientProperties.setProperty(
149: ClientProperties.BROWSER_INTERNET_EXPLORER,
150: Boolean.TRUE);
151: if (userAgent.indexOf("msie 6.") != -1) {
152: majorVersion = 6;
153: } else if (userAgent.indexOf("msie 7.") != -1) {
154: majorVersion = 7;
155: }
156: }
157:
158: if (majorVersion != -1) {
159: clientProperties.setProperty(
160: ClientProperties.BROWSER_VERSION_MAJOR, Integer
161: .toString(majorVersion));
162: }
163:
164: if (minorVersion != -1) {
165: clientProperties.setProperty(
166: ClientProperties.BROWSER_VERSION_MINOR, Integer
167: .toString(minorVersion));
168: }
169:
170: // Set quirk flags.
171: if (browserInternetExplorer) {
172: clientProperties.setProperty(
173: ClientProperties.QUIRK_IE_REPAINT, Boolean.TRUE);
174: clientProperties.setProperty(
175: ClientProperties.QUIRK_TEXTAREA_CONTENT,
176: Boolean.TRUE);
177: clientProperties
178: .setProperty(
179: ClientProperties.QUIRK_IE_TEXTAREA_NEWLINE_OBLITERATION,
180: Boolean.TRUE);
181: clientProperties.setProperty(
182: ClientProperties.QUIRK_IE_SELECT_LIST_DOM_UPDATE,
183: Boolean.TRUE);
184: clientProperties.setProperty(
185: ClientProperties.QUIRK_CSS_BORDER_COLLAPSE_INSIDE,
186: Boolean.TRUE);
187: clientProperties
188: .setProperty(
189: ClientProperties.QUIRK_CSS_BORDER_COLLAPSE_FOR_0_PADDING,
190: Boolean.TRUE);
191:
192: clientProperties.setProperty(
193: ClientProperties.NOT_SUPPORTED_CSS_OPACITY,
194: Boolean.TRUE);
195:
196: clientProperties
197: .setProperty(
198: ClientProperties.PROPRIETARY_IE_CSS_EXPRESSIONS_SUPPORTED,
199: Boolean.TRUE);
200: clientProperties
201: .setProperty(
202: ClientProperties.PROPRIETARY_EVENT_MOUSE_ENTER_LEAVE_SUPPORTED,
203: Boolean.TRUE);
204: clientProperties
205: .setProperty(
206: ClientProperties.PROPRIETARY_IE_OPACITY_FILTER_REQUIRED,
207: Boolean.TRUE);
208:
209: // The following flaws are verified as present in IE7.0Beta2:
210: clientProperties
211: .setProperty(
212: ClientProperties.QUIRK_IE_TABLE_PERCENT_WIDTH_SCROLLBAR_ERROR,
213: Boolean.TRUE);
214: clientProperties.setProperty(
215: ClientProperties.QUIRK_IE_SELECT_PERCENT_WIDTH,
216: Boolean.TRUE);
217:
218: if (majorVersion < 7) {
219: // The following flaws are verified as fixed in IE7.0Beta2:
220: clientProperties
221: .setProperty(
222: ClientProperties.PROPRIETARY_IE_PNG_ALPHA_FILTER_REQUIRED,
223: Boolean.TRUE);
224: clientProperties
225: .setProperty(
226: ClientProperties.QUIRK_CSS_POSITIONING_ONE_SIDE_ONLY,
227: Boolean.TRUE);
228: clientProperties
229: .setProperty(
230: ClientProperties.QUIRK_CSS_BACKGROUND_ATTACHMENT_USE_FIXED,
231: Boolean.TRUE);
232: clientProperties.setProperty(
233: ClientProperties.QUIRK_IE_SELECT_Z_INDEX,
234: Boolean.TRUE);
235: }
236: }
237: if (browserOpera) {
238: clientProperties.setProperty(
239: ClientProperties.QUIRK_TEXTAREA_CONTENT,
240: Boolean.TRUE);
241: clientProperties.setProperty(
242: ClientProperties.QUIRK_OPERA_NO_CSS_TEXT,
243: Boolean.TRUE);
244: clientProperties.setProperty(
245: ClientProperties.QUIRK_SELECT_REQUIRES_NULL_OPTION,
246: Boolean.TRUE);
247: clientProperties.setProperty(
248: ClientProperties.QUIRK_IE_SELECT_PERCENT_WIDTH,
249: Boolean.TRUE);
250: if (majorVersion < 9) {
251: clientProperties.setProperty(
252: ClientProperties.NOT_SUPPORTED_CSS_OPACITY,
253: Boolean.TRUE);
254: }
255: }
256: if (browserMozilla) {
257: clientProperties.setProperty(
258: ClientProperties.QUIRK_SELECT_REQUIRES_NULL_OPTION,
259: Boolean.TRUE);
260: clientProperties.setProperty(
261: ClientProperties.QUIRK_MOZILLA_TEXT_INPUT_REPAINT,
262: Boolean.TRUE);
263: clientProperties
264: .setProperty(
265: ClientProperties.QUIRK_MOZILLA_PERFORMANCE_LARGE_DOM_REMOVE,
266: Boolean.TRUE);
267: }
268:
269: if (browserKonqueror) {
270: clientProperties.setProperty(
271: ClientProperties.QUIRK_SELECT_REQUIRES_NULL_OPTION,
272: Boolean.TRUE);
273: clientProperties.setProperty(
274: ClientProperties.QUIRK_IE_SELECT_PERCENT_WIDTH,
275: Boolean.TRUE);
276: clientProperties.setProperty(
277: ClientProperties.NOT_SUPPORTED_CSS_MANIPULATION,
278: Boolean.TRUE);
279: }
280:
281: if (browserSafari) {
282: clientProperties.setProperty(
283: ClientProperties.NOT_SUPPORTED_CSS_MANIPULATION,
284: Boolean.TRUE);
285: }
286: }
287:
288: /**
289: * @see nextapp.echo2.webrender.service.SynchronizeService.ClientMessagePartProcessor#getName()
290: */
291: public String getName() {
292: return "EchoClientAnalyzer";
293: }
294:
295: /**
296: * @see nextapp.echo2.webrender.service.SynchronizeService.ClientMessagePartProcessor#process(
297: * nextapp.echo2.webrender.UserInstance, org.w3c.dom.Element)
298: */
299: public void process(UserInstance userInstance,
300: Element messagePartElement) {
301: ClientProperties clientProperties = new ClientProperties();
302: Element[] propertyElements = DomUtil.getChildElementsByTagName(
303: messagePartElement, "property");
304: for (int i = 0; i < propertyElements.length; ++i) {
305: String propertyName = propertyElements[i]
306: .getAttribute("name");
307: if (VALID_PROPERTIES.contains(propertyName)) {
308: String type = propertyElements[i].getAttribute("type");
309: if ("text".equals(type)) {
310: clientProperties.setProperty(propertyName,
311: propertyElements[i].getAttribute("value"));
312: } else if ("integer".equals(type)) {
313: try {
314: clientProperties.setProperty(propertyName,
315: new Integer(propertyElements[i]
316: .getAttribute("value")));
317: } catch (NumberFormatException ex) {
318: }
319: } else if ("boolean".equals(type)) {
320: clientProperties.setProperty(propertyName,
321: new Boolean("true"
322: .equals(propertyElements[i]
323: .getAttribute("value"))));
324: }
325: }
326: }
327: userInstance.setClientProperties(clientProperties);
328: analyze(clientProperties);
329: }
330: }
|