1 | // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
2 | // Use of this source code is governed by a BSD-style license that can be |
3 | // found in the LICENSE file. |
4 | |
5 | package org.chromium.android_webview; |
6 | |
7 | import android.graphics.Picture; |
8 | import android.os.Handler; |
9 | import android.os.Looper; |
10 | import android.os.Message; |
11 | import android.os.SystemClock; |
12 | |
13 | import org.chromium.content.browser.ContentViewCore; |
14 | |
15 | import java.util.concurrent.Callable; |
16 | |
17 | /** |
18 | * This class is responsible for calling certain client callbacks on the UI thread. |
19 | * |
20 | * Most callbacks do no go through here, but get forwarded to AwContentsClient directly. The |
21 | * messages processed here may originate from the IO or UI thread. |
22 | */ |
23 | class AwContentsClientCallbackHelper { |
24 | |
25 | // TODO(boliu): Consider removing DownloadInfo and LoginRequestInfo by using native |
26 | // MessageLoop to post directly to AwContents. |
27 | |
28 | private static class DownloadInfo { |
29 | final String mUrl; |
30 | final String mUserAgent; |
31 | final String mContentDisposition; |
32 | final String mMimeType; |
33 | final long mContentLength; |
34 | |
35 | DownloadInfo(String url, |
36 | String userAgent, |
37 | String contentDisposition, |
38 | String mimeType, |
39 | long contentLength) { |
40 | mUrl = url; |
41 | mUserAgent = userAgent; |
42 | mContentDisposition = contentDisposition; |
43 | mMimeType = mimeType; |
44 | mContentLength = contentLength; |
45 | } |
46 | } |
47 | |
48 | private static class LoginRequestInfo { |
49 | final String mRealm; |
50 | final String mAccount; |
51 | final String mArgs; |
52 | |
53 | LoginRequestInfo(String realm, String account, String args) { |
54 | mRealm = realm; |
55 | mAccount = account; |
56 | mArgs = args; |
57 | } |
58 | } |
59 | |
60 | private static class OnReceivedErrorInfo { |
61 | final int mErrorCode; |
62 | final String mDescription; |
63 | final String mFailingUrl; |
64 | |
65 | OnReceivedErrorInfo(int errorCode, String description, String failingUrl) { |
66 | mErrorCode = errorCode; |
67 | mDescription = description; |
68 | mFailingUrl = failingUrl; |
69 | } |
70 | } |
71 | |
72 | private final static int MSG_ON_LOAD_RESOURCE = 1; |
73 | private final static int MSG_ON_PAGE_STARTED = 2; |
74 | private final static int MSG_ON_DOWNLOAD_START = 3; |
75 | private final static int MSG_ON_RECEIVED_LOGIN_REQUEST = 4; |
76 | private final static int MSG_ON_RECEIVED_ERROR = 5; |
77 | private final static int MSG_ON_NEW_PICTURE = 6; |
78 | |
79 | // Minimum period allowed between consecutive onNewPicture calls, to rate-limit the callbacks. |
80 | private static final long ON_NEW_PICTURE_MIN_PERIOD_MILLIS = 500; |
81 | // Timestamp of the most recent onNewPicture callback. |
82 | private long mLastPictureTime = 0; |
83 | // True when a onNewPicture callback is currenly in flight. |
84 | private boolean mHasPendingOnNewPicture = false; |
85 | |
86 | private final AwContentsClient mContentsClient; |
87 | |
88 | private final Handler mHandler = new Handler(Looper.getMainLooper()) { |
89 | @Override |
90 | public void handleMessage(Message msg) { |
91 | switch(msg.what) { |
92 | case MSG_ON_LOAD_RESOURCE: { |
93 | final String url = (String) msg.obj; |
94 | mContentsClient.onLoadResource(url); |
95 | break; |
96 | } |
97 | case MSG_ON_PAGE_STARTED: { |
98 | final String url = (String) msg.obj; |
99 | mContentsClient.onPageStarted(url); |
100 | break; |
101 | } |
102 | case MSG_ON_DOWNLOAD_START: { |
103 | DownloadInfo info = (DownloadInfo) msg.obj; |
104 | mContentsClient.onDownloadStart(info.mUrl, info.mUserAgent, |
105 | info.mContentDisposition, info.mMimeType, info.mContentLength); |
106 | break; |
107 | } |
108 | case MSG_ON_RECEIVED_LOGIN_REQUEST: { |
109 | LoginRequestInfo info = (LoginRequestInfo) msg.obj; |
110 | mContentsClient.onReceivedLoginRequest(info.mRealm, info.mAccount, info.mArgs); |
111 | break; |
112 | } |
113 | case MSG_ON_RECEIVED_ERROR: { |
114 | OnReceivedErrorInfo info = (OnReceivedErrorInfo) msg.obj; |
115 | mContentsClient.onReceivedError(info.mErrorCode, info.mDescription, |
116 | info.mFailingUrl); |
117 | break; |
118 | } |
119 | case MSG_ON_NEW_PICTURE: { |
120 | Picture picture = null; |
121 | try { |
122 | if (msg.obj != null) picture = (Picture) ((Callable<?>) msg.obj).call(); |
123 | } catch (Exception e) { |
124 | throw new RuntimeException("Error getting picture", e); |
125 | } |
126 | mContentsClient.onNewPicture(picture); |
127 | mLastPictureTime = SystemClock.uptimeMillis(); |
128 | mHasPendingOnNewPicture = false; |
129 | break; |
130 | } |
131 | default: |
132 | throw new IllegalStateException( |
133 | "AwContentsClientCallbackHelper: unhandled message " + msg.what); |
134 | } |
135 | } |
136 | }; |
137 | |
138 | public AwContentsClientCallbackHelper(AwContentsClient contentsClient) { |
139 | mContentsClient = contentsClient; |
140 | } |
141 | |
142 | public void postOnLoadResource(String url) { |
143 | mHandler.sendMessage(mHandler.obtainMessage(MSG_ON_LOAD_RESOURCE, url)); |
144 | } |
145 | |
146 | public void postOnPageStarted(String url) { |
147 | mHandler.sendMessage(mHandler.obtainMessage(MSG_ON_PAGE_STARTED, url)); |
148 | } |
149 | |
150 | public void postOnDownloadStart(String url, String userAgent, String contentDisposition, |
151 | String mimeType, long contentLength) { |
152 | DownloadInfo info = new DownloadInfo(url, userAgent, contentDisposition, mimeType, |
153 | contentLength); |
154 | mHandler.sendMessage(mHandler.obtainMessage(MSG_ON_DOWNLOAD_START, info)); |
155 | } |
156 | |
157 | public void postOnReceivedLoginRequest(String realm, String account, String args) { |
158 | LoginRequestInfo info = new LoginRequestInfo(realm, account, args); |
159 | mHandler.sendMessage(mHandler.obtainMessage(MSG_ON_RECEIVED_LOGIN_REQUEST, info)); |
160 | } |
161 | |
162 | public void postOnReceivedError(int errorCode, String description, String failingUrl) { |
163 | OnReceivedErrorInfo info = new OnReceivedErrorInfo(errorCode, description, failingUrl); |
164 | mHandler.sendMessage(mHandler.obtainMessage(MSG_ON_RECEIVED_ERROR, info)); |
165 | } |
166 | |
167 | public void postOnNewPicture(Callable<Picture> pictureProvider) { |
168 | if (mHasPendingOnNewPicture) return; |
169 | mHasPendingOnNewPicture = true; |
170 | long pictureTime = java.lang.Math.max(mLastPictureTime + ON_NEW_PICTURE_MIN_PERIOD_MILLIS, |
171 | SystemClock.uptimeMillis()); |
172 | mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ON_NEW_PICTURE, pictureProvider), |
173 | pictureTime); |
174 | } |
175 | } |