зеркало из https://github.com/mozilla/pjs.git
Merge backout; a=me
This commit is contained in:
Коммит
f8e1d233a4
|
@ -92,13 +92,17 @@
|
|||
<field name="tabs" readonly="true">
|
||||
this.tabContainer.childNodes;
|
||||
</field>
|
||||
|
||||
<property name="visibleTabs" readonly="true">
|
||||
<getter><![CDATA[
|
||||
return Array.filter(this.tabs, function(tab) {
|
||||
return !tab.hidden && !tab.closing;
|
||||
});
|
||||
if (!this._visibleTabs)
|
||||
this._visibleTabs = Array.filter(this.tabs,
|
||||
function (tab) !tab.hidden && !tab.closing);
|
||||
return this._visibleTabs;
|
||||
]]></getter>
|
||||
</property>
|
||||
<field name="_visibleTabs">null</field>
|
||||
|
||||
<field name="mURIFixup" readonly="true">
|
||||
Components.classes["@mozilla.org/docshell/urifixup;1"]
|
||||
.getService(Components.interfaces.nsIURIFixup);
|
||||
|
@ -151,9 +155,6 @@
|
|||
false
|
||||
#endif
|
||||
</field>
|
||||
<field name="_browsers">
|
||||
null
|
||||
</field>
|
||||
|
||||
<field name="_autoScrollPopup">
|
||||
null
|
||||
|
@ -1240,8 +1241,6 @@
|
|||
aIsUTF8 = params.isUTF8;
|
||||
}
|
||||
|
||||
this._browsers = null; // invalidate cache
|
||||
|
||||
// if we're adding tabs, we're past interrupt mode, ditch the owner
|
||||
if (this.mCurrentTab.owner)
|
||||
this.mCurrentTab.owner = null;
|
||||
|
@ -1291,10 +1290,11 @@
|
|||
}, 0, this.tabContainer);
|
||||
}
|
||||
|
||||
this.tabContainer.appendChild(t);
|
||||
|
||||
// invalidate cache, because tabContainer is about to change
|
||||
// invalidate caches
|
||||
this._browsers = null;
|
||||
this._visibleTabs = null;
|
||||
|
||||
this.tabContainer.appendChild(t);
|
||||
|
||||
// If this new tab is owned by another, assert that relationship
|
||||
if (aOwner)
|
||||
|
@ -1625,6 +1625,7 @@
|
|||
|
||||
aTab.closing = true;
|
||||
this._removingTabs.push(aTab);
|
||||
this._visibleTabs = null; // invalidate cache
|
||||
if (newTab)
|
||||
this.addTab(BROWSER_NEW_TAB_URL, {skipAnimation: true});
|
||||
else
|
||||
|
@ -2009,7 +2010,10 @@
|
|||
<![CDATA[
|
||||
if (aTab.hidden) {
|
||||
aTab.removeAttribute("hidden");
|
||||
this._visibleTabs = null; // invalidate cache
|
||||
|
||||
this.tabContainer.adjustTabstrip();
|
||||
|
||||
let event = document.createEvent("Events");
|
||||
event.initEvent("TabShow", true, false);
|
||||
aTab.dispatchEvent(event);
|
||||
|
@ -2025,7 +2029,10 @@
|
|||
if (!aTab.hidden && !aTab.pinned && !aTab.selected &&
|
||||
!aTab.closing) {
|
||||
aTab.setAttribute("hidden", "true");
|
||||
this._visibleTabs = null; // invalidate cache
|
||||
|
||||
this.tabContainer.adjustTabstrip();
|
||||
|
||||
let event = document.createEvent("Events");
|
||||
event.initEvent("TabHide", true, false);
|
||||
aTab.dispatchEvent(event);
|
||||
|
@ -2081,6 +2088,7 @@
|
|||
]]>
|
||||
</getter>
|
||||
</property>
|
||||
<field name="_browsers">null</field>
|
||||
|
||||
<!-- Moves a tab to a new browser window, unless it's already the only tab
|
||||
in the current window, in which case this will do nothing. -->
|
||||
|
@ -2126,11 +2134,14 @@
|
|||
|
||||
aIndex = aIndex < aTab._tPos ? aIndex: aIndex+1;
|
||||
this.mCurrentTab._selected = false;
|
||||
|
||||
// invalidate caches
|
||||
this._browsers = null;
|
||||
this._visibleTabs = null;
|
||||
|
||||
// use .item() instead of [] because dragging to the end of the strip goes out of
|
||||
// bounds: .item() returns null (so it acts like appendChild), but [] throws
|
||||
this.tabContainer.insertBefore(aTab, this.tabs.item(aIndex));
|
||||
// invalidate cache, because tabContainer is about to change
|
||||
this._browsers = null;
|
||||
|
||||
for (let i = 0; i < this.tabs.length; i++) {
|
||||
this.tabs[i]._tPos = i;
|
||||
|
|
|
@ -60,6 +60,7 @@ import android.widget.Button;
|
|||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RelativeLayout.LayoutParams;
|
||||
import android.widget.TextView;
|
||||
import android.widget.TextSwitcher;
|
||||
import android.widget.ViewSwitcher.ViewFactory;
|
||||
|
@ -172,6 +173,18 @@ public class BrowserToolbar {
|
|||
|
||||
mFavicon = (ImageButton) mLayout.findViewById(R.id.favicon);
|
||||
mSiteSecurity = (ImageButton) mLayout.findViewById(R.id.site_security);
|
||||
mSiteSecurity.setOnClickListener(new Button.OnClickListener() {
|
||||
public void onClick(View view) {
|
||||
int[] lockLocation = new int[2];
|
||||
view.getLocationOnScreen(lockLocation);
|
||||
LayoutParams lockLayoutParams = (LayoutParams) view.getLayoutParams();
|
||||
|
||||
// Calculate the left margin for the arrow based on the position of the lock icon.
|
||||
int leftMargin = lockLocation[0] - lockLayoutParams.rightMargin;
|
||||
GeckoApp.mSiteIdentityPopup.show(leftMargin);
|
||||
}
|
||||
});
|
||||
|
||||
mProgressSpinner = (AnimationDrawable) resources.getDrawable(R.drawable.progress_spinner);
|
||||
|
||||
mStop = (ImageButton) mLayout.findViewById(R.id.stop);
|
||||
|
@ -322,9 +335,9 @@ public class BrowserToolbar {
|
|||
public void setSecurityMode(String mode) {
|
||||
mTitleCanExpand = false;
|
||||
|
||||
if (mode.equals("identified")) {
|
||||
if (mode.equals(SiteIdentityPopup.IDENTIFIED)) {
|
||||
mSiteSecurity.setImageLevel(1);
|
||||
} else if (mode.equals("verified")) {
|
||||
} else if (mode.equals(SiteIdentityPopup.VERIFIED)) {
|
||||
mSiteSecurity.setImageLevel(2);
|
||||
} else {
|
||||
mSiteSecurity.setImageLevel(0);
|
||||
|
|
|
@ -138,6 +138,7 @@ abstract public class GeckoApp
|
|||
|
||||
public static BrowserToolbar mBrowserToolbar;
|
||||
public static DoorHangerPopup mDoorHangerPopup;
|
||||
public static SiteIdentityPopup mSiteIdentityPopup;
|
||||
public static FormAssistPopup mFormAssistPopup;
|
||||
public Favicons mFavicons;
|
||||
|
||||
|
@ -665,7 +666,7 @@ abstract public class GeckoApp
|
|||
tab.setContentType(contentType);
|
||||
tab.updateFavicon(null);
|
||||
tab.updateFaviconURL(null);
|
||||
tab.updateSecurityMode("unknown");
|
||||
tab.updateIdentityData(null);
|
||||
tab.removeTransientDoorHangers();
|
||||
tab.setHasTouchListeners(false);
|
||||
tab.setCheckerboardColor(Color.WHITE);
|
||||
|
@ -677,7 +678,7 @@ abstract public class GeckoApp
|
|||
if (Tabs.getInstance().isSelectedTab(tab)) {
|
||||
mBrowserToolbar.setTitle(uri);
|
||||
mBrowserToolbar.setFavicon(null);
|
||||
mBrowserToolbar.setSecurityMode("unknown");
|
||||
mBrowserToolbar.setSecurityMode(tab.getSecurityMode());
|
||||
mDoorHangerPopup.updatePopup();
|
||||
mBrowserToolbar.setShadowVisibility(!(tab.getURL().startsWith("about:")));
|
||||
|
||||
|
@ -688,17 +689,17 @@ abstract public class GeckoApp
|
|||
});
|
||||
}
|
||||
|
||||
void handleSecurityChange(final int tabId, final String mode) {
|
||||
void handleSecurityChange(final int tabId, final JSONObject identityData) {
|
||||
final Tab tab = Tabs.getInstance().getTab(tabId);
|
||||
if (tab == null)
|
||||
return;
|
||||
|
||||
tab.updateSecurityMode(mode);
|
||||
tab.updateIdentityData(identityData);
|
||||
|
||||
mMainHandler.post(new Runnable() {
|
||||
public void run() {
|
||||
if (Tabs.getInstance().isSelectedTab(tab))
|
||||
mBrowserToolbar.setSecurityMode(mode);
|
||||
mBrowserToolbar.setSecurityMode(tab.getSecurityMode());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -871,9 +872,9 @@ abstract public class GeckoApp
|
|||
handleLocationChange(tabId, uri, documentURI, contentType, sameDocument);
|
||||
} else if (event.equals("Content:SecurityChange")) {
|
||||
final int tabId = message.getInt("tabID");
|
||||
final String mode = message.getString("mode");
|
||||
Log.i(LOGTAG, "Security Mode - " + mode);
|
||||
handleSecurityChange(tabId, mode);
|
||||
final JSONObject identity = message.getJSONObject("identity");
|
||||
Log.i(LOGTAG, "Security Mode - " + identity.getString("mode"));
|
||||
handleSecurityChange(tabId, identity);
|
||||
} else if (event.equals("Content:StateChange")) {
|
||||
final int tabId = message.getInt("tabID");
|
||||
final String uri = message.getString("uri");
|
||||
|
@ -1204,7 +1205,7 @@ abstract public class GeckoApp
|
|||
return;
|
||||
|
||||
tab.setState("about:home".equals(uri) ? Tab.STATE_SUCCESS : Tab.STATE_LOADING);
|
||||
tab.updateSecurityMode("unknown");
|
||||
tab.updateIdentityData(null);
|
||||
if (Tabs.getInstance().isSelectedTab(tab))
|
||||
getLayerController().getView().getRenderer().resetCheckerboard();
|
||||
mMainHandler.post(new Runnable() {
|
||||
|
@ -1665,6 +1666,7 @@ abstract public class GeckoApp
|
|||
mPluginContainer = (AbsoluteLayout) findViewById(R.id.plugin_container);
|
||||
|
||||
mDoorHangerPopup = new DoorHangerPopup(this);
|
||||
mSiteIdentityPopup = new SiteIdentityPopup(this);
|
||||
mFormAssistPopup = (FormAssistPopup) findViewById(R.id.form_assist_popup);
|
||||
|
||||
Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - UI almost up");
|
||||
|
@ -1955,6 +1957,9 @@ abstract public class GeckoApp
|
|||
// Undo whatever we did in onPause.
|
||||
super.onResume();
|
||||
|
||||
if (mSiteIdentityPopup != null)
|
||||
mSiteIdentityPopup.dismiss();
|
||||
|
||||
int newOrientation = getResources().getConfiguration().orientation;
|
||||
|
||||
if (mOrientation != newOrientation) {
|
||||
|
@ -2081,6 +2086,8 @@ abstract public class GeckoApp
|
|||
mOrientation = newConfig.orientation;
|
||||
if (mFormAssistPopup != null)
|
||||
mFormAssistPopup.hide();
|
||||
if (mSiteIdentityPopup != null)
|
||||
mSiteIdentityPopup.dismiss();
|
||||
refreshActionBar();
|
||||
}
|
||||
}
|
||||
|
@ -2551,6 +2558,11 @@ abstract public class GeckoApp
|
|||
return;
|
||||
}
|
||||
|
||||
if (mSiteIdentityPopup.isShowing()) {
|
||||
mSiteIdentityPopup.dismiss();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mDOMFullScreen) {
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("FullScreen:Exit", null));
|
||||
return;
|
||||
|
|
|
@ -45,6 +45,7 @@ import java.lang.ref.SoftReference;
|
|||
|
||||
import android.content.ContentResolver;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
|
||||
|
@ -115,12 +116,49 @@ class GlobalHistory {
|
|||
GeckoAppShell.notifyUriVisited(uri);
|
||||
}
|
||||
|
||||
// Logic ported from nsNavHistory::CanAddURI.
|
||||
// http://mxr.mozilla.org/mozilla-central/source/toolkit/components/places/nsNavHistory.cpp#1272
|
||||
private boolean canAddURI(String uri) {
|
||||
if (uri == null || uri.length() == 0)
|
||||
return false;
|
||||
|
||||
// First, heck the most common cases (HTTP, HTTPS) to avoid most of the work.
|
||||
if (uri.startsWith("http:") || uri.startsWith("https:"))
|
||||
return true;
|
||||
|
||||
String scheme = Uri.parse(uri).getScheme();
|
||||
if (scheme == null)
|
||||
return false;
|
||||
|
||||
// Now check for all bad things.
|
||||
if (scheme.equals("about") ||
|
||||
scheme.equals("imap") ||
|
||||
scheme.equals("news") ||
|
||||
scheme.equals("mailbox") ||
|
||||
scheme.equals("moz-anno") ||
|
||||
scheme.equals("view-source") ||
|
||||
scheme.equals("chrome") ||
|
||||
scheme.equals("resource") ||
|
||||
scheme.equals("data") ||
|
||||
scheme.equals("wyciwyg") ||
|
||||
scheme.equals("javascript"))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void add(String uri) {
|
||||
if (!canAddURI(uri))
|
||||
return;
|
||||
|
||||
BrowserDB.updateVisitedHistory(GeckoApp.mAppContext.getContentResolver(), uri);
|
||||
addToGeckoOnly(uri);
|
||||
}
|
||||
|
||||
public void update(String uri, String title) {
|
||||
if (!canAddURI(uri))
|
||||
return;
|
||||
|
||||
ContentResolver resolver = GeckoApp.mAppContext.getContentResolver();
|
||||
BrowserDB.updateHistoryTitle(resolver, uri, title);
|
||||
}
|
||||
|
|
|
@ -110,6 +110,7 @@ FENNEC_JAVA_FILES = \
|
|||
sqlite/SQLiteBridgeException.java \
|
||||
RemoteTabs.java \
|
||||
SetupScreen.java \
|
||||
SiteIdentityPopup.java \
|
||||
SurfaceBits.java \
|
||||
Tab.java \
|
||||
Tabs.java \
|
||||
|
@ -272,6 +273,7 @@ RES_LAYOUT = \
|
|||
res/layout/notification_progress_text.xml \
|
||||
res/layout/site_setting_title.xml \
|
||||
res/layout/setup_screen.xml \
|
||||
res/layout/site_identity_popup.xml \
|
||||
res/layout/remote_tabs.xml \
|
||||
res/layout/remote_tabs_child.xml \
|
||||
res/layout/remote_tabs_group.xml \
|
||||
|
@ -366,6 +368,8 @@ RES_DRAWABLE_BASE = \
|
|||
res/drawable/doorhanger_bg.9.png \
|
||||
res/drawable/doorhanger_shadow_bg.9.png \
|
||||
res/drawable/doorhanger_popup_bg.9.png \
|
||||
res/drawable/larry_blue.png \
|
||||
res/drawable/larry_green.png \
|
||||
res/drawable/site_security_identified.png \
|
||||
res/drawable/site_security_verified.png \
|
||||
res/drawable/urlbar_stop.png \
|
||||
|
@ -423,6 +427,8 @@ RES_DRAWABLE_HDPI = \
|
|||
res/drawable-hdpi/doorhanger_bg.9.png \
|
||||
res/drawable-hdpi/doorhanger_shadow_bg.9.png \
|
||||
res/drawable-hdpi/doorhanger_popup_bg.9.png \
|
||||
res/drawable-hdpi/larry_blue.png \
|
||||
res/drawable-hdpi/larry_green.png \
|
||||
res/drawable-hdpi/site_security_identified.png \
|
||||
res/drawable-hdpi/site_security_verified.png \
|
||||
res/drawable-hdpi/urlbar_stop.png \
|
||||
|
@ -492,6 +498,8 @@ RES_DRAWABLE_XHDPI_V11 = \
|
|||
res/drawable-xhdpi-v11/doorhanger_shadow_bg.9.png \
|
||||
res/drawable-xhdpi-v11/doorhanger_popup_bg.9.png \
|
||||
res/drawable-xhdpi-v11/urlbar_stop.png \
|
||||
res/drawable-xhdpi-v11/larry_blue.png \
|
||||
res/drawable-xhdpi-v11/larry_green.png \
|
||||
res/drawable-xhdpi-v11/site_security_identified.png \
|
||||
res/drawable-xhdpi-v11/site_security_verified.png \
|
||||
res/drawable-xhdpi-v11/tabs_button_tail.9.png \
|
||||
|
|
|
@ -0,0 +1,142 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.PopupWindow;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.RelativeLayout.LayoutParams;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONException;
|
||||
|
||||
public class SiteIdentityPopup extends PopupWindow {
|
||||
private static final String LOGTAG = "GeckoSiteIdentityPopup";
|
||||
|
||||
public static final String UNKNOWN = "unknown";
|
||||
public static final String VERIFIED = "verified";
|
||||
public static final String IDENTIFIED = "identified";
|
||||
|
||||
private Context mContext;
|
||||
private Resources mResources;
|
||||
private boolean mInflated;
|
||||
|
||||
private TextView mHost;
|
||||
private TextView mOwner;
|
||||
private TextView mSupplemental;
|
||||
private TextView mVerifier;
|
||||
private TextView mEncrypted;
|
||||
|
||||
private ImageView mLarry;
|
||||
private ImageView mArrow;
|
||||
|
||||
public SiteIdentityPopup(Context aContext) {
|
||||
super(aContext);
|
||||
mContext = aContext;
|
||||
mResources = aContext.getResources();
|
||||
mInflated = false;
|
||||
}
|
||||
|
||||
private void init() {
|
||||
setBackgroundDrawable(new BitmapDrawable());
|
||||
setOutsideTouchable(true);
|
||||
setWindowLayoutMode(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
|
||||
|
||||
LayoutInflater inflater = LayoutInflater.from(mContext);
|
||||
RelativeLayout layout = (RelativeLayout) inflater.inflate(R.layout.site_identity_popup, null);
|
||||
setContentView(layout);
|
||||
|
||||
mHost = (TextView) layout.findViewById(R.id.host);
|
||||
mOwner = (TextView) layout.findViewById(R.id.owner);
|
||||
mSupplemental = (TextView) layout.findViewById(R.id.supplemental);
|
||||
mVerifier = (TextView) layout.findViewById(R.id.verifier);
|
||||
mEncrypted = (TextView) layout.findViewById(R.id.encrypted);
|
||||
|
||||
mLarry = (ImageView) layout.findViewById(R.id.larry);
|
||||
mArrow = (ImageView) layout.findViewById(R.id.arrow);
|
||||
|
||||
mInflated = true;
|
||||
}
|
||||
|
||||
public void show(int leftMargin) {
|
||||
JSONObject identityData = Tabs.getInstance().getSelectedTab().getIdentityData();
|
||||
if (identityData == null) {
|
||||
Log.e(LOGTAG, "Tab has no identity data");
|
||||
return;
|
||||
}
|
||||
|
||||
String mode;
|
||||
try {
|
||||
mode = identityData.getString("mode");
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOGTAG, "Exception trying to get identity mode: " + e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mode.equals(VERIFIED) && !mode.equals(IDENTIFIED)) {
|
||||
Log.e(LOGTAG, "Can't show site identity popup in non-identified state");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mInflated)
|
||||
init();
|
||||
|
||||
try {
|
||||
String host = identityData.getString("host");
|
||||
mHost.setText(host);
|
||||
|
||||
String owner = identityData.getString("owner");
|
||||
mOwner.setText(owner);
|
||||
|
||||
String verifier = identityData.getString("verifier");
|
||||
mVerifier.setText(verifier);
|
||||
|
||||
String encrypted = identityData.getString("encrypted");
|
||||
mEncrypted.setText(encrypted);
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOGTAG, "Exception trying to get identity data: " + e);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
String supplemental = identityData.getString("supplemental");
|
||||
mSupplemental.setText(supplemental);
|
||||
mSupplemental.setVisibility(View.VISIBLE);
|
||||
} catch (JSONException e) {
|
||||
mSupplemental.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
|
||||
if (mode.equals(VERIFIED)) {
|
||||
// Use a blue theme for SSL
|
||||
mLarry.setImageResource(R.drawable.larry_blue);
|
||||
mHost.setTextColor(mResources.getColor(R.color.identity_verified));
|
||||
mOwner.setTextColor(mResources.getColor(R.color.identity_verified));
|
||||
mSupplemental.setTextColor(mResources.getColor(R.color.identity_verified));
|
||||
} else {
|
||||
// Use a green theme for EV
|
||||
mLarry.setImageResource(R.drawable.larry_green);
|
||||
mHost.setTextColor(mResources.getColor(R.color.identity_identified));
|
||||
mOwner.setTextColor(mResources.getColor(R.color.identity_identified));
|
||||
mSupplemental.setTextColor(mResources.getColor(R.color.identity_identified));
|
||||
}
|
||||
|
||||
// Position the mArrow according to lock position
|
||||
LayoutParams layoutParams = (LayoutParams) mArrow.getLayoutParams();
|
||||
LayoutParams newLayoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
|
||||
newLayoutParams.setMargins(leftMargin, layoutParams.topMargin, 0, 0);
|
||||
mArrow.setLayoutParams(newLayoutParams);
|
||||
|
||||
// This will place the popup at the correct vertical position
|
||||
showAsDropDown(GeckoApp.mBrowserToolbar.mSiteSecurity);
|
||||
}
|
||||
}
|
|
@ -77,7 +77,7 @@ public final class Tab {
|
|||
private String mTitle;
|
||||
private Drawable mFavicon;
|
||||
private String mFaviconUrl;
|
||||
private String mSecurityMode;
|
||||
private JSONObject mIdentityData;
|
||||
private Drawable mThumbnail;
|
||||
private List<HistoryEntry> mHistory;
|
||||
private int mHistoryIndex;
|
||||
|
@ -119,7 +119,7 @@ public final class Tab {
|
|||
mTitle = title;
|
||||
mFavicon = null;
|
||||
mFaviconUrl = null;
|
||||
mSecurityMode = "unknown";
|
||||
mIdentityData = null;
|
||||
mThumbnail = null;
|
||||
mHistory = new ArrayList<HistoryEntry>();
|
||||
mHistoryIndex = -1;
|
||||
|
@ -270,7 +270,16 @@ public final class Tab {
|
|||
}
|
||||
|
||||
public String getSecurityMode() {
|
||||
return mSecurityMode;
|
||||
try {
|
||||
return mIdentityData.getString("mode");
|
||||
} catch (Exception e) {
|
||||
// If mIdentityData is null, or we get a JSONException
|
||||
return SiteIdentityPopup.UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
public JSONObject getIdentityData() {
|
||||
return mIdentityData;
|
||||
}
|
||||
|
||||
public boolean isBookmark() {
|
||||
|
@ -368,8 +377,9 @@ public final class Tab {
|
|||
Log.i(LOGTAG, "Updated favicon URL for tab with id: " + mId);
|
||||
}
|
||||
|
||||
public void updateSecurityMode(String mode) {
|
||||
mSecurityMode = mode;
|
||||
|
||||
public void updateIdentityData(JSONObject identityData) {
|
||||
mIdentityData = identityData;
|
||||
}
|
||||
|
||||
private void updateBookmark() {
|
||||
|
|
|
@ -167,8 +167,10 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
|||
// approximation using the Cauchy distribution: multiplier = 15^2 / (age^2 + 15^2).
|
||||
// Using 15 as our scale parameter, we get a constant 15^2 = 225. Following this math,
|
||||
// frecencyScore = numVisits * max(1, 100 * 225 / (age*age + 225)). (See bug 704977)
|
||||
// We also give bookmarks an extra bonus boost by adding 100 points to their frecency score.
|
||||
final String age = "(" + Combined.DATE_LAST_VISITED + " - " + System.currentTimeMillis() + ") / 86400000";
|
||||
final String sortOrder = Combined.VISITS + " * MAX(1, 100 * 225 / (" + age + "*" + age + " + 225)) DESC";
|
||||
final String sortOrder = "(CASE WHEN " + Combined.BOOKMARK_ID + " > -1 THEN 100 ELSE 0 END) + " +
|
||||
Combined.VISITS + " * MAX(1, 100 * 225 / (" + age + "*" + age + " + 225)) DESC";
|
||||
|
||||
Cursor c = cr.query(combinedUriWithLimit(limit),
|
||||
projection,
|
||||
|
|
|
@ -126,3 +126,18 @@
|
|||
<!ENTITY filepicker_audio_title "Choose or record a sound">
|
||||
<!ENTITY filepicker_image_title "Choose or take a picture">
|
||||
<!ENTITY filepicker_video_title "Choose or record a video">
|
||||
|
||||
<!-- Site identity popup -->
|
||||
<!ENTITY identity_connected_to "You are connected to">
|
||||
<!-- Localization note (identity_run_by) : This string appears between a
|
||||
domain name (above) and an organization name (below). E.g.
|
||||
|
||||
example.com
|
||||
which is run by
|
||||
Example Enterprises, Inc.
|
||||
|
||||
The layout of the identity dialog prevents combining this into a single string with
|
||||
substitution variables. If it is difficult to translate the sense of the string
|
||||
with that structure, consider a translation which ignores the preceding domain and
|
||||
just addresses the organization to follow, e.g. "This site is run by " -->
|
||||
<!ENTITY identity_run_by "which is run by">
|
||||
|
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 5.1 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 5.1 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 7.3 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 7.3 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 3.0 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 3.1 KiB |
|
@ -0,0 +1,82 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<RelativeLayout android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dip"
|
||||
android:layout_alignParentTop="true"
|
||||
android:background="@drawable/doorhanger_popup_bg">
|
||||
|
||||
<LinearLayout android:id="@+id/identity_info"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="12dip"
|
||||
android:background="@drawable/doorhanger_bg"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:text="@string/identity_connected_to"/>
|
||||
|
||||
<TextView android:id="@+id/host"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:textStyle="bold"/>
|
||||
|
||||
<TextView android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:text="@string/identity_run_by"/>
|
||||
|
||||
<TextView android:id="@+id/owner"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:textStyle="bold"/>
|
||||
|
||||
<TextView android:id="@+id/supplemental"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"/>
|
||||
|
||||
<TextView android:id="@+id/verifier"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textColor="?android:attr/textColorPrimary"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView android:id="@+id/larry"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="12dip"
|
||||
android:layout_alignParentRight="true"/>
|
||||
|
||||
<TextView android:id="@+id/encrypted"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignBottom="@id/identity_info"
|
||||
android:padding="12dip"
|
||||
android:gravity="right"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textColor="?android:attr/textColorPrimary"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<ImageView android:id="@+id/arrow"
|
||||
android:layout_width="44dip"
|
||||
android:layout_height="16dip"
|
||||
android:layout_marginTop="9dip"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:src="@drawable/doorhanger_arrow"
|
||||
android:scaleType="fitXY"/>
|
||||
|
||||
</RelativeLayout>
|
|
@ -6,5 +6,7 @@
|
|||
<color name="splash_content">#ffffff</color>
|
||||
<color name="doorhanger_link">#ACC4D5</color>
|
||||
<color name="validation_message_text">#ffffff</color>
|
||||
<color name="identity_verified">#77BAFF</color>
|
||||
<color name="identity_identified">#B7D46A</color>
|
||||
</resources>
|
||||
|
||||
|
|
|
@ -148,4 +148,8 @@
|
|||
|
||||
<string name="bookmarkdefaults_title_abouthome">@bookmarks_aboutHome@</string>
|
||||
<string name="bookmarkdefaults_url_abouthome">about:home</string>
|
||||
|
||||
<!-- Site identity popup -->
|
||||
<string name="identity_connected_to">&identity_connected_to;</string>
|
||||
<string name="identity_run_by">&identity_run_by;</string>
|
||||
</resources>
|
||||
|
|
|
@ -227,8 +227,14 @@ abstract class Axis {
|
|||
* possible and this axis has not been scroll locked while panning. Otherwise, returns false.
|
||||
*/
|
||||
private boolean scrollable() {
|
||||
return getViewportLength() <= getPageLength() - MIN_SCROLLABLE_DISTANCE &&
|
||||
!mScrollingDisabled;
|
||||
// If we're scrolling a subdocument, ignore the viewport length restrictions (since those
|
||||
// apply to the top-level document) and only take into account axis locking.
|
||||
if (mSubscroller.scrolling()) {
|
||||
return !mScrollingDisabled;
|
||||
} else {
|
||||
return getViewportLength() <= getPageLength() - MIN_SCROLLABLE_DISTANCE &&
|
||||
!mScrollingDisabled;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -305,8 +311,9 @@ abstract class Axis {
|
|||
|
||||
// Performs displacement of the viewport position according to the current velocity.
|
||||
void displace() {
|
||||
if (!mSubscroller.scrolling() && !scrollable())
|
||||
if (!scrollable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mFlingState == FlingStates.PANNING)
|
||||
mDisplacement += (mLastTouchPos - mTouchPos) * getEdgeResistance();
|
||||
|
|
|
@ -597,14 +597,14 @@ public class PanZoomController
|
|||
return getVelocity() < STOPPED_THRESHOLD;
|
||||
}
|
||||
|
||||
PointF getDisplacement() {
|
||||
PointF resetDisplacement() {
|
||||
return new PointF(mX.resetDisplacement(), mY.resetDisplacement());
|
||||
}
|
||||
|
||||
private void updatePosition() {
|
||||
mX.displace();
|
||||
mY.displace();
|
||||
PointF displacement = getDisplacement();
|
||||
PointF displacement = resetDisplacement();
|
||||
if (FloatUtils.fuzzyEquals(displacement.x, 0.0f) && FloatUtils.fuzzyEquals(displacement.y, 0.0f)) {
|
||||
return;
|
||||
}
|
||||
|
@ -947,12 +947,6 @@ public class PanZoomController
|
|||
sendPointToGecko("Gesture:LongPress", motionEvent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDown(MotionEvent motionEvent) {
|
||||
sendPointToGecko("Gesture:ShowPress", motionEvent);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSingleTapConfirmed(MotionEvent motionEvent) {
|
||||
GeckoApp.mFormAssistPopup.hide();
|
||||
|
|
|
@ -57,15 +57,31 @@ class SubdocumentScrollHelper implements GeckoEventListener {
|
|||
private final PanZoomController mPanZoomController;
|
||||
private final Handler mUiHandler;
|
||||
|
||||
/* This is the amount of displacement we have accepted but not yet sent to JS; this is
|
||||
* only valid when mOverrideScrollPending is true. */
|
||||
private final PointF mPendingDisplacement;
|
||||
|
||||
/* When this is true, we're sending scroll events to JS to scroll the active subdocument. */
|
||||
private boolean mOverridePanning;
|
||||
|
||||
/* When this is true, we have received an ack for the last scroll event we sent to JS, and
|
||||
* are ready to send the next scroll event. Note we only ever have one scroll event inflight
|
||||
* at a time. */
|
||||
private boolean mOverrideScrollAck;
|
||||
|
||||
/* When this is true, we have a pending scroll that we need to send to JS; we were unable
|
||||
* to send it when it was initially requested because mOverrideScrollAck was not true. */
|
||||
private boolean mOverrideScrollPending;
|
||||
|
||||
/* When this is true, the last scroll event we sent actually did some amount of scrolling on
|
||||
* the subdocument; we use this to decide when we have reached the end of the subdocument. */
|
||||
private boolean mScrollSucceeded;
|
||||
|
||||
SubdocumentScrollHelper(PanZoomController controller) {
|
||||
mPanZoomController = controller;
|
||||
// mUiHandler will be bound to the UI thread since that's where this constructor runs
|
||||
mUiHandler = new Handler();
|
||||
mPendingDisplacement = new PointF();
|
||||
|
||||
GeckoAppShell.registerGeckoEventListener(MESSAGE_PANNING_OVERRIDE, this);
|
||||
GeckoAppShell.registerGeckoEventListener(MESSAGE_CANCEL_OVERRIDE, this);
|
||||
|
@ -79,12 +95,11 @@ class SubdocumentScrollHelper implements GeckoEventListener {
|
|||
|
||||
if (! mOverrideScrollAck) {
|
||||
mOverrideScrollPending = true;
|
||||
mPendingDisplacement.x += displacement.x;
|
||||
mPendingDisplacement.y += displacement.y;
|
||||
return true;
|
||||
}
|
||||
|
||||
mOverrideScrollAck = false;
|
||||
mOverrideScrollPending = false;
|
||||
|
||||
JSONObject json = new JSONObject();
|
||||
try {
|
||||
json.put("x", displacement.x);
|
||||
|
@ -94,6 +109,13 @@ class SubdocumentScrollHelper implements GeckoEventListener {
|
|||
}
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent(MESSAGE_SCROLL, json.toString()));
|
||||
|
||||
mOverrideScrollAck = false;
|
||||
mOverrideScrollPending = false;
|
||||
// clear the |mPendingDisplacement| after serializing |displacement| to
|
||||
// JSON because they might be the same object
|
||||
mPendingDisplacement.x = 0;
|
||||
mPendingDisplacement.y = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -128,7 +150,7 @@ class SubdocumentScrollHelper implements GeckoEventListener {
|
|||
mOverrideScrollAck = true;
|
||||
mScrollSucceeded = message.getBoolean("scrolled");
|
||||
if (mOverridePanning && mOverrideScrollPending) {
|
||||
scrollBy(mPanZoomController.getDisplacement());
|
||||
scrollBy(mPendingDisplacement);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
|
|
@ -81,6 +81,7 @@ let Downloads = {
|
|||
switch (aTopic) {
|
||||
case "dl-failed":
|
||||
case "dl-cancel":
|
||||
break;
|
||||
case "dl-done":
|
||||
if (!this._getElementForDownload(download.id)) {
|
||||
let item = this._createItem(downloadTemplate, {
|
||||
|
@ -102,10 +103,10 @@ let Downloads = {
|
|||
this._stmt.finalize();
|
||||
|
||||
this._stmt = this._dlmgr.DBConnection.createStatement(
|
||||
"SELECT id, name, source, state, startTime, endTime, referrer, " +
|
||||
"currBytes, maxBytes, state IN (?1, ?2, ?3, ?4, ?5) isActive " +
|
||||
"SELECT id, name, source, startTime, endTime, referrer, " +
|
||||
"currBytes, maxBytes " +
|
||||
"FROM moz_downloads " +
|
||||
"WHERE NOT isActive " +
|
||||
"WHERE state = :download_state " +
|
||||
"ORDER BY endTime DESC");
|
||||
},
|
||||
|
||||
|
@ -177,11 +178,7 @@ let Downloads = {
|
|||
clearTimeout(this._timeoutID);
|
||||
|
||||
this._stmt.reset();
|
||||
this._stmt.bindInt32Parameter(0, Ci.nsIDownloadManager.DOWNLOAD_NOTSTARTED);
|
||||
this._stmt.bindInt32Parameter(1, Ci.nsIDownloadManager.DOWNLOAD_DOWNLOADING);
|
||||
this._stmt.bindInt32Parameter(2, Ci.nsIDownloadManager.DOWNLOAD_PAUSED);
|
||||
this._stmt.bindInt32Parameter(3, Ci.nsIDownloadManager.DOWNLOAD_QUEUED);
|
||||
this._stmt.bindInt32Parameter(4, Ci.nsIDownloadManager.DOWNLOAD_SCANNING);
|
||||
this._stmt.params.download_state = Ci.nsIDownloadManager.DOWNLOAD_FINISHED;
|
||||
|
||||
// Take a quick break before we actually start building the list
|
||||
let self = this;
|
||||
|
|
|
@ -2213,6 +2213,8 @@ Tab.prototype = {
|
|||
if (contentWin != contentWin.top)
|
||||
return;
|
||||
|
||||
this._hostChanged = true;
|
||||
|
||||
let browser = BrowserApp.getBrowserForWindow(contentWin);
|
||||
let uri = browser.currentURI.spec;
|
||||
let documentURI = "";
|
||||
|
@ -2249,26 +2251,29 @@ Tab.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
// Properties used to cache security state used to update the UI
|
||||
_state: null,
|
||||
_hostChanged: false, // onLocationChange will flip this bit
|
||||
|
||||
onSecurityChange: function(aWebProgress, aRequest, aState) {
|
||||
let mode = "unknown";
|
||||
if (aState & Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL)
|
||||
mode = "identified";
|
||||
else if (aState & Ci.nsIWebProgressListener.STATE_SECURE_HIGH)
|
||||
mode = "verified";
|
||||
else if (aState & Ci.nsIWebProgressListener.STATE_IS_BROKEN)
|
||||
mode = "mixed";
|
||||
else
|
||||
mode = "unknown";
|
||||
// Don't need to do anything if the data we use to update the UI hasn't changed
|
||||
if (this._state == aState && !this._hostChanged)
|
||||
return;
|
||||
|
||||
this._state = aState;
|
||||
this._hostChanged = false;
|
||||
|
||||
let identity = IdentityHandler.checkIdentity(aState, this.browser);
|
||||
|
||||
let message = {
|
||||
gecko: {
|
||||
type: "Content:SecurityChange",
|
||||
tabID: this.id,
|
||||
mode: mode
|
||||
identity: identity
|
||||
}
|
||||
};
|
||||
|
||||
sendMessageToJava(message);
|
||||
sendMessageToJava(message);
|
||||
},
|
||||
|
||||
onProgressChange: function(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress) {
|
||||
|
@ -2521,13 +2526,44 @@ Tab.prototype = {
|
|||
var BrowserEventHandler = {
|
||||
init: function init() {
|
||||
Services.obs.addObserver(this, "Gesture:SingleTap", false);
|
||||
Services.obs.addObserver(this, "Gesture:ShowPress", false);
|
||||
Services.obs.addObserver(this, "Gesture:CancelTouch", false);
|
||||
Services.obs.addObserver(this, "Gesture:DoubleTap", false);
|
||||
Services.obs.addObserver(this, "Gesture:Scroll", false);
|
||||
Services.obs.addObserver(this, "dom-touch-listener-added", false);
|
||||
|
||||
BrowserApp.deck.addEventListener("DOMUpdatePageReport", PopupBlockerObserver.onUpdatePageReport, false);
|
||||
BrowserApp.deck.addEventListener("touchstart", this, false);
|
||||
},
|
||||
|
||||
handleEvent: function(aEvent) {
|
||||
if (!BrowserApp.isBrowserContentDocumentDisplayed() || aEvent.touches.length > 1 || aEvent.defaultPrevented)
|
||||
return;
|
||||
|
||||
let closest = aEvent.target;
|
||||
|
||||
if (closest) {
|
||||
// If we've pressed a scrollable element, let Java know that we may
|
||||
// want to override the scroll behaviour (for document sub-frames)
|
||||
this._scrollableElement = this._findScrollableElement(closest, true);
|
||||
this._firstScrollEvent = true;
|
||||
|
||||
if (this._scrollableElement != null) {
|
||||
// Discard if it's the top-level scrollable, we let Java handle this
|
||||
let doc = BrowserApp.selectedBrowser.contentDocument;
|
||||
if (this._scrollableElement != doc.body && this._scrollableElement != doc.documentElement)
|
||||
sendMessageToJava({ gecko: { type: "Panning:Override" } });
|
||||
}
|
||||
}
|
||||
|
||||
if (!ElementTouchHelper.isElementClickable(closest))
|
||||
closest = ElementTouchHelper.elementFromPoint(BrowserApp.selectedBrowser.contentWindow,
|
||||
aEvent.changedTouches[0].screenX,
|
||||
aEvent.changedTouches[0].screenY);
|
||||
if (!closest)
|
||||
closest = aEvent.target;
|
||||
|
||||
if (closest)
|
||||
this._doTapHighlight(closest);
|
||||
},
|
||||
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
|
@ -2562,12 +2598,19 @@ var BrowserEventHandler = {
|
|||
// the user wanted, and neither can any non-root sub-frame, cancel the
|
||||
// override so that Java can handle panning the main document.
|
||||
let data = JSON.parse(aData);
|
||||
|
||||
// round the scroll amounts because they come in as floats and might be
|
||||
// subject to minor rounding errors because of zoom values. I've seen values
|
||||
// like 0.99 come in here and get truncated to 0; this avoids that problem.
|
||||
data.x = Math.round(data.x);
|
||||
data.y = Math.round(data.y);
|
||||
|
||||
if (this._firstScrollEvent) {
|
||||
while (this._scrollableElement != null && !this._elementCanScroll(this._scrollableElement, data.x, data.y))
|
||||
this._scrollableElement = this._findScrollableElement(this._scrollableElement, false);
|
||||
|
||||
let doc = BrowserApp.selectedBrowser.contentDocument;
|
||||
if (this._scrollableElement == doc.body || this._scrollableElement == doc.documentElement) {
|
||||
if (this._scrollableElement == null || this._scrollableElement == doc.body || this._scrollableElement == doc.documentElement) {
|
||||
sendMessageToJava({ gecko: { type: "Panning:CancelOverride" } });
|
||||
return;
|
||||
}
|
||||
|
@ -2584,26 +2627,6 @@ var BrowserEventHandler = {
|
|||
}
|
||||
} else if (aTopic == "Gesture:CancelTouch") {
|
||||
this._cancelTapHighlight();
|
||||
} else if (aTopic == "Gesture:ShowPress") {
|
||||
let data = JSON.parse(aData);
|
||||
let closest = ElementTouchHelper.elementFromPoint(BrowserApp.selectedBrowser.contentWindow, data.x, data.y);
|
||||
if (!closest)
|
||||
closest = ElementTouchHelper.anyElementFromPoint(BrowserApp.selectedBrowser.contentWindow, data.x, data.y);
|
||||
if (closest) {
|
||||
this._doTapHighlight(closest);
|
||||
|
||||
// If we've pressed a scrollable element, let Java know that we may
|
||||
// want to override the scroll behaviour (for document sub-frames)
|
||||
this._scrollableElement = this._findScrollableElement(closest, true);
|
||||
this._firstScrollEvent = true;
|
||||
|
||||
if (this._scrollableElement != null) {
|
||||
// Discard if it's the top-level scrollable, we let Java handle this
|
||||
let doc = BrowserApp.selectedBrowser.contentDocument;
|
||||
if (this._scrollableElement != doc.body && this._scrollableElement != doc.documentElement)
|
||||
sendMessageToJava({ gecko: { type: "Panning:Override" } });
|
||||
}
|
||||
}
|
||||
} else if (aTopic == "Gesture:SingleTap") {
|
||||
let element = this._highlightElement;
|
||||
if (element && !SelectHelper.handleClick(element)) {
|
||||
|
@ -2824,24 +2847,11 @@ var BrowserEventHandler = {
|
|||
},
|
||||
|
||||
_elementCanScroll: function(elem, x, y) {
|
||||
let scrollX = true;
|
||||
let scrollY = true;
|
||||
let scrollX = (x < 0 && elem.scrollLeft > 0)
|
||||
|| (x > 0 && elem.scrollLeft < (elem.scrollWidth - elem.clientWidth));
|
||||
|
||||
if (x < 0) {
|
||||
if (elem.scrollLeft <= 0) {
|
||||
scrollX = false;
|
||||
}
|
||||
} else if (elem.scrollLeft >= (elem.scrollWidth - elem.clientWidth)) {
|
||||
scrollX = false;
|
||||
}
|
||||
|
||||
if (y < 0) {
|
||||
if (elem.scrollTop <= 0) {
|
||||
scrollY = false;
|
||||
}
|
||||
} else if (elem.scrollTop >= (elem.scrollHeight - elem.clientHeight)) {
|
||||
scrollY = false;
|
||||
}
|
||||
let scrollY = (y < 0 && elem.scrollTop > 0)
|
||||
|| (y > 0 && elem.scrollTop < (elem.scrollHeight - elem.clientHeight));
|
||||
|
||||
return scrollX || scrollY;
|
||||
}
|
||||
|
@ -3593,14 +3603,11 @@ var ViewportHandler = {
|
|||
if (doctype && /(WAP|WML|Mobile)/.test(doctype.publicId))
|
||||
return { defaultZoom: 1, autoSize: true, allowZoom: true, autoScale: true };
|
||||
|
||||
let windowUtils = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
|
||||
let handheldFriendly = windowUtils.getDocumentMetadata("HandheldFriendly");
|
||||
if (handheldFriendly == "true")
|
||||
return { defaultZoom: 1, autoSize: true, allowZoom: true, autoScale: true };
|
||||
|
||||
if (aWindow.document instanceof XULDocument)
|
||||
return { defaultZoom: 1, autoSize: true, allowZoom: false, autoScale: false };
|
||||
|
||||
let windowUtils = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
|
||||
|
||||
// viewport details found here
|
||||
// http://developer.apple.com/safari/library/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/MetaTags.html
|
||||
// http://developer.apple.com/safari/library/documentation/AppleApplications/Reference/SafariWebContent/UsingtheViewport/UsingtheViewport.html
|
||||
|
@ -3619,6 +3626,15 @@ var ViewportHandler = {
|
|||
let allowZoomStr = windowUtils.getDocumentMetadata("viewport-user-scalable");
|
||||
let allowZoom = !/^(0|no|false)$/.test(allowZoomStr); // WebKit allows 0, "no", or "false"
|
||||
|
||||
|
||||
if (scale == NaN && minScale == NaN && maxScale == NaN && allowZoomStr == "" && widthStr == "" && heightStr == "") {
|
||||
// Only check for HandheldFriendly if we don't have a viewport meta tag
|
||||
let handheldFriendly = windowUtils.getDocumentMetadata("HandheldFriendly");
|
||||
|
||||
if (handheldFriendly == "true")
|
||||
return { defaultZoom: 1, autoSize: true, allowZoom: true, autoScale: true };
|
||||
}
|
||||
|
||||
scale = this.clamp(scale, kViewportMinScale, kViewportMaxScale);
|
||||
minScale = this.clamp(minScale, kViewportMinScale, kViewportMaxScale);
|
||||
maxScale = this.clamp(maxScale, kViewportMinScale, kViewportMaxScale);
|
||||
|
@ -4605,6 +4621,159 @@ var CharacterEncoding = {
|
|||
}
|
||||
};
|
||||
|
||||
var IdentityHandler = {
|
||||
// Mode strings used to control CSS display
|
||||
IDENTITY_MODE_IDENTIFIED : "identified", // High-quality identity information
|
||||
IDENTITY_MODE_DOMAIN_VERIFIED : "verified", // Minimal SSL CA-signed domain verification
|
||||
IDENTITY_MODE_UNKNOWN : "unknown", // No trusted identity information
|
||||
|
||||
// Cache the most recent SSLStatus and Location seen in getIdentityStrings
|
||||
_lastStatus : null,
|
||||
_lastLocation : null,
|
||||
|
||||
/**
|
||||
* Helper to parse out the important parts of _lastStatus (of the SSL cert in
|
||||
* particular) for use in constructing identity UI strings
|
||||
*/
|
||||
getIdentityData : function() {
|
||||
let result = {};
|
||||
let status = this._lastStatus.QueryInterface(Components.interfaces.nsISSLStatus);
|
||||
let cert = status.serverCert;
|
||||
|
||||
// Human readable name of Subject
|
||||
result.subjectOrg = cert.organization;
|
||||
|
||||
// SubjectName fields, broken up for individual access
|
||||
if (cert.subjectName) {
|
||||
result.subjectNameFields = {};
|
||||
cert.subjectName.split(",").forEach(function(v) {
|
||||
let field = v.split("=");
|
||||
this[field[0]] = field[1];
|
||||
}, result.subjectNameFields);
|
||||
|
||||
// Call out city, state, and country specifically
|
||||
result.city = result.subjectNameFields.L;
|
||||
result.state = result.subjectNameFields.ST;
|
||||
result.country = result.subjectNameFields.C;
|
||||
}
|
||||
|
||||
// Human readable name of Certificate Authority
|
||||
result.caOrg = cert.issuerOrganization || cert.issuerCommonName;
|
||||
result.cert = cert;
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
getIdentityMode: function getIdentityMode(aState) {
|
||||
if (aState & Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL)
|
||||
return this.IDENTITY_MODE_IDENTIFIED;
|
||||
|
||||
if (aState & Ci.nsIWebProgressListener.STATE_SECURE_HIGH)
|
||||
return this.IDENTITY_MODE_DOMAIN_VERIFIED;
|
||||
|
||||
return this.IDENTITY_MODE_UNKNOWN;
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine the identity of the page being displayed by examining its SSL cert
|
||||
* (if available). Return the data needed to update the UI.
|
||||
*/
|
||||
checkIdentity: function checkIdentity(aState, aBrowser) {
|
||||
this._lastStatus = aBrowser.securityUI
|
||||
.QueryInterface(Components.interfaces.nsISSLStatusProvider)
|
||||
.SSLStatus;
|
||||
|
||||
// Don't pass in the actual location object, since it can cause us to
|
||||
// hold on to the window object too long. Just pass in the fields we
|
||||
// care about. (bug 424829)
|
||||
let locationObj = {};
|
||||
try {
|
||||
let location = aBrowser.contentWindow.location;
|
||||
locationObj.host = location.host;
|
||||
locationObj.hostname = location.hostname;
|
||||
locationObj.port = location.port;
|
||||
} catch (ex) {
|
||||
// Can sometimes throw if the URL being visited has no host/hostname,
|
||||
// e.g. about:blank. The _state for these pages means we won't need these
|
||||
// properties anyways, though.
|
||||
}
|
||||
this._lastLocation = locationObj;
|
||||
|
||||
let mode = this.getIdentityMode(aState);
|
||||
let result = { mode: mode };
|
||||
|
||||
// We can't to do anything else for pages without identity data
|
||||
if (mode == this.IDENTITY_MODE_UNKNOWN)
|
||||
return result;
|
||||
|
||||
// Ideally we'd just make this a Java string
|
||||
result.encrypted = Strings.browser.GetStringFromName("identity.encrypted2");
|
||||
result.host = this.getEffectiveHost();
|
||||
|
||||
let iData = this.getIdentityData();
|
||||
result.verifier = Strings.browser.formatStringFromName("identity.identified.verifier", [iData.caOrg], 1);
|
||||
|
||||
// If the cert is identified, then we can populate the results with credentials
|
||||
if (mode == this.IDENTITY_MODE_IDENTIFIED) {
|
||||
result.owner = iData.subjectOrg;
|
||||
|
||||
// Build an appropriate supplemental block out of whatever location data we have
|
||||
let supplemental = "";
|
||||
if (iData.city)
|
||||
supplemental += iData.city + "\n";
|
||||
if (iData.state && iData.country)
|
||||
supplemental += Strings.browser.formatStringFromName("identity.identified.state_and_country", [iData.state, iData.country], 2);
|
||||
else if (iData.state) // State only
|
||||
supplemental += iData.state;
|
||||
else if (iData.country) // Country only
|
||||
supplemental += iData.country;
|
||||
result.supplemental = supplemental;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Otherwise, we don't know the cert owner
|
||||
result.owner = Strings.browser.GetStringFromName("identity.ownerUnknown2");
|
||||
|
||||
// Cache the override service the first time we need to check it
|
||||
if (!this._overrideService)
|
||||
this._overrideService = Cc["@mozilla.org/security/certoverride;1"].getService(Ci.nsICertOverrideService);
|
||||
|
||||
// Check whether this site is a security exception. XPConnect does the right
|
||||
// thing here in terms of converting _lastLocation.port from string to int, but
|
||||
// the overrideService doesn't like undefined ports, so make sure we have
|
||||
// something in the default case (bug 432241).
|
||||
// .hostname can return an empty string in some exceptional cases -
|
||||
// hasMatchingOverride does not handle that, so avoid calling it.
|
||||
// Updating the tooltip value in those cases isn't critical.
|
||||
// FIXME: Fixing bug 646690 would probably makes this check unnecessary
|
||||
if (this._lastLocation.hostname &&
|
||||
this._overrideService.hasMatchingOverride(this._lastLocation.hostname,
|
||||
(this._lastLocation.port || 443),
|
||||
iData.cert, {}, {}))
|
||||
result.verifier = Strings.browser.GetStringFromName("identity.identified.verified_by_you");
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the eTLD+1 version of the current hostname
|
||||
*/
|
||||
getEffectiveHost: function getEffectiveHost() {
|
||||
if (!this._IDNService)
|
||||
this._IDNService = Cc["@mozilla.org/network/idn-service;1"]
|
||||
.getService(Ci.nsIIDNService);
|
||||
try {
|
||||
let baseDomain = Services.eTLD.getBaseDomainFromHost(this._lastLocation.hostname);
|
||||
return this._IDNService.convertToDisplayIDN(baseDomain, {});
|
||||
} catch (e) {
|
||||
// If something goes wrong (e.g. hostname is an IP address) just fail back
|
||||
// to the full domain.
|
||||
return this._lastLocation.hostname;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function OverscrollController(aTab) {
|
||||
this.tab = aTab;
|
||||
}
|
||||
|
|
|
@ -75,8 +75,6 @@ identity.identified.verified_by_you=You have added a security exception for this
|
|||
identity.identified.state_and_country=%S, %S
|
||||
identity.identified.title_with_country=%S (%S)
|
||||
identity.encrypted2=Encrypted
|
||||
identity.unencrypted2=Not encrypted
|
||||
identity.unknown.tooltip=This website does not supply identity information.
|
||||
identity.ownerUnknown2=(unknown)
|
||||
|
||||
# Geolocation UI
|
||||
|
|
|
@ -46,3 +46,55 @@ class TestSwitchFrame(MarionetteTestCase):
|
|||
self.assertEqual("Marionette IFrame Test", self.marionette.execute_script("return window.document.title;"))
|
||||
self.marionette.switch_to_frame("test_iframe")
|
||||
self.assertTrue("test.html" in self.marionette.get_url())
|
||||
|
||||
def test_switch_nested(self):
|
||||
self.assertTrue(self.marionette.execute_script("window.location.href = 'about:blank'; return true;"))
|
||||
self.assertEqual("about:blank", self.marionette.execute_script("return window.location.href;"))
|
||||
test_html = self.marionette.absolute_url("test_nested_iframe.html")
|
||||
self.marionette.navigate(test_html)
|
||||
self.assertNotEqual("about:blank", self.marionette.execute_script("return window.location.href;"))
|
||||
self.assertEqual("Marionette IFrame Test", self.marionette.execute_script("return window.document.title;"))
|
||||
self.marionette.switch_to_frame("test_iframe")
|
||||
self.assertTrue("test_inner_iframe.html" in self.marionette.get_url())
|
||||
self.marionette.switch_to_frame("inner_frame")
|
||||
self.assertTrue("test.html" in self.marionette.get_url())
|
||||
self.marionette.switch_to_frame() # go back to main frame
|
||||
self.assertTrue("test_nested_iframe.html" in self.marionette.get_url())
|
||||
#test that we're using the right window object server-side
|
||||
self.assertTrue("test_nested_iframe.html" in self.marionette.execute_script("return window.location.href;"))
|
||||
|
||||
class TestSwitchFrameChrome(MarionetteTestCase):
|
||||
def setUp(self):
|
||||
MarionetteTestCase.setUp(self)
|
||||
self.marionette.set_context("chrome")
|
||||
self.win = self.marionette.get_window()
|
||||
#need to get the file:// path for xul
|
||||
unit = os.path.abspath(os.path.join(os.path.realpath(__file__), os.path.pardir))
|
||||
tests = os.path.abspath(os.path.join(unit, os.path.pardir))
|
||||
mpath = os.path.abspath(os.path.join(tests, os.path.pardir))
|
||||
xul = "file://" + os.path.join(mpath, "www", "test.xul")
|
||||
self.marionette.execute_script("window.open('" + xul +"', '_blank', 'chrome,centerscreen');")
|
||||
|
||||
def tearDown(self):
|
||||
self.marionette.execute_script("window.close();")
|
||||
self.marionette.switch_to_window(self.win)
|
||||
MarionetteTestCase.tearDown(self)
|
||||
|
||||
def test_switch_simple(self):
|
||||
self.assertTrue("test.xul" in self.marionette.get_url())
|
||||
self.marionette.switch_to_frame(0)
|
||||
self.assertTrue("test2.xul" in self.marionette.get_url())
|
||||
self.marionette.switch_to_frame()
|
||||
self.assertTrue("test.xul" in self.marionette.get_url())
|
||||
self.marionette.switch_to_frame("iframe")
|
||||
self.assertTrue("test2.xul" in self.marionette.get_url())
|
||||
self.marionette.switch_to_frame()
|
||||
self.assertTrue("test.xul" in self.marionette.get_url())
|
||||
self.marionette.switch_to_frame("iframename")
|
||||
self.assertTrue("test2.xul" in self.marionette.get_url())
|
||||
self.marionette.switch_to_frame()
|
||||
self.assertTrue("test.xul" in self.marionette.get_url())
|
||||
|
||||
#I can't seem to access a xul iframe within a xul iframe
|
||||
def test_switch_nested(self):
|
||||
pass
|
||||
|
|
|
@ -12,4 +12,6 @@
|
|||
<textbox id="textInput3" class="asdf" size="6" value="test" label="input" />
|
||||
</vbox>
|
||||
|
||||
<iframe id="iframe" name="iframename" src="test2.xul"/>
|
||||
<iframe id="iframe" name="iframename" src="test_nested_iframe.xul"/>
|
||||
</dialog>
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0"?>
|
||||
<!DOCTYPE window [
|
||||
]>
|
||||
|
||||
<dialog id="dia"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<vbox id="things">
|
||||
<checkbox id="testBox" label="box" />
|
||||
<textbox id="textInput" size="6" value="test" label="input" />
|
||||
<textbox id="textInput2" size="6" value="test" label="input" />
|
||||
<textbox id="textInput3" class="asdf" size="6" value="test" label="input" />
|
||||
</vbox>
|
||||
|
||||
</dialog>
|
|
@ -0,0 +1,9 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Inner Iframe</title>
|
||||
</head>
|
||||
<body>
|
||||
<iframe src="test.html" id="inner_frame"></iframe>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,9 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Marionette IFrame Test</title>
|
||||
</head>
|
||||
<body>
|
||||
<iframe src="test_inner_iframe.html" id="test_iframe"></iframe>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0"?>
|
||||
<!DOCTYPE window [
|
||||
]>
|
||||
|
||||
<iframe id="iframe" name="iframename" src="test2.xul"/>
|
||||
</dialog>
|
|
@ -119,6 +119,8 @@ function MarionetteDriverActor(aConnection)
|
|||
this.timer = null;
|
||||
this.marionetteLog = new MarionetteLogObj();
|
||||
this.command_id = null;
|
||||
this.mainFrame = null; //topmost chrome frame
|
||||
this.curFrame = null; //subframe that currently has focus
|
||||
|
||||
//register all message listeners
|
||||
this.messageManager.addMessageListener("Marionette:ok", this);
|
||||
|
@ -219,10 +221,15 @@ MarionetteDriverActor.prototype = {
|
|||
*/
|
||||
getCurrentWindow: function MDA_getCurrentWindow() {
|
||||
let type = null;
|
||||
if (appName != "B2G" && this.context == "content") {
|
||||
type = 'navigator:browser';
|
||||
if (this.curFrame == null) {
|
||||
if (appName != "B2G" && this.context == "content") {
|
||||
type = 'navigator:browser';
|
||||
}
|
||||
return this.windowMediator.getMostRecentWindow(type);
|
||||
}
|
||||
else {
|
||||
return this.curFrame;
|
||||
}
|
||||
return this.windowMediator.getMostRecentWindow(type);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -275,6 +282,8 @@ MarionetteDriverActor.prototype = {
|
|||
* True if this is the first time we're talking to this browser
|
||||
*/
|
||||
startBrowser: function MDA_startBrowser(win, newSession) {
|
||||
this.mainFrame = win;
|
||||
this.curFrame = null;
|
||||
this.addBrowser(win);
|
||||
this.curBrowser.newSession = newSession;
|
||||
this.curBrowser.startSession(newSession);
|
||||
|
@ -711,7 +720,7 @@ MarionetteDriverActor.prototype = {
|
|||
* Searches based on name, then id.
|
||||
*
|
||||
* @param object aRequest
|
||||
* 'value' member holds the id of the window to switch to
|
||||
* 'value' member holds the name or id of the window to switch to
|
||||
*/
|
||||
switchToWindow: function MDA_switchToWindow(aRequest) {
|
||||
let winEn = this.getWinEnumerator();
|
||||
|
@ -738,10 +747,73 @@ MarionetteDriverActor.prototype = {
|
|||
* Switch to a given frame within the current window
|
||||
*
|
||||
* @param object aRequest
|
||||
* 'value' holds the id of the frame to switch to
|
||||
* 'element' is the element to switch to
|
||||
* 'value' if element is not set, then this
|
||||
* holds either the id, name or index
|
||||
* of the frame to switch to
|
||||
*/
|
||||
switchToFrame: function MDA_switchToFrame(aRequest) {
|
||||
this.sendAsync("switchToFrame", aRequest);
|
||||
let curWindow = this.getCurrentWindow();
|
||||
if (this.context == "chrome") {
|
||||
let foundFrame = null;
|
||||
if ((aRequest.value == null) && (aRequest.element == null)) {
|
||||
this.curFrame = null;
|
||||
this.mainFrame.focus();
|
||||
this.sendOk();
|
||||
return;
|
||||
}
|
||||
if (aRequest.element != undefined) {
|
||||
if (this.curBrowser.elementManager.seenItems[aRequest.element] != undefined) {
|
||||
let wantedFrame = this.curBrowser.elementManager.getKnownElement(aRequest.element, curWindow); //HTMLIFrameElement
|
||||
let numFrames = curWindow.frames.length;
|
||||
for (let i = 0; i < numFrames; i++) {
|
||||
if (curWindow.frames[i].frameElement == wantedFrame) {
|
||||
curWindow = curWindow.frames[i];
|
||||
this.curFrame = curWindow;
|
||||
this.curFrame.focus();
|
||||
this.sendOk();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
switch(typeof(aRequest.value)) {
|
||||
case "string" :
|
||||
let foundById = null;
|
||||
let numFrames = curWindow.frames.length;
|
||||
for (let i = 0; i < numFrames; i++) {
|
||||
//give precedence to name
|
||||
let frame = curWindow.frames[i];
|
||||
let frameElement = frame.frameElement;
|
||||
if (frame.name == aRequest.value) {
|
||||
foundFrame = i;
|
||||
break;
|
||||
} else if ((foundById == null) && (frameElement.id == aRequest.value)) {
|
||||
foundById = i;
|
||||
}
|
||||
}
|
||||
if ((foundFrame == null) && (foundById != null)) {
|
||||
foundFrame = foundById;
|
||||
}
|
||||
break;
|
||||
case "number":
|
||||
if (curWindow.frames[aRequest.value] != undefined) {
|
||||
foundFrame = aRequest.value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (foundFrame != null) {
|
||||
curWindow = curWindow.frames[foundFrame];
|
||||
this.curFrame = curWindow;
|
||||
this.curFrame.focus();
|
||||
this.sendOk();
|
||||
} else {
|
||||
this.sendError("Unable to locate frame: " + aRequest.value, 8, null);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.sendAsync("switchToFrame", aRequest);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1106,11 +1178,6 @@ MarionetteDriverActor.prototype = {
|
|||
}
|
||||
}
|
||||
return reg;
|
||||
case "Marionette:goUrl":
|
||||
// if content determines that the goUrl call is directed at a top level window (not an iframe)
|
||||
// it calls back into chrome to load the uri.
|
||||
this.curBrowser.loadURI(message.json.value, this);
|
||||
break;
|
||||
}
|
||||
},
|
||||
/**
|
||||
|
@ -1243,25 +1310,6 @@ BrowserObj.prototype = {
|
|||
this.tab = this.browser.addTab(uri, true);
|
||||
},
|
||||
|
||||
/**
|
||||
* Load a uri in the current tab
|
||||
*
|
||||
* @param string uri
|
||||
* URI to load
|
||||
* @param EventListener listener
|
||||
* event listener fired on load
|
||||
*/
|
||||
loadURI: function BO_openURI(uri, listener) {
|
||||
if (appName != "B2G") {
|
||||
this.browser.addEventListener("DOMContentLoaded", listener, false);
|
||||
this.browser.loadURI(uri);
|
||||
}
|
||||
else {
|
||||
this.messageManager.addMessageListener("DOMContentLoaded", listener, true);
|
||||
this.browser.selectedBrowser.loadURI(uri);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Loads content listeners if we don't already have them
|
||||
*
|
||||
|
|
|
@ -26,7 +26,7 @@ let marionetteTimeout = null;
|
|||
let winUtil = content.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIDOMWindowUtils);
|
||||
let listenerId = null; //unique ID of this listener
|
||||
let activeFrame = null;
|
||||
let win = content;
|
||||
let curWindow = content;
|
||||
let elementManager = new ElementManager([]);
|
||||
|
||||
/**
|
||||
|
@ -179,6 +179,7 @@ function sendError(message, status, trace, command_id) {
|
|||
*/
|
||||
function resetValues() {
|
||||
marionetteTimeout = null;
|
||||
curWin = content;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -227,9 +228,9 @@ function createExecuteContentSandbox(aWindow, marionette, args) {
|
|||
*/
|
||||
function executeScript(msg, directInject) {
|
||||
let script = msg.json.value;
|
||||
let marionette = new Marionette(false, win, "content", marionetteLogObj);
|
||||
let marionette = new Marionette(false, curWindow, "content", marionetteLogObj);
|
||||
|
||||
let sandbox = createExecuteContentSandbox(win, marionette, msg.json.args);
|
||||
let sandbox = createExecuteContentSandbox(curWindow, marionette, msg.json.args);
|
||||
if (!sandbox)
|
||||
return;
|
||||
|
||||
|
@ -299,7 +300,7 @@ function executeJSScript(msg) {
|
|||
* method is called, or if it times out.
|
||||
*/
|
||||
function executeWithCallback(msg, timeout) {
|
||||
win.addEventListener("unload", errUnload, false);
|
||||
curWindow.addEventListener("unload", errUnload, false);
|
||||
let script = msg.json.value;
|
||||
let command_id = msg.json.id;
|
||||
|
||||
|
@ -308,21 +309,21 @@ function executeWithCallback(msg, timeout) {
|
|||
// However Selenium code returns 28, see
|
||||
// http://code.google.com/p/selenium/source/browse/trunk/javascript/firefox-driver/js/evaluate.js.
|
||||
// We'll stay compatible with the Selenium code.
|
||||
let timeoutId = win.setTimeout(function() {
|
||||
let timeoutId = curWindow.setTimeout(function() {
|
||||
contentAsyncReturnFunc('timed out', 28);
|
||||
}, marionetteTimeout);
|
||||
win.addEventListener('error', function win__onerror(evt) {
|
||||
win.removeEventListener('error', win__onerror, true);
|
||||
curWindow.addEventListener('error', function win__onerror(evt) {
|
||||
curWindow.removeEventListener('error', win__onerror, true);
|
||||
contentAsyncReturnFunc(evt, 17);
|
||||
return true;
|
||||
}, true);
|
||||
|
||||
function contentAsyncReturnFunc(value, status) {
|
||||
win.removeEventListener("unload", errUnload, false);
|
||||
curWindow.removeEventListener("unload", errUnload, false);
|
||||
|
||||
/* clear all timeouts potentially generated by the script*/
|
||||
for(let i=0; i<=timeoutId; i++) {
|
||||
win.clearTimeout(i);
|
||||
curWindow.clearTimeout(i);
|
||||
}
|
||||
|
||||
sendSyncMessage("Marionette:testLog", {value: elementManager.wrapValue(marionetteLogObj.getLogs())});
|
||||
|
@ -349,9 +350,9 @@ function executeWithCallback(msg, timeout) {
|
|||
"__marionetteFunc.apply(null, __marionetteParams); ";
|
||||
}
|
||||
|
||||
let marionette = new Marionette(true, win, "content", marionetteLogObj);
|
||||
let marionette = new Marionette(true, curWindow, "content", marionetteLogObj);
|
||||
|
||||
let sandbox = createExecuteContentSandbox(win, marionette, msg.json.args);
|
||||
let sandbox = createExecuteContentSandbox(curWindow, marionette, msg.json.args);
|
||||
if (!sandbox)
|
||||
return;
|
||||
|
||||
|
@ -387,37 +388,32 @@ function setSearchTimeout(msg) {
|
|||
* All other navigation is handled by the server (in chrome space).
|
||||
*/
|
||||
function goUrl(msg) {
|
||||
if (activeFrame != null) {
|
||||
win.document.location = msg.json.value;
|
||||
//TODO: replace this with event firing when Bug 720714 is resolved
|
||||
let checkTimer = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer);
|
||||
let checkLoad = function () {
|
||||
if (win.document.readyState == "complete") {
|
||||
sendOk();
|
||||
}
|
||||
else {
|
||||
checkTimer.initWithCallback(checkLoad, 100, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
|
||||
}
|
||||
};
|
||||
checkLoad();
|
||||
}
|
||||
else {
|
||||
sendAsyncMessage("Marionette:goUrl", {value: msg.json.value});
|
||||
}
|
||||
curWindow.location = msg.json.value;
|
||||
//TODO: replace this with DOMContentLoaded event listening when Bug 720714 is resolved
|
||||
let checkTimer = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer);
|
||||
let checkLoad = function () {
|
||||
if (curWindow.document.readyState == "complete") {
|
||||
sendOk();
|
||||
}
|
||||
else {
|
||||
checkTimer.initWithCallback(checkLoad, 100, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
|
||||
}
|
||||
};
|
||||
checkTimer.initWithCallback(checkLoad, 100, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current URI
|
||||
*/
|
||||
function getUrl(msg) {
|
||||
sendResponse({value: win.location.href});
|
||||
sendResponse({value: curWindow.location.href});
|
||||
}
|
||||
|
||||
/**
|
||||
* Go back in history
|
||||
*/
|
||||
function goBack(msg) {
|
||||
win.history.back();
|
||||
curWindow.history.back();
|
||||
sendOk();
|
||||
}
|
||||
|
||||
|
@ -425,7 +421,7 @@ function goBack(msg) {
|
|||
* Go forward in history
|
||||
*/
|
||||
function goForward(msg) {
|
||||
win.history.forward();
|
||||
curWindow.history.forward();
|
||||
sendOk();
|
||||
}
|
||||
|
||||
|
@ -433,7 +429,7 @@ function goForward(msg) {
|
|||
* Refresh the page
|
||||
*/
|
||||
function refresh(msg) {
|
||||
win.location.reload(true);
|
||||
curWindow.location.reload(true);
|
||||
let listen = function() { removeEventListener("DOMContentLoaded", arguments.callee, false); sendOk() } ;
|
||||
addEventListener("DOMContentLoaded", listen, false);
|
||||
}
|
||||
|
@ -445,8 +441,7 @@ function findElementContent(msg) {
|
|||
let id;
|
||||
try {
|
||||
let notify = function(id) { sendResponse({value:id});};
|
||||
let curWin = activeFrame ? win.frames[activeFrame] : win;
|
||||
id = elementManager.find(curWin, msg.json, notify, false);
|
||||
id = elementManager.find(curWindow, msg.json, notify, false);
|
||||
}
|
||||
catch (e) {
|
||||
sendError(e.message, e.num, e.stack);
|
||||
|
@ -460,8 +455,7 @@ function findElementsContent(msg) {
|
|||
let id;
|
||||
try {
|
||||
let notify = function(id) { sendResponse({value:id});};
|
||||
let curWin = activeFrame ? win.frames[activeFrame] : win;
|
||||
id = elementManager.find(curWin, msg.json, notify, true);
|
||||
id = elementManager.find(curWindow, msg.json, notify, true);
|
||||
}
|
||||
catch (e) {
|
||||
sendError(e.message, e.num, e.stack);
|
||||
|
@ -474,8 +468,7 @@ function findElementsContent(msg) {
|
|||
function clickElement(msg) {
|
||||
let el;
|
||||
try {
|
||||
//el = elementManager.click(msg.json.element, win);
|
||||
el = elementManager.getKnownElement(msg.json.element, win);
|
||||
el = elementManager.getKnownElement(msg.json.element, curWindow);
|
||||
utils.click(el);
|
||||
sendOk();
|
||||
}
|
||||
|
@ -489,7 +482,7 @@ function clickElement(msg) {
|
|||
*/
|
||||
function getElementAttribute(msg) {
|
||||
try {
|
||||
let el = elementManager.getKnownElement(msg.json.element, win);
|
||||
let el = elementManager.getKnownElement(msg.json.element, curWindow);
|
||||
sendResponse({value: utils.getElementAttribute(el, msg.json.name)});
|
||||
}
|
||||
catch (e) {
|
||||
|
@ -502,7 +495,7 @@ function getElementAttribute(msg) {
|
|||
*/
|
||||
function getElementText(msg) {
|
||||
try {
|
||||
let el = elementManager.getKnownElement(msg.json.element, win);
|
||||
let el = elementManager.getKnownElement(msg.json.element, curWindow);
|
||||
sendResponse({value: utils.getElementText(el)});
|
||||
}
|
||||
catch (e) {
|
||||
|
@ -515,7 +508,7 @@ function getElementText(msg) {
|
|||
*/
|
||||
function isElementDisplayed(msg) {
|
||||
try {
|
||||
let el = elementManager.getKnownElement(msg.json.element, win);
|
||||
let el = elementManager.getKnownElement(msg.json.element, curWindow);
|
||||
sendResponse({value: utils.isElementDisplayed(el)});
|
||||
}
|
||||
catch (e) {
|
||||
|
@ -528,7 +521,7 @@ function isElementDisplayed(msg) {
|
|||
*/
|
||||
function isElementEnabled(msg) {
|
||||
try {
|
||||
let el = elementManager.getKnownElement(msg.json.element, win);
|
||||
let el = elementManager.getKnownElement(msg.json.element, curWindow);
|
||||
sendResponse({value: utils.isElementEnabled(el)});
|
||||
}
|
||||
catch (e) {
|
||||
|
@ -541,7 +534,7 @@ function isElementEnabled(msg) {
|
|||
*/
|
||||
function isElementSelected(msg) {
|
||||
try {
|
||||
let el = elementManager.getKnownElement(msg.json.element, win);
|
||||
let el = elementManager.getKnownElement(msg.json.element, curWindow);
|
||||
sendResponse({value: utils.isElementSelected(el)});
|
||||
}
|
||||
catch (e) {
|
||||
|
@ -554,7 +547,7 @@ function isElementSelected(msg) {
|
|||
*/
|
||||
function sendKeysToElement(msg) {
|
||||
try {
|
||||
let el = elementManager.getKnownElement(msg.json.element, win);
|
||||
let el = elementManager.getKnownElement(msg.json.element, curWindow);
|
||||
utils.sendKeysToElement(el, msg.json.value);
|
||||
sendOk();
|
||||
}
|
||||
|
@ -568,7 +561,7 @@ function sendKeysToElement(msg) {
|
|||
*/
|
||||
function clearElement(msg) {
|
||||
try {
|
||||
let el = elementManager.getKnownElement(msg.json.element, win);
|
||||
let el = elementManager.getKnownElement(msg.json.element, curWindow);
|
||||
utils.clearElement(el);
|
||||
sendOk();
|
||||
}
|
||||
|
@ -584,21 +577,19 @@ function clearElement(msg) {
|
|||
function switchToFrame(msg) {
|
||||
let foundFrame = null;
|
||||
if ((msg.json.value == null) && (msg.json.element == null)) {
|
||||
win = content;
|
||||
activeFrame = null;
|
||||
content.focus();
|
||||
curWindow = content;
|
||||
curWindow.focus();
|
||||
sendOk();
|
||||
return;
|
||||
}
|
||||
if (msg.json.element != undefined) {
|
||||
if (elementManager.seenItems[msg.json.element] != undefined) {
|
||||
let wantedFrame = elementManager.getKnownElement(msg.json.element, win);//HTMLIFrameElement
|
||||
let numFrames = win.frames.length;
|
||||
let wantedFrame = elementManager.getKnownElement(msg.json.element, curWindow); //HTMLIFrameElement
|
||||
let numFrames = curWindow.frames.length;
|
||||
for (let i = 0; i < numFrames; i++) {
|
||||
if (win.frames[i].frameElement == wantedFrame) {
|
||||
win = win.frames[i];
|
||||
activeFrame = i;
|
||||
win.focus();
|
||||
if (curWindow.frames[i].frameElement == wantedFrame) {
|
||||
curWindow = curWindow.frames[i];
|
||||
curWindow.focus();
|
||||
sendOk();
|
||||
return;
|
||||
}
|
||||
|
@ -608,10 +599,10 @@ function switchToFrame(msg) {
|
|||
switch(typeof(msg.json.value)) {
|
||||
case "string" :
|
||||
let foundById = null;
|
||||
let numFrames = win.frames.length;
|
||||
let numFrames = curWindow.frames.length;
|
||||
for (let i = 0; i < numFrames; i++) {
|
||||
//give precedence to name
|
||||
let frame = win.frames[i];
|
||||
let frame = curWindow.frames[i];
|
||||
let frameElement = frame.frameElement;
|
||||
if (frameElement.name == msg.json.value) {
|
||||
foundFrame = i;
|
||||
|
@ -625,17 +616,14 @@ function switchToFrame(msg) {
|
|||
}
|
||||
break;
|
||||
case "number":
|
||||
if (win.frames[msg.json.value] != undefined) {
|
||||
if (curWindow.frames[msg.json.value] != undefined) {
|
||||
foundFrame = msg.json.value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
//TODO: implement index
|
||||
if (foundFrame != null) {
|
||||
let frameWindow = win.frames[foundFrame];
|
||||
activeFrame = foundFrame;
|
||||
win = frameWindow;
|
||||
win.focus();
|
||||
curWindow = curWindow.frames[foundFrame];
|
||||
curWindow.focus();
|
||||
sendOk();
|
||||
} else {
|
||||
sendError("Unable to locate frame: " + msg.json.value, 8, null);
|
||||
|
|
|
@ -48,8 +48,8 @@ def main():
|
|||
entity = get_value(jsonFilename, key)
|
||||
if passesRestrictions(options.talos_json_url, entity["url"]):
|
||||
# the key is at the same time the filename e.g. talos.zip
|
||||
print "INFO: Downloading %s as %s" % (entity["url"], os.path.join(entity["path"], key))
|
||||
download_file(entity["url"], entity["path"], key)
|
||||
print "INFO: %s -> %s" % (entity["url"], os.path.join(entity["path"], key))
|
||||
else:
|
||||
print "ERROR: You have tried to download a file " + \
|
||||
"from: %s " % fileUrl + \
|
||||
|
|
Двоичный файл не отображается.
Двоичный файл не отображается.
|
@ -87,11 +87,14 @@ const APPLY_TO_DIR_SUFFIX = "_applyToDir/";
|
|||
const HELPER_BIN_FILE = "TestAUSHelper" + BIN_SUFFIX;
|
||||
const MAR_COMPLETE_FILE = "data/complete.mar";
|
||||
const MAR_PARTIAL_FILE = "data/partial.mar";
|
||||
const MAR_OLD_VERSION_FILE = "data/old_version_mar.mar";
|
||||
const MAR_WRONG_CHANNEL_FILE = "data/wrong_product_channel_mar.mar";
|
||||
const UPDATER_BIN_FILE = "updater" + BIN_SUFFIX;
|
||||
const MAINTENANCE_SERVICE_BIN_FILE = "maintenanceservice.exe";
|
||||
const MAINTENANCE_SERVICE_INSTALLER_BIN_FILE = "maintenanceservice_installer.exe";
|
||||
const UPDATE_SETTINGS_INI_FILE = "update-settings.ini";
|
||||
const UPDATE_SETTINGS_CONTENTS = "[Settings]\nMAR_CHANNEL_ID=xpcshell-test\n"
|
||||
const UPDATE_SETTINGS_CONTENTS = "[Settings]\n" +
|
||||
"ACCEPTED_MAR_CHANNEL_IDS=xpcshell-test\n"
|
||||
const UPDATES_DIR_SUFFIX = "_mar";
|
||||
|
||||
const LOG_COMPLETE_SUCCESS = "data/complete_log_success";
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (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.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2012
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Brian R. Bondy <netzen@gmail.com> (Original Author)
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/* Test version downgrade MAR security check */
|
||||
|
||||
const TEST_ID = "0113";
|
||||
|
||||
// We don't actually care if the MAR has any data, we only care about the
|
||||
// application return code and update.status result.
|
||||
const TEST_FILES = [];
|
||||
|
||||
const VERSION_DOWNGRADE_ERROR = "23";
|
||||
|
||||
function run_test() {
|
||||
// Setup an old version MAR file
|
||||
do_register_cleanup(cleanupUpdaterTest);
|
||||
setupUpdaterTest(MAR_OLD_VERSION_FILE);
|
||||
|
||||
// Apply the MAR
|
||||
let exitValue = runUpdate();
|
||||
logTestInfo("testing updater binary process exitValue for failure when " +
|
||||
"applying a version downgrade MAR");
|
||||
// Make sure the updater executed successfully
|
||||
do_check_eq(exitValue, 0);
|
||||
let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
|
||||
|
||||
//Make sure we get a version downgrade error
|
||||
let updateStatus = readStatusFile(updatesDir);
|
||||
do_check_eq(updateStatus.split(": ")[1], VERSION_DOWNGRADE_ERROR);
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (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.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2012
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Brian R. Bondy <netzen@gmail.com> (Original Author)
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/* Test product/channel MAR security check */
|
||||
|
||||
const TEST_ID = "0114";
|
||||
|
||||
// We don't actually care if the MAR has any data, we only care about the
|
||||
// application return code and update.status result.
|
||||
const TEST_FILES = [];
|
||||
|
||||
const MAR_CHANNEL_MISMATCH_ERROR = "22";
|
||||
|
||||
function run_test() {
|
||||
// Setup a wrong channel MAR file
|
||||
do_register_cleanup(cleanupUpdaterTest);
|
||||
setupUpdaterTest(MAR_WRONG_CHANNEL_FILE);
|
||||
|
||||
// Apply the MAR
|
||||
let exitValue = runUpdate();
|
||||
logTestInfo("testing updater binary process exitValue for failure when " +
|
||||
"applying a wrong product and channel MAR file");
|
||||
// Make sure the updater executed successfully
|
||||
do_check_eq(exitValue, 0);
|
||||
let updatesDir = do_get_file(TEST_ID + UPDATES_DIR_SUFFIX);
|
||||
|
||||
//Make sure we get a version downgrade error
|
||||
let updateStatus = readStatusFile(updatesDir);
|
||||
do_check_eq(updateStatus.split(": ")[1], MAR_CHANNEL_MISMATCH_ERROR);
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
[test_0113_versionDowngradeCheck.js]
|
||||
[test_0114_productChannelCheck.js]
|
||||
[test_0150_appBinReplaced_xp_win_complete.js]
|
||||
[test_0151_appBinPatched_xp_win_partial.js]
|
||||
[test_0160_appInUse_xp_win_complete.js]
|
||||
|
|
|
@ -16,6 +16,7 @@ const allowedOrigins = [
|
|||
"https://www.facebook.com",
|
||||
"https://accounts.google.com",
|
||||
"https://www.google.com",
|
||||
"https://twitter.com",
|
||||
"https://api.twitter.com",
|
||||
];
|
||||
|
||||
|
|
|
@ -139,6 +139,10 @@
|
|||
onset="this.setAttribute('timeout', val); return val;"
|
||||
onget="return parseInt(this.getAttribute('timeout')) || 0;"/>
|
||||
|
||||
<property name="searchParam"
|
||||
onget="return this.getAttribute('autocompletesearchparam') || '';"
|
||||
onset="this.setAttribute('autocompletesearchparam', val); return val;"/>
|
||||
|
||||
<method name="onSearchBegin">
|
||||
<body><![CDATA[
|
||||
this._fireEvent("searchbegin");
|
||||
|
@ -378,7 +382,7 @@
|
|||
param: this,
|
||||
onStartLookup: function(aSearchString, aPreviousSearchResult, aListener) {
|
||||
this.session.startSearch(aSearchString,
|
||||
this.param.getAttribute("autocompletesearchparam") || "",
|
||||
this.param.searchParam,
|
||||
aPreviousSearchResult && aPreviousSearchResult.lastResult,
|
||||
aListener);
|
||||
},
|
||||
|
|
Загрузка…
Ссылка в новой задаче