//
// ERXBasicBrowser.java
// Project ERExtensions
//
// Created by tatsuya on Tue Jul 23 2002
//
package er.extensions.appserver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.webobjects.foundation.NSDictionary;
import com.webobjects.foundation.NSMutableDictionary;
import er.extensions.foundation.ERXStringUtilities;
/**
* <div class="en">
* <code>ERXBasicBrowser</code> is a concrete subclass of {@link ERXBrowser}
* that defines browser object. A browser object represents the web browser
* that the current request-response cycle is dealing with. It holds the
* information retrieved from HTTP request's <code>"user-agent"</code>
* header, and such information includes web browser's name, version, Mozilla
* compatible version and platform (OS). Also, a browser object can answer
* boolean questions such as {@link #isIE}, {@link #isOmniWeb},
* {@link #isVersion5} and {@link #isMozilla40Compatible}, and even more
* specific questions like {@link #isIFrameSupported} and
* {@link #willRenderNestedTablesFast}.
* <p>
* <code>ERXBasicBrowser</code> is immutable and shared by different sessions
* and direct actions. The shared instances are managed by
* {@link ERXBrowserFactory} which is also responsible to parse <code>"user-agent"</code>
* header in a {@link com.webobjects.appserver.WORequest WORequest} object and
* to get an appropriate browser object.
* <p>
* You can extends <code>ERXBasicBrowser</code> or its abstract parent <code>ERXBrowser</code>
* to implement more specific questions for your application. One potential
* example will be to have a question <code>isSupportedBrowser</code> that
* checks if the client is using one of the supported browsers for your
* application.
* <p>
* {@link ERXSession} holds a browser object that represent the web browser for
* that session and {@link ERXSession#browser() browser()} method returns the
* object.
* <p>
* To access <code>ERXBasicBrowser</code>'s boolean questions from <code>WOConditionals</code>
* on a web component, set the key path like <code>"session.brower.isNetscape"</code>
* to their condition bindings.
* <p>
* {@link ERXDirectAction} also holds a browser object for the current request.
* Use its {@link ERXDirectAction#browser() browser()} method to access the
* object from a session-less direct action.
*
*
* <h3>Some browser user-agents</h3>
*
* <p><strong>IE 5.17 OS 9</strong><br>
* user-agent = (Mozilla/4.0 (compatible; MSIE 5.17; Mac_PowerPC)); ua-os = (MacOS); ua-cpu = (PPC);
*
* IE 5.0 OS 9: user-agent = (Mozilla/4.0 (compatible; MSIE 5.0; Mac_PowerPC));
* ua-os = (MacOS); ua-cpu = (PPC);
*
* <p><strong>FireFox OS X 10.3.3</strong><br>
* user-agent = (Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.6) Gecko/20040206 Firefox/0.8);
*
* <p><strong>IE 5.2 MacOS X</strong><br>
* user-agent = (Mozilla/4.0 (compatible; MSIE 5.23; Mac_PowerPC)); ua-os = (MacOS); ua-cpu = (PPC);
*
* <p><strong>Safari</strong><br>
* user-agent = ("Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/124 (KHTML, like Gecko) Safari/125.1");
*
* <p><strong>IE WIndows 6.02</strong><br>
* user-agent = (Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0));
* </div>
*
* <div class="ja">
* <code>ERXBasicBrowser</code> はブラウザ・オブジェクトを定義する {@link ERXBrowser} の明確なサブクラスです。
* ブラウザ・オブジェクトはカレント・リクエスト・レスポンス・ループの Webブラウザを表します。
* HTTP リクエスト・ヘッダー <code>"user-agent"</code> の情報を保持し、さらに
* Webブラウザ名、プラットフォームや Mozilla互換性の情報を含みます。
* ブラウザ・オブジェクトは基本なメソッド <code>isIE</code> や <code>isVersion5</code> のみではなく、
* もっと確実な <code>isIFrameSupported</code>と <code>willRenderNestedTablesFast</code> を boolean で
* 回答します。<br>
*
* ERXBasicBrowser は不変で、他のセッションとダイレクト・アクションで共有されています。
* 共有インスタンスは ERXBrowserFactory で管理されています。他には ERXBrowserFactory が
* WORequest の "user-agent" パースとブラウザ・オブジェクトの作成を担当しています。<br>
*
* 自分のアプリケーションの為に ERXBrowser や ERXBasicBrowser のサブクラスをつくることができます。
* 例:アプリケーションでサポートされているブラウザかどうかの <code>isSupportedBrowser</code> を追加できます。<br>
*
* ERXSession はブラウザ・オブジェクトを保持し、セッションにアクセスしている Web Browser の情報を持っている。
* <code>browser</code> メソッドでオブジェクトを取得できます。<br>
*
* コンポーネント内の WOConditionals より ERXBasicBrowser の boolean を問い合わせにアクセスする時、
* 次のようなキーパス "session.brower.isNetscape" をバインディングします。<br>
*
* ERXDirectAction もカレント・リクエストのブラウザ・オブジェクトを保持します。
* オブジェクトをアクセスするには <code>browser</code> メソッドを使用します。<br>
*
* <h3>ブラウザの user-agents 一部: </h3>
*
* <p><strong>IE 5.17 OS 9: </strong><br>
* user-agent = (Mozilla/4.0 (compatible; MSIE 5.17; Mac_PowerPC)); ua-os = (MacOS); ua-cpu = (PPC);
*
* IE 5.0 OS 9: user-agent = (Mozilla/4.0 (compatible; MSIE 5.0; Mac_PowerPC));
* ua-os = (MacOS); ua-cpu = (PPC);
*
* <p><strong>FireFox OS X 10.3.3: </strong><br>
* user-agent = (Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.6) Gecko/20040206 Firefox/0.8);
*
* <p><strong>IE 5.2 MacOS X: </strong><br>
* user-agent = (Mozilla/4.0 (compatible; MSIE 5.23; Mac_PowerPC)); ua-os = (MacOS); ua-cpu = (PPC);
*
* <p><strong>Safari: </strong><br>
* user-agent = ("Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/124 (KHTML, like Gecko) Safari/125.1");
*
* <p><strong>IE WIndows 6.02: </strong><br>
* user-agent = (Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0));
* </div>
*/
public class ERXBasicBrowser extends ERXBrowser {
private static final Logger log = LoggerFactory.getLogger(ERXBasicBrowser.class);
private final String _browserName;
private final String _version;
private final Integer _majorVersion;
private final String _mozillaVersion;
private final String _platform;
private final String _cpu;
private final String _geckoRevision;
private final NSDictionary _userInfo;
private final boolean _isRobot;
private final boolean _isICab;
private final boolean _isEdge;
private final boolean _isIE;
private final boolean _isNetscape;
private final boolean _isOmniWeb;
private final boolean _isOpera;
private final boolean _isSafari;
private final boolean _isFirefox;
private final boolean _isMozilla;
private final boolean _isChrome;
private final boolean _isUnknownBrowser;
private final boolean _isMozillaVersion50;
private final boolean _isMozillaVersion45;
private final boolean _isMozillaVersion40;
private final boolean _isVersion13;
private final boolean _isVersion12;
private final boolean _isVersion11;
private final boolean _isVersion10;
private final boolean _isVersion9;
private final boolean _isVersion8;
private final boolean _isVersion7;
private final boolean _isVersion6;
private final boolean _isVersion5;
private final boolean _isVersion51;
private final boolean _isVersion45;
private final boolean _isVersion41;
private final boolean _isVersion40;
private final boolean _isVersion4;
private final boolean _isVersion3;
private final boolean _isVersion2;
private final boolean _isMacOS;
private final boolean _isWindows;
private final boolean _isLinux;
private final boolean _isIPhone;
private final boolean _isIPad;
private final boolean _isUnknownPlatform;
public ERXBasicBrowser(String browserName, String version, String mozillaVersion, String platform, NSDictionary userInfo) {
if (userInfo instanceof NSMutableDictionary) userInfo = new NSDictionary(userInfo);
_userInfo = userInfo != null ? userInfo : null;
_browserName = browserName != null ? browserName : UNKNOWN_BROWSER;
_version = version != null ? version : UNKNOWN_VERSION;
_mozillaVersion = mozillaVersion != null ? mozillaVersion : UNKNOWN_VERSION;
_platform = platform != null ? platform : UNKNOWN_PLATFORM;
String tempCpu = userInfo != null ? (String) userInfo.objectForKey("cpu") : UNKNOWN_CPU;
_cpu = tempCpu != null ? tempCpu : UNKNOWN_CPU;
_geckoRevision = userInfo != null ? (String) userInfo.objectForKey("geckoRevision") : null;
_isRobot = _browserName.equals(ROBOT);
_isICab = _browserName.equals(ICAB);
_isIE = _browserName.equals(IE);
_isEdge = _browserName.equals(EDGE);
_isNetscape = _browserName.equals(NETSCAPE);
_isOmniWeb = _browserName.equals(OMNIWEB);
_isOpera = _browserName.equals(OPERA);
_isSafari = _browserName.equals(SAFARI);
_isFirefox = _browserName.equals(FIREFOX);
_isMozilla = (_browserName.equals(MOZILLA) || _browserName.equals(FIREFOX));
_isChrome = _browserName.equals(CHROME);
_isUnknownBrowser = _browserName.equals(UNKNOWN_BROWSER);
_isMozillaVersion50 = -1 < _mozillaVersion.indexOf("5.0");
_isMozillaVersion45 = (-1 < _mozillaVersion.indexOf("4.5")) || (-1 < _mozillaVersion.indexOf("4.6"))
|| (-1 < _mozillaVersion.indexOf("4.7"));
_isMozillaVersion40 = -1 < _mozillaVersion.indexOf("4.0");
String normalizedVersion = ERXStringUtilities.removeExtraDotsFromVersionString(_version);
_isVersion13 = normalizedVersion.startsWith("13.");
_isVersion12 = normalizedVersion.startsWith("12.");
_isVersion11 = normalizedVersion.startsWith("11.");
_isVersion10 = normalizedVersion.startsWith("10.");
_isVersion9 = normalizedVersion.startsWith("9.");
_isVersion8 = normalizedVersion.startsWith("8.");
_isVersion7 = normalizedVersion.startsWith("7.");
_isVersion6 = normalizedVersion.startsWith("6.");
_isVersion5 = normalizedVersion.startsWith("5.");
_isVersion51 = normalizedVersion.startsWith("5.1");
_isVersion45 = normalizedVersion.startsWith("4.5") || normalizedVersion.startsWith("4.6") || normalizedVersion.startsWith("4.7");
_isVersion41 = normalizedVersion.startsWith("4.1");
_isVersion40 = normalizedVersion.startsWith("4.0");
_isVersion4 = normalizedVersion.startsWith("4.");
_isVersion3 = normalizedVersion.startsWith("3.");
_isVersion2 = normalizedVersion.startsWith("2.");
_isMacOS = _platform.equals(MACOS);
_isWindows = _platform.equals(WINDOWS);
_isLinux = _platform.equals(LINUX);
_isIPhone = _platform.equals(IPHONE);
_isIPad = _platform.equals(IPAD);
_isUnknownPlatform = _platform.equals(UNKNOWN_PLATFORM);
if (_version.equals(UNKNOWN_VERSION)) {
_majorVersion = Integer.valueOf(0);
} else {
String majorVersion = normalizedVersion;
if (majorVersion.indexOf(".") != -1) {
majorVersion = majorVersion.substring(0, majorVersion.indexOf("."));
}
Integer mj = -1;
try {
mj = Integer.valueOf(majorVersion);
} catch (NumberFormatException e) {
log.info("could not determine major version from '{}'", majorVersion, e);
}
_majorVersion = mj;
}
}
@Override
public String browserName() {
return _browserName;
}
@Override
public String version() {
return _version;
}
@Override
public Integer majorVersion() {
return _majorVersion;
}
@Override
public String mozillaVersion() {
return _mozillaVersion;
}
@Override
public String platform() {
return _platform;
}
/**
* <div class="en">
* CPU string
* </div>
*
* <div class="ja">
* ブラウザが動作している CPU を戻します
* </div>
*
* @return <div class="en">what processor that the browser is running on</div>
* <div class="ja">ブラウザが動作している CPU</div>
*/
public String cpu() {
return _cpu;
}
@Override
public NSDictionary userInfo() {
return _userInfo;
}
@Override
public boolean isUnknownBrowser() {
return _isUnknownBrowser;
}
@Override
public boolean isRobot() {
return _isRobot;
}
@Override
public boolean isICab() {
return _isICab;
}
@Override
public boolean isEdge() {
return _isEdge;
}
@Override
public boolean isIE() {
return _isIE;
}
@Override
public boolean isNetscape() {
return _isNetscape;
}
@Override
public boolean isNotNetscape() {
return !_isNetscape;
}
@Override
public boolean isOmniWeb() {
return _isOmniWeb;
}
@Override
public boolean isOpera() {
return _isOpera;
}
@Override
public boolean isSafari() {
return _isSafari;
}
@Override
public boolean isFirefox() {
return _isFirefox;
}
@Override
public boolean isChrome() {
return _isChrome;
}
/**
* <div class="ja">
* Mozilla ですか?
*
* @return Mozilla の場合には true を戻します
* </div>
*/
public boolean isMozilla() {
return _isMozilla;
}
@Override
public boolean isMozilla50Compatible() {
return _isMozillaVersion50;
}
@Override
public boolean isMozilla45Compatible() {
return _isMozillaVersion45;
}
@Override
public boolean isMozilla40Compatible() {
return _isMozillaVersion40 || _isMozillaVersion45;
}
@Override
public boolean isVersion9() {
return _isVersion9;
}
@Override
public boolean isVersion8() {
return _isVersion8;
}
@Override
public boolean isVersion7() {
return _isVersion7;
}
@Override
public boolean isVersion6() {
return _isVersion6;
}
@Override
public boolean isVersion5() {
return _isVersion5;
}
@Override
public boolean isVersion51() {
return _isVersion51;
}
// Netscape 4.5 to 4.7 is very different from 4.0
// NOTE: 4.6 and 4.7 fell into this group
@Override
public boolean isVersion45() {
return _isVersion45;
}
// IE 4.1 for Mac is somewhat different from 4.0
@Override
public boolean isVersion41() {
return _isVersion41;
}
@Override
public boolean isVersion40() {
return _isVersion40;
}
@Override
public boolean isVersion4() {
return _isVersion4;
}
@Override
public boolean isVersion3() {
return _isVersion3;
}
@Override
public boolean isVersion2() {
return _isVersion2;
}
@Override
public boolean isUnknownPlatform() {
return _isUnknownPlatform;
}
@Override
public boolean isMacOS() {
return _isMacOS;
}
@Override
public boolean isWindows() {
return _isWindows;
}
@Override
public boolean isLinux() {
return _isLinux;
}
@Override
public boolean isIPhone() {
return _isIPhone;
}
@Override
public boolean isIPad() {
return _isIPad;
}
/**
* Returns the gecko revision of the browser or {@link ERXBrowser#NO_GECKO}.
*
* @return the gecko revision of the browser or {@link ERXBrowser#NO_GECKO}.
*/
@Override
public String geckoRevision() {
return _geckoRevision;
}
/**
* <div class="en">
* Does the browser support IFrames?
* </div>
*
* <div class="ja">
* ブラウザが iFrames をサポートしていますか?
* </div>
*
* @return <div class="en">true if the browser is IE.</div>
* <div class="ja">iFrames サポートの場合には true を戻します</div>
*/
public boolean isIFrameSupported() {
return isIE();
}
/**
* <div class="en">
* Browser is not netscape or is a version 5 browser.
* </div>
*
* <div class="ja">
* ネストされているテーブルを高速でレンダリング可能?
* Browser is not netscape or is a version 5 browser.
* </div>
*
* @return <div class="en">true if this browser can handle nested tables</div>
* <div class="ja">ネストされているテーブルを高速でレンダリング可能の場合には true を戻します</div>
*/
public boolean willRenderNestedTablesFast() {
return isNotNetscape() || isMozilla50Compatible();
}
/**
* <div class="ja">
* Javascript OnImage ボタンがサポートされていますか?
*
* @return Javascript OnImage ボタンがサポートされている場合には true を戻します
* </div>
*/
public boolean isJavaScriptOnImageButtonSupported() {
return isNotNetscape() || isMozilla50Compatible();
}
}