Merge fx-team to m-c.

This commit is contained in:
Ryan VanderMeulen 2014-04-18 21:50:12 -04:00
commit e6c203239c
52 changed files with 594 additions and 206 deletions

View File

@ -199,6 +199,11 @@ AutoCompleteResult.prototype =
return "";
},
getFinalCompleteValueAt: function(aIndex)
{
return this.getValueAt(aIndex);
},
removeValueAt: function (aRowIndex, aRemoveFromDb) {},
// nsISupports implementation

View File

@ -122,37 +122,38 @@ tabbrowser {
visibility: hidden;
}
.tab-background {
/* Explicitly set the visibility to override the value (collapsed)
* we inherit from #TabsToolbar[collapsed] upon opening a browser window. */
visibility: visible;
/* The transition is only delayed for opening tabs. */
transition: visibility 0ms 25ms;
}
.tab-background:not([fadein]):not([pinned]) {
visibility: hidden;
/* Closing tabs are hidden without a delay. */
transition-delay: 0ms;
}
.tab-background,
.tab-close-button,
.tab-label {
/* Explicitly set the visibility to override the value (collapsed)
* we inherit from #TabsToolbar[collapsed] upon opening a browser window. */
visibility: visible;
}
.tab-background[fadein] {
/* This transition is only wanted for opening tabs. */
transition: visibility 0ms 25ms;
}
.tab-background:not([fadein]) {
visibility: hidden;
}
.tab-close-button[fadein],
.tab-label[fadein] {
/* This transition is only wanted for opening tabs. */
transition: opacity 70ms 230ms,
visibility 0ms 230ms;
}
.tab-close-button:not([fadein]):not([pinned]),
.tab-label:not([fadein]):not([pinned]) {
.tab-close-button:not([fadein]),
.tab-label:not([fadein]) {
visibility: collapse;
opacity: .6;
}
.tab-throbber:not([fadein]):not([pinned]),
.tab-icon-image:not([fadein]):not([pinned]) {
.tab-throbber:not([fadein]),
.tab-icon-image:not([fadein]) {
display: none;
}

View File

@ -19,9 +19,10 @@ let gPage = {
// Listen for 'unload' to unregister this page.
addEventListener("unload", this, false);
// Listen for toggle button clicks.
let button = document.getElementById("newtab-toggle");
button.addEventListener("click", this, false);
// XXX bug 991111 - Not all click events are correctly triggered when
// listening from xhtml nodes -- in particular middle clicks on sites, so
// listen from the xul window and filter then delegate
addEventListener("click", this, false);
// Initialize sponsored panel
this._sponsoredPanel = document.getElementById("sponsored-panel");
@ -196,7 +197,22 @@ let gPage = {
gAllPages.unregister(this);
break;
case "click":
gAllPages.enabled = !gAllPages.enabled;
let {button, target} = aEvent;
if (target.id == "newtab-toggle") {
if (button == 0) {
gAllPages.enabled = !gAllPages.enabled;
}
break;
}
// Go up ancestors until we find a Site or not
while (target) {
if (target.hasOwnProperty("_newtabSite")) {
target._newtabSite.onClick(aEvent);
break;
}
target = target.parentNode;
}
break;
case "dragover":
if (gDrag.isValid(aEvent) && gDrag.draggedSite)

View File

@ -170,10 +170,6 @@ Site.prototype = {
this._node.addEventListener("dragend", this, false);
this._node.addEventListener("mouseover", this, false);
// XXX bug 991111 - Not all click events are correctly triggered when
// listening from the xhtml node, so listen from the xul window and filter
addEventListener("click", this, false);
// Specially treat the sponsored icon to prevent regular hover effects
let sponsored = this._querySelector(".newtab-control-sponsored");
sponsored.addEventListener("mouseover", () => {
@ -218,11 +214,19 @@ Site.prototype = {
/**
* Handles site click events.
*/
_onClick: function Site_onClick(aEvent) {
let target = aEvent.target;
onClick: function Site_onClick(aEvent) {
let {button, target} = aEvent;
if (target.classList.contains("newtab-link") ||
target.parentElement.classList.contains("newtab-link")) {
this._recordSiteClicked(this.cell.index);
// Record for primary and middle clicks
if (button == 0 || button == 1) {
this._recordSiteClicked(this.cell.index);
}
return;
}
// Only handle primary clicks for the remaining targets
if (button != 0) {
return;
}
@ -242,13 +246,6 @@ Site.prototype = {
*/
handleEvent: function Site_handleEvent(aEvent) {
switch (aEvent.type) {
case "click":
// Check the bitmask if the click event is for the site's descendants
if (this._node.compareDocumentPosition(aEvent.target) &
this._node.DOCUMENT_POSITION_CONTAINED_BY) {
this._onClick(aEvent);
}
break;
case "mouseover":
this._node.removeEventListener("mouseover", this, false);
this._speculativeConnect();

View File

@ -290,7 +290,6 @@
return;
this.moveTabTo(aTab, this._numPinnedTabs - 1);
aTab.setAttribute("fadein", "true");
aTab.removeAttribute("pinned");
aTab.style.MozMarginStart = "";
this.tabContainer._unlockTabSizing();

View File

@ -17,6 +17,7 @@ skip-if = os == "mac" # Intermittent failures, bug 898317
[browser_newtab_bug876313.js]
[browser_newtab_bug991111.js]
[browser_newtab_bug991210.js]
[browser_newtab_bug998387.js]
[browser_newtab_disable.js]
[browser_newtab_drag_drop.js]
[browser_newtab_drag_drop_ext.js]

View File

@ -8,7 +8,7 @@ function runTests() {
// Remember if the click handler was triggered
let cell = getCell(0);
let clicked = false;
cell.site._onClick = e => {
cell.site.onClick = e => {
clicked = true;
executeSoon(TestRunner.next);
};

View File

@ -0,0 +1,25 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
function runTests() {
yield setLinks("0");
yield addNewTabPageTab();
// Remember if the click handler was triggered
let {site} = getCell(0);
let origOnClick = site.onClick;
let clicked = false;
site.onClick = e => {
origOnClick.call(site, e);
clicked = true;
executeSoon(TestRunner.next);
};
// Send a middle-click and make sure it happened
let block = getContentDocument().querySelector(".newtab-control-block");
yield EventUtils.synthesizeMouseAtCenter(block, {button: 1}, getContentWindow());
ok(clicked, "middle click triggered click listener");
// Make sure the cell didn't actually get blocked
checkGrid("0");
}

View File

@ -149,6 +149,7 @@ support-files =
[browser_dbg_listaddons.js]
[browser_dbg_listtabs-01.js]
[browser_dbg_listtabs-02.js]
[browser_dbg_listtabs-03.js]
[browser_dbg_location-changes-01-simple.js]
[browser_dbg_location-changes-02-blank.js]
[browser_dbg_location-changes-03-new.js]

View File

@ -0,0 +1,80 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Make sure the listTabs request works as specified.
*/
const TAB1_URL = EXAMPLE_URL + "doc_empty-tab-01.html";
let gTab1, gTab1Actor, gTab2, gTab2Actor, gClient;
function listTabs() {
let deferred = promise.defer();
gClient.listTabs(aResponse => {
deferred.resolve(aResponse.tabs);
});
return deferred.promise;
}
function request(params) {
let deferred = promise.defer();
gClient.request(params, deferred.resolve);
return deferred.promise;
}
function test() {
if (!DebuggerServer.initialized) {
DebuggerServer.init(() => true);
DebuggerServer.addBrowserActors();
}
let transport = DebuggerServer.connectPipe();
gClient = new DebuggerClient(transport);
gClient.connect(Task.async(function*(aType, aTraits) {
is(aType, "browser",
"Root actor should identify itself as a browser.");
let tab = yield addTab(TAB1_URL);
let tabs = yield listTabs();
is(tabs.length, 2, "Should be two tabs");
let tabGrip = tabs.filter(a => a.url ==TAB1_URL).pop();
ok(tabGrip, "Should have an actor for the tab");
let response = yield request({ to: tabGrip.actor, type: "attach" });
is(response.type, "tabAttached", "Should have attached");
tabs = yield listTabs();
response = yield request({ to: tabGrip.actor, type: "detach" });
is(response.type, "detached", "Should have detached");
let newGrip = tabs.filter(a => a.url ==TAB1_URL).pop();
is(newGrip.actor, tabGrip.actor, "Should have the same actor for the same tab");
response = yield request({ to: tabGrip.actor, type: "attach" });
is(response.type, "tabAttached", "Should have attached");
response = yield request({ to: tabGrip.actor, type: "detach" });
is(response.type, "detached", "Should have detached");
yield removeTab(tab);
yield closeConnection();
finish();
}));
}
function closeConnection() {
let deferred = promise.defer();
gClient.close(deferred.resolve);
return deferred.promise;
}
registerCleanupFunction(function() {
gTab1 = null;
gTab1Actor = null;
gTab2 = null;
gTab2Actor = null;
gClient = null;
});

View File

@ -12,6 +12,7 @@ import org.mozilla.gecko.background.common.log.Logger;
import org.mozilla.gecko.fxa.authenticator.AccountPickler;
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
import org.mozilla.gecko.fxa.sync.FxAccountSyncAdapter;
import org.mozilla.gecko.fxa.sync.FxAccountSyncStatusHelper;
import org.mozilla.gecko.sync.ThreadPool;
import org.mozilla.gecko.sync.Utils;
@ -61,6 +62,13 @@ public class FirefoxAccounts {
SyncHint.IGNORE_LOCAL_RATE_LIMIT,
SyncHint.IGNORE_REMOTE_SERVER_BACKOFF);
public interface SyncStatusListener {
public Context getContext();
public Account getAccount();
public void onSyncStarted();
public void onSyncFinished();
}
/**
* Returns true if a FirefoxAccount exists, false otherwise.
*
@ -200,13 +208,15 @@ public class FirefoxAccounts {
* Any hints are strictly optional: the actual requested sync is scheduled by
* the Android sync scheduler, and the sync mechanism may ignore hints as it
* sees fit.
* <p>
* It is safe to call this method from any thread.
*
* @param account to sync.
* @param syncHints to pass to sync.
* @param stagesToSync stage names to sync.
* @param stagesToSkip stage names to skip.
*/
public static void requestSync(Account account, EnumSet<SyncHint> syncHints, String[] stagesToSync, String[] stagesToSkip) {
public static void requestSync(final Account account, EnumSet<SyncHint> syncHints, String[] stagesToSync, String[] stagesToSkip) {
if (account == null) {
throw new IllegalArgumentException("account must not be null");
}
@ -221,8 +231,37 @@ public class FirefoxAccounts {
Logger.info(LOG_TAG, "Requesting sync.");
logSyncHints(syncHints);
for (String authority : AndroidFxAccount.getAndroidAuthorities()) {
ContentResolver.requestSync(account, authority, extras);
}
// We get strict mode warnings on some devices, so make the request on a
// background thread.
ThreadPool.run(new Runnable() {
@Override
public void run() {
for (String authority : AndroidFxAccount.getAndroidAuthorities()) {
ContentResolver.requestSync(account, authority, extras);
}
}
});
}
/**
* Start notifying <code>syncStatusListener</code> of sync status changes.
* <p>
* Only a weak reference to <code>syncStatusListener</code> is held.
*
* @param syncStatusListener to start notifying.
*/
public static void addSyncStatusListener(SyncStatusListener syncStatusListener) {
// startObserving null-checks its argument.
FxAccountSyncStatusHelper.getInstance().startObserving(syncStatusListener);
}
/**
* Stop notifying <code>syncStatusListener</code> of sync status changes.
*
* @param syncStatusListener to stop notifying.
*/
public static void removeSyncStatusListener(SyncStatusListener syncStatusListener) {
// stopObserving null-checks its argument.
FxAccountSyncStatusHelper.getInstance().stopObserving(syncStatusListener);
}
}

View File

@ -21,6 +21,7 @@ import org.mozilla.gecko.fxa.login.State.Action;
import org.mozilla.gecko.fxa.sync.FxAccountSyncStatusHelper;
import org.mozilla.gecko.sync.setup.activities.ActivityUtils;
import android.accounts.Account;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
@ -43,7 +44,7 @@ public class FxAccountConfirmAccountActivity extends FxAccountAbstractActivity i
// Set in onResume.
protected AndroidFxAccount fxAccount;
protected final SyncStatusDelegate syncStatusDelegate = new SyncStatusDelegate();
protected final InnerSyncStatusDelegate syncStatusDelegate = new InnerSyncStatusDelegate();
public FxAccountConfirmAccountActivity() {
super(CANNOT_RESUME_WHEN_NO_ACCOUNTS_EXIST);
@ -102,7 +103,7 @@ public class FxAccountConfirmAccountActivity extends FxAccountAbstractActivity i
}
}
protected class SyncStatusDelegate implements FxAccountSyncStatusHelper.Delegate {
protected class InnerSyncStatusDelegate implements FirefoxAccounts.SyncStatusListener {
protected final Runnable refreshRunnable = new Runnable() {
@Override
public void run() {
@ -111,17 +112,22 @@ public class FxAccountConfirmAccountActivity extends FxAccountAbstractActivity i
};
@Override
public AndroidFxAccount getAccount() {
return fxAccount;
public Context getContext() {
return FxAccountConfirmAccountActivity.this;
}
@Override
public void handleSyncStarted() {
public Account getAccount() {
return fxAccount.getAndroidAccount();
}
@Override
public void onSyncStarted() {
Logger.info(LOG_TAG, "Got sync started message; ignoring.");
}
@Override
public void handleSyncFinished() {
public void onSyncFinished() {
if (fxAccount == null) {
return;
}

View File

@ -19,7 +19,9 @@ import org.mozilla.gecko.fxa.login.State;
import org.mozilla.gecko.fxa.sync.FxAccountSyncStatusHelper;
import org.mozilla.gecko.sync.SyncConfiguration;
import android.accounts.Account;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
@ -72,7 +74,7 @@ public class FxAccountStatusFragment extends PreferenceFragment implements OnPre
// single account. (That is, it does not capture a single account instance.)
protected Runnable requestSyncRunnable;
protected final SyncStatusDelegate syncStatusDelegate = new SyncStatusDelegate();
protected final InnerSyncStatusDelegate syncStatusDelegate = new InnerSyncStatusDelegate();
protected Preference ensureFindPreference(String key) {
Preference preference = findPreference(key);
@ -240,7 +242,7 @@ public class FxAccountStatusFragment extends PreferenceFragment implements OnPre
setCheckboxesEnabled(true);
}
protected class SyncStatusDelegate implements FxAccountSyncStatusHelper.Delegate {
protected class InnerSyncStatusDelegate implements FirefoxAccounts.SyncStatusListener {
protected final Runnable refreshRunnable = new Runnable() {
@Override
public void run() {
@ -249,12 +251,17 @@ public class FxAccountStatusFragment extends PreferenceFragment implements OnPre
};
@Override
public AndroidFxAccount getAccount() {
return fxAccount;
public Context getContext() {
return FxAccountStatusFragment.this.getActivity();
}
@Override
public void handleSyncStarted() {
public Account getAccount() {
return fxAccount.getAndroidAccount();
}
@Override
public void onSyncStarted() {
if (fxAccount == null) {
return;
}
@ -263,7 +270,7 @@ public class FxAccountStatusFragment extends PreferenceFragment implements OnPre
}
@Override
public void handleSyncFinished() {
public void onSyncFinished() {
if (fxAccount == null) {
return;
}

View File

@ -8,6 +8,7 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.WeakHashMap;
import org.mozilla.gecko.fxa.FirefoxAccounts;
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
import android.content.ContentResolver;
@ -17,10 +18,6 @@ import android.content.SyncStatusObserver;
* Abstract away some details of Android's SyncStatusObserver.
* <p>
* Provides a simplified sync started/sync finished delegate.
* <p>
* We would prefer to register multiple observers, but it's of limited value
* right now, so we support only a single observer, and we are as tolerant as
* possible of non-paired add/remove calls.
*/
public class FxAccountSyncStatusHelper implements SyncStatusObserver {
@SuppressWarnings("unused")
@ -35,27 +32,18 @@ public class FxAccountSyncStatusHelper implements SyncStatusObserver {
return sInstance;
}
public interface Delegate {
public AndroidFxAccount getAccount();
public void handleSyncStarted();
public void handleSyncFinished();
}
// Used to unregister this as a listener.
protected Object handle = null;
// Maps delegates to whether their underlying Android account was syncing the
// last time we observed a status change.
protected Map<Delegate, Boolean> delegates = new WeakHashMap<Delegate, Boolean>();
protected Map<FirefoxAccounts.SyncStatusListener, Boolean> delegates = new WeakHashMap<FirefoxAccounts.SyncStatusListener, Boolean>();
@Override
public synchronized void onStatusChanged(int which) {
for (Entry<Delegate, Boolean> entry : delegates.entrySet()) {
final Delegate delegate = entry.getKey();
final AndroidFxAccount fxAccount = delegate.getAccount();
if (fxAccount == null) {
continue;
}
for (Entry<FirefoxAccounts.SyncStatusListener, Boolean> entry : delegates.entrySet()) {
final FirefoxAccounts.SyncStatusListener delegate = entry.getKey();
final AndroidFxAccount fxAccount = new AndroidFxAccount(delegate.getContext(), delegate.getAccount());
final boolean active = fxAccount.isCurrentlySyncing();
// Remember for later.
boolean wasActiveLastTime = entry.getValue();
@ -64,11 +52,11 @@ public class FxAccountSyncStatusHelper implements SyncStatusObserver {
if (active && !wasActiveLastTime) {
// We've started a sync.
delegate.handleSyncStarted();
delegate.onSyncStarted();
}
if (!active && wasActiveLastTime) {
// We've finished a sync.
delegate.handleSyncFinished();
delegate.onSyncFinished();
}
}
}
@ -89,7 +77,7 @@ public class FxAccountSyncStatusHelper implements SyncStatusObserver {
}
}
public synchronized void startObserving(Delegate delegate) {
public synchronized void startObserving(FirefoxAccounts.SyncStatusListener delegate) {
if (delegate == null) {
throw new IllegalArgumentException("delegate must not be null");
}
@ -103,7 +91,7 @@ public class FxAccountSyncStatusHelper implements SyncStatusObserver {
delegates.put(delegate, Boolean.FALSE);
}
public synchronized void stopObserving(Delegate delegate) {
public synchronized void stopObserving(FirefoxAccounts.SyncStatusListener delegate) {
delegates.remove(delegate);
// If we are the last delegate leaving the party, stop listening.
if (delegates.isEmpty()) {

View File

@ -410,7 +410,7 @@ nsAutoCompleteController::HandleKeyNavigation(uint32_t aKey, bool *_retval)
if (selectedIndex >= 0) {
// A result is selected, so fill in its value
nsAutoString value;
if (NS_SUCCEEDED(GetResultValueAt(selectedIndex, true, value))) {
if (NS_SUCCEEDED(GetResultValueAt(selectedIndex, false, value))) {
input->SetTextValue(value);
input->SelectTextRange(value.Length(), value.Length());
}
@ -481,7 +481,7 @@ nsAutoCompleteController::HandleKeyNavigation(uint32_t aKey, bool *_retval)
if (selectedIndex >= 0) {
// The pop-up is open and has a selection, take its value
nsAutoString value;
if (NS_SUCCEEDED(GetResultValueAt(selectedIndex, true, value))) {
if (NS_SUCCEEDED(GetResultValueAt(selectedIndex, false, value))) {
input->SetTextValue(value);
input->SelectTextRange(value.Length(), value.Length());
}
@ -579,7 +579,7 @@ nsAutoCompleteController::HandleDelete(bool *_retval)
input->GetCompleteDefaultIndex(&shouldComplete);
if (shouldComplete) {
nsAutoString value;
if (NS_SUCCEEDED(GetResultValueAt(index, true, value))) {
if (NS_SUCCEEDED(GetResultValueAt(index, false, value))) {
CompleteValue(value);
}
}
@ -616,7 +616,7 @@ nsAutoCompleteController::GetResultAt(int32_t aIndex, nsIAutoCompleteResult** aR
NS_IMETHODIMP
nsAutoCompleteController::GetValueAt(int32_t aIndex, nsAString & _retval)
{
GetResultLabelAt(aIndex, false, _retval);
GetResultLabelAt(aIndex, _retval);
return NS_OK;
}
@ -624,7 +624,7 @@ nsAutoCompleteController::GetValueAt(int32_t aIndex, nsAString & _retval)
NS_IMETHODIMP
nsAutoCompleteController::GetLabelAt(int32_t aIndex, nsAString & _retval)
{
GetResultLabelAt(aIndex, false, _retval);
GetResultLabelAt(aIndex, _retval);
return NS_OK;
}
@ -662,6 +662,18 @@ nsAutoCompleteController::GetImageAt(int32_t aIndex, nsAString & _retval)
return result->GetImageAt(rowIndex, _retval);
}
NS_IMETHODIMP
nsAutoCompleteController::GetFinalCompleteValueAt(int32_t aIndex,
nsAString & _retval)
{
int32_t rowIndex;
nsIAutoCompleteResult* result;
nsresult rv = GetResultAt(aIndex, &result, &rowIndex);
NS_ENSURE_SUCCESS(rv, rv);
return result->GetFinalCompleteValueAt(rowIndex, _retval);
}
NS_IMETHODIMP
nsAutoCompleteController::SetSearchString(const nsAString &aSearchString)
{
@ -1181,9 +1193,14 @@ nsAutoCompleteController::EnterMatch(bool aIsPopupSelection)
// enter it into the textbox. If completeselectedindex is true, or
// EnterMatch was called via other means, for instance pressing Enter,
// don't fill in the value as it will have already been filled in as
// needed.
if (!completeSelection || aIsPopupSelection)
GetResultValueAt(selectedIndex, true, value);
// needed, unless the final complete value differs.
nsAutoString finalValue, inputValue;
GetResultValueAt(selectedIndex, true, finalValue);
input->GetTextValue(inputValue);
if (!completeSelection || aIsPopupSelection ||
!finalValue.Equals(inputValue)) {
value = finalValue;
}
}
else if (shouldComplete) {
// We usually try to preserve the casing of what user has typed, but
@ -1206,7 +1223,7 @@ nsAutoCompleteController::EnterMatch(bool aIsPopupSelection)
int32_t defaultIndex;
result->GetDefaultIndex(&defaultIndex);
if (defaultIndex >= 0) {
result->GetValueAt(defaultIndex, value);
result->GetFinalCompleteValueAt(defaultIndex, value);
break;
}
}
@ -1549,18 +1566,10 @@ nsAutoCompleteController::GetFinalDefaultCompleteValue(nsAString &_retval)
return NS_ERROR_FAILURE;
}
// Hack: For typeAheadResults allow the comment to be used as the final
// defaultComplete value if provided, otherwise fall back to the usual
// value. This allows to provide a different complete text when the user
// confirms the match. Don't rely on this for production code, since it's a
// temporary solution that needs a dedicated API (bug 754265).
bool isTypeAheadResult = false;
nsAutoString commentValue;
if (NS_SUCCEEDED(result->GetTypeAheadResult(&isTypeAheadResult)) &&
isTypeAheadResult &&
NS_SUCCEEDED(result->GetCommentAt(defaultIndex, commentValue)) &&
!commentValue.IsEmpty()) {
_retval = commentValue;
nsAutoString finalCompleteValue;
rv = result->GetFinalCompleteValueAt(defaultIndex, finalCompleteValue);
if (NS_SUCCEEDED(rv)) {
_retval = finalCompleteValue;
}
MOZ_ASSERT(FindInReadable(inputValue, _retval, nsCaseInsensitiveStringComparator()),
@ -1625,20 +1634,23 @@ nsAutoCompleteController::CompleteValue(nsString &aValue)
}
nsresult
nsAutoCompleteController::GetResultLabelAt(int32_t aIndex, bool aValueOnly, nsAString & _retval)
nsAutoCompleteController::GetResultLabelAt(int32_t aIndex, nsAString & _retval)
{
return GetResultValueLabelAt(aIndex, aValueOnly, false, _retval);
return GetResultValueLabelAt(aIndex, false, false, _retval);
}
nsresult
nsAutoCompleteController::GetResultValueAt(int32_t aIndex, bool aValueOnly, nsAString & _retval)
nsAutoCompleteController::GetResultValueAt(int32_t aIndex, bool aGetFinalValue,
nsAString & _retval)
{
return GetResultValueLabelAt(aIndex, aValueOnly, true, _retval);
return GetResultValueLabelAt(aIndex, aGetFinalValue, true, _retval);
}
nsresult
nsAutoCompleteController::GetResultValueLabelAt(int32_t aIndex, bool aValueOnly,
bool aGetValue, nsAString & _retval)
nsAutoCompleteController::GetResultValueLabelAt(int32_t aIndex,
bool aGetFinalValue,
bool aGetValue,
nsAString & _retval)
{
NS_ENSURE_TRUE(aIndex >= 0 && (uint32_t) aIndex < mRowCount, NS_ERROR_ILLEGAL_VALUE);
@ -1651,12 +1663,14 @@ nsAutoCompleteController::GetResultValueLabelAt(int32_t aIndex, bool aValueOnly,
result->GetSearchResult(&searchResult);
if (searchResult == nsIAutoCompleteResult::RESULT_FAILURE) {
if (aValueOnly)
if (aGetValue)
return NS_ERROR_FAILURE;
result->GetErrorDescription(_retval);
} else if (searchResult == nsIAutoCompleteResult::RESULT_SUCCESS ||
searchResult == nsIAutoCompleteResult::RESULT_SUCCESS_ONGOING) {
if (aGetValue)
if (aGetFinalValue)
result->GetFinalCompleteValueAt(rowIndex, _retval);
else if (aGetValue)
result->GetValueAt(rowIndex, _retval);
else
result->GetLabelAt(rowIndex, _retval);

View File

@ -59,12 +59,11 @@ protected:
nsresult GetResultAt(int32_t aIndex, nsIAutoCompleteResult** aResult,
int32_t* aRowIndex);
nsresult GetResultValueAt(int32_t aIndex, bool aValueOnly,
nsAString & _retval);
nsresult GetResultLabelAt(int32_t aIndex, bool aValueOnly,
nsresult GetResultValueAt(int32_t aIndex, bool aGetFinalValue,
nsAString & _retval);
nsresult GetResultLabelAt(int32_t aIndex, nsAString & _retval);
private:
nsresult GetResultValueLabelAt(int32_t aIndex, bool aValueOnly,
nsresult GetResultValueLabelAt(int32_t aIndex, bool aGetFinalValue,
bool aGetValue, nsAString & _retval);
protected:

View File

@ -90,7 +90,8 @@ NS_IMETHODIMP
nsAutoCompleteSimpleResult::AppendMatch(const nsAString& aValue,
const nsAString& aComment,
const nsAString& aImage,
const nsAString& aStyle)
const nsAString& aStyle,
const nsAString& aFinalCompleteValue)
{
CheckInvariants();
@ -111,6 +112,13 @@ nsAutoCompleteSimpleResult::AppendMatch(const nsAString& aValue,
mImages.RemoveElementAt(mImages.Length() - 1);
return NS_ERROR_OUT_OF_MEMORY;
}
if (!mFinalCompleteValues.AppendElement(aFinalCompleteValue)) {
mValues.RemoveElementAt(mValues.Length() - 1);
mComments.RemoveElementAt(mComments.Length() - 1);
mImages.RemoveElementAt(mImages.Length() - 1);
mStyles.RemoveElementAt(mStyles.Length() - 1);
return NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}
@ -170,6 +178,19 @@ nsAutoCompleteSimpleResult::GetStyleAt(int32_t aIndex, nsAString& _retval)
return NS_OK;
}
NS_IMETHODIMP
nsAutoCompleteSimpleResult::GetFinalCompleteValueAt(int32_t aIndex,
nsAString& _retval)
{
NS_ENSURE_TRUE(aIndex >= 0 && aIndex < int32_t(mFinalCompleteValues.Length()),
NS_ERROR_ILLEGAL_VALUE);
CheckInvariants();
_retval = mFinalCompleteValues[aIndex];
if (_retval.Length() == 0)
_retval = mValues[aIndex];
return NS_OK;
}
NS_IMETHODIMP
nsAutoCompleteSimpleResult::SetListener(nsIAutoCompleteSimpleResultListener* aListener)
{
@ -189,6 +210,7 @@ nsAutoCompleteSimpleResult::RemoveValueAt(int32_t aRowIndex,
mComments.RemoveElementAt(aRowIndex);
mImages.RemoveElementAt(aRowIndex);
mStyles.RemoveElementAt(aRowIndex);
mFinalCompleteValues.RemoveElementAt(aRowIndex);
if (mListener)
mListener->OnValueRemoved(this, removedValue, aRemoveFromDb);

View File

@ -21,6 +21,7 @@ public:
NS_ASSERTION(mValues.Length() == mComments.Length(), "Arrays out of sync");
NS_ASSERTION(mValues.Length() == mImages.Length(), "Arrays out of sync");
NS_ASSERTION(mValues.Length() == mStyles.Length(), "Arrays out of sync");
NS_ASSERTION(mValues.Length() == mFinalCompleteValues.Length(), "Arrays out of sync");
}
NS_DECL_ISUPPORTS
@ -39,6 +40,7 @@ protected:
nsTArray<nsString> mComments;
nsTArray<nsString> mImages;
nsTArray<nsString> mStyles;
nsTArray<nsString> mFinalCompleteValues;
nsString mSearchString;
nsString mErrorDescription;

View File

@ -6,7 +6,7 @@
interface nsIAutoCompleteInput;
[scriptable, uuid(dd2c4489-e4bd-4702-86bc-e1691744e556)]
[scriptable, uuid(ff9f8465-204a-47a6-b3c9-0628b3856684)]
interface nsIAutoCompleteController : nsISupports
{
/*
@ -134,6 +134,12 @@ interface nsIAutoCompleteController : nsISupports
*/
AString getImageAt(in long index);
/*
* For the last completed search, get the final value that should be completed
* when the user confirms the match at the given index
*/
AString getFinalCompleteValueAt(in long index);
/*
* Get / set the current search string. Note, setting will not start searching
*/

View File

@ -4,7 +4,7 @@
#include "nsISupports.idl"
[scriptable, uuid(7b43fad1-c735-4b45-9383-c3f057fed20d)]
[scriptable, uuid(9203c031-c4e7-4537-a4ec-81443d623d5a)]
interface nsIAutoCompleteResult : nsISupports
{
/**
@ -81,6 +81,12 @@ interface nsIAutoCompleteResult : nsISupports
*/
AString getImageAt(in long index);
/**
* Get the final value that should be completed when the user confirms
* the match at the given index.
*/
AString getFinalCompleteValueAt(in long index);
/**
* Remove the value at the given index from the autocomplete results.
* If removeFromDb is set to true, the value should be removed from

View File

@ -14,7 +14,7 @@ interface nsIAutoCompleteSimpleResultListener;
* an array.
*/
[scriptable, uuid(c738dc26-aa71-4561-a3fd-b5a0e4aa80d2)]
[scriptable, uuid(fe8802f9-c2b7-4141-8e5b-280df3f62251)]
interface nsIAutoCompleteSimpleResult : nsIAutoCompleteResult
{
/**
@ -48,12 +48,25 @@ interface nsIAutoCompleteSimpleResult : nsIAutoCompleteResult
void setTypeAheadResult(in boolean aHidden);
/**
* Appends a result item consisting of the given value, comment, image and style.
* This is how you add results. Note: image and style are optional.
* Appends a match consisting of the given value, comment, image, style and
* the value to use for defaultIndex completion.
* @param aValue
* The value to autocomplete to
* @param aComment
* Comment shown in the autocomplete widget to describe this match
* @param aImage
* Image shown in the autocomplete widget for this match.
* @param aStyle
* Describes how to style the match in the autocomplete widget
* @param aFinalCompleteValue
* Value used when the user confirms selecting this match. If not
* provided, aValue will be used.
*/
void appendMatch(in AString aValue, in AString aComment,
[optional] in AString aImage,
[optional] in AString aStyle);
void appendMatch(in AString aValue,
in AString aComment,
[optional] in AString aImage,
[optional] in AString aStyle,
[optional] in AString aFinalCompleteValue);
/**
* Sets a listener for changes in the result.

View File

@ -78,7 +78,8 @@ AutoCompleteResultBase.prototype = {
_values: null,
_comments: [],
_styles: [],
_finalCompleteValues: [],
searchString: "",
searchResult: null,
@ -113,6 +114,10 @@ AutoCompleteResultBase.prototype = {
return "";
},
getFinalCompleteValueAt: function(aIndex) {
return this._finalCompleteValues[aIndex] || this._values[aIndex];
},
removeValueAt: function (aRowIndex, aRemoveFromDb) {},
// nsISupports implementation

View File

@ -123,6 +123,10 @@ AutoCompleteResult.prototype = {
return "";
},
getFinalCompleteValueAt: function(aIndex) {
return this.getValueAt(aIndex);
},
removeValueAt: function (aRowIndex, aRemoveFromDb) {},
// nsISupports implementation

View File

@ -122,6 +122,10 @@ AutoCompleteResult.prototype = {
return "";
},
getFinalCompleteValueAt: function(aIndex) {
return this.getValueAt(aIndex);
},
removeValueAt: function (aRowIndex, aRemoveFromDb) {},
// nsISupports implementation

View File

@ -121,6 +121,10 @@ AutoCompleteResult.prototype = {
return "";
},
getFinalCompleteValueAt: function(aIndex) {
return this.getValueAt(aIndex);
},
removeValueAt: function (aRowIndex, aRemoveFromDb) {},
// nsISupports implementation

View File

@ -110,6 +110,10 @@ AutoCompleteResult.prototype = {
return "";
},
getFinalCompleteValueAt: function(aIndex) {
return this.getValueAt(aIndex);
},
removeValueAt: function (aRowIndex, aRemoveFromDb) {},
// nsISupports implementation

View File

@ -0,0 +1,54 @@
/* 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/. */
function AutoCompleteResult(aValues, aFinalCompleteValues) {
this._values = aValues;
this._finalCompleteValues = aFinalCompleteValues;
}
AutoCompleteResult.prototype = Object.create(AutoCompleteResultBase.prototype);
function AutoCompleteInput(aSearches) {
this.searches = aSearches;
this.popup.selectedIndex = 0;
}
AutoCompleteInput.prototype = Object.create(AutoCompleteInputBase.prototype);
function run_test() {
run_next_test();
}
add_test(function test_handleEnter() {
doSearch("moz", "mozilla.com", "http://www.mozilla.com", function(aController) {
do_check_eq(aController.input.textValue, "moz");
do_check_eq(aController.getFinalCompleteValueAt(0), "http://www.mozilla.com");
aController.handleEnter(false);
do_check_eq(aController.input.textValue, "http://www.mozilla.com");
});
});
function doSearch(aSearchString, aResultValue, aFinalCompleteValue, aOnCompleteCallback) {
let search = new AutoCompleteSearchBase(
"search",
new AutoCompleteResult([ aResultValue ], [ aFinalCompleteValue ])
);
registerAutoCompleteSearch(search);
let controller = Cc["@mozilla.org/autocomplete/controller;1"].
getService(Ci.nsIAutoCompleteController);
// Make an AutoCompleteInput that uses our searches and confirms results.
let input = new AutoCompleteInput([ search.name ]);
input.textValue = aSearchString;
controller.input = input;
controller.startSearch(aSearchString);
input.onSearchComplete = function onSearchComplete() {
aOnCompleteCallback(controller);
// Clean up.
unregisterAutoCompleteSearch(search);
run_next_test();
};
}

View File

@ -0,0 +1,56 @@
/* 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/. */
function AutoCompleteResult(aValues, aFinalCompleteValues) {
this._values = aValues;
this._finalCompleteValues = aFinalCompleteValues;
this.defaultIndex = 0;
}
AutoCompleteResult.prototype = Object.create(AutoCompleteResultBase.prototype);
function AutoCompleteInput(aSearches) {
this.searches = aSearches;
this.popup.selectedIndex = -1;
}
AutoCompleteInput.prototype = Object.create(AutoCompleteInputBase.prototype);
function run_test() {
run_next_test();
}
add_test(function test_handleEnter() {
doSearch("", "mozilla.com", "http://www.mozilla.com", function(aController) {
do_check_eq(aController.input.textValue, "");
do_check_eq(aController.getFinalCompleteValueAt(0), "http://www.mozilla.com");
aController.input.forceComplete = true;
aController.handleEnter(false);
do_check_eq(aController.input.textValue, "http://www.mozilla.com");
});
});
function doSearch(aSearchString, aResultValue, aFinalCompleteValue, aOnCompleteCallback) {
let search = new AutoCompleteSearchBase(
"search",
new AutoCompleteResult([ aResultValue ], [ aFinalCompleteValue ])
);
registerAutoCompleteSearch(search);
let controller = Cc["@mozilla.org/autocomplete/controller;1"].
getService(Ci.nsIAutoCompleteController);
// Make an AutoCompleteInput that uses our searches and confirms results.
let input = new AutoCompleteInput([ search.name ]);
input.textValue = aSearchString;
controller.input = input;
controller.startSearch(aSearchString);
input.onSearchComplete = function onSearchComplete() {
aOnCompleteCallback(controller);
// Clean up.
unregisterAutoCompleteSearch(search);
run_next_test();
};
}

View File

@ -2,11 +2,10 @@
* 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/. */
function AutoCompleteResult(aValues, aComments) {
function AutoCompleteResult(aValues, aFinalCompleteValues) {
this._values = aValues;
this._comments = aComments;
this._finalCompleteValues = aFinalCompleteValues;
this.defaultIndex = 0;
this._typeAheadResult = true;
}
AutoCompleteResult.prototype = Object.create(AutoCompleteResultBase.prototype);
@ -37,10 +36,10 @@ add_test(function test_handleEnter() {
});
});
function doSearch(aSearchString, aResultValue, aCommentValue, aOnCompleteCallback) {
function doSearch(aSearchString, aResultValue, aFinalCompleteValue, aOnCompleteCallback) {
let search = new AutoCompleteSearchBase(
"search",
new AutoCompleteResult([ aResultValue ], [ aCommentValue ], 0)
new AutoCompleteResult([ aResultValue ], [ aFinalCompleteValue ])
);
registerAutoCompleteSearch(search);

View File

@ -2,17 +2,16 @@
* 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/. */
function AutoCompleteTypeAheadResult(aValues, aComments) {
function AutoCompleteTypeAheadResult(aValues, aFinalCompleteValues) {
this._values = aValues;
this._comments = aComments;
this._finalCompleteValues = aFinalCompleteValues;
this.defaultIndex = 0;
this._typeAheadResult = true;
}
AutoCompleteTypeAheadResult.prototype = Object.create(AutoCompleteResultBase.prototype);
function AutoCompleteResult(aValues, aComments) {
function AutoCompleteResult(aValues) {
this._values = aValues;
this._comments = aComments;
}
AutoCompleteResult.prototype = Object.create(AutoCompleteResultBase.prototype);
@ -45,7 +44,7 @@ function doSearch(aSearchString, aOnCompleteCallback) {
let search = new AutoCompleteSearchBase(
"search",
new AutoCompleteResult([ "mozilla.org" ], [ "http://www.mozilla.org" ])
new AutoCompleteResult([ "mozilla.org" ])
);
registerAutoCompleteSearch(search);

View File

@ -121,6 +121,10 @@ AutoCompleteResult.prototype = {
return "";
},
getFinalCompleteValueAt: function(aIndex) {
return this.getValueAt(aIndex);
},
removeValueAt: function (aRowIndex, aRemoveFromDb) {},
// nsISupports implementation

View File

@ -11,6 +11,8 @@ tail =
[test_autocomplete_multiple.js]
[test_badDefaultIndex.js]
[test_completeDefaultIndex_casing.js]
[test_finalCompleteValue.js]
[test_finalCompleteValue_forceComplete.js]
[test_finalDefaultCompleteValue.js]
[test_hiddenResult.js]
[test_immediate_search.js]

View File

@ -172,6 +172,11 @@ NS_IMETHODIMP nsFileResult::GetImageAt(int32_t index, nsAString & aImage)
aImage.Truncate();
return NS_OK;
}
NS_IMETHODIMP nsFileResult::GetFinalCompleteValueAt(int32_t index,
nsAString & aValue)
{
return GetValueAt(index, aValue);
}
NS_IMETHODIMP nsFileResult::RemoveValueAt(int32_t rowIndex, bool removeFromDb)
{

View File

@ -611,6 +611,10 @@ UserAutoCompleteResult.prototype = {
return "";
},
getFinalCompleteValueAt : function (index) {
return this.getValueAt(index);
},
removeValueAt : function (index, removeFromDB) {
if (index < 0 || index >= this.logins.length)
throw "Index out of range.";

View File

@ -156,9 +156,6 @@ this.PlacesUtils = {
* @returns true if the node is a Bookmark folder, false otherwise
*/
nodeIsFolder: function PU_nodeIsFolder(aNode) {
if (!(aNode instanceof Ci.nsINavHistoryResultNode)) {
throw new Error("Invalid Places node");
}
return (aNode.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER ||
aNode.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT);
},
@ -170,9 +167,6 @@ this.PlacesUtils = {
* @returns true if the node represents a bookmarked URI, false otherwise
*/
nodeIsBookmark: function PU_nodeIsBookmark(aNode) {
if (!(aNode instanceof Ci.nsINavHistoryResultNode)) {
throw new Error("Invalid Places node");
}
return aNode.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_URI &&
aNode.itemId != -1;
},
@ -184,9 +178,6 @@ this.PlacesUtils = {
* @returns true if the node is a Bookmark separator, false otherwise
*/
nodeIsSeparator: function PU_nodeIsSeparator(aNode) {
if (!(aNode instanceof Ci.nsINavHistoryResultNode)) {
throw new Error("Invalid Places node");
}
return aNode.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR;
},
@ -197,9 +188,6 @@ this.PlacesUtils = {
* @returns true if the node is a URL item, false otherwise
*/
nodeIsURI: function PU_nodeIsURI(aNode) {
if (!(aNode instanceof Ci.nsINavHistoryResultNode)) {
throw new Error("Invalid Places node");
}
return aNode.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_URI;
},
@ -210,9 +198,6 @@ this.PlacesUtils = {
* @returns true if the node is a Query item, false otherwise
*/
nodeIsQuery: function PU_nodeIsQuery(aNode) {
if (!(aNode instanceof Ci.nsINavHistoryResultNode)) {
throw new Error("Invalid Places node");
}
return aNode.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_QUERY;
},
@ -372,9 +357,6 @@ this.PlacesUtils = {
* @returns true if the node is readonly, false otherwise
*/
nodeIsReadOnly: function PU_nodeIsReadOnly(aNode) {
if (!(aNode instanceof Ci.nsINavHistoryResultNode)) {
throw new Error("Invalid Places node");
}
let itemId = aNode.itemId;
if (itemId != -1) {
return this._readOnly.indexOf(itemId) != -1;
@ -394,9 +376,6 @@ this.PlacesUtils = {
* @returns true if the node is a host container, false otherwise
*/
nodeIsHost: function PU_nodeIsHost(aNode) {
if (!(aNode instanceof Ci.nsINavHistoryResultNode)) {
throw new Error("Invalid Places node");
}
return aNode.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_QUERY &&
aNode.parent &&
asQuery(aNode.parent).queryOptions.resultType ==
@ -410,9 +389,6 @@ this.PlacesUtils = {
* @returns true if the node is a day container, false otherwise
*/
nodeIsDay: function PU_nodeIsDay(aNode) {
if (!(aNode instanceof Ci.nsINavHistoryResultNode)) {
throw new Error("Invalid Places node");
}
var resultType;
return aNode.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_QUERY &&
aNode.parent &&
@ -428,9 +404,6 @@ this.PlacesUtils = {
* @returns true if the node is a tag container, false otherwise
*/
nodeIsTagQuery: function PU_nodeIsTagQuery(aNode) {
if (!(aNode instanceof Ci.nsINavHistoryResultNode)) {
throw new Error("Invalid Places node");
}
return aNode.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_QUERY &&
asQuery(aNode).queryOptions.resultType ==
Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_CONTENTS;
@ -446,9 +419,6 @@ this.PlacesUtils = {
Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT,
Ci.nsINavHistoryResultNode.RESULT_TYPE_QUERY],
nodeIsContainer: function PU_nodeIsContainer(aNode) {
if (!(aNode instanceof Ci.nsINavHistoryResultNode)) {
throw new Error("Invalid Places node");
}
return this.containerTypes.indexOf(aNode.type) != -1;
},

View File

@ -1404,9 +1404,7 @@ urlInlineComplete.prototype = {
untrimmedHost = null;
}
// TODO (bug 754265): this is a temporary solution introduced while
// waiting for a propert dedicated API.
ac._result.appendMatch(ac._strippedPrefix + trimmedHost, untrimmedHost);
ac._result.appendMatch(ac._strippedPrefix + trimmedHost, "", "", "", untrimmedHost);
// handleCompletion() will cause the result listener to be called, and
// will display the result in the UI.
@ -1480,9 +1478,7 @@ urlInlineComplete.prototype = {
untrimmedURL = null;
}
// TODO (bug 754265): this is a temporary solution introduced while
// waiting for a propert dedicated API.
ac._result.appendMatch(ac._strippedPrefix + url, untrimmedURL);
ac._result.appendMatch(ac._strippedPrefix + url, "", "", "", untrimmedURL);
// handleCompletion() will cause the result listener to be called, and
// will display the result in the UI.

View File

@ -542,6 +542,13 @@ TagAutoCompleteResult.prototype = {
return null;
},
/**
* Get the image for the result at the given index
*/
getFinalCompleteValueAt: function PTACR_getFinalCompleteValueAt(index) {
return this.getValueAt(index);
},
/**
* Remove the value at the given index from the autocomplete results.
* If removeFromDb is set to true, the value should be removed from

View File

@ -1,24 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
function run_test() {
let nodeIsMethods = [
"nodeIsFolder",
"nodeIsBookmark",
"nodeIsSeparator",
"nodeIsURI",
"nodeIsQuery",
"nodeIsReadOnly",
"nodeIsHost",
"nodeIsDay",
"nodeIsTagQuery",
"nodeIsContainer",
"nodeIsHistoryContainer",
"nodeIsQuery"
];
for (let methodName of nodeIsMethods) {
Assert.throws(() => PlacesUtils[methodName](true), /Invalid Places node/);
}
}

View File

@ -117,7 +117,6 @@ skip-if = true
[test_placeURIs.js]
[test_PlacesUtils_asyncGetBookmarkIds.js]
[test_PlacesUtils_lazyobservers.js]
[test_PlacesUtils_nodeIsXXX_invalidArg.js]
[test_placesTxn.js]
[test_preventive_maintenance.js]
# Bug 676989: test hangs consistently on Android

View File

@ -476,6 +476,10 @@ FormAutoCompleteResult.prototype = {
return "";
},
getFinalCompleteValueAt : function (index) {
return this.getValueAt(index);
},
removeValueAt : function (index, removeFromDB) {
this._checkIndexBounds(index);

View File

@ -151,6 +151,15 @@ FormAutoCompleteResult.prototype = {
return "";
},
/**
* Retrieves a result
* @param index the index of the result requested
* @return the result at the specified index
*/
getFinalCompleteValueAt: function(index) {
return this.getValueAt(index);
},
/**
* Removes a result from the resultset
* @param index the index of the result to remove

View File

@ -44,6 +44,7 @@ nsAutoCompleteSimpleResult.prototype = {
getCommentAt: function() { return null; },
getStyleAt: function() { return null; },
getImageAt: function() { return null; },
getFinalCompleteValueAt: function() { return this.getValueAt(); },
getLabelAt: function() { return null; },
removeValueAt: function() {}
};

View File

@ -44,6 +44,7 @@ nsAutoCompleteSimpleResult.prototype = {
getCommentAt: function() { return null; },
getStyleAt: function() { return null; },
getImageAt: function() { return null; },
getFinalCompleteValueAt: function() { return this.getValueAt(); },
getLabelAt: function() { return null; },
removeValueAt: function() {}
};

View File

@ -46,6 +46,7 @@ nsAutoCompleteSimpleResult.prototype = {
getCommentAt: function() { return null; },
getStyleAt: function() { return null; },
getImageAt: function() { return null; },
getFinalCompleteValueAt: function() { return this.getValueAt(); },
getLabelAt: function() { return null; },
removeValueAt: function() {}
};

View File

@ -42,6 +42,7 @@ nsAutoCompleteSimpleResult.prototype = {
getCommentAt: function() { return null; },
getStyleAt: function() { return null; },
getImageAt: function() { return null; },
getFinalCompleteValueAt: function() { return this.getValueAt(); },
getLabelAt: function() { return null; },
removeValueAt: function() {}
};

View File

@ -42,6 +42,7 @@ autoCompleteSimpleResult.prototype = {
getCommentAt: function() { return null; },
getStyleAt: function() { return null; },
getImageAt: function() { return null; },
getFinalCompleteValueAt: function() { return this.getValueAt(); },
getLabelAt: function() { return null; },
removeValueAt: function() {}
};

View File

@ -57,6 +57,7 @@ nsAutoCompleteSimpleResult.prototype = {
getCommentAt: function() { return null; },
getStyleAt: function() { return null; },
getImageAt: function() { return null; },
getFinalCompleteValueAt: function(aIndex) { return this.getValueAt(aIndex); },
getLabelAt: function() { return null; },
removeValueAt: function() {}
};

View File

@ -34,6 +34,10 @@
cursor: none;
}
.controlsOverlay[scaled] {
-moz-box-align: center;
}
/* CSS Transitions
*
* These are overriden by the default theme; the rules here just

View File

@ -325,6 +325,7 @@
controlsSpacer : null,
clickToPlay : null,
stats : {},
controlsOverlay : null,
fullscreenButton : null,
randomID : 0,
@ -1390,10 +1391,36 @@
let videoHeight = isAudioOnly ? minHeightForControlBar : this.video.clientHeight;
let videoWidth = isAudioOnly ? minWidthAllControls : this.video.clientWidth;
if ((this._overlayPlayButtonHeight + this._controlBarHeight) > videoHeight || this._overlayPlayButtonWidth > videoWidth)
// Adapt the size of the controls to the size of the video
if (this.video.readyState >= this.video.HAVE_METADATA) {
if (!this.isAudioOnly && this.video.videoWidth && this.video.videoHeight) {
var rect = this.video.getBoundingClientRect();
var widthRatio = rect.width / this.video.videoWidth;
var heightRatio = rect.height / this.video.videoHeight;
var width = this.video.videoWidth * Math.min(widthRatio, heightRatio);
this.controlsOverlay.setAttribute("scaled", true);
this.controlsOverlay.style.width = width + "px";
this.controlsSpacer.style.width = width + "px";
this.controlBar.style.width = width + "px";
} else {
this.controlsOverlay.removeAttribute("scaled");
this.controlsOverlay.style.width = "";
this.controlsSpacer.style.width = "";
this.controlBar.style.width = "";
}
}
if ((this._overlayPlayButtonHeight + this._controlBarHeight) > videoHeight ||
this._overlayPlayButtonWidth > videoWidth) {
this.clickToPlay.hidden = true;
else if (this.clickToPlay.hidden && !this.video.played.length)
} else if (this.clickToPlay.hidden &&
!this.video.played.length &&
this.video.paused) {
// Check this.video.paused to handle when a video is
// playing but hasn't processed any frames yet
this.clickToPlay.hidden = false;
}
let size = "normal";
if (videoHeight < minHeightForControlBar)
@ -1422,6 +1449,7 @@
this.positionLabel = document.getAnonymousElementByAttribute(binding, "class", "positionLabel");
this.statusOverlay = document.getAnonymousElementByAttribute(binding, "class", "statusOverlay");
this.statsOverlay = document.getAnonymousElementByAttribute(binding, "class", "statsOverlay");
this.controlsOverlay = document.getAnonymousElementByAttribute(binding, "class", "controlsOverlay");
this.controlsSpacer = document.getAnonymousElementByAttribute(binding, "class", "controlsSpacer");
this.clickToPlay = document.getAnonymousElementByAttribute(binding, "class", "clickToPlay");
this.fullscreenButton = document.getAnonymousElementByAttribute(binding, "class", "fullscreenButton");

View File

@ -942,9 +942,15 @@ TabActor.prototype = {
* True if the window.console object is native, or false otherwise.
*/
hasNativeConsoleAPI: function BTA_hasNativeConsoleAPI(aWindow) {
// Do not expose WebConsoleActor function directly as it is always
// loaded after the BrowserTabActor
return WebConsoleActor.prototype.hasNativeConsoleAPI(aWindow);
let isNative = false;
try {
// We are very explicitly examining the "console" property of
// the non-Xrayed object here.
let console = aWindow.wrappedJSObject.console;
isNative = console instanceof aWindow.Console;
}
catch (ex) { }
return isNative;
}
};

View File

@ -6,28 +6,34 @@
"use strict";
let Cc = Components.classes;
let Ci = Components.interfaces;
let Cu = Components.utils;
let {Cc, Ci, Cu} = require("chrome");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
let devtools = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
let { DebuggerServer, ActorPool } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
// Symbols from script.js
let { ThreadActor, EnvironmentActor, ObjectActor, LongStringActor } = DebuggerServer;
Cu.import("resource://gre/modules/jsdebugger.jsm");
addDebuggerToGlobal(this);
XPCOMUtils.defineLazyModuleGetter(this, "Services",
"resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyGetter(this, "NetworkMonitor", () => {
return devtools.require("devtools/toolkit/webconsole/network-monitor")
return require("devtools/toolkit/webconsole/network-monitor")
.NetworkMonitor;
});
XPCOMUtils.defineLazyGetter(this, "NetworkMonitorChild", () => {
return devtools.require("devtools/toolkit/webconsole/network-monitor")
return require("devtools/toolkit/webconsole/network-monitor")
.NetworkMonitorChild;
});
XPCOMUtils.defineLazyGetter(this, "ConsoleProgressListener", () => {
return devtools.require("devtools/toolkit/webconsole/network-monitor")
return require("devtools/toolkit/webconsole/network-monitor")
.ConsoleProgressListener;
});
XPCOMUtils.defineLazyGetter(this, "events", () => {
return require("sdk/event/core");
});
for (let name of ["WebConsoleUtils", "ConsoleServiceListener",
"ConsoleAPIListener", "JSTermHelpers", "JSPropertyProvider",
@ -37,7 +43,7 @@ for (let name of ["WebConsoleUtils", "ConsoleServiceListener",
if (prop == "WebConsoleUtils") {
prop = "Utils";
}
return devtools.require("devtools/toolkit/webconsole/utils")[prop];
return require("devtools/toolkit/webconsole/utils")[prop];
}.bind(null, name),
configurable: true,
enumerable: true
@ -1828,6 +1834,12 @@ NetworkEventActor.prototype.requestTypes =
"getEventTimings": NetworkEventActor.prototype.onGetEventTimings,
};
DebuggerServer.addTabActor(WebConsoleActor, "consoleActor");
DebuggerServer.addGlobalActor(WebConsoleActor, "consoleActor");
exports.register = function(handle) {
handle.addGlobalActor(WebConsoleActor, "consoleActor");
handle.addTabActor(WebConsoleActor, "consoleActor");
};
exports.unregister = function(handle) {
handle.removeGlobalActor(WebConsoleActor, "consoleActor");
handle.removeTabActor(WebConsoleActor, "consoleActor");
};

View File

@ -360,7 +360,7 @@ var DebuggerServer = {
// In case of apps being loaded in parent process, DebuggerServer is already
// initialized and browser actors are already loaded,
// but childtab.js hasn't been loaded yet.
if (!("WebConsoleActor" in this)) {
if (!DebuggerServer.tabActorFactories.hasOwnProperty("consoleActor")) {
this.addTabActors();
}
// But webbrowser.js and childtab.js aren't loaded from shell.js.
@ -377,7 +377,7 @@ var DebuggerServer = {
*/
addTabActors: function() {
this.addActors("resource://gre/modules/devtools/server/actors/script.js");
this.addActors("resource://gre/modules/devtools/server/actors/webconsole.js");
this.registerModule("devtools/server/actors/webconsole");
this.registerModule("devtools/server/actors/inspector");
this.registerModule("devtools/server/actors/call-watcher");
this.registerModule("devtools/server/actors/canvas");