diff options
Diffstat (limited to 'java/com/google/gwtexpui/user/client/UserAgent.java')
-rw-r--r-- | java/com/google/gwtexpui/user/client/UserAgent.java | 168 |
1 files changed, 168 insertions, 0 deletions
diff --git a/java/com/google/gwtexpui/user/client/UserAgent.java b/java/com/google/gwtexpui/user/client/UserAgent.java new file mode 100644 index 0000000000..1660a62c27 --- /dev/null +++ b/java/com/google/gwtexpui/user/client/UserAgent.java @@ -0,0 +1,168 @@ +// Copyright (C) 2009 The Android Open Source Project +// +// 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.gwtexpui.user.client; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.user.client.Window; + +/** + * User agent feature tests we don't create permutations for. + * + * <p>Some features aren't worth creating full permutations in GWT for, as each new boolean + * permutation (only two settings) doubles the compile time required. If the setting only affects a + * couple of lines of JavaScript code, the slightly larger cache files for user agents that lack the + * functionality requested is trivial compared to the time developers lose building their + * application. + */ +public class UserAgent { + private static boolean jsClip = guessJavaScriptClipboard(); + + public static boolean hasJavaScriptClipboard() { + return jsClip; + } + + public static void disableJavaScriptClipboard() { + jsClip = false; + } + + private static native boolean nativeHasCopy() + /*-{ return $doc['queryCommandSupported'] && $doc.queryCommandSupported('copy') }-*/ ; + + private static boolean guessJavaScriptClipboard() { + String ua = Window.Navigator.getUserAgent(); + int chrome = major(ua, "Chrome/"); + if (chrome > 0) { + return 42 <= chrome; + } + + int ff = major(ua, "Firefox/"); + if (ff > 0) { + return 41 <= ff; + } + + int opera = major(ua, "OPR/"); + if (opera > 0) { + return 29 <= opera; + } + + int msie = major(ua, "MSIE "); + if (msie > 0) { + return 9 <= msie; + } + + if (nativeHasCopy()) { + // Firefox 39.0 lies and says it supports copy, then fails. + // So we try this after the browser specific test above. + return true; + } + + // Safari is not planning to support document.execCommand('copy'). + // Assume the browser does not have the feature. + return false; + } + + private static int major(String ua, String product) { + int entry = ua.indexOf(product); + if (entry >= 0) { + String s = ua.substring(entry + product.length()); + String p = s.split("[ /;,.)]", 2)[0]; + try { + return Integer.parseInt(p); + } catch (NumberFormatException nan) { + // Ignored + } + } + return -1; + } + + public static class Flash { + private static boolean checked; + private static boolean installed; + + /** + * Does the browser have ShockwaveFlash plugin installed? + * + * <p>This method may still return true if the user has disabled Flash or set the plugin to + * "click to run". + */ + public static boolean isInstalled() { + if (!checked) { + installed = hasFlash(); + checked = true; + } + return installed; + } + + private static native boolean hasFlash() /*-{ + if (navigator.plugins && navigator.plugins.length) { + if (navigator.plugins['Shockwave Flash']) return true; + if (navigator.plugins['Shockwave Flash 2.0']) return true; + + } else if (navigator.mimeTypes && navigator.mimeTypes.length) { + var mimeType = navigator.mimeTypes['application/x-shockwave-flash']; + if (mimeType && mimeType.enabledPlugin) return true; + + } else { + try { new ActiveXObject('ShockwaveFlash.ShockwaveFlash.7'); return true; } catch (e) {} + try { new ActiveXObject('ShockwaveFlash.ShockwaveFlash.6'); return true; } catch (e) {} + try { new ActiveXObject('ShockwaveFlash.ShockwaveFlash'); return true; } catch (e) {} + } + return false; + }-*/; + } + + /** + * Test for and disallow running this application in an <iframe>. + * + * <p>If the application is running within an iframe this method requests a browser generated + * redirect to pop the application out of the iframe into the top level window, and then aborts + * execution by throwing an exception. This is call should be placed early within the module's + * onLoad() method, before any real UI can be initialized that an attacking site could try to snip + * out and present in a confusing context. + * + * <p>If the break out works, execution will restart automatically in a proper top level window, + * where the script has full control over the display. If the break out fails, execution will + * abort and stop immediately, preventing UI widgets from being created, leaving the user with an + * empty frame. + */ + public static void assertNotInIFrame() { + if (GWT.isScript() && amInsideIFrame()) { + bustOutOfIFrame(Window.Location.getHref()); + throw new RuntimeException(); + } + } + + private static native boolean amInsideIFrame() /*-{ return top.location != $wnd.location; }-*/; + + private static native void bustOutOfIFrame(String newloc) /*-{ top.location.href = newloc }-*/; + + /** + * Test if Gerrit is running on a mobile browser. This check could be incomplete, but should cover + * most cases. Regexes shamelessly borrowed from CodeMirror. + */ + public static native boolean isMobile() /*-{ + var ua = $wnd.navigator.userAgent; + var ios = /AppleWebKit/.test(ua) && /Mobile\/\w+/.test(ua); + return ios + || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(ua); + }-*/; + + /** Check if the height of the browser view is greater than its width. */ + public static boolean isPortrait() { + return Window.getClientHeight() > Window.getClientWidth(); + } + + private UserAgent() {} +} |