// 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() {} }