This commit is contained in:
Ehsan Akhgari 2012-05-02 15:39:41 -04:00
Родитель cbe821f3a9 d9f31121e6
Коммит f8e1d233a4
41 изменённых файлов: 1001 добавлений и 206 удалений

Просмотреть файл

@ -92,13 +92,17 @@
<field name="tabs" readonly="true"> <field name="tabs" readonly="true">
this.tabContainer.childNodes; this.tabContainer.childNodes;
</field> </field>
<property name="visibleTabs" readonly="true"> <property name="visibleTabs" readonly="true">
<getter><![CDATA[ <getter><![CDATA[
return Array.filter(this.tabs, function(tab) { if (!this._visibleTabs)
return !tab.hidden && !tab.closing; this._visibleTabs = Array.filter(this.tabs,
}); function (tab) !tab.hidden && !tab.closing);
return this._visibleTabs;
]]></getter> ]]></getter>
</property> </property>
<field name="_visibleTabs">null</field>
<field name="mURIFixup" readonly="true"> <field name="mURIFixup" readonly="true">
Components.classes["@mozilla.org/docshell/urifixup;1"] Components.classes["@mozilla.org/docshell/urifixup;1"]
.getService(Components.interfaces.nsIURIFixup); .getService(Components.interfaces.nsIURIFixup);
@ -151,9 +155,6 @@
false false
#endif #endif
</field> </field>
<field name="_browsers">
null
</field>
<field name="_autoScrollPopup"> <field name="_autoScrollPopup">
null null
@ -1240,8 +1241,6 @@
aIsUTF8 = params.isUTF8; aIsUTF8 = params.isUTF8;
} }
this._browsers = null; // invalidate cache
// if we're adding tabs, we're past interrupt mode, ditch the owner // if we're adding tabs, we're past interrupt mode, ditch the owner
if (this.mCurrentTab.owner) if (this.mCurrentTab.owner)
this.mCurrentTab.owner = null; this.mCurrentTab.owner = null;
@ -1291,10 +1290,11 @@
}, 0, this.tabContainer); }, 0, this.tabContainer);
} }
this.tabContainer.appendChild(t); // invalidate caches
// invalidate cache, because tabContainer is about to change
this._browsers = null; this._browsers = null;
this._visibleTabs = null;
this.tabContainer.appendChild(t);
// If this new tab is owned by another, assert that relationship // If this new tab is owned by another, assert that relationship
if (aOwner) if (aOwner)
@ -1625,6 +1625,7 @@
aTab.closing = true; aTab.closing = true;
this._removingTabs.push(aTab); this._removingTabs.push(aTab);
this._visibleTabs = null; // invalidate cache
if (newTab) if (newTab)
this.addTab(BROWSER_NEW_TAB_URL, {skipAnimation: true}); this.addTab(BROWSER_NEW_TAB_URL, {skipAnimation: true});
else else
@ -2009,7 +2010,10 @@
<![CDATA[ <![CDATA[
if (aTab.hidden) { if (aTab.hidden) {
aTab.removeAttribute("hidden"); aTab.removeAttribute("hidden");
this._visibleTabs = null; // invalidate cache
this.tabContainer.adjustTabstrip(); this.tabContainer.adjustTabstrip();
let event = document.createEvent("Events"); let event = document.createEvent("Events");
event.initEvent("TabShow", true, false); event.initEvent("TabShow", true, false);
aTab.dispatchEvent(event); aTab.dispatchEvent(event);
@ -2025,7 +2029,10 @@
if (!aTab.hidden && !aTab.pinned && !aTab.selected && if (!aTab.hidden && !aTab.pinned && !aTab.selected &&
!aTab.closing) { !aTab.closing) {
aTab.setAttribute("hidden", "true"); aTab.setAttribute("hidden", "true");
this._visibleTabs = null; // invalidate cache
this.tabContainer.adjustTabstrip(); this.tabContainer.adjustTabstrip();
let event = document.createEvent("Events"); let event = document.createEvent("Events");
event.initEvent("TabHide", true, false); event.initEvent("TabHide", true, false);
aTab.dispatchEvent(event); aTab.dispatchEvent(event);
@ -2081,6 +2088,7 @@
]]> ]]>
</getter> </getter>
</property> </property>
<field name="_browsers">null</field>
<!-- Moves a tab to a new browser window, unless it's already the only tab <!-- 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. --> in the current window, in which case this will do nothing. -->
@ -2126,11 +2134,14 @@
aIndex = aIndex < aTab._tPos ? aIndex: aIndex+1; aIndex = aIndex < aTab._tPos ? aIndex: aIndex+1;
this.mCurrentTab._selected = false; 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 // 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 // bounds: .item() returns null (so it acts like appendChild), but [] throws
this.tabContainer.insertBefore(aTab, this.tabs.item(aIndex)); 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++) { for (let i = 0; i < this.tabs.length; i++) {
this.tabs[i]._tPos = i; this.tabs[i]._tPos = i;

Просмотреть файл

@ -60,6 +60,7 @@ import android.widget.Button;
import android.widget.ImageButton; import android.widget.ImageButton;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.RelativeLayout.LayoutParams;
import android.widget.TextView; import android.widget.TextView;
import android.widget.TextSwitcher; import android.widget.TextSwitcher;
import android.widget.ViewSwitcher.ViewFactory; import android.widget.ViewSwitcher.ViewFactory;
@ -172,6 +173,18 @@ public class BrowserToolbar {
mFavicon = (ImageButton) mLayout.findViewById(R.id.favicon); mFavicon = (ImageButton) mLayout.findViewById(R.id.favicon);
mSiteSecurity = (ImageButton) mLayout.findViewById(R.id.site_security); 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); mProgressSpinner = (AnimationDrawable) resources.getDrawable(R.drawable.progress_spinner);
mStop = (ImageButton) mLayout.findViewById(R.id.stop); mStop = (ImageButton) mLayout.findViewById(R.id.stop);
@ -322,9 +335,9 @@ public class BrowserToolbar {
public void setSecurityMode(String mode) { public void setSecurityMode(String mode) {
mTitleCanExpand = false; mTitleCanExpand = false;
if (mode.equals("identified")) { if (mode.equals(SiteIdentityPopup.IDENTIFIED)) {
mSiteSecurity.setImageLevel(1); mSiteSecurity.setImageLevel(1);
} else if (mode.equals("verified")) { } else if (mode.equals(SiteIdentityPopup.VERIFIED)) {
mSiteSecurity.setImageLevel(2); mSiteSecurity.setImageLevel(2);
} else { } else {
mSiteSecurity.setImageLevel(0); mSiteSecurity.setImageLevel(0);

Просмотреть файл

@ -138,6 +138,7 @@ abstract public class GeckoApp
public static BrowserToolbar mBrowserToolbar; public static BrowserToolbar mBrowserToolbar;
public static DoorHangerPopup mDoorHangerPopup; public static DoorHangerPopup mDoorHangerPopup;
public static SiteIdentityPopup mSiteIdentityPopup;
public static FormAssistPopup mFormAssistPopup; public static FormAssistPopup mFormAssistPopup;
public Favicons mFavicons; public Favicons mFavicons;
@ -665,7 +666,7 @@ abstract public class GeckoApp
tab.setContentType(contentType); tab.setContentType(contentType);
tab.updateFavicon(null); tab.updateFavicon(null);
tab.updateFaviconURL(null); tab.updateFaviconURL(null);
tab.updateSecurityMode("unknown"); tab.updateIdentityData(null);
tab.removeTransientDoorHangers(); tab.removeTransientDoorHangers();
tab.setHasTouchListeners(false); tab.setHasTouchListeners(false);
tab.setCheckerboardColor(Color.WHITE); tab.setCheckerboardColor(Color.WHITE);
@ -677,7 +678,7 @@ abstract public class GeckoApp
if (Tabs.getInstance().isSelectedTab(tab)) { if (Tabs.getInstance().isSelectedTab(tab)) {
mBrowserToolbar.setTitle(uri); mBrowserToolbar.setTitle(uri);
mBrowserToolbar.setFavicon(null); mBrowserToolbar.setFavicon(null);
mBrowserToolbar.setSecurityMode("unknown"); mBrowserToolbar.setSecurityMode(tab.getSecurityMode());
mDoorHangerPopup.updatePopup(); mDoorHangerPopup.updatePopup();
mBrowserToolbar.setShadowVisibility(!(tab.getURL().startsWith("about:"))); 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); final Tab tab = Tabs.getInstance().getTab(tabId);
if (tab == null) if (tab == null)
return; return;
tab.updateSecurityMode(mode); tab.updateIdentityData(identityData);
mMainHandler.post(new Runnable() { mMainHandler.post(new Runnable() {
public void run() { public void run() {
if (Tabs.getInstance().isSelectedTab(tab)) 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); handleLocationChange(tabId, uri, documentURI, contentType, sameDocument);
} else if (event.equals("Content:SecurityChange")) { } else if (event.equals("Content:SecurityChange")) {
final int tabId = message.getInt("tabID"); final int tabId = message.getInt("tabID");
final String mode = message.getString("mode"); final JSONObject identity = message.getJSONObject("identity");
Log.i(LOGTAG, "Security Mode - " + mode); Log.i(LOGTAG, "Security Mode - " + identity.getString("mode"));
handleSecurityChange(tabId, mode); handleSecurityChange(tabId, identity);
} else if (event.equals("Content:StateChange")) { } else if (event.equals("Content:StateChange")) {
final int tabId = message.getInt("tabID"); final int tabId = message.getInt("tabID");
final String uri = message.getString("uri"); final String uri = message.getString("uri");
@ -1204,7 +1205,7 @@ abstract public class GeckoApp
return; return;
tab.setState("about:home".equals(uri) ? Tab.STATE_SUCCESS : Tab.STATE_LOADING); tab.setState("about:home".equals(uri) ? Tab.STATE_SUCCESS : Tab.STATE_LOADING);
tab.updateSecurityMode("unknown"); tab.updateIdentityData(null);
if (Tabs.getInstance().isSelectedTab(tab)) if (Tabs.getInstance().isSelectedTab(tab))
getLayerController().getView().getRenderer().resetCheckerboard(); getLayerController().getView().getRenderer().resetCheckerboard();
mMainHandler.post(new Runnable() { mMainHandler.post(new Runnable() {
@ -1665,6 +1666,7 @@ abstract public class GeckoApp
mPluginContainer = (AbsoluteLayout) findViewById(R.id.plugin_container); mPluginContainer = (AbsoluteLayout) findViewById(R.id.plugin_container);
mDoorHangerPopup = new DoorHangerPopup(this); mDoorHangerPopup = new DoorHangerPopup(this);
mSiteIdentityPopup = new SiteIdentityPopup(this);
mFormAssistPopup = (FormAssistPopup) findViewById(R.id.form_assist_popup); mFormAssistPopup = (FormAssistPopup) findViewById(R.id.form_assist_popup);
Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - UI almost up"); Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - UI almost up");
@ -1955,6 +1957,9 @@ abstract public class GeckoApp
// Undo whatever we did in onPause. // Undo whatever we did in onPause.
super.onResume(); super.onResume();
if (mSiteIdentityPopup != null)
mSiteIdentityPopup.dismiss();
int newOrientation = getResources().getConfiguration().orientation; int newOrientation = getResources().getConfiguration().orientation;
if (mOrientation != newOrientation) { if (mOrientation != newOrientation) {
@ -2081,6 +2086,8 @@ abstract public class GeckoApp
mOrientation = newConfig.orientation; mOrientation = newConfig.orientation;
if (mFormAssistPopup != null) if (mFormAssistPopup != null)
mFormAssistPopup.hide(); mFormAssistPopup.hide();
if (mSiteIdentityPopup != null)
mSiteIdentityPopup.dismiss();
refreshActionBar(); refreshActionBar();
} }
} }
@ -2551,6 +2558,11 @@ abstract public class GeckoApp
return; return;
} }
if (mSiteIdentityPopup.isShowing()) {
mSiteIdentityPopup.dismiss();
return;
}
if (mDOMFullScreen) { if (mDOMFullScreen) {
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("FullScreen:Exit", null)); GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("FullScreen:Exit", null));
return; return;

Просмотреть файл

@ -45,6 +45,7 @@ import java.lang.ref.SoftReference;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.database.Cursor; import android.database.Cursor;
import android.net.Uri;
import android.os.Handler; import android.os.Handler;
import android.util.Log; import android.util.Log;
@ -115,12 +116,49 @@ class GlobalHistory {
GeckoAppShell.notifyUriVisited(uri); 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) { public void add(String uri) {
if (!canAddURI(uri))
return;
BrowserDB.updateVisitedHistory(GeckoApp.mAppContext.getContentResolver(), uri); BrowserDB.updateVisitedHistory(GeckoApp.mAppContext.getContentResolver(), uri);
addToGeckoOnly(uri); addToGeckoOnly(uri);
} }
public void update(String uri, String title) { public void update(String uri, String title) {
if (!canAddURI(uri))
return;
ContentResolver resolver = GeckoApp.mAppContext.getContentResolver(); ContentResolver resolver = GeckoApp.mAppContext.getContentResolver();
BrowserDB.updateHistoryTitle(resolver, uri, title); BrowserDB.updateHistoryTitle(resolver, uri, title);
} }

Просмотреть файл

@ -110,6 +110,7 @@ FENNEC_JAVA_FILES = \
sqlite/SQLiteBridgeException.java \ sqlite/SQLiteBridgeException.java \
RemoteTabs.java \ RemoteTabs.java \
SetupScreen.java \ SetupScreen.java \
SiteIdentityPopup.java \
SurfaceBits.java \ SurfaceBits.java \
Tab.java \ Tab.java \
Tabs.java \ Tabs.java \
@ -272,6 +273,7 @@ RES_LAYOUT = \
res/layout/notification_progress_text.xml \ res/layout/notification_progress_text.xml \
res/layout/site_setting_title.xml \ res/layout/site_setting_title.xml \
res/layout/setup_screen.xml \ res/layout/setup_screen.xml \
res/layout/site_identity_popup.xml \
res/layout/remote_tabs.xml \ res/layout/remote_tabs.xml \
res/layout/remote_tabs_child.xml \ res/layout/remote_tabs_child.xml \
res/layout/remote_tabs_group.xml \ res/layout/remote_tabs_group.xml \
@ -366,6 +368,8 @@ RES_DRAWABLE_BASE = \
res/drawable/doorhanger_bg.9.png \ res/drawable/doorhanger_bg.9.png \
res/drawable/doorhanger_shadow_bg.9.png \ res/drawable/doorhanger_shadow_bg.9.png \
res/drawable/doorhanger_popup_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_identified.png \
res/drawable/site_security_verified.png \ res/drawable/site_security_verified.png \
res/drawable/urlbar_stop.png \ res/drawable/urlbar_stop.png \
@ -423,6 +427,8 @@ RES_DRAWABLE_HDPI = \
res/drawable-hdpi/doorhanger_bg.9.png \ res/drawable-hdpi/doorhanger_bg.9.png \
res/drawable-hdpi/doorhanger_shadow_bg.9.png \ res/drawable-hdpi/doorhanger_shadow_bg.9.png \
res/drawable-hdpi/doorhanger_popup_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_identified.png \
res/drawable-hdpi/site_security_verified.png \ res/drawable-hdpi/site_security_verified.png \
res/drawable-hdpi/urlbar_stop.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_shadow_bg.9.png \
res/drawable-xhdpi-v11/doorhanger_popup_bg.9.png \ res/drawable-xhdpi-v11/doorhanger_popup_bg.9.png \
res/drawable-xhdpi-v11/urlbar_stop.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_identified.png \
res/drawable-xhdpi-v11/site_security_verified.png \ res/drawable-xhdpi-v11/site_security_verified.png \
res/drawable-xhdpi-v11/tabs_button_tail.9.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 String mTitle;
private Drawable mFavicon; private Drawable mFavicon;
private String mFaviconUrl; private String mFaviconUrl;
private String mSecurityMode; private JSONObject mIdentityData;
private Drawable mThumbnail; private Drawable mThumbnail;
private List<HistoryEntry> mHistory; private List<HistoryEntry> mHistory;
private int mHistoryIndex; private int mHistoryIndex;
@ -119,7 +119,7 @@ public final class Tab {
mTitle = title; mTitle = title;
mFavicon = null; mFavicon = null;
mFaviconUrl = null; mFaviconUrl = null;
mSecurityMode = "unknown"; mIdentityData = null;
mThumbnail = null; mThumbnail = null;
mHistory = new ArrayList<HistoryEntry>(); mHistory = new ArrayList<HistoryEntry>();
mHistoryIndex = -1; mHistoryIndex = -1;
@ -270,7 +270,16 @@ public final class Tab {
} }
public String getSecurityMode() { 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() { public boolean isBookmark() {
@ -368,8 +377,9 @@ public final class Tab {
Log.i(LOGTAG, "Updated favicon URL for tab with id: " + mId); 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() { 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). // 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, // 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) // 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 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), Cursor c = cr.query(combinedUriWithLimit(limit),
projection, projection,

Просмотреть файл

@ -126,3 +126,18 @@
<!ENTITY filepicker_audio_title "Choose or record a sound"> <!ENTITY filepicker_audio_title "Choose or record a sound">
<!ENTITY filepicker_image_title "Choose or take a picture"> <!ENTITY filepicker_image_title "Choose or take a picture">
<!ENTITY filepicker_video_title "Choose or record a video"> <!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">

Двоичные данные
mobile/android/base/resources/drawable-hdpi/larry_blue.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 5.1 KiB

Двоичные данные
mobile/android/base/resources/drawable-hdpi/larry_green.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 5.1 KiB

Двоичные данные
mobile/android/base/resources/drawable-xhdpi-v11/larry_blue.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 7.3 KiB

Двоичные данные
mobile/android/base/resources/drawable-xhdpi-v11/larry_green.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 7.3 KiB

Двоичные данные
mobile/android/base/resources/drawable/larry_blue.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 3.0 KiB

Двоичные данные
mobile/android/base/resources/drawable/larry_green.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 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="splash_content">#ffffff</color>
<color name="doorhanger_link">#ACC4D5</color> <color name="doorhanger_link">#ACC4D5</color>
<color name="validation_message_text">#ffffff</color> <color name="validation_message_text">#ffffff</color>
<color name="identity_verified">#77BAFF</color>
<color name="identity_identified">#B7D46A</color>
</resources> </resources>

Просмотреть файл

@ -148,4 +148,8 @@
<string name="bookmarkdefaults_title_abouthome">@bookmarks_aboutHome@</string> <string name="bookmarkdefaults_title_abouthome">@bookmarks_aboutHome@</string>
<string name="bookmarkdefaults_url_abouthome">about:home</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> </resources>

Просмотреть файл

@ -227,8 +227,14 @@ abstract class Axis {
* possible and this axis has not been scroll locked while panning. Otherwise, returns false. * possible and this axis has not been scroll locked while panning. Otherwise, returns false.
*/ */
private boolean scrollable() { private boolean scrollable() {
return getViewportLength() <= getPageLength() - MIN_SCROLLABLE_DISTANCE && // If we're scrolling a subdocument, ignore the viewport length restrictions (since those
!mScrollingDisabled; // 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. // Performs displacement of the viewport position according to the current velocity.
void displace() { void displace() {
if (!mSubscroller.scrolling() && !scrollable()) if (!scrollable()) {
return; return;
}
if (mFlingState == FlingStates.PANNING) if (mFlingState == FlingStates.PANNING)
mDisplacement += (mLastTouchPos - mTouchPos) * getEdgeResistance(); mDisplacement += (mLastTouchPos - mTouchPos) * getEdgeResistance();

Просмотреть файл

@ -597,14 +597,14 @@ public class PanZoomController
return getVelocity() < STOPPED_THRESHOLD; return getVelocity() < STOPPED_THRESHOLD;
} }
PointF getDisplacement() { PointF resetDisplacement() {
return new PointF(mX.resetDisplacement(), mY.resetDisplacement()); return new PointF(mX.resetDisplacement(), mY.resetDisplacement());
} }
private void updatePosition() { private void updatePosition() {
mX.displace(); mX.displace();
mY.displace(); mY.displace();
PointF displacement = getDisplacement(); PointF displacement = resetDisplacement();
if (FloatUtils.fuzzyEquals(displacement.x, 0.0f) && FloatUtils.fuzzyEquals(displacement.y, 0.0f)) { if (FloatUtils.fuzzyEquals(displacement.x, 0.0f) && FloatUtils.fuzzyEquals(displacement.y, 0.0f)) {
return; return;
} }
@ -947,12 +947,6 @@ public class PanZoomController
sendPointToGecko("Gesture:LongPress", motionEvent); sendPointToGecko("Gesture:LongPress", motionEvent);
} }
@Override
public boolean onDown(MotionEvent motionEvent) {
sendPointToGecko("Gesture:ShowPress", motionEvent);
return false;
}
@Override @Override
public boolean onSingleTapConfirmed(MotionEvent motionEvent) { public boolean onSingleTapConfirmed(MotionEvent motionEvent) {
GeckoApp.mFormAssistPopup.hide(); GeckoApp.mFormAssistPopup.hide();

Просмотреть файл

@ -57,15 +57,31 @@ class SubdocumentScrollHelper implements GeckoEventListener {
private final PanZoomController mPanZoomController; private final PanZoomController mPanZoomController;
private final Handler mUiHandler; 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; 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; 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; 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; private boolean mScrollSucceeded;
SubdocumentScrollHelper(PanZoomController controller) { SubdocumentScrollHelper(PanZoomController controller) {
mPanZoomController = controller; mPanZoomController = controller;
// mUiHandler will be bound to the UI thread since that's where this constructor runs // mUiHandler will be bound to the UI thread since that's where this constructor runs
mUiHandler = new Handler(); mUiHandler = new Handler();
mPendingDisplacement = new PointF();
GeckoAppShell.registerGeckoEventListener(MESSAGE_PANNING_OVERRIDE, this); GeckoAppShell.registerGeckoEventListener(MESSAGE_PANNING_OVERRIDE, this);
GeckoAppShell.registerGeckoEventListener(MESSAGE_CANCEL_OVERRIDE, this); GeckoAppShell.registerGeckoEventListener(MESSAGE_CANCEL_OVERRIDE, this);
@ -79,12 +95,11 @@ class SubdocumentScrollHelper implements GeckoEventListener {
if (! mOverrideScrollAck) { if (! mOverrideScrollAck) {
mOverrideScrollPending = true; mOverrideScrollPending = true;
mPendingDisplacement.x += displacement.x;
mPendingDisplacement.y += displacement.y;
return true; return true;
} }
mOverrideScrollAck = false;
mOverrideScrollPending = false;
JSONObject json = new JSONObject(); JSONObject json = new JSONObject();
try { try {
json.put("x", displacement.x); json.put("x", displacement.x);
@ -94,6 +109,13 @@ class SubdocumentScrollHelper implements GeckoEventListener {
} }
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent(MESSAGE_SCROLL, json.toString())); 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; return true;
} }
@ -128,7 +150,7 @@ class SubdocumentScrollHelper implements GeckoEventListener {
mOverrideScrollAck = true; mOverrideScrollAck = true;
mScrollSucceeded = message.getBoolean("scrolled"); mScrollSucceeded = message.getBoolean("scrolled");
if (mOverridePanning && mOverrideScrollPending) { if (mOverridePanning && mOverrideScrollPending) {
scrollBy(mPanZoomController.getDisplacement()); scrollBy(mPendingDisplacement);
} }
} }
} catch (Exception e) { } catch (Exception e) {

Просмотреть файл

@ -81,6 +81,7 @@ let Downloads = {
switch (aTopic) { switch (aTopic) {
case "dl-failed": case "dl-failed":
case "dl-cancel": case "dl-cancel":
break;
case "dl-done": case "dl-done":
if (!this._getElementForDownload(download.id)) { if (!this._getElementForDownload(download.id)) {
let item = this._createItem(downloadTemplate, { let item = this._createItem(downloadTemplate, {
@ -102,10 +103,10 @@ let Downloads = {
this._stmt.finalize(); this._stmt.finalize();
this._stmt = this._dlmgr.DBConnection.createStatement( this._stmt = this._dlmgr.DBConnection.createStatement(
"SELECT id, name, source, state, startTime, endTime, referrer, " + "SELECT id, name, source, startTime, endTime, referrer, " +
"currBytes, maxBytes, state IN (?1, ?2, ?3, ?4, ?5) isActive " + "currBytes, maxBytes " +
"FROM moz_downloads " + "FROM moz_downloads " +
"WHERE NOT isActive " + "WHERE state = :download_state " +
"ORDER BY endTime DESC"); "ORDER BY endTime DESC");
}, },
@ -177,11 +178,7 @@ let Downloads = {
clearTimeout(this._timeoutID); clearTimeout(this._timeoutID);
this._stmt.reset(); this._stmt.reset();
this._stmt.bindInt32Parameter(0, Ci.nsIDownloadManager.DOWNLOAD_NOTSTARTED); this._stmt.params.download_state = Ci.nsIDownloadManager.DOWNLOAD_FINISHED;
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);
// Take a quick break before we actually start building the list // Take a quick break before we actually start building the list
let self = this; let self = this;

Просмотреть файл

@ -2213,6 +2213,8 @@ Tab.prototype = {
if (contentWin != contentWin.top) if (contentWin != contentWin.top)
return; return;
this._hostChanged = true;
let browser = BrowserApp.getBrowserForWindow(contentWin); let browser = BrowserApp.getBrowserForWindow(contentWin);
let uri = browser.currentURI.spec; let uri = browser.currentURI.spec;
let documentURI = ""; 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) { onSecurityChange: function(aWebProgress, aRequest, aState) {
let mode = "unknown"; // Don't need to do anything if the data we use to update the UI hasn't changed
if (aState & Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL) if (this._state == aState && !this._hostChanged)
mode = "identified"; return;
else if (aState & Ci.nsIWebProgressListener.STATE_SECURE_HIGH)
mode = "verified"; this._state = aState;
else if (aState & Ci.nsIWebProgressListener.STATE_IS_BROKEN) this._hostChanged = false;
mode = "mixed";
else let identity = IdentityHandler.checkIdentity(aState, this.browser);
mode = "unknown";
let message = { let message = {
gecko: { gecko: {
type: "Content:SecurityChange", type: "Content:SecurityChange",
tabID: this.id, tabID: this.id,
mode: mode identity: identity
} }
}; };
sendMessageToJava(message); sendMessageToJava(message);
}, },
onProgressChange: function(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress) { onProgressChange: function(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress) {
@ -2521,13 +2526,44 @@ Tab.prototype = {
var BrowserEventHandler = { var BrowserEventHandler = {
init: function init() { init: function init() {
Services.obs.addObserver(this, "Gesture:SingleTap", false); 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:CancelTouch", false);
Services.obs.addObserver(this, "Gesture:DoubleTap", false); Services.obs.addObserver(this, "Gesture:DoubleTap", false);
Services.obs.addObserver(this, "Gesture:Scroll", false); Services.obs.addObserver(this, "Gesture:Scroll", false);
Services.obs.addObserver(this, "dom-touch-listener-added", false); Services.obs.addObserver(this, "dom-touch-listener-added", false);
BrowserApp.deck.addEventListener("DOMUpdatePageReport", PopupBlockerObserver.onUpdatePageReport, 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) { observe: function(aSubject, aTopic, aData) {
@ -2562,12 +2598,19 @@ var BrowserEventHandler = {
// the user wanted, and neither can any non-root sub-frame, cancel the // the user wanted, and neither can any non-root sub-frame, cancel the
// override so that Java can handle panning the main document. // override so that Java can handle panning the main document.
let data = JSON.parse(aData); 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) { if (this._firstScrollEvent) {
while (this._scrollableElement != null && !this._elementCanScroll(this._scrollableElement, data.x, data.y)) while (this._scrollableElement != null && !this._elementCanScroll(this._scrollableElement, data.x, data.y))
this._scrollableElement = this._findScrollableElement(this._scrollableElement, false); this._scrollableElement = this._findScrollableElement(this._scrollableElement, false);
let doc = BrowserApp.selectedBrowser.contentDocument; 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" } }); sendMessageToJava({ gecko: { type: "Panning:CancelOverride" } });
return; return;
} }
@ -2584,32 +2627,12 @@ var BrowserEventHandler = {
} }
} else if (aTopic == "Gesture:CancelTouch") { } else if (aTopic == "Gesture:CancelTouch") {
this._cancelTapHighlight(); 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") { } else if (aTopic == "Gesture:SingleTap") {
let element = this._highlightElement; let element = this._highlightElement;
if (element && !SelectHelper.handleClick(element)) { if (element && !SelectHelper.handleClick(element)) {
try { try {
let data = JSON.parse(aData); let data = JSON.parse(aData);
this._sendMouseEvent("mousemove", element, data.x, data.y); this._sendMouseEvent("mousemove", element, data.x, data.y);
this._sendMouseEvent("mousedown", element, data.x, data.y); this._sendMouseEvent("mousedown", element, data.x, data.y);
this._sendMouseEvent("mouseup", element, data.x, data.y); this._sendMouseEvent("mouseup", element, data.x, data.y);
@ -2824,24 +2847,11 @@ var BrowserEventHandler = {
}, },
_elementCanScroll: function(elem, x, y) { _elementCanScroll: function(elem, x, y) {
let scrollX = true; let scrollX = (x < 0 && elem.scrollLeft > 0)
let scrollY = true; || (x > 0 && elem.scrollLeft < (elem.scrollWidth - elem.clientWidth));
if (x < 0) { let scrollY = (y < 0 && elem.scrollTop > 0)
if (elem.scrollLeft <= 0) { || (y > 0 && elem.scrollTop < (elem.scrollHeight - elem.clientHeight));
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;
}
return scrollX || scrollY; return scrollX || scrollY;
} }
@ -3593,14 +3603,11 @@ var ViewportHandler = {
if (doctype && /(WAP|WML|Mobile)/.test(doctype.publicId)) if (doctype && /(WAP|WML|Mobile)/.test(doctype.publicId))
return { defaultZoom: 1, autoSize: true, allowZoom: true, autoScale: true }; 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) if (aWindow.document instanceof XULDocument)
return { defaultZoom: 1, autoSize: true, allowZoom: false, autoScale: false }; return { defaultZoom: 1, autoSize: true, allowZoom: false, autoScale: false };
let windowUtils = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
// viewport details found here // 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/SafariHTMLRef/Articles/MetaTags.html
// http://developer.apple.com/safari/library/documentation/AppleApplications/Reference/SafariWebContent/UsingtheViewport/UsingtheViewport.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 allowZoomStr = windowUtils.getDocumentMetadata("viewport-user-scalable");
let allowZoom = !/^(0|no|false)$/.test(allowZoomStr); // WebKit allows 0, "no", or "false" 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); scale = this.clamp(scale, kViewportMinScale, kViewportMaxScale);
minScale = this.clamp(minScale, kViewportMinScale, kViewportMaxScale); minScale = this.clamp(minScale, kViewportMinScale, kViewportMaxScale);
maxScale = this.clamp(maxScale, 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) { function OverscrollController(aTab) {
this.tab = 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.state_and_country=%S, %S
identity.identified.title_with_country=%S (%S) identity.identified.title_with_country=%S (%S)
identity.encrypted2=Encrypted identity.encrypted2=Encrypted
identity.unencrypted2=Not encrypted
identity.unknown.tooltip=This website does not supply identity information.
identity.ownerUnknown2=(unknown) identity.ownerUnknown2=(unknown)
# Geolocation UI # Geolocation UI

Просмотреть файл

@ -46,3 +46,55 @@ class TestSwitchFrame(MarionetteTestCase):
self.assertEqual("Marionette IFrame Test", self.marionette.execute_script("return window.document.title;")) self.assertEqual("Marionette IFrame Test", self.marionette.execute_script("return window.document.title;"))
self.marionette.switch_to_frame("test_iframe") self.marionette.switch_to_frame("test_iframe")
self.assertTrue("test.html" in self.marionette.get_url()) 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" /> <textbox id="textInput3" class="asdf" size="6" value="test" label="input" />
</vbox> </vbox>
<iframe id="iframe" name="iframename" src="test2.xul"/>
<iframe id="iframe" name="iframename" src="test_nested_iframe.xul"/>
</dialog> </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.timer = null;
this.marionetteLog = new MarionetteLogObj(); this.marionetteLog = new MarionetteLogObj();
this.command_id = null; this.command_id = null;
this.mainFrame = null; //topmost chrome frame
this.curFrame = null; //subframe that currently has focus
//register all message listeners //register all message listeners
this.messageManager.addMessageListener("Marionette:ok", this); this.messageManager.addMessageListener("Marionette:ok", this);
@ -219,10 +221,15 @@ MarionetteDriverActor.prototype = {
*/ */
getCurrentWindow: function MDA_getCurrentWindow() { getCurrentWindow: function MDA_getCurrentWindow() {
let type = null; let type = null;
if (appName != "B2G" && this.context == "content") { if (this.curFrame == null) {
type = 'navigator:browser'; 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 * True if this is the first time we're talking to this browser
*/ */
startBrowser: function MDA_startBrowser(win, newSession) { startBrowser: function MDA_startBrowser(win, newSession) {
this.mainFrame = win;
this.curFrame = null;
this.addBrowser(win); this.addBrowser(win);
this.curBrowser.newSession = newSession; this.curBrowser.newSession = newSession;
this.curBrowser.startSession(newSession); this.curBrowser.startSession(newSession);
@ -711,7 +720,7 @@ MarionetteDriverActor.prototype = {
* Searches based on name, then id. * Searches based on name, then id.
* *
* @param object aRequest * @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) { switchToWindow: function MDA_switchToWindow(aRequest) {
let winEn = this.getWinEnumerator(); let winEn = this.getWinEnumerator();
@ -738,10 +747,73 @@ MarionetteDriverActor.prototype = {
* Switch to a given frame within the current window * Switch to a given frame within the current window
* *
* @param object aRequest * @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) { 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; 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); 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 * 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 winUtil = content.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIDOMWindowUtils);
let listenerId = null; //unique ID of this listener let listenerId = null; //unique ID of this listener
let activeFrame = null; let activeFrame = null;
let win = content; let curWindow = content;
let elementManager = new ElementManager([]); let elementManager = new ElementManager([]);
/** /**
@ -179,6 +179,7 @@ function sendError(message, status, trace, command_id) {
*/ */
function resetValues() { function resetValues() {
marionetteTimeout = null; marionetteTimeout = null;
curWin = content;
} }
/** /**
@ -227,9 +228,9 @@ function createExecuteContentSandbox(aWindow, marionette, args) {
*/ */
function executeScript(msg, directInject) { function executeScript(msg, directInject) {
let script = msg.json.value; 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) if (!sandbox)
return; return;
@ -299,7 +300,7 @@ function executeJSScript(msg) {
* method is called, or if it times out. * method is called, or if it times out.
*/ */
function executeWithCallback(msg, timeout) { function executeWithCallback(msg, timeout) {
win.addEventListener("unload", errUnload, false); curWindow.addEventListener("unload", errUnload, false);
let script = msg.json.value; let script = msg.json.value;
let command_id = msg.json.id; let command_id = msg.json.id;
@ -308,21 +309,21 @@ function executeWithCallback(msg, timeout) {
// However Selenium code returns 28, see // However Selenium code returns 28, see
// http://code.google.com/p/selenium/source/browse/trunk/javascript/firefox-driver/js/evaluate.js. // http://code.google.com/p/selenium/source/browse/trunk/javascript/firefox-driver/js/evaluate.js.
// We'll stay compatible with the Selenium code. // We'll stay compatible with the Selenium code.
let timeoutId = win.setTimeout(function() { let timeoutId = curWindow.setTimeout(function() {
contentAsyncReturnFunc('timed out', 28); contentAsyncReturnFunc('timed out', 28);
}, marionetteTimeout); }, marionetteTimeout);
win.addEventListener('error', function win__onerror(evt) { curWindow.addEventListener('error', function win__onerror(evt) {
win.removeEventListener('error', win__onerror, true); curWindow.removeEventListener('error', win__onerror, true);
contentAsyncReturnFunc(evt, 17); contentAsyncReturnFunc(evt, 17);
return true; return true;
}, true); }, true);
function contentAsyncReturnFunc(value, status) { function contentAsyncReturnFunc(value, status) {
win.removeEventListener("unload", errUnload, false); curWindow.removeEventListener("unload", errUnload, false);
/* clear all timeouts potentially generated by the script*/ /* clear all timeouts potentially generated by the script*/
for(let i=0; i<=timeoutId; i++) { for(let i=0; i<=timeoutId; i++) {
win.clearTimeout(i); curWindow.clearTimeout(i);
} }
sendSyncMessage("Marionette:testLog", {value: elementManager.wrapValue(marionetteLogObj.getLogs())}); sendSyncMessage("Marionette:testLog", {value: elementManager.wrapValue(marionetteLogObj.getLogs())});
@ -349,9 +350,9 @@ function executeWithCallback(msg, timeout) {
"__marionetteFunc.apply(null, __marionetteParams); "; "__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) if (!sandbox)
return; return;
@ -387,37 +388,32 @@ function setSearchTimeout(msg) {
* All other navigation is handled by the server (in chrome space). * All other navigation is handled by the server (in chrome space).
*/ */
function goUrl(msg) { function goUrl(msg) {
if (activeFrame != null) { curWindow.location = msg.json.value;
win.document.location = msg.json.value; //TODO: replace this with DOMContentLoaded event listening when Bug 720714 is resolved
//TODO: replace this with event firing when Bug 720714 is resolved let checkTimer = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer);
let checkTimer = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer); let checkLoad = function () {
let checkLoad = function () { if (curWindow.document.readyState == "complete") {
if (win.document.readyState == "complete") { sendOk();
sendOk(); }
} else {
else { checkTimer.initWithCallback(checkLoad, 100, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
checkTimer.initWithCallback(checkLoad, 100, Components.interfaces.nsITimer.TYPE_ONE_SHOT); }
} };
}; checkTimer.initWithCallback(checkLoad, 100, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
checkLoad();
}
else {
sendAsyncMessage("Marionette:goUrl", {value: msg.json.value});
}
} }
/** /**
* Get the current URI * Get the current URI
*/ */
function getUrl(msg) { function getUrl(msg) {
sendResponse({value: win.location.href}); sendResponse({value: curWindow.location.href});
} }
/** /**
* Go back in history * Go back in history
*/ */
function goBack(msg) { function goBack(msg) {
win.history.back(); curWindow.history.back();
sendOk(); sendOk();
} }
@ -425,7 +421,7 @@ function goBack(msg) {
* Go forward in history * Go forward in history
*/ */
function goForward(msg) { function goForward(msg) {
win.history.forward(); curWindow.history.forward();
sendOk(); sendOk();
} }
@ -433,7 +429,7 @@ function goForward(msg) {
* Refresh the page * Refresh the page
*/ */
function refresh(msg) { function refresh(msg) {
win.location.reload(true); curWindow.location.reload(true);
let listen = function() { removeEventListener("DOMContentLoaded", arguments.callee, false); sendOk() } ; let listen = function() { removeEventListener("DOMContentLoaded", arguments.callee, false); sendOk() } ;
addEventListener("DOMContentLoaded", listen, false); addEventListener("DOMContentLoaded", listen, false);
} }
@ -445,8 +441,7 @@ function findElementContent(msg) {
let id; let id;
try { try {
let notify = function(id) { sendResponse({value:id});}; let notify = function(id) { sendResponse({value:id});};
let curWin = activeFrame ? win.frames[activeFrame] : win; id = elementManager.find(curWindow, msg.json, notify, false);
id = elementManager.find(curWin, msg.json, notify, false);
} }
catch (e) { catch (e) {
sendError(e.message, e.num, e.stack); sendError(e.message, e.num, e.stack);
@ -460,8 +455,7 @@ function findElementsContent(msg) {
let id; let id;
try { try {
let notify = function(id) { sendResponse({value:id});}; let notify = function(id) { sendResponse({value:id});};
let curWin = activeFrame ? win.frames[activeFrame] : win; id = elementManager.find(curWindow, msg.json, notify, true);
id = elementManager.find(curWin, msg.json, notify, true);
} }
catch (e) { catch (e) {
sendError(e.message, e.num, e.stack); sendError(e.message, e.num, e.stack);
@ -474,8 +468,7 @@ function findElementsContent(msg) {
function clickElement(msg) { function clickElement(msg) {
let el; let el;
try { try {
//el = elementManager.click(msg.json.element, win); el = elementManager.getKnownElement(msg.json.element, curWindow);
el = elementManager.getKnownElement(msg.json.element, win);
utils.click(el); utils.click(el);
sendOk(); sendOk();
} }
@ -489,7 +482,7 @@ function clickElement(msg) {
*/ */
function getElementAttribute(msg) { function getElementAttribute(msg) {
try { 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)}); sendResponse({value: utils.getElementAttribute(el, msg.json.name)});
} }
catch (e) { catch (e) {
@ -502,7 +495,7 @@ function getElementAttribute(msg) {
*/ */
function getElementText(msg) { function getElementText(msg) {
try { try {
let el = elementManager.getKnownElement(msg.json.element, win); let el = elementManager.getKnownElement(msg.json.element, curWindow);
sendResponse({value: utils.getElementText(el)}); sendResponse({value: utils.getElementText(el)});
} }
catch (e) { catch (e) {
@ -515,7 +508,7 @@ function getElementText(msg) {
*/ */
function isElementDisplayed(msg) { function isElementDisplayed(msg) {
try { try {
let el = elementManager.getKnownElement(msg.json.element, win); let el = elementManager.getKnownElement(msg.json.element, curWindow);
sendResponse({value: utils.isElementDisplayed(el)}); sendResponse({value: utils.isElementDisplayed(el)});
} }
catch (e) { catch (e) {
@ -528,7 +521,7 @@ function isElementDisplayed(msg) {
*/ */
function isElementEnabled(msg) { function isElementEnabled(msg) {
try { try {
let el = elementManager.getKnownElement(msg.json.element, win); let el = elementManager.getKnownElement(msg.json.element, curWindow);
sendResponse({value: utils.isElementEnabled(el)}); sendResponse({value: utils.isElementEnabled(el)});
} }
catch (e) { catch (e) {
@ -541,7 +534,7 @@ function isElementEnabled(msg) {
*/ */
function isElementSelected(msg) { function isElementSelected(msg) {
try { try {
let el = elementManager.getKnownElement(msg.json.element, win); let el = elementManager.getKnownElement(msg.json.element, curWindow);
sendResponse({value: utils.isElementSelected(el)}); sendResponse({value: utils.isElementSelected(el)});
} }
catch (e) { catch (e) {
@ -554,7 +547,7 @@ function isElementSelected(msg) {
*/ */
function sendKeysToElement(msg) { function sendKeysToElement(msg) {
try { try {
let el = elementManager.getKnownElement(msg.json.element, win); let el = elementManager.getKnownElement(msg.json.element, curWindow);
utils.sendKeysToElement(el, msg.json.value); utils.sendKeysToElement(el, msg.json.value);
sendOk(); sendOk();
} }
@ -568,7 +561,7 @@ function sendKeysToElement(msg) {
*/ */
function clearElement(msg) { function clearElement(msg) {
try { try {
let el = elementManager.getKnownElement(msg.json.element, win); let el = elementManager.getKnownElement(msg.json.element, curWindow);
utils.clearElement(el); utils.clearElement(el);
sendOk(); sendOk();
} }
@ -584,21 +577,19 @@ function clearElement(msg) {
function switchToFrame(msg) { function switchToFrame(msg) {
let foundFrame = null; let foundFrame = null;
if ((msg.json.value == null) && (msg.json.element == null)) { if ((msg.json.value == null) && (msg.json.element == null)) {
win = content; curWindow = content;
activeFrame = null; curWindow.focus();
content.focus();
sendOk(); sendOk();
return; return;
} }
if (msg.json.element != undefined) { if (msg.json.element != undefined) {
if (elementManager.seenItems[msg.json.element] != undefined) { if (elementManager.seenItems[msg.json.element] != undefined) {
let wantedFrame = elementManager.getKnownElement(msg.json.element, win);//HTMLIFrameElement let wantedFrame = elementManager.getKnownElement(msg.json.element, curWindow); //HTMLIFrameElement
let numFrames = win.frames.length; let numFrames = curWindow.frames.length;
for (let i = 0; i < numFrames; i++) { for (let i = 0; i < numFrames; i++) {
if (win.frames[i].frameElement == wantedFrame) { if (curWindow.frames[i].frameElement == wantedFrame) {
win = win.frames[i]; curWindow = curWindow.frames[i];
activeFrame = i; curWindow.focus();
win.focus();
sendOk(); sendOk();
return; return;
} }
@ -608,10 +599,10 @@ function switchToFrame(msg) {
switch(typeof(msg.json.value)) { switch(typeof(msg.json.value)) {
case "string" : case "string" :
let foundById = null; let foundById = null;
let numFrames = win.frames.length; let numFrames = curWindow.frames.length;
for (let i = 0; i < numFrames; i++) { for (let i = 0; i < numFrames; i++) {
//give precedence to name //give precedence to name
let frame = win.frames[i]; let frame = curWindow.frames[i];
let frameElement = frame.frameElement; let frameElement = frame.frameElement;
if (frameElement.name == msg.json.value) { if (frameElement.name == msg.json.value) {
foundFrame = i; foundFrame = i;
@ -625,17 +616,14 @@ function switchToFrame(msg) {
} }
break; break;
case "number": case "number":
if (win.frames[msg.json.value] != undefined) { if (curWindow.frames[msg.json.value] != undefined) {
foundFrame = msg.json.value; foundFrame = msg.json.value;
} }
break; break;
} }
//TODO: implement index
if (foundFrame != null) { if (foundFrame != null) {
let frameWindow = win.frames[foundFrame]; curWindow = curWindow.frames[foundFrame];
activeFrame = foundFrame; curWindow.focus();
win = frameWindow;
win.focus();
sendOk(); sendOk();
} else { } else {
sendError("Unable to locate frame: " + msg.json.value, 8, null); sendError("Unable to locate frame: " + msg.json.value, 8, null);

Просмотреть файл

@ -48,8 +48,8 @@ def main():
entity = get_value(jsonFilename, key) entity = get_value(jsonFilename, key)
if passesRestrictions(options.talos_json_url, entity["url"]): if passesRestrictions(options.talos_json_url, entity["url"]):
# the key is at the same time the filename e.g. talos.zip # 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) download_file(entity["url"], entity["path"], key)
print "INFO: %s -> %s" % (entity["url"], os.path.join(entity["path"], key))
else: else:
print "ERROR: You have tried to download a file " + \ print "ERROR: You have tried to download a file " + \
"from: %s " % fileUrl + \ "from: %s " % fileUrl + \

Двоичные данные
toolkit/mozapps/update/test/unit/data/old_version_mar.mar Normal file

Двоичный файл не отображается.

Двоичный файл не отображается.

Просмотреть файл

@ -87,11 +87,14 @@ const APPLY_TO_DIR_SUFFIX = "_applyToDir/";
const HELPER_BIN_FILE = "TestAUSHelper" + BIN_SUFFIX; const HELPER_BIN_FILE = "TestAUSHelper" + BIN_SUFFIX;
const MAR_COMPLETE_FILE = "data/complete.mar"; const MAR_COMPLETE_FILE = "data/complete.mar";
const MAR_PARTIAL_FILE = "data/partial.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 UPDATER_BIN_FILE = "updater" + BIN_SUFFIX;
const MAINTENANCE_SERVICE_BIN_FILE = "maintenanceservice.exe"; const MAINTENANCE_SERVICE_BIN_FILE = "maintenanceservice.exe";
const MAINTENANCE_SERVICE_INSTALLER_BIN_FILE = "maintenanceservice_installer.exe"; const MAINTENANCE_SERVICE_INSTALLER_BIN_FILE = "maintenanceservice_installer.exe";
const UPDATE_SETTINGS_INI_FILE = "update-settings.ini"; 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 UPDATES_DIR_SUFFIX = "_mar";
const LOG_COMPLETE_SUCCESS = "data/complete_log_success"; 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_0150_appBinReplaced_xp_win_complete.js]
[test_0151_appBinPatched_xp_win_partial.js] [test_0151_appBinPatched_xp_win_partial.js]
[test_0160_appInUse_xp_win_complete.js] [test_0160_appInUse_xp_win_complete.js]

Просмотреть файл

@ -16,6 +16,7 @@ const allowedOrigins = [
"https://www.facebook.com", "https://www.facebook.com",
"https://accounts.google.com", "https://accounts.google.com",
"https://www.google.com", "https://www.google.com",
"https://twitter.com",
"https://api.twitter.com", "https://api.twitter.com",
]; ];

Просмотреть файл

@ -139,6 +139,10 @@
onset="this.setAttribute('timeout', val); return val;" onset="this.setAttribute('timeout', val); return val;"
onget="return parseInt(this.getAttribute('timeout')) || 0;"/> 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"> <method name="onSearchBegin">
<body><![CDATA[ <body><![CDATA[
this._fireEvent("searchbegin"); this._fireEvent("searchbegin");
@ -378,7 +382,7 @@
param: this, param: this,
onStartLookup: function(aSearchString, aPreviousSearchResult, aListener) { onStartLookup: function(aSearchString, aPreviousSearchResult, aListener) {
this.session.startSearch(aSearchString, this.session.startSearch(aSearchString,
this.param.getAttribute("autocompletesearchparam") || "", this.param.searchParam,
aPreviousSearchResult && aPreviousSearchResult.lastResult, aPreviousSearchResult && aPreviousSearchResult.lastResult,
aListener); aListener);
}, },