Merge inbound to m-c. a=merge

This commit is contained in:
Ryan VanderMeulen 2015-07-01 16:37:26 -04:00
commit ced0ea44b5
67 changed files with 1076 additions and 1032 deletions

View File

@ -255,8 +255,8 @@ nsAccUtils::TableFor(Accessible* aRow)
tableRole = table->Role();
}
return tableRole == roles::TABLE || tableRole == roles::TREE_TABLE ?
table : nullptr;
return (tableRole == roles::TABLE || tableRole == roles::TREE_TABLE ||
tableRole == roles::MATHML_TABLE) ? table : nullptr;
}
}

View File

@ -823,6 +823,8 @@ ConvertToNSArray(nsTArray<Accessible*>& aArray)
return @"AXMathSubscriptSuperscript";
case roles::MATHML_ROW:
case roles::MATHML_STYLE:
case roles::MATHML_ERROR:
return @"AXMathRow";
case roles::MATHML_UNDER:

View File

@ -23,42 +23,42 @@ AUTOMATION_UPLOAD_OUTPUT = $(DIST)/automation-upload.txt
# Helper variables to convert from MOZ_AUTOMATION_* variables to the
# corresponding the make target
tier_BUILD_SYMBOLS = buildsymbols
tier_L10N_CHECK = l10n-check
tier_PRETTY_L10N_CHECK = pretty-l10n-check
tier_INSTALLER = installer
tier_PRETTY_INSTALLER = pretty-installer
tier_PACKAGE = package
tier_PRETTY_PACKAGE = pretty-package
tier_PACKAGE_TESTS = package-tests
tier_PRETTY_PACKAGE_TESTS = pretty-package-tests
tier_UPDATE_PACKAGING = update-packaging
tier_PRETTY_UPDATE_PACKAGING = pretty-update-packaging
tier_UPLOAD_SYMBOLS = uploadsymbols
tier_UPLOAD = upload
tier_SDK = sdk
tier_MOZ_AUTOMATION_BUILD_SYMBOLS = buildsymbols
tier_MOZ_AUTOMATION_L10N_CHECK = l10n-check
tier_MOZ_AUTOMATION_PRETTY_L10N_CHECK = pretty-l10n-check
tier_MOZ_AUTOMATION_INSTALLER = installer
tier_MOZ_AUTOMATION_PRETTY_INSTALLER = pretty-installer
tier_MOZ_AUTOMATION_PACKAGE = package
tier_MOZ_AUTOMATION_PRETTY_PACKAGE = pretty-package
tier_MOZ_AUTOMATION_PACKAGE_TESTS = package-tests
tier_MOZ_AUTOMATION_PRETTY_PACKAGE_TESTS = pretty-package-tests
tier_MOZ_AUTOMATION_UPDATE_PACKAGING = update-packaging
tier_MOZ_AUTOMATION_PRETTY_UPDATE_PACKAGING = pretty-update-packaging
tier_MOZ_AUTOMATION_UPLOAD_SYMBOLS = uploadsymbols
tier_MOZ_AUTOMATION_UPLOAD = upload
tier_MOZ_AUTOMATION_SDK = sdk
# Automation build steps. Everything in MOZ_AUTOMATION_TIERS also gets used in
# TIERS for mach display. As such, the MOZ_AUTOMATION_TIERS are roughly sorted
# here in the order that they will be executed (since mach doesn't know of the
# dependencies between them).
moz_automation_symbols = \
PACKAGE_TESTS \
PRETTY_PACKAGE_TESTS \
BUILD_SYMBOLS \
UPLOAD_SYMBOLS \
PACKAGE \
PRETTY_PACKAGE \
INSTALLER \
PRETTY_INSTALLER \
UPDATE_PACKAGING \
PRETTY_UPDATE_PACKAGING \
L10N_CHECK \
PRETTY_L10N_CHECK \
UPLOAD \
SDK \
MOZ_AUTOMATION_PACKAGE_TESTS \
MOZ_AUTOMATION_PRETTY_PACKAGE_TESTS \
MOZ_AUTOMATION_BUILD_SYMBOLS \
MOZ_AUTOMATION_UPLOAD_SYMBOLS \
MOZ_AUTOMATION_PACKAGE \
MOZ_AUTOMATION_PRETTY_PACKAGE \
MOZ_AUTOMATION_INSTALLER \
MOZ_AUTOMATION_PRETTY_INSTALLER \
MOZ_AUTOMATION_UPDATE_PACKAGING \
MOZ_AUTOMATION_PRETTY_UPDATE_PACKAGING \
MOZ_AUTOMATION_L10N_CHECK \
MOZ_AUTOMATION_PRETTY_L10N_CHECK \
MOZ_AUTOMATION_UPLOAD \
MOZ_AUTOMATION_SDK \
$(NULL)
MOZ_AUTOMATION_TIERS := $(foreach sym,$(moz_automation_symbols),$(if $(filter 1,$(MOZ_AUTOMATION_$(sym))),$(tier_$(sym))))
MOZ_AUTOMATION_TIERS := $(foreach sym,$(moz_automation_symbols),$(if $(filter 1,$($(sym))),$(tier_$(sym))))
# Dependencies between automation build steps
automation/uploadsymbols: automation/buildsymbols
@ -119,10 +119,14 @@ AUTOMATION_EXTRA_CMDLINE-pretty-package-tests = -j1
# However, the target automation/buildsymbols will still be executed in this
# case because it is a prerequisite of automation/upload.
define automation_commands
$(call BUILDSTATUS,TIER_START $1)
@$(MAKE) $1 $(AUTOMATION_EXTRA_CMDLINE-$1)
$(call BUILDSTATUS,TIER_FINISH $1)
endef
automation/%:
# The tier start message is in a separate target so make doesn't buffer it
# until the step completes with output syncing enabled.
automation-start/%:
$(if $(filter $*,$(MOZ_AUTOMATION_TIERS)),$(call BUILDSTATUS,TIER_START $*))
automation/%: automation-start/%
$(if $(filter $*,$(MOZ_AUTOMATION_TIERS)),$(call automation_commands,$*))

View File

@ -499,7 +499,14 @@ function runTest() {
var chromeDoc = SpecialPowers.wrap(document);
ok(chromeDoc.documentURI.indexOf("pushStateTest") > -1);
SimpleTest.executeSoon(function() { gen.next(); });
yield undefined;
history.back();
SimpleTest.executeSoon(function() { gen.next(); });
yield undefined;
SimpleTest.finish();
SpecialPowers.removePermission("systemXHR", document);

View File

@ -8,6 +8,7 @@
#include "mozilla/IMEStateManager.h"
#include "mozilla/TextEvents.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/Selection.h"
#include "nsCaret.h"
#include "nsCOMPtr.h"
#include "nsContentUtils.h"
@ -19,8 +20,6 @@
#include "nsIPresShell.h"
#include "nsISelection.h"
#include "nsISelectionController.h"
#include "nsISelectionPrivate.h"
#include "nsIDOMRange.h"
#include "nsIFrame.h"
#include "nsIObjectFrame.h"
#include "nsLayoutUtils.h"
@ -75,16 +74,19 @@ ContentEventHandler::InitCommon()
nsresult rv = InitBasic();
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsISelection> sel;
nsCopySupport::GetSelectionForCopy(mPresShell->GetDocument(),
getter_AddRefs(mSelection));
nsCOMPtr<nsIDOMRange> firstRange;
rv = mSelection->GetRangeAt(0, getter_AddRefs(firstRange));
// This shell doesn't support selection.
if (NS_FAILED(rv)) {
getter_AddRefs(sel));
mSelection = static_cast<Selection*>(sel.get());
if (NS_WARN_IF(!mSelection)) {
return NS_ERROR_NOT_AVAILABLE;
}
mFirstSelectedRange = static_cast<nsRange*>(firstRange.get());
// This shell doesn't support selection.
if (NS_WARN_IF(!mSelection->RangeCount())) {
return NS_ERROR_NOT_AVAILABLE;
}
mFirstSelectedRange = mSelection->GetRangeAt(0);
nsINode* startNode = mFirstSelectedRange->GetStartParent();
NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
@ -114,15 +116,16 @@ ContentEventHandler::Init(WidgetQueryContentEvent* aEvent)
aEvent->mReply.mContentsRoot = mRootContent.get();
bool isCollapsed;
rv = mSelection->GetIsCollapsed(&isCollapsed);
NS_ENSURE_SUCCESS(rv, NS_ERROR_NOT_AVAILABLE);
aEvent->mReply.mHasSelection = !isCollapsed;
aEvent->mReply.mHasSelection = !mSelection->IsCollapsed();
nsRect r;
nsIFrame* frame = nsCaret::GetGeometry(mSelection, &r);
NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
if (!frame) {
frame = mRootContent->GetPrimaryFrame();
if (NS_WARN_IF(!frame)) {
return NS_ERROR_FAILURE;
}
}
aEvent->mReply.mFocusedWidget = frame->GetNearestWidget();
return NS_OK;
@ -750,7 +753,7 @@ ContentEventHandler::SetRangeFromFlatTextOffset(nsRange* aRange,
}
}
rv = aRange->SetEnd(mRootContent, int32_t(mRootContent->GetChildCount()));
NS_ASSERTION(NS_SUCCEEDED(rv), "nsIDOMRange::SetEnd failed");
NS_ASSERTION(NS_SUCCEEDED(rv), "nsRange::SetEnd failed");
return rv;
}
@ -806,21 +809,17 @@ ContentEventHandler::OnQuerySelectedText(WidgetQueryContentEvent* aEvent)
&aEvent->mReply.mOffset, lineBreakType);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDOMNode> anchorDomNode, focusDomNode;
rv = mSelection->GetAnchorNode(getter_AddRefs(anchorDomNode));
NS_ENSURE_TRUE(anchorDomNode, NS_ERROR_FAILURE);
rv = mSelection->GetFocusNode(getter_AddRefs(focusDomNode));
NS_ENSURE_TRUE(focusDomNode, NS_ERROR_FAILURE);
nsCOMPtr<nsINode> anchorNode = mSelection->GetAnchorNode();
nsCOMPtr<nsINode> focusNode = mSelection->GetFocusNode();
if (NS_WARN_IF(!anchorNode) || NS_WARN_IF(!focusNode)) {
return NS_ERROR_FAILURE;
}
int32_t anchorOffset, focusOffset;
rv = mSelection->GetAnchorOffset(&anchorOffset);
NS_ENSURE_SUCCESS(rv, rv);
rv = mSelection->GetFocusOffset(&focusOffset);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsINode> anchorNode(do_QueryInterface(anchorDomNode));
nsCOMPtr<nsINode> focusNode(do_QueryInterface(focusDomNode));
NS_ENSURE_TRUE(anchorNode && focusNode, NS_ERROR_UNEXPECTED);
int32_t anchorOffset = static_cast<int32_t>(mSelection->AnchorOffset());
int32_t focusOffset = static_cast<int32_t>(mSelection->FocusOffset());
if (NS_WARN_IF(anchorOffset < 0) || NS_WARN_IF(focusOffset < 0)) {
return NS_ERROR_FAILURE;
}
int16_t compare = nsContentUtils::ComparePoints(anchorNode, anchorOffset,
focusNode, focusOffset);
@ -1032,32 +1031,29 @@ ContentEventHandler::OnQueryCaretRect(WidgetQueryContentEvent* aEvent)
LineBreakType lineBreakType = GetLineBreakType(aEvent);
nsRect caretRect;
// When the selection is collapsed and the queried offset is current caret
// position, we should return the "real" caret rect.
bool selectionIsCollapsed;
rv = mSelection->GetIsCollapsed(&selectionIsCollapsed);
NS_ENSURE_SUCCESS(rv, rv);
nsRect caretRect;
nsIFrame* caretFrame = nsCaret::GetGeometry(mSelection, &caretRect);
if (selectionIsCollapsed) {
uint32_t offset;
rv = GetFlatTextOffsetOfRange(mRootContent, mFirstSelectedRange, &offset,
lineBreakType);
NS_ENSURE_SUCCESS(rv, rv);
if (offset == aEvent->mInput.mOffset) {
if (!caretFrame) {
return NS_ERROR_FAILURE;
}
rv = ConvertToRootViewRelativeOffset(caretFrame, caretRect);
if (mSelection->IsCollapsed()) {
nsIFrame* caretFrame = nsCaret::GetGeometry(mSelection, &caretRect);
if (caretFrame) {
uint32_t offset;
rv = GetFlatTextOffsetOfRange(mRootContent, mFirstSelectedRange, &offset,
lineBreakType);
NS_ENSURE_SUCCESS(rv, rv);
aEvent->mReply.mRect = LayoutDevicePixel::FromUntyped(
caretRect.ToOutsidePixels(caretFrame->PresContext()->AppUnitsPerDevPixel()));
aEvent->mReply.mWritingMode = caretFrame->GetWritingMode();
aEvent->mReply.mOffset = aEvent->mInput.mOffset;
aEvent->mSucceeded = true;
return NS_OK;
if (offset == aEvent->mInput.mOffset) {
rv = ConvertToRootViewRelativeOffset(caretFrame, caretRect);
NS_ENSURE_SUCCESS(rv, rv);
nscoord appUnitsPerDevPixel =
caretFrame->PresContext()->AppUnitsPerDevPixel();
aEvent->mReply.mRect = LayoutDevicePixel::FromUntyped(
caretRect.ToOutsidePixels(appUnitsPerDevPixel));
aEvent->mReply.mWritingMode = caretFrame->GetWritingMode();
aEvent->mReply.mOffset = aEvent->mInput.mOffset;
aEvent->mSucceeded = true;
return NS_OK;
}
}
}
@ -1527,9 +1523,11 @@ ContentEventHandler::OnSelectionEvent(WidgetSelectionEvent* aEvent)
// Get selection to manipulate
// XXX why do we need to get them from ISM? This method should work fine
// without ISM.
nsCOMPtr<nsISelection> sel;
nsresult rv =
IMEStateManager::GetFocusSelectionAndRoot(getter_AddRefs(mSelection),
IMEStateManager::GetFocusSelectionAndRoot(getter_AddRefs(sel),
getter_AddRefs(mRootContent));
mSelection = static_cast<Selection*>(sel.get());
if (rv != NS_ERROR_NOT_AVAILABLE) {
NS_ENSURE_SUCCESS(rv, rv);
} else {
@ -1550,36 +1548,35 @@ ContentEventHandler::OnSelectionEvent(WidgetSelectionEvent* aEvent)
int32_t endNodeOffset = range->EndOffset();
AdjustRangeForSelection(mRootContent, &startNode, &startNodeOffset);
AdjustRangeForSelection(mRootContent, &endNode, &endNodeOffset);
if (NS_WARN_IF(!startNode) || NS_WARN_IF(!endNode) ||
NS_WARN_IF(startNodeOffset < 0) || NS_WARN_IF(endNodeOffset < 0)) {
return NS_ERROR_UNEXPECTED;
}
nsCOMPtr<nsIDOMNode> startDomNode(do_QueryInterface(startNode));
nsCOMPtr<nsIDOMNode> endDomNode(do_QueryInterface(endNode));
NS_ENSURE_TRUE(startDomNode && endDomNode, NS_ERROR_UNEXPECTED);
nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(mSelection));
selPrivate->StartBatchChanges();
mSelection->StartBatchChanges();
// Clear selection first before setting
rv = mSelection->RemoveAllRanges();
// Need to call EndBatchChanges at the end even if call failed
if (NS_SUCCEEDED(rv)) {
if (aEvent->mReversed) {
rv = mSelection->Collapse(endDomNode, endNodeOffset);
rv = mSelection->Collapse(endNode, endNodeOffset);
} else {
rv = mSelection->Collapse(startDomNode, startNodeOffset);
rv = mSelection->Collapse(startNode, startNodeOffset);
}
if (NS_SUCCEEDED(rv) &&
(startDomNode != endDomNode || startNodeOffset != endNodeOffset)) {
(startNode != endNode || startNodeOffset != endNodeOffset)) {
if (aEvent->mReversed) {
rv = mSelection->Extend(startDomNode, startNodeOffset);
rv = mSelection->Extend(startNode, startNodeOffset);
} else {
rv = mSelection->Extend(endDomNode, endNodeOffset);
rv = mSelection->Extend(endNode, endNodeOffset);
}
}
}
selPrivate->EndBatchChanges();
mSelection->EndBatchChanges();
NS_ENSURE_SUCCESS(rv, rv);
selPrivate->ScrollIntoViewInternal(
mSelection->ScrollIntoViewInternal(
nsISelectionController::SELECTION_FOCUS_REGION,
false, nsIPresShell::ScrollAxis(), nsIPresShell::ScrollAxis());
aEvent->mSucceeded = true;

View File

@ -8,8 +8,8 @@
#define mozilla_ContentEventHandler_h_
#include "mozilla/EventForwards.h"
#include "mozilla/dom/Selection.h"
#include "nsCOMPtr.h"
#include "nsISelection.h"
#include "nsRange.h"
class nsPresContext;
@ -35,6 +35,8 @@ enum LineBreakType
class MOZ_STACK_CLASS ContentEventHandler
{
public:
typedef dom::Selection Selection;
explicit ContentEventHandler(nsPresContext* aPresContext);
// NS_QUERY_SELECTED_TEXT event handler
@ -62,7 +64,7 @@ public:
protected:
nsPresContext* mPresContext;
nsCOMPtr<nsIPresShell> mPresShell;
nsCOMPtr<nsISelection> mSelection;
nsRefPtr<Selection> mSelection;
nsRefPtr<nsRange> mFirstSelectedRange;
nsCOMPtr<nsIContent> mRootContent;

View File

@ -3385,7 +3385,7 @@ EventStateManager::RemoteQueryContentEvent(WidgetEvent* aEvent)
TabParent*
EventStateManager::GetCrossProcessTarget()
{
return TabParent::GetIMETabParent();
return IMEStateManager::GetActiveTabParent();
}
bool
@ -3396,7 +3396,7 @@ EventStateManager::IsTargetCrossProcess(WidgetGUIEvent* aEvent)
nsIContent *focusedContent = GetFocusedContent();
if (focusedContent && focusedContent->IsEditable())
return false;
return TabParent::GetIMETabParent() != nullptr;
return IMEStateManager::GetActiveTabParent() != nullptr;
}
void

View File

@ -16,6 +16,7 @@
#include "mozilla/Services.h"
#include "mozilla/TextComposition.h"
#include "mozilla/TextEvents.h"
#include "mozilla/unused.h"
#include "mozilla/dom/HTMLFormElement.h"
#include "mozilla/dom/TabParent.h"
@ -180,19 +181,17 @@ GetNotifyIMEMessageName(IMEMessage aMessage)
}
}
nsIContent* IMEStateManager::sContent = nullptr;
StaticRefPtr<nsIContent> IMEStateManager::sContent;
nsPresContext* IMEStateManager::sPresContext = nullptr;
StaticRefPtr<nsIWidget> IMEStateManager::sFocusedIMEWidget;
StaticRefPtr<TabParent> IMEStateManager::sActiveTabParent;
StaticRefPtr<IMEContentObserver> IMEStateManager::sActiveIMEContentObserver;
TextCompositionArray* IMEStateManager::sTextCompositions = nullptr;
bool IMEStateManager::sInstalledMenuKeyboardListener = false;
bool IMEStateManager::sIsGettingNewIMEState = false;
bool IMEStateManager::sCheckForIMEUnawareWebApps = false;
bool IMEStateManager::sRemoteHasFocus = false;
// sActiveIMEContentObserver points to the currently active IMEContentObserver.
// sActiveIMEContentObserver is null if there is no focused editor.
IMEContentObserver* IMEStateManager::sActiveIMEContentObserver = nullptr;
TextCompositionArray* IMEStateManager::sTextCompositions = nullptr;
// static
void
IMEStateManager::Init()
@ -221,6 +220,43 @@ IMEStateManager::Shutdown()
sTextCompositions = nullptr;
}
// static
void
IMEStateManager::OnTabParentDestroying(TabParent* aTabParent)
{
if (sActiveTabParent != aTabParent) {
return;
}
MOZ_LOG(sISMLog, LogLevel::Info,
("ISM: IMEStateManager::OnTabParentDestroying(aTabParent=0x%p), "
"The active TabParent is being destroyed", aTabParent));
// The active remote process might have crashed.
sActiveTabParent = nullptr;
// TODO: Need to cancel composition without TextComposition and make
// disable IME.
}
// static
void
IMEStateManager::StopIMEStateManagement()
{
MOZ_LOG(sISMLog, LogLevel::Info,
("ISM: IMEStateManager::StopIMEStateManagement()"));
// NOTE: Don't set input context from here since this has already lost
// the rights to change input context.
if (sTextCompositions && sPresContext) {
NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, sPresContext);
}
sPresContext = nullptr;
sContent = nullptr;
sActiveTabParent = nullptr;
DestroyIMEContentObserver();
}
// static
nsresult
IMEStateManager::OnDestroyPresContext(nsPresContext* aPresContext)
@ -255,7 +291,7 @@ IMEStateManager::OnDestroyPresContext(nsPresContext* aPresContext)
MOZ_LOG(sISMLog, LogLevel::Info,
("ISM: IMEStateManager::OnDestroyPresContext(aPresContext=0x%p), "
"sPresContext=0x%p, sContent=0x%p, sTextCompositions=0x%p",
aPresContext, sPresContext, sContent, sTextCompositions));
aPresContext, sPresContext, sContent.get(), sTextCompositions));
DestroyIMEContentObserver();
@ -266,8 +302,9 @@ IMEStateManager::OnDestroyPresContext(nsPresContext* aPresContext)
InputContextAction::LOST_FOCUS);
SetIMEState(newState, nullptr, widget, action);
}
NS_IF_RELEASE(sContent);
sContent = nullptr;
sPresContext = nullptr;
sActiveTabParent = nullptr;
return NS_OK;
}
@ -310,7 +347,7 @@ IMEStateManager::OnRemoveContent(nsPresContext* aPresContext,
MOZ_LOG(sISMLog, LogLevel::Info,
("ISM: IMEStateManager::OnRemoveContent(aPresContext=0x%p, "
"aContent=0x%p), sPresContext=0x%p, sContent=0x%p, sTextCompositions=0x%p",
aPresContext, aContent, sPresContext, sContent, sTextCompositions));
aPresContext, aContent, sPresContext, sContent.get(), sTextCompositions));
DestroyIMEContentObserver();
@ -323,8 +360,9 @@ IMEStateManager::OnRemoveContent(nsPresContext* aPresContext,
SetIMEState(newState, nullptr, widget, action);
}
NS_IF_RELEASE(sContent);
sContent = nullptr;
sPresContext = nullptr;
sActiveTabParent = nullptr;
return NS_OK;
}
@ -350,16 +388,23 @@ IMEStateManager::OnChangeFocusInternal(nsPresContext* aPresContext,
nsIContent* aContent,
InputContextAction aAction)
{
nsRefPtr<TabParent> newTabParent = TabParent::GetFrom(aContent);
MOZ_LOG(sISMLog, LogLevel::Info,
("ISM: IMEStateManager::OnChangeFocusInternal(aPresContext=0x%p, "
"aContent=0x%p, aAction={ mCause=%s, mFocusChange=%s }), "
"sPresContext=0x%p, sContent=0x%p, sActiveIMEContentObserver=0x%p",
aPresContext, aContent, GetActionCauseName(aAction.mCause),
"aContent=0x%p (TabParent=0x%p), aAction={ mCause=%s, mFocusChange=%s }), "
"sPresContext=0x%p, sContent=0x%p, sActiveTabParent=0x%p, "
"sActiveIMEContentObserver=0x%p, sInstalledMenuKeyboardListener=%s",
aPresContext, aContent, newTabParent.get(),
GetActionCauseName(aAction.mCause),
GetActionFocusChangeName(aAction.mFocusChange),
sPresContext, sContent, sActiveIMEContentObserver));
sPresContext, sContent.get(), sActiveTabParent.get(),
sActiveIMEContentObserver.get(),
GetBoolName(sInstalledMenuKeyboardListener)));
bool focusActuallyChanging =
(sContent != aContent || sPresContext != aPresContext);
(sContent != aContent || sPresContext != aPresContext ||
sActiveTabParent != newTabParent);
nsCOMPtr<nsIWidget> oldWidget =
sPresContext ? sPresContext->GetRootWidget() : nullptr;
@ -384,6 +429,18 @@ IMEStateManager::OnChangeFocusInternal(nsPresContext* aPresContext,
return NS_OK;
}
nsIContentParent* currentContentParent =
sActiveTabParent ? sActiveTabParent->Manager() : nullptr;
nsIContentParent* newContentParent =
newTabParent ? newTabParent->Manager() : nullptr;
if (sActiveTabParent && currentContentParent != newContentParent) {
MOZ_LOG(sISMLog, LogLevel::Debug,
("ISM: IMEStateManager::OnChangeFocusInternal(), notifying previous "
"focused child process of parent process or another child process "
"getting focus"));
unused << sActiveTabParent->SendStopIMEStateManagement();
}
nsCOMPtr<nsIWidget> widget =
(sPresContext == aPresContext) ? oldWidget.get() :
aPresContext->GetRootWidget();
@ -394,61 +451,85 @@ IMEStateManager::OnChangeFocusInternal(nsPresContext* aPresContext,
return NS_OK;
}
IMEState newState = GetNewIMEState(aPresContext, aContent);
// If a child process has focus, we should disable IME state until the child
// process actually gets focus because if user types keys before that they
// are handled by IME.
IMEState newState =
newTabParent ? IMEState(IMEState::DISABLED) :
GetNewIMEState(aPresContext, aContent);
bool setIMEState = true;
// In e10s, remote content may have IME focus. The main process (i.e. this process)
// would attempt to set state to DISABLED if, for example, the user clicks
// some other remote content. The content process would later re-ENABLE IME, meaning
// that all state-changes were unnecessary.
// Here we filter the common case where the main process knows that the remote
// process controls IME focus. The DISABLED->re-ENABLED progression can
// still happen since remote content may be concurrently communicating its claim
// on focus to the main process... but this cannot cause bugs like missed keypresses.
// (It just means a lot of needless IPC.)
if ((newState.mEnabled == IMEState::DISABLED) && TabParent::GetIMETabParent()) {
MOZ_LOG(sISMLog, LogLevel::Debug,
("ISM: IMEStateManager::OnChangeFocusInternal(), "
"Parent process cancels to set DISABLED state because the content process "
"has IME focus and has already sets IME state"));
MOZ_ASSERT(XRE_IsParentProcess(),
"TabParent::GetIMETabParent() should never return non-null value "
"in the content process");
return NS_OK;
}
if (!focusActuallyChanging) {
// actual focus isn't changing, but if IME enabled state is changing,
// we should do it.
InputContext context = widget->GetInputContext();
if (context.mIMEState.mEnabled == newState.mEnabled) {
if (newTabParent) {
if (aAction.mFocusChange == InputContextAction::MENU_GOT_PSEUDO_FOCUS ||
aAction.mFocusChange == InputContextAction::MENU_LOST_PSEUDO_FOCUS) {
// XXX When menu keyboard listener is being uninstalled, IME state needs
// to be restored by the child process asynchronously. Therefore,
// some key events which are fired immediately after closing menu
// may not be handled by IME.
unused << newTabParent->
SendMenuKeyboardListenerInstalled(sInstalledMenuKeyboardListener);
setIMEState = sInstalledMenuKeyboardListener;
} else if (focusActuallyChanging) {
InputContext context = widget->GetInputContext();
if (context.mIMEState.mEnabled == IMEState::DISABLED) {
setIMEState = false;
MOZ_LOG(sISMLog, LogLevel::Debug,
("ISM: IMEStateManager::OnChangeFocusInternal(), doesn't set IME "
"state because focused element (or document) is in a child process "
"and the IME state is already disabled"));
} else {
MOZ_LOG(sISMLog, LogLevel::Debug,
("ISM: IMEStateManager::OnChangeFocusInternal(), will disable IME "
"until new focused element (or document) in the child process "
"will get focus actually"));
}
} else {
// When focus is NOT changed actually, we shouldn't set IME state since
// that means that the window is being activated and the child process
// may have composition. Then, we shouldn't commit the composition with
// making IME state disabled.
setIMEState = false;
MOZ_LOG(sISMLog, LogLevel::Debug,
("ISM: IMEStateManager::OnChangeFocusInternal(), "
"neither focus nor IME state is changing"));
return NS_OK;
("ISM: IMEStateManager::OnChangeFocusInternal(), doesn't set IME "
"state because focused element (or document) is already in the child "
"process"));
}
aAction.mFocusChange = InputContextAction::FOCUS_NOT_CHANGED;
// Even if focus isn't changing actually, we should commit current
// composition here since the IME state is changing.
if (sPresContext && oldWidget && !focusActuallyChanging) {
NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, oldWidget);
}
} else if (aAction.mFocusChange == InputContextAction::FOCUS_NOT_CHANGED) {
// If aContent isn't null or aContent is null but editable, somebody gets
// focus.
bool gotFocus = aContent || (newState.mEnabled == IMEState::ENABLED);
aAction.mFocusChange =
gotFocus ? InputContextAction::GOT_FOCUS : InputContextAction::LOST_FOCUS;
}
// Update IME state for new focus widget
SetIMEState(newState, aContent, widget, aAction);
if (setIMEState) {
if (!focusActuallyChanging) {
// actual focus isn't changing, but if IME enabled state is changing,
// we should do it.
InputContext context = widget->GetInputContext();
if (context.mIMEState.mEnabled == newState.mEnabled) {
MOZ_LOG(sISMLog, LogLevel::Debug,
("ISM: IMEStateManager::OnChangeFocusInternal(), "
"neither focus nor IME state is changing"));
return NS_OK;
}
aAction.mFocusChange = InputContextAction::FOCUS_NOT_CHANGED;
// Even if focus isn't changing actually, we should commit current
// composition here since the IME state is changing.
if (sPresContext && oldWidget && !focusActuallyChanging) {
NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, oldWidget);
}
} else if (aAction.mFocusChange == InputContextAction::FOCUS_NOT_CHANGED) {
// If aContent isn't null or aContent is null but editable, somebody gets
// focus.
bool gotFocus = aContent || (newState.mEnabled == IMEState::ENABLED);
aAction.mFocusChange =
gotFocus ? InputContextAction::GOT_FOCUS :
InputContextAction::LOST_FOCUS;
}
// Update IME state for new focus widget
SetIMEState(newState, aContent, widget, aAction);
}
sActiveTabParent = newTabParent;
sPresContext = aPresContext;
if (sContent != aContent) {
NS_IF_RELEASE(sContent);
NS_IF_ADDREF(sContent = aContent);
}
sContent = aContent;
// Don't call CreateIMEContentObserver() here, it should be called from
// focus event handler of editor.
@ -482,7 +563,7 @@ IMEStateManager::OnMouseButtonEventInEditor(nsPresContext* aPresContext,
MOZ_LOG(sISMLog, LogLevel::Info,
("ISM: IMEStateManager::OnMouseButtonEventInEditor(aPresContext=0x%p, "
"aContent=0x%p, aMouseEvent=0x%p), sPresContext=0x%p, sContent=0x%p",
aPresContext, aContent, aMouseEvent, sPresContext, sContent));
aPresContext, aContent, aMouseEvent, sPresContext, sContent.get()));
if (sPresContext != aPresContext || sContent != aContent) {
MOZ_LOG(sISMLog, LogLevel::Debug,
@ -539,7 +620,7 @@ IMEStateManager::OnClickInEditor(nsPresContext* aPresContext,
MOZ_LOG(sISMLog, LogLevel::Info,
("ISM: IMEStateManager::OnClickInEditor(aPresContext=0x%p, aContent=0x%p, "
"aMouseEvent=0x%p), sPresContext=0x%p, sContent=0x%p",
aPresContext, aContent, aMouseEvent, sPresContext, sContent));
aPresContext, aContent, aMouseEvent, sPresContext, sContent.get()));
if (sPresContext != aPresContext || sContent != aContent) {
MOZ_LOG(sISMLog, LogLevel::Debug,
@ -597,8 +678,8 @@ IMEStateManager::OnFocusInEditor(nsPresContext* aPresContext,
("ISM: IMEStateManager::OnFocusInEditor(aPresContext=0x%p, aContent=0x%p, "
"aEditor=0x%p), sPresContext=0x%p, sContent=0x%p, "
"sActiveIMEContentObserver=0x%p",
aPresContext, aContent, aEditor, sPresContext, sContent,
sActiveIMEContentObserver));
aPresContext, aContent, aEditor, sPresContext, sContent.get(),
sActiveIMEContentObserver.get()));
if (sPresContext != aPresContext || sContent != aContent) {
MOZ_LOG(sISMLog, LogLevel::Debug,
@ -669,7 +750,7 @@ IMEStateManager::UpdateIMEState(const IMEState& aNewIMEState,
"sIsGettingNewIMEState=%s",
GetIMEStateEnabledName(aNewIMEState.mEnabled),
GetIMEStateSetOpenName(aNewIMEState.mOpen), aContent, aEditor,
sPresContext, sContent, sActiveIMEContentObserver,
sPresContext, sContent.get(), sActiveIMEContentObserver.get(),
GetBoolName(sIsGettingNewIMEState)));
if (sIsGettingNewIMEState) {
@ -842,6 +923,55 @@ MayBeIMEUnawareWebApp(nsINode* aNode)
return haveKeyEventsListener;
}
// static
void
IMEStateManager::SetInputContextForChildProcess(
TabParent* aTabParent,
const InputContext& aInputContext,
const InputContextAction& aAction)
{
MOZ_LOG(sISMLog, LogLevel::Info,
("ISM: IMEStateManager::SetInputContextForChildProcess(aTabParent=0x%p, "
"aInputContext={ mIMEState={ mEnabled=%s, mOpen=%s }, "
"mHTMLInputType=\"%s\", mHTMLInputInputmode=\"%s\", mActionHint=\"%s\" }, "
"aAction={ mCause=%s, mAction=%s }, aTabParent=0x%p), sPresContext=0x%p, "
"sActiveTabParent=0x%p",
aTabParent, GetIMEStateEnabledName(aInputContext.mIMEState.mEnabled),
GetIMEStateSetOpenName(aInputContext.mIMEState.mOpen),
NS_ConvertUTF16toUTF8(aInputContext.mHTMLInputType).get(),
NS_ConvertUTF16toUTF8(aInputContext.mHTMLInputInputmode).get(),
NS_ConvertUTF16toUTF8(aInputContext.mActionHint).get(),
GetActionCauseName(aAction.mCause),
GetActionFocusChangeName(aAction.mFocusChange),
sPresContext, sActiveTabParent.get()));
if (NS_WARN_IF(aTabParent != sActiveTabParent)) {
MOZ_LOG(sISMLog, LogLevel::Error,
("ISM: IMEStateManager::SetInputContextForChildProcess(), FAILED, "
"because non-focused tab parent tries to set input context"));
return;
}
if (NS_WARN_IF(!sPresContext)) {
MOZ_LOG(sISMLog, LogLevel::Error,
("ISM: IMEStateManager::SetInputContextForChildProcess(), FAILED, "
"due to no focused presContext"));
return;
}
nsCOMPtr<nsIWidget> widget = sPresContext->GetRootWidget();
if (NS_WARN_IF(!widget)) {
MOZ_LOG(sISMLog, LogLevel::Error,
("ISM: IMEStateManager::SetInputContextForChildProcess(), FAILED, "
"due to no widget in the focused presContext"));
return;
}
MOZ_ASSERT(aInputContext.mOrigin == InputContext::ORIGIN_CONTENT);
SetInputContext(widget, aInputContext, aAction);
}
// static
void
IMEStateManager::SetIMEState(const IMEState& aState,
@ -851,9 +981,11 @@ IMEStateManager::SetIMEState(const IMEState& aState,
{
MOZ_LOG(sISMLog, LogLevel::Info,
("ISM: IMEStateManager::SetIMEState(aState={ mEnabled=%s, mOpen=%s }, "
"aContent=0x%p, aWidget=0x%p, aAction={ mCause=%s, mFocusChange=%s })",
"aContent=0x%p (TabParent=0x%p), aWidget=0x%p, aAction={ mCause=%s, "
"mFocusChange=%s })",
GetIMEStateEnabledName(aState.mEnabled),
GetIMEStateSetOpenName(aState.mOpen), aContent, aWidget,
GetIMEStateSetOpenName(aState.mOpen), aContent,
TabParent::GetFrom(aContent), aWidget,
GetActionCauseName(aAction.mCause),
GetActionFocusChangeName(aAction.mFocusChange)));
@ -936,27 +1068,41 @@ IMEStateManager::SetIMEState(const IMEState& aState,
aAction.mCause = InputContextAction::CAUSE_UNKNOWN_CHROME;
}
SetInputContext(aWidget, context, aAction);
}
MOZ_LOG(sISMLog, LogLevel::Debug,
("ISM: IMEStateManager::SetIMEState(), "
"calling nsIWidget::SetInputContext(context={ mIMEState={ mEnabled=%s, "
"mOpen=%s }, mHTMLInputType=\"%s\", mHTMLInputInputmode=\"%s\", "
"mActionHint=\"%s\" }, aAction={ mCause=%s, mAction=%s })",
GetIMEStateEnabledName(context.mIMEState.mEnabled),
GetIMEStateSetOpenName(context.mIMEState.mOpen),
NS_ConvertUTF16toUTF8(context.mHTMLInputType).get(),
NS_ConvertUTF16toUTF8(context.mHTMLInputInputmode).get(),
NS_ConvertUTF16toUTF8(context.mActionHint).get(),
// static
void
IMEStateManager::SetInputContext(nsIWidget* aWidget,
const InputContext& aInputContext,
const InputContextAction& aAction)
{
MOZ_LOG(sISMLog, LogLevel::Info,
("ISM: IMEStateManager::SetInputContext(aWidget=0x%p, aInputContext={ "
"mIMEState={ mEnabled=%s, mOpen=%s }, mHTMLInputType=\"%s\", "
"mHTMLInputInputmode=\"%s\", mActionHint=\"%s\" }, "
"aAction={ mCause=%s, mAction=%s }), sActiveTabParent=0x%p",
aWidget,
GetIMEStateEnabledName(aInputContext.mIMEState.mEnabled),
GetIMEStateSetOpenName(aInputContext.mIMEState.mOpen),
NS_ConvertUTF16toUTF8(aInputContext.mHTMLInputType).get(),
NS_ConvertUTF16toUTF8(aInputContext.mHTMLInputInputmode).get(),
NS_ConvertUTF16toUTF8(aInputContext.mActionHint).get(),
GetActionCauseName(aAction.mCause),
GetActionFocusChangeName(aAction.mFocusChange)));
GetActionFocusChangeName(aAction.mFocusChange),
sActiveTabParent.get()));
aWidget->SetInputContext(context, aAction);
if (oldContext.mIMEState.mEnabled == context.mIMEState.mEnabled) {
MOZ_RELEASE_ASSERT(aWidget);
InputContext oldContext = aWidget->GetInputContext();
aWidget->SetInputContext(aInputContext, aAction);
if (oldContext.mIMEState.mEnabled == aInputContext.mIMEState.mEnabled) {
return;
}
nsContentUtils::AddScriptRunner(
new IMEEnabledStateChangedEvent(context.mIMEState.mEnabled));
new IMEEnabledStateChangedEvent(aInputContext.mIMEState.mEnabled));
}
// static
@ -1334,7 +1480,7 @@ IMEStateManager::DestroyIMEContentObserver()
MOZ_LOG(sISMLog, LogLevel::Info,
("ISM: IMEStateManager::DestroyIMEContentObserver(), "
"sActiveIMEContentObserver=0x%p",
sActiveIMEContentObserver));
sActiveIMEContentObserver.get()));
if (!sActiveIMEContentObserver) {
MOZ_LOG(sISMLog, LogLevel::Debug,
@ -1345,8 +1491,8 @@ IMEStateManager::DestroyIMEContentObserver()
MOZ_LOG(sISMLog, LogLevel::Debug,
("ISM: IMEStateManager::DestroyIMEContentObserver(), destroying "
"the active IMEContentObserver..."));
nsRefPtr<IMEContentObserver> tsm;
tsm.swap(sActiveIMEContentObserver);
nsRefPtr<IMEContentObserver> tsm = sActiveIMEContentObserver.get();
sActiveIMEContentObserver = nullptr;
tsm->Destroy();
}
@ -1358,7 +1504,7 @@ IMEStateManager::CreateIMEContentObserver(nsIEditor* aEditor)
("ISM: IMEStateManager::CreateIMEContentObserver(aEditor=0x%p), "
"sPresContext=0x%p, sContent=0x%p, sActiveIMEContentObserver=0x%p, "
"sActiveIMEContentObserver->IsManaging(sPresContext, sContent)=%s",
aEditor, sPresContext, sContent, sActiveIMEContentObserver,
aEditor, sPresContext, sContent.get(), sActiveIMEContentObserver.get(),
GetBoolName(sActiveIMEContentObserver ?
sActiveIMEContentObserver->IsManaging(sPresContext, sContent) : false)));
@ -1390,7 +1536,6 @@ IMEStateManager::CreateIMEContentObserver(nsIEditor* aEditor)
("ISM: IMEStateManager::CreateIMEContentObserver() is creating an "
"IMEContentObserver instance..."));
sActiveIMEContentObserver = new IMEContentObserver();
NS_ADDREF(sActiveIMEContentObserver);
// IMEContentObserver::Init() might create another IMEContentObserver
// instance. So, sActiveIMEContentObserver would be replaced with new one.

View File

@ -9,6 +9,7 @@
#include "mozilla/EventForwards.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/dom/TabParent.h"
#include "nsIWidget.h"
class nsIContent;
@ -33,6 +34,7 @@ class TextComposition;
class IMEStateManager
{
typedef dom::TabParent TabParent;
typedef widget::IMEMessage IMEMessage;
typedef widget::IMENotification IMENotification;
typedef widget::IMEState IMEState;
@ -43,6 +45,39 @@ public:
static void Init();
static void Shutdown();
/**
* GetActiveTabParent() returns a pointer to a TabParent instance which is
* managed by the focused content (sContent). If the focused content isn't
* managing another process, this returns nullptr.
*/
static TabParent* GetActiveTabParent()
{
// If menu has pseudo focus, we should ignore active child process.
if (sInstalledMenuKeyboardListener) {
return nullptr;
}
return sActiveTabParent.get();
}
/**
* OnTabParentDestroying() is called when aTabParent is being destroyed.
*/
static void OnTabParentDestroying(TabParent* aTabParent);
/**
* SetIMEContextForChildProcess() is called when aTabParent receives
* SetInputContext() from the remote process.
*/
static void SetInputContextForChildProcess(TabParent* aTabParent,
const InputContext& aInputContext,
const InputContextAction& aAction);
/**
* StopIMEStateManagement() is called when the process should stop managing
* IME state.
*/
static void StopIMEStateManagement();
static nsresult OnDestroyPresContext(nsPresContext* aPresContext);
static nsresult OnRemoveContent(nsPresContext* aPresContext,
nsIContent* aContent);
@ -163,6 +198,9 @@ protected:
nsIContent* aContent,
nsIWidget* aWidget,
InputContextAction aAction);
static void SetInputContext(nsIWidget* aWidget,
const InputContext& aInputContext,
const InputContextAction& aAction);
static IMEState GetNewIMEState(nsPresContext* aPresContext,
nsIContent* aContent);
@ -174,9 +212,20 @@ protected:
static bool IsIMEObserverNeeded(const IMEState& aState);
static nsIContent* sContent;
static StaticRefPtr<nsIContent> sContent;
static nsPresContext* sPresContext;
static StaticRefPtr<nsIWidget> sFocusedIMEWidget;
static StaticRefPtr<TabParent> sActiveTabParent;
// sActiveIMEContentObserver points to the currently active
// IMEContentObserver. This is null if there is no focused editor.
static StaticRefPtr<IMEContentObserver> sActiveIMEContentObserver;
// All active compositions in the process are stored by this array.
// When you get an item of this array and use it, please be careful.
// The instances in this array can be destroyed automatically if you do
// something to cause committing or canceling the composition.
static TextCompositionArray* sTextCompositions;
static bool sInstalledMenuKeyboardListener;
static bool sIsGettingNewIMEState;
static bool sCheckForIMEUnawareWebApps;
@ -197,14 +246,6 @@ protected:
private:
bool mOldValue;
};
static IMEContentObserver* sActiveIMEContentObserver;
// All active compositions in the process are stored by this array.
// When you get an item of this array and use it, please be careful.
// The instances in this array can be destroyed automatically if you do
// something to cause committing or canceling the composition.
static TextCompositionArray* sTextCompositions;
};
} // namespace mozilla

View File

@ -435,7 +435,14 @@ const kEventConstructors = {
return new SpeechRecognitionEvent(aName, aProps);
},
},
SpeechSynthesisErrorEvent: { create: function (aName, aProps) {
aProps.error = "synthesis-unavailable";
aProps.utterance = new SpeechSynthesisUtterance("Hello World");
return new SpeechSynthesisErrorEvent(aName, aProps);
},
},
SpeechSynthesisEvent: { create: function (aName, aProps) {
aProps.utterance = new SpeechSynthesisUtterance("Hello World");
return new SpeechSynthesisEvent(aName, aProps);
},
},

View File

@ -576,6 +576,18 @@ child:
ParentActivated(bool aActivated);
/**
* StopIMEStateManagement() is called when the process loses focus and
* should stop managing IME state.
*/
StopIMEStateManagement();
/**
* MenuKeyboardListenerInstalled() is called when menu keyboard listener
* is installed in the parent process.
*/
MenuKeyboardListenerInstalled(bool aInstalled);
/**
* @see nsIDOMWindowUtils sendMouseEvent.
*/

View File

@ -20,6 +20,7 @@
#include "mozilla/dom/workers/ServiceWorkerManager.h"
#include "mozilla/dom/indexedDB/PIndexedDBPermissionRequestChild.h"
#include "mozilla/plugins/PluginWidgetChild.h"
#include "mozilla/IMEStateManager.h"
#include "mozilla/ipc/DocumentRendererChild.h"
#include "mozilla/ipc/FileDescriptorUtils.h"
#include "mozilla/layers/APZCCallbackHelper.h"
@ -2199,6 +2200,20 @@ bool TabChild::RecvParentActivated(const bool& aActivated)
return true;
}
bool
TabChild::RecvStopIMEStateManagement()
{
IMEStateManager::StopIMEStateManagement();
return true;
}
bool
TabChild::RecvMenuKeyboardListenerInstalled(const bool& aInstalled)
{
IMEStateManager::OnInstalledMenuKeyboardListener(aInstalled);
return true;
}
bool
TabChild::RecvMouseEvent(const nsString& aType,
const float& aX,

View File

@ -529,6 +529,10 @@ protected:
virtual bool RecvParentActivated(const bool& aActivated) override;
virtual bool RecvStopIMEStateManagement() override;
virtual bool RecvMenuKeyboardListenerInstalled(
const bool& aInstalled) override;
#ifdef MOZ_WIDGET_GONK
void MaybeRequestPreinitCamera();
#endif

View File

@ -248,7 +248,6 @@ private:
namespace mozilla {
namespace dom {
TabParent *TabParent::mIMETabParent = nullptr;
TabParent::LayerToTabParentTable* TabParent::sLayerToTabParentTable = nullptr;
NS_IMPL_ISUPPORTS(TabParent,
@ -481,9 +480,8 @@ TabParent::Recv__delete__()
void
TabParent::ActorDestroy(ActorDestroyReason why)
{
if (mIMETabParent == this) {
mIMETabParent = nullptr;
}
IMEStateManager::OnTabParentDestroying(this);
nsRefPtr<nsFrameLoader> frameLoader = GetFrameLoader(true);
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
if (frameLoader) {
@ -1934,7 +1932,6 @@ TabParent::RecvNotifyIMEFocus(const bool& aFocus,
return true;
}
mIMETabParent = aFocus ? this : nullptr;
IMENotification notification(aFocus ? NOTIFY_IME_OF_FOCUS :
NOTIFY_IME_OF_BLUR);
mContentCache.AssignContent(aContentCache, &notification);
@ -2388,26 +2385,6 @@ TabParent::RecvSetInputContext(const int32_t& aIMEEnabled,
const int32_t& aCause,
const int32_t& aFocusChange)
{
nsCOMPtr<nsIWidget> widget = GetWidget();
if (!widget || !AllowContentIME()) {
return true;
}
InputContext oldContext = widget->GetInputContext();
// Ignore if current widget IME setting is not DISABLED and didn't come
// from remote content. Chrome content may have taken over.
if (oldContext.mIMEState.mEnabled != IMEState::DISABLED &&
oldContext.IsOriginMainProcess()) {
return true;
}
// mIMETabParent (which is actually static) tracks which if any TabParent has IMEFocus
// When the input mode is set to anything but IMEState::DISABLED,
// mIMETabParent should be set to this
mIMETabParent =
aIMEEnabled != static_cast<int32_t>(IMEState::DISABLED) ? this : nullptr;
InputContext context;
context.mIMEState.mEnabled = static_cast<IMEState::Enabled>(aIMEEnabled);
context.mIMEState.mOpen = static_cast<IMEState::Open>(aIMEOpen);
@ -2419,15 +2396,8 @@ TabParent::RecvSetInputContext(const int32_t& aIMEEnabled,
InputContextAction action(
static_cast<InputContextAction::Cause>(aCause),
static_cast<InputContextAction::FocusChange>(aFocusChange));
widget->SetInputContext(context, action);
nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
if (!observerService)
return true;
nsAutoString state;
state.AppendInt(aIMEEnabled);
observerService->NotifyObservers(nullptr, "ime-enabled-state-changed", state.get());
IMEStateManager::SetInputContextForChildProcess(this, context, action);
return true;
}
@ -2617,19 +2587,6 @@ TabParent::RecvGetRenderFrameInfo(PRenderFrameParent* aRenderFrame,
return true;
}
bool
TabParent::AllowContentIME()
{
nsFocusManager* fm = nsFocusManager::GetFocusManager();
NS_ENSURE_TRUE(fm, false);
nsCOMPtr<nsIContent> focusedContent = fm->GetFocusedContent();
if (focusedContent && focusedContent->IsEditable())
return false;
return true;
}
already_AddRefed<nsFrameLoader>
TabParent::GetFrameLoader(bool aUseCachedFrameLoaderAfterDestroy) const
{

View File

@ -366,7 +366,6 @@ public:
NS_DECL_NSIAUTHPROMPTPROVIDER
NS_DECL_NSISECUREBROWSERUI
static TabParent *GetIMETabParent() { return mIMETabParent; }
bool HandleQueryContentEvent(mozilla::WidgetQueryContentEvent& aEvent);
bool SendCompositionEvent(mozilla::WidgetCompositionEvent& event);
bool SendSelectionEvent(mozilla::WidgetSelectionEvent& event);
@ -447,8 +446,6 @@ protected:
Element* mFrameElement;
nsCOMPtr<nsIBrowserDOMWindow> mBrowserDOMWindow;
bool AllowContentIME();
virtual PRenderFrameParent* AllocPRenderFrameParent() override;
virtual bool DeallocPRenderFrameParent(PRenderFrameParent* aFrame) override;
@ -467,8 +464,6 @@ protected:
void SetHasContentOpener(bool aHasContentOpener);
// IME
static TabParent *mIMETabParent;
ContentCacheInParent mContentCache;
nsIntRect mRect;

View File

@ -225,7 +225,9 @@ SpeechSynthesis::GetVoices(nsTArray< nsRefPtr<SpeechSynthesisVoice> >& aResult)
uint32_t voiceCount = 0;
nsresult rv = nsSynthVoiceRegistry::GetInstance()->GetVoiceCount(&voiceCount);
NS_ENSURE_SUCCESS_VOID(rv);
if(NS_WARN_IF(NS_FAILED(rv))) {
return;
}
for (uint32_t i = 0; i < voiceCount; i++) {
nsAutoString uri;

View File

@ -162,6 +162,7 @@ SpeechSynthesisUtterance::DispatchSpeechSynthesisEvent(const nsAString& aEventTy
SpeechSynthesisEventInit init;
init.mBubbles = false;
init.mCancelable = false;
init.mUtterance = this;
init.mCharIndex = aCharIndex;
init.mElapsedTime = aElapsedTime;
init.mName = aName;

View File

@ -123,7 +123,9 @@ nsresult
SpeechTaskParent::DispatchStartImpl(const nsAString& aUri)
{
MOZ_ASSERT(mActor);
NS_ENSURE_TRUE(mActor->SendOnStart(nsString(aUri)), NS_ERROR_FAILURE);
if(NS_WARN_IF(!(mActor->SendOnStart(nsString(aUri))))) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
@ -134,8 +136,9 @@ SpeechTaskParent::DispatchEndImpl(float aElapsedTime, uint32_t aCharIndex)
MOZ_ASSERT(mActor);
SpeechSynthesisRequestParent* actor = mActor;
mActor = nullptr;
NS_ENSURE_TRUE(actor->Send__delete__(actor, false, aElapsedTime, aCharIndex),
NS_ERROR_FAILURE);
if(NS_WARN_IF(!(actor->Send__delete__(actor, false, aElapsedTime, aCharIndex)))) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
@ -144,7 +147,9 @@ nsresult
SpeechTaskParent::DispatchPauseImpl(float aElapsedTime, uint32_t aCharIndex)
{
MOZ_ASSERT(mActor);
NS_ENSURE_TRUE(mActor->SendOnPause(aElapsedTime, aCharIndex), NS_ERROR_FAILURE);
if(NS_WARN_IF(!(mActor->SendOnPause(aElapsedTime, aCharIndex)))) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
@ -153,7 +158,9 @@ nsresult
SpeechTaskParent::DispatchResumeImpl(float aElapsedTime, uint32_t aCharIndex)
{
MOZ_ASSERT(mActor);
NS_ENSURE_TRUE(mActor->SendOnResume(aElapsedTime, aCharIndex), NS_ERROR_FAILURE);
if(NS_WARN_IF(!(mActor->SendOnResume(aElapsedTime, aCharIndex)))) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
@ -164,8 +171,9 @@ SpeechTaskParent::DispatchErrorImpl(float aElapsedTime, uint32_t aCharIndex)
MOZ_ASSERT(mActor);
SpeechSynthesisRequestParent* actor = mActor;
mActor = nullptr;
NS_ENSURE_TRUE(actor->Send__delete__(actor, true, aElapsedTime, aCharIndex),
NS_ERROR_FAILURE);
if(NS_WARN_IF(!(actor->Send__delete__(actor, true, aElapsedTime, aCharIndex)))) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
@ -175,8 +183,9 @@ SpeechTaskParent::DispatchBoundaryImpl(const nsAString& aName,
float aElapsedTime, uint32_t aCharIndex)
{
MOZ_ASSERT(mActor);
NS_ENSURE_TRUE(mActor->SendOnBoundary(nsString(aName), aElapsedTime, aCharIndex),
NS_ERROR_FAILURE);
if(NS_WARN_IF(!(mActor->SendOnBoundary(nsString(aName), aElapsedTime, aCharIndex)))) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
@ -186,8 +195,9 @@ SpeechTaskParent::DispatchMarkImpl(const nsAString& aName,
float aElapsedTime, uint32_t aCharIndex)
{
MOZ_ASSERT(mActor);
NS_ENSURE_TRUE(mActor->SendOnMark(nsString(aName), aElapsedTime, aCharIndex),
NS_ERROR_FAILURE);
if(NS_WARN_IF(!(mActor->SendOnMark(nsString(aName), aElapsedTime, aCharIndex)))) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}

View File

@ -165,7 +165,9 @@ nsSpeechTask::Setup(nsISpeechTaskCallback* aCallback,
mStream->AddListener(new SynthStreamListener(this));
// XXX: Support more than one channel
NS_ENSURE_TRUE(aChannels == 1, NS_ERROR_FAILURE);
if(NS_WARN_IF(!(aChannels == 1))) {
return NS_ERROR_FAILURE;
}
mChannels = aChannels;
@ -197,10 +199,18 @@ nsSpeechTask::SendAudio(JS::Handle<JS::Value> aData, JS::Handle<JS::Value> aLand
{
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
NS_ENSURE_TRUE(mStream, NS_ERROR_NOT_AVAILABLE);
NS_ENSURE_FALSE(mStream->IsDestroyed(), NS_ERROR_NOT_AVAILABLE);
NS_ENSURE_TRUE(mChannels, NS_ERROR_FAILURE);
NS_ENSURE_TRUE(aData.isObject(), NS_ERROR_INVALID_ARG);
if(NS_WARN_IF(!(mStream))) {
return NS_ERROR_NOT_AVAILABLE;
}
if(NS_WARN_IF(mStream->IsDestroyed())) {
return NS_ERROR_NOT_AVAILABLE;
}
if(NS_WARN_IF(!(mChannels))) {
return NS_ERROR_FAILURE;
}
if(NS_WARN_IF(!(aData.isObject()))) {
return NS_ERROR_INVALID_ARG;
}
if (mIndirectAudio) {
NS_WARNING("Can't call SendAudio from an indirect audio speech service.");
@ -239,9 +249,15 @@ nsSpeechTask::SendAudioNative(int16_t* aData, uint32_t aDataLen)
{
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
NS_ENSURE_TRUE(mStream, NS_ERROR_NOT_AVAILABLE);
NS_ENSURE_FALSE(mStream->IsDestroyed(), NS_ERROR_NOT_AVAILABLE);
NS_ENSURE_TRUE(mChannels, NS_ERROR_FAILURE);
if(NS_WARN_IF(!(mStream))) {
return NS_ERROR_NOT_AVAILABLE;
}
if(NS_WARN_IF(mStream->IsDestroyed())) {
return NS_ERROR_NOT_AVAILABLE;
}
if(NS_WARN_IF(!(mChannels))) {
return NS_ERROR_FAILURE;
}
if (mIndirectAudio) {
NS_WARNING("Can't call SendAudio from an indirect audio speech service.");
@ -293,8 +309,9 @@ nsSpeechTask::DispatchStartImpl(const nsAString& aUri)
LOG(LogLevel::Debug, ("nsSpeechTask::DispatchStart"));
MOZ_ASSERT(mUtterance);
NS_ENSURE_TRUE(mUtterance->mState == SpeechSynthesisUtterance::STATE_PENDING,
NS_ERROR_NOT_AVAILABLE);
if(NS_WARN_IF(!(mUtterance->mState == SpeechSynthesisUtterance::STATE_PENDING))) {
return NS_ERROR_NOT_AVAILABLE;
}
mUtterance->mState = SpeechSynthesisUtterance::STATE_SPEAKING;
mUtterance->mChosenVoiceURI = aUri;
@ -321,8 +338,9 @@ nsSpeechTask::DispatchEndImpl(float aElapsedTime, uint32_t aCharIndex)
LOG(LogLevel::Debug, ("nsSpeechTask::DispatchEnd\n"));
MOZ_ASSERT(mUtterance);
NS_ENSURE_FALSE(mUtterance->mState == SpeechSynthesisUtterance::STATE_ENDED,
NS_ERROR_NOT_AVAILABLE);
if(NS_WARN_IF(mUtterance->mState == SpeechSynthesisUtterance::STATE_ENDED)) {
return NS_ERROR_NOT_AVAILABLE;
}
// XXX: This should not be here, but it prevents a crash in MSG.
if (mStream) {
@ -363,9 +381,12 @@ nsSpeechTask::DispatchPauseImpl(float aElapsedTime, uint32_t aCharIndex)
{
LOG(LogLevel::Debug, ("nsSpeechTask::DispatchPause"));
MOZ_ASSERT(mUtterance);
NS_ENSURE_FALSE(mUtterance->mPaused, NS_ERROR_NOT_AVAILABLE);
NS_ENSURE_FALSE(mUtterance->mState == SpeechSynthesisUtterance::STATE_ENDED,
NS_ERROR_NOT_AVAILABLE);
if(NS_WARN_IF(mUtterance->mPaused)) {
return NS_ERROR_NOT_AVAILABLE;
}
if(NS_WARN_IF(mUtterance->mState == SpeechSynthesisUtterance::STATE_ENDED)) {
return NS_ERROR_NOT_AVAILABLE;
}
mUtterance->mPaused = true;
mUtterance->DispatchSpeechSynthesisEvent(NS_LITERAL_STRING("pause"),
@ -390,9 +411,12 @@ nsSpeechTask::DispatchResumeImpl(float aElapsedTime, uint32_t aCharIndex)
{
LOG(LogLevel::Debug, ("nsSpeechTask::DispatchResume"));
MOZ_ASSERT(mUtterance);
NS_ENSURE_TRUE(mUtterance->mPaused, NS_ERROR_NOT_AVAILABLE);
NS_ENSURE_FALSE(mUtterance->mState == SpeechSynthesisUtterance::STATE_ENDED,
NS_ERROR_NOT_AVAILABLE);
if(NS_WARN_IF(!(mUtterance->mPaused))) {
return NS_ERROR_NOT_AVAILABLE;
}
if(NS_WARN_IF(mUtterance->mState == SpeechSynthesisUtterance::STATE_ENDED)) {
return NS_ERROR_NOT_AVAILABLE;
}
mUtterance->mPaused = false;
mUtterance->DispatchSpeechSynthesisEvent(NS_LITERAL_STRING("resume"),
@ -416,8 +440,9 @@ nsresult
nsSpeechTask::DispatchErrorImpl(float aElapsedTime, uint32_t aCharIndex)
{
MOZ_ASSERT(mUtterance);
NS_ENSURE_FALSE(mUtterance->mState == SpeechSynthesisUtterance::STATE_ENDED,
NS_ERROR_NOT_AVAILABLE);
if(NS_WARN_IF(mUtterance->mState == SpeechSynthesisUtterance::STATE_ENDED)) {
return NS_ERROR_NOT_AVAILABLE;
}
mUtterance->mState = SpeechSynthesisUtterance::STATE_ENDED;
mUtterance->DispatchSpeechSynthesisEvent(NS_LITERAL_STRING("error"),
@ -443,8 +468,9 @@ nsSpeechTask::DispatchBoundaryImpl(const nsAString& aName,
float aElapsedTime, uint32_t aCharIndex)
{
MOZ_ASSERT(mUtterance);
NS_ENSURE_TRUE(mUtterance->mState == SpeechSynthesisUtterance::STATE_SPEAKING,
NS_ERROR_NOT_AVAILABLE);
if(NS_WARN_IF(!(mUtterance->mState == SpeechSynthesisUtterance::STATE_SPEAKING))) {
return NS_ERROR_NOT_AVAILABLE;
}
mUtterance->DispatchSpeechSynthesisEvent(NS_LITERAL_STRING("boundary"),
aCharIndex, aElapsedTime,
@ -469,8 +495,9 @@ nsSpeechTask::DispatchMarkImpl(const nsAString& aName,
float aElapsedTime, uint32_t aCharIndex)
{
MOZ_ASSERT(mUtterance);
NS_ENSURE_TRUE(mUtterance->mState == SpeechSynthesisUtterance::STATE_SPEAKING,
NS_ERROR_NOT_AVAILABLE);
if(NS_WARN_IF(!(mUtterance->mState == SpeechSynthesisUtterance::STATE_SPEAKING))) {
return NS_ERROR_NOT_AVAILABLE;
}
mUtterance->DispatchSpeechSynthesisEvent(NS_LITERAL_STRING("mark"),
aCharIndex, aElapsedTime,

View File

@ -236,8 +236,9 @@ nsSynthVoiceRegistry::AddVoice(nsISpeechService* aService,
NS_ConvertUTF16toUTF8(aLang).get(),
aLocalService ? "true" : "false"));
NS_ENSURE_FALSE(XRE_GetProcessType() == GeckoProcessType_Content,
NS_ERROR_NOT_AVAILABLE);
if(NS_WARN_IF(XRE_GetProcessType() == GeckoProcessType_Content)) {
return NS_ERROR_NOT_AVAILABLE;
}
return AddVoiceImpl(aService, aUri, aName, aLang,
aLocalService);
@ -255,8 +256,12 @@ nsSynthVoiceRegistry::RemoveVoice(nsISpeechService* aService,
bool found = false;
VoiceData* retval = mUriVoiceMap.GetWeak(aUri, &found);
NS_ENSURE_TRUE(found, NS_ERROR_NOT_AVAILABLE);
NS_ENSURE_TRUE(aService == retval->mService, NS_ERROR_INVALID_ARG);
if(NS_WARN_IF(!(found))) {
return NS_ERROR_NOT_AVAILABLE;
}
if(NS_WARN_IF(!(aService == retval->mService))) {
return NS_ERROR_INVALID_ARG;
}
mVoices.RemoveElement(retval);
mDefaultVoices.RemoveElement(retval);
@ -277,7 +282,9 @@ nsSynthVoiceRegistry::SetDefaultVoice(const nsAString& aUri,
{
bool found = false;
VoiceData* retval = mUriVoiceMap.GetWeak(aUri, &found);
NS_ENSURE_TRUE(found, NS_ERROR_NOT_AVAILABLE);
if(NS_WARN_IF(!(found))) {
return NS_ERROR_NOT_AVAILABLE;
}
mDefaultVoices.RemoveElement(retval);
@ -312,7 +319,9 @@ nsSynthVoiceRegistry::GetVoiceCount(uint32_t* aRetval)
NS_IMETHODIMP
nsSynthVoiceRegistry::GetVoice(uint32_t aIndex, nsAString& aRetval)
{
NS_ENSURE_TRUE(aIndex < mVoices.Length(), NS_ERROR_INVALID_ARG);
if(NS_WARN_IF(!(aIndex < mVoices.Length()))) {
return NS_ERROR_INVALID_ARG;
}
aRetval = mVoices[aIndex]->mUri;
@ -324,7 +333,9 @@ nsSynthVoiceRegistry::IsDefaultVoice(const nsAString& aUri, bool* aRetval)
{
bool found;
VoiceData* voice = mUriVoiceMap.GetWeak(aUri, &found);
NS_ENSURE_TRUE(found, NS_ERROR_NOT_AVAILABLE);
if(NS_WARN_IF(!(found))) {
return NS_ERROR_NOT_AVAILABLE;
}
for (int32_t i = mDefaultVoices.Length(); i > 0; ) {
VoiceData* defaultVoice = mDefaultVoices[--i];
@ -344,7 +355,9 @@ nsSynthVoiceRegistry::IsLocalVoice(const nsAString& aUri, bool* aRetval)
{
bool found;
VoiceData* voice = mUriVoiceMap.GetWeak(aUri, &found);
NS_ENSURE_TRUE(found, NS_ERROR_NOT_AVAILABLE);
if(NS_WARN_IF(!(found))) {
return NS_ERROR_NOT_AVAILABLE;
}
*aRetval = voice->mIsLocal;
return NS_OK;
@ -355,7 +368,9 @@ nsSynthVoiceRegistry::GetVoiceLang(const nsAString& aUri, nsAString& aRetval)
{
bool found;
VoiceData* voice = mUriVoiceMap.GetWeak(aUri, &found);
NS_ENSURE_TRUE(found, NS_ERROR_NOT_AVAILABLE);
if(NS_WARN_IF(!(found))) {
return NS_ERROR_NOT_AVAILABLE;
}
aRetval = voice->mLang;
return NS_OK;
@ -366,7 +381,9 @@ nsSynthVoiceRegistry::GetVoiceName(const nsAString& aUri, nsAString& aRetval)
{
bool found;
VoiceData* voice = mUriVoiceMap.GetWeak(aUri, &found);
NS_ENSURE_TRUE(found, NS_ERROR_NOT_AVAILABLE);
if(NS_WARN_IF(!(found))) {
return NS_ERROR_NOT_AVAILABLE;
}
aRetval = voice->mName;
return NS_OK;
@ -381,7 +398,9 @@ nsSynthVoiceRegistry::AddVoiceImpl(nsISpeechService* aService,
{
bool found = false;
mUriVoiceMap.GetWeak(aUri, &found);
NS_ENSURE_FALSE(found, NS_ERROR_INVALID_ARG);
if(NS_WARN_IF(found)) {
return NS_ERROR_INVALID_ARG;
}
nsRefPtr<VoiceData> voice = new VoiceData(aService, aUri, aName, aLang,
aLocalService);
@ -477,11 +496,15 @@ nsSynthVoiceRegistry::FindBestMatch(const nsAString& aUri,
// Try UI language.
nsresult rv;
nsCOMPtr<nsILocaleService> localeService = do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, nullptr);
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
nsAutoString uiLang;
rv = localeService->GetLocaleComponentForUserAgent(uiLang);
NS_ENSURE_SUCCESS(rv, nullptr);
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
if (FindVoiceByLang(uiLang, &retval)) {
LOG(LogLevel::Debug,

View File

@ -451,7 +451,9 @@ nsPicoService::Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData)
{
MOZ_ASSERT(NS_IsMainThread());
NS_ENSURE_TRUE(!strcmp(aTopic, "profile-after-change"), NS_ERROR_UNEXPECTED);
if(NS_WARN_IF(!(!strcmp(aTopic, "profile-after-change")))) {
return NS_ERROR_UNEXPECTED;
}
if (!Preferences::GetBool("media.webspeech.synth.enabled") ||
Preferences::GetBool("media.webspeech.synth.test")) {
@ -470,12 +472,16 @@ nsPicoService::Speak(const nsAString& aText, const nsAString& aUri,
float aVolume, float aRate, float aPitch,
nsISpeechTask* aTask)
{
NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_AVAILABLE);
if(NS_WARN_IF(!(mInitialized))) {
return NS_ERROR_NOT_AVAILABLE;
}
MonitorAutoLock autoLock(mVoicesMonitor);
bool found = false;
PicoVoice* voice = mVoices.GetWeak(aUri, &found);
NS_ENSURE_TRUE(found, NS_ERROR_NOT_AVAILABLE);
if(NS_WARN_IF(!(found))) {
return NS_ERROR_NOT_AVAILABLE;
}
mCurrentTask = aTask;
nsRefPtr<PicoCallbackRunnable> cb = new PicoCallbackRunnable(aText, voice, aRate, aPitch, aTask, this);

View File

@ -27,6 +27,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=525444
ok(SpeechSynthesis, "SpeechSynthesis exists in global scope");
ok(SpeechSynthesisVoice, "SpeechSynthesisVoice exists in global scope");
ok(SpeechSynthesisErrorEvent, "SpeechSynthesisErrorEvent exists in global scope");
ok(SpeechSynthesisEvent, "SpeechSynthesisEvent exists in global scope");
// SpeechSynthesisUtterance is the only type that has a constructor

View File

@ -311,7 +311,9 @@ nsFakeSynthServices::Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData)
{
MOZ_ASSERT(NS_IsMainThread());
NS_ENSURE_TRUE(!strcmp(aTopic, "profile-after-change"), NS_ERROR_UNEXPECTED);
if(NS_WARN_IF(!(!strcmp(aTopic, "profile-after-change")))) {
return NS_ERROR_UNEXPECTED;
}
if (Preferences::GetBool("media.webspeech.synth.test")) {
Init();

View File

@ -964,6 +964,8 @@ var interfaceNamesInGlobalScope =
{name: "SourceBuffer", linux: false, release: false},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "SourceBufferList", linux: false, release: false},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "SpeechSynthesisErrorEvent", b2g: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "SpeechSynthesisEvent", b2g: true},
// IMPORTANT: Do not change this list without review from a DOM peer!

View File

@ -0,0 +1,36 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/.
*
* The origin of this IDL file is
* http://dvcs.w3.org/hg/speech-api/raw-file/tip/speechapi.html
*
* Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
* liability, trademark and document use rules apply.
*/
enum SpeechSynthesisErrorCode {
"canceled",
"interrupted",
"audio-busy",
"audio-hardware",
"network",
"synthesis-unavailable",
"synthesis-failed",
"language-unavailable",
"voice-unavailable",
"text-too-long",
"invalid-argument",
};
[Constructor(DOMString type, optional SpeechSynthesisErrorEventInit eventInitDict),
Pref="media.webspeech.synth.enabled"]
interface SpeechSynthesisErrorEvent : SpeechSynthesisEvent {
readonly attribute SpeechSynthesisErrorCode error;
};
dictionary SpeechSynthesisErrorEventInit : SpeechSynthesisEventInit
{
required SpeechSynthesisErrorCode error;
};

View File

@ -14,6 +14,7 @@
Pref="media.webspeech.synth.enabled"]
interface SpeechSynthesisEvent : Event
{
readonly attribute SpeechSynthesisUtterance utterance;
readonly attribute unsigned long charIndex;
readonly attribute float elapsedTime;
readonly attribute DOMString? name;
@ -21,6 +22,7 @@ interface SpeechSynthesisEvent : Event
dictionary SpeechSynthesisEventInit : EventInit
{
required SpeechSynthesisUtterance utterance;
unsigned long charIndex = 0;
float elapsedTime = 0;
DOMString name = "";

View File

@ -611,6 +611,7 @@ if CONFIG['MOZ_WEBSPEECH']:
'SpeechRecognitionResult.webidl',
'SpeechRecognitionResultList.webidl',
'SpeechSynthesis.webidl',
'SpeechSynthesisErrorEvent.webidl',
'SpeechSynthesisEvent.webidl',
'SpeechSynthesisUtterance.webidl',
'SpeechSynthesisVoice.webidl',
@ -788,6 +789,7 @@ GENERATED_EVENTS_WEBIDL_FILES = [
if CONFIG['MOZ_WEBSPEECH']:
GENERATED_EVENTS_WEBIDL_FILES += [
'SpeechRecognitionEvent.webidl',
'SpeechSynthesisErrorEvent.webidl',
'SpeechSynthesisEvent.webidl',
]

View File

@ -60,11 +60,6 @@ static inline int floor_div(int a, int b)
}
}
// An abstract implementation of a tile buffer. This code covers the logic of
// moving and reusing tiles and leaves the validation up to the implementor. To
// avoid the overhead of virtual dispatch, we employ the curiously recurring
// template pattern.
//
// Tiles are aligned to a grid with one of the grid points at (0,0) and other
// grid points spaced evenly in the x- and y-directions by GetTileSize()
// multiplied by mResolution. GetScaledTileSize() provides convenience for
@ -82,30 +77,6 @@ static inline int floor_div(int a, int b)
// the tile type should be a reference or some other type with an efficient
// copy constructor.
//
// It is required that the derived class specify the base class as a friend. It
// must also implement the following public method:
//
// Tile GetPlaceholderTile() const;
//
// Returns a temporary placeholder tile used as a marker. This placeholder tile
// must never be returned by validateTile and must be == to every instance
// of a placeholder tile.
//
// Additionally, it must implement the following protected methods:
//
// Tile ValidateTile(Tile aTile, const nsIntPoint& aTileOrigin,
// const nsIntRegion& aDirtyRect);
//
// Validates the dirtyRect. The returned Tile will replace the tile.
//
// void ReleaseTile(Tile aTile);
//
// Destroys the given tile.
//
// void SwapTiles(Tile& aTileA, Tile& aTileB);
//
// Swaps two tiles.
//
// The contents of the tile buffer will be rendered at the resolution specified
// in mResolution, which can be altered with SetResolution. The resolution
// should always be a factor of the tile length, to avoid tiles covering
@ -146,12 +117,23 @@ struct TilesPlacement {
);
}
bool HasTile(TileIntPoint aPosition) {
bool HasTile(TileIntPoint aPosition) const {
return aPosition.x >= mFirst.x && aPosition.x < mFirst.x + mSize.width &&
aPosition.y >= mFirst.y && aPosition.y < mFirst.y + mSize.height;
}
};
// Given a position i, this function returns the position inside the current tile.
inline int GetTileStart(int i, int aTileLength) {
return (i >= 0) ? (i % aTileLength)
: ((aTileLength - (-i % aTileLength)) %
aTileLength);
}
// Rounds the given coordinate down to the nearest tile boundary.
inline int RoundDownToTileEdge(int aX, int aTileLength) { return aX - GetTileStart(aX, aTileLength); }
template<typename Derived, typename Tile>
class TiledLayerBuffer
{
@ -165,21 +147,6 @@ public:
~TiledLayerBuffer() {}
// Given a tile origin aligned to a multiple of GetScaledTileSize,
// return the tile that describes that region.
// NOTE: To get the valid area of that tile you must intersect
// (aTileOrigin.x, aTileOrigin.y,
// GetScaledTileSize().width, GetScaledTileSize().height)
// and GetValidRegion() to get the area of the tile that is valid.
Tile& GetTile(const gfx::IntPoint& aTileOrigin);
// Given a tile x, y relative to the top left of the layer, this function
// will return the tile for
// (x*GetScaledTileSize().width, y*GetScaledTileSize().height,
// GetScaledTileSize().width, GetScaledTileSize().height)
Tile& GetTile(int x, int y);
Tile& GetTile(size_t i) { return mRetainedTiles[i]; }
gfx::IntPoint GetTileOffset(TileIntPoint aPosition) const {
gfx::IntSize scaledTileSize = GetScaledTileSize();
return gfx::IntPoint(aPosition.x * scaledTileSize.width,
@ -188,48 +155,18 @@ public:
const TilesPlacement& GetPlacement() const { return mTiles; }
int TileIndex(const gfx::IntPoint& aTileOrigin) const;
int TileIndex(int x, int y) const { return x * mTiles.mSize.height + y; }
bool HasTile(int index) const { return index >= 0 && index < (int)mRetainedTiles.Length(); }
bool HasTile(const gfx::IntPoint& aTileOrigin) const;
bool HasTile(int x, int y) const {
return x >= 0 && x < mTiles.mSize.width && y >= 0 && y < mTiles.mSize.height;
}
const gfx::IntSize& GetTileSize() const { return mTileSize; }
gfx::IntSize GetScaledTileSize() const { return RoundedToInt(gfx::Size(mTileSize) / mResolution); }
unsigned int GetTileCount() const { return mRetainedTiles.Length(); }
Tile& GetTile(size_t i) { return mRetainedTiles[i]; }
const nsIntRegion& GetValidRegion() const { return mValidRegion; }
const nsIntRegion& GetPaintedRegion() const { return mPaintedRegion; }
void ClearPaintedRegion() { mPaintedRegion.SetEmpty(); }
void ResetPaintedAndValidState() {
mPaintedRegion.SetEmpty();
mValidRegion.SetEmpty();
mTiles.mSize.width = 0;
mTiles.mSize.height = 0;
for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
if (!mRetainedTiles[i].IsPlaceholderTile()) {
AsDerived().ReleaseTile(mRetainedTiles[i]);
}
}
mRetainedTiles.Clear();
}
// Given a position i, this function returns the position inside the current tile.
int GetTileStart(int i, int aTileLength) const {
return (i >= 0) ? (i % aTileLength)
: ((aTileLength - (-i % aTileLength)) %
aTileLength);
}
// Rounds the given coordinate down to the nearest tile boundary.
int RoundDownToTileEdge(int aX, int aTileLength) const { return aX - GetTileStart(aX, aTileLength); }
// Get and set draw scaling. mResolution affects the resolution at which the
// contents of the buffer are drawn. mResolution has no effect on the
// coordinate space of the valid region, but does affect the size of an
@ -238,22 +175,9 @@ public:
float GetResolution() const { return mResolution; }
bool IsLowPrecision() const { return mResolution < 1; }
typedef Tile* Iterator;
Iterator TilesBegin() { return mRetainedTiles.Elements(); }
Iterator TilesEnd() { return mRetainedTiles.Elements() + mRetainedTiles.Length(); }
void Dump(std::stringstream& aStream, const char* aPrefix, bool aDumpHtml);
protected:
// The implementor should call Update() to change
// the new valid region. This implementation will call
// validateTile on each tile that is dirty, which is left
// to the implementor.
void Update(const nsIntRegion& aNewValidRegion, const nsIntRegion& aPaintRegion);
// Return a reference to this tile in GetTile when the requested tile offset
// does not exist.
Tile mPlaceHolderTile;
nsIntRegion mValidRegion;
nsIntRegion mPaintedRegion;
@ -270,411 +194,25 @@ protected:
TilesPlacement mTiles;
float mResolution;
gfx::IntSize mTileSize;
private:
const Derived& AsDerived() const { return *static_cast<const Derived*>(this); }
Derived& AsDerived() { return *static_cast<Derived*>(this); }
};
class ClientTiledLayerBuffer;
class SurfaceDescriptorTiles;
class ISurfaceAllocator;
// Shadow layers may implement this interface in order to be notified when a
// tiled layer buffer is updated.
class TiledLayerComposer
{
public:
/**
* Update the current retained layer with the updated layer data.
* It is expected that the tiles described by aTiledDescriptor are all in the
* ReadLock state, so that the locks can be adopted when recreating a
* ClientTiledLayerBuffer locally. This lock will be retained until the buffer
* has completed uploading.
*
* Returns false if a deserialization error happened, in which case we will
* have to kill the child process.
*/
virtual bool UseTiledLayerBuffer(ISurfaceAllocator* aAllocator,
const SurfaceDescriptorTiles& aTiledDescriptor) = 0;
/**
* If some part of the buffer is being rendered at a lower precision, this
* returns that region. If it is not, an empty region will be returned.
*/
virtual const nsIntRegion& GetValidLowPrecisionRegion() const = 0;
virtual const nsIntRegion& GetValidRegion() const = 0;
};
template<typename Derived, typename Tile> bool
TiledLayerBuffer<Derived, Tile>::HasTile(const gfx::IntPoint& aTileOrigin) const {
gfx::IntSize scaledTileSize = GetScaledTileSize();
return HasTile(floor_div(aTileOrigin.x, scaledTileSize.width) - mTiles.mFirst.x,
floor_div(aTileOrigin.y, scaledTileSize.height) - mTiles.mFirst.y);
}
template<typename Derived, typename Tile> Tile&
TiledLayerBuffer<Derived, Tile>::GetTile(const nsIntPoint& aTileOrigin)
{
if (HasTile(aTileOrigin)) {
return mRetainedTiles[TileIndex(aTileOrigin)];
}
return mPlaceHolderTile;
}
template<typename Derived, typename Tile> int
TiledLayerBuffer<Derived, Tile>::TileIndex(const gfx::IntPoint& aTileOrigin) const
{
// Find the tile x/y of the first tile and the target tile relative to the (0, 0)
// origin, the difference is the tile x/y relative to the start of the tile buffer.
gfx::IntSize scaledTileSize = GetScaledTileSize();
return TileIndex(floor_div(aTileOrigin.x, scaledTileSize.width) - mTiles.mFirst.x,
floor_div(aTileOrigin.y, scaledTileSize.height) - mTiles.mFirst.y);
}
template<typename Derived, typename Tile> Tile&
TiledLayerBuffer<Derived, Tile>::GetTile(int x, int y)
{
if (HasTile(x, y)) {
return mRetainedTiles[TileIndex(x, y)];
}
return mPlaceHolderTile;
}
template<typename Derived, typename Tile> void
TiledLayerBuffer<Derived, Tile>::Dump(std::stringstream& aStream,
const char* aPrefix,
bool aDumpHtml)
{
gfx::IntRect visibleRect = GetValidRegion().GetBounds();
gfx::IntSize scaledTileSize = GetScaledTileSize();
for (int32_t x = visibleRect.x; x < visibleRect.x + visibleRect.width;) {
int32_t tileStartX = GetTileStart(x, scaledTileSize.width);
int32_t w = scaledTileSize.width - tileStartX;
for (size_t i = 0; i < mRetainedTiles.Length(); ++i) {
const TileIntPoint tilePosition = mTiles.TilePosition(i);
gfx::IntPoint tileOffset = GetTileOffset(tilePosition);
for (int32_t y = visibleRect.y; y < visibleRect.y + visibleRect.height;) {
int32_t tileStartY = GetTileStart(y, scaledTileSize.height);
nsIntPoint tileOrigin = nsIntPoint(RoundDownToTileEdge(x, scaledTileSize.width),
RoundDownToTileEdge(y, scaledTileSize.height));
Tile& tileTexture = GetTile(tileOrigin);
int32_t h = scaledTileSize.height - tileStartY;
aStream << "\n" << aPrefix << "Tile (x=" <<
RoundDownToTileEdge(x, scaledTileSize.width) << ", y=" <<
RoundDownToTileEdge(y, scaledTileSize.height) << "): ";
if (!tileTexture.IsPlaceholderTile()) {
tileTexture.DumpTexture(aStream);
} else {
aStream << "empty tile";
}
y += h;
}
x += w;
}
}
template<typename Derived, typename Tile> void
TiledLayerBuffer<Derived, Tile>::Update(const nsIntRegion& newValidRegion,
const nsIntRegion& aPaintRegion)
{
gfx::IntSize scaledTileSize = GetScaledTileSize();
nsTArray<Tile> newRetainedTiles;
nsTArray<Tile>& oldRetainedTiles = mRetainedTiles;
const gfx::IntRect oldBound = mValidRegion.GetBounds();
const gfx::IntRect newBound = newValidRegion.GetBounds();
const nsIntPoint oldBufferOrigin(RoundDownToTileEdge(oldBound.x, scaledTileSize.width),
RoundDownToTileEdge(oldBound.y, scaledTileSize.height));
const nsIntPoint newBufferOrigin(RoundDownToTileEdge(newBound.x, scaledTileSize.width),
RoundDownToTileEdge(newBound.y, scaledTileSize.height));
// This is the reason we break the style guide with newValidRegion instead
// of aNewValidRegion - so that the names match better and code easier to read
const nsIntRegion& oldValidRegion = mValidRegion;
const int oldRetainedHeight = mTiles.mSize.height;
#ifdef GFX_TILEDLAYER_RETAINING_LOG
{ // scope ss
std::stringstream ss;
ss << "TiledLayerBuffer " << this << " starting update"
<< " on bounds ";
AppendToString(ss, newBound);
ss << " with mResolution=" << mResolution << "\n";
for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
ss << "mRetainedTiles[" << i << "] = ";
mRetainedTiles[i].Dump(ss);
ss << "\n";
}
print_stderr(ss);
}
#endif
// Pass 1: Recycle valid content from the old buffer
// Recycle tiles from the old buffer that contain valid regions.
// Insert placeholders tiles if we have no valid area for that tile
// which we will allocate in pass 2.
// TODO: Add a tile pool to reduce new allocation
int tileX = 0;
int tileY = 0;
int tilesMissing = 0;
// Iterate over the new drawing bounds in steps of tiles.
for (int32_t x = newBound.x; x < newBound.XMost(); tileX++) {
// Compute tileRect(x,y,width,height) in layer space coordinate
// giving us the rect of the tile that hits the newBounds.
int width = scaledTileSize.width - GetTileStart(x, scaledTileSize.width);
if (x + width > newBound.XMost()) {
width = newBound.x + newBound.width - x;
}
tileY = 0;
for (int32_t y = newBound.y; y < newBound.YMost(); tileY++) {
int height = scaledTileSize.height - GetTileStart(y, scaledTileSize.height);
if (y + height > newBound.y + newBound.height) {
height = newBound.y + newBound.height - y;
}
const gfx::IntRect tileRect(x,y,width,height);
if (oldValidRegion.Intersects(tileRect) && newValidRegion.Intersects(tileRect)) {
// This old tiles contains some valid area so move it to the new tile
// buffer. Replace the tile in the old buffer with a placeholder
// to leave the old buffer index unaffected.
int tileX = floor_div(x - oldBufferOrigin.x, scaledTileSize.width);
int tileY = floor_div(y - oldBufferOrigin.y, scaledTileSize.height);
int index = tileX * oldRetainedHeight + tileY;
// The tile may have been removed, skip over it in this case.
if (oldRetainedTiles.
SafeElementAt(index, AsDerived().GetPlaceholderTile()).IsPlaceholderTile()) {
newRetainedTiles.AppendElement(AsDerived().GetPlaceholderTile());
} else {
Tile tileWithPartialValidContent = oldRetainedTiles[index];
newRetainedTiles.AppendElement(tileWithPartialValidContent);
oldRetainedTiles[index] = AsDerived().GetPlaceholderTile();
}
} else {
// This tile is either:
// 1) Outside the new valid region and will simply be an empty
// placeholder forever.
// 2) The old buffer didn't have any data for this tile. We postpone
// the allocation of this tile after we've reused any tile with
// valid content because then we know we can safely recycle
// with taking from a tile that has recyclable content.
newRetainedTiles.AppendElement(AsDerived().GetPlaceholderTile());
if (aPaintRegion.Intersects(tileRect)) {
tilesMissing++;
}
}
y += height;
}
x += width;
}
// Keep track of the number of horizontal/vertical tiles
// in the buffer so that we can easily look up a tile.
mTiles.mSize.width = tileX;
mTiles.mSize.height = tileY;
#ifdef GFX_TILEDLAYER_RETAINING_LOG
{ // scope ss
std::stringstream ss;
ss << "TiledLayerBuffer " << this << " finished pass 1 of update;"
<< " tilesMissing=" << tilesMissing << "\n";
for (size_t i = 0; i < oldRetainedTiles.Length(); i++) {
ss << "oldRetainedTiles[" << i << "] = ";
oldRetainedTiles[i].Dump(ss);
ss << "\n";
}
print_stderr(ss);
}
#endif
// Pass 1.5: Release excess tiles in oldRetainedTiles
// Tiles in oldRetainedTiles that aren't in newRetainedTiles will be recycled
// before creating new ones, but there could still be excess unnecessary
// tiles. As tiles may not have a fixed memory cost (for example, due to
// double-buffering), we should release these excess tiles first.
int oldTileCount = 0;
for (size_t i = 0; i < oldRetainedTiles.Length(); i++) {
Tile oldTile = oldRetainedTiles[i];
if (oldTile.IsPlaceholderTile()) {
continue;
}
if (oldTileCount >= tilesMissing) {
oldRetainedTiles[i] = AsDerived().GetPlaceholderTile();
AsDerived().ReleaseTile(oldTile);
aStream << "\n" << aPrefix << "Tile (x=" <<
tileOffset.x << ", y=" << tileOffset.y << "): ";
if (!mRetainedTiles[i].IsPlaceholderTile()) {
mRetainedTiles[i].DumpTexture(aStream);
} else {
oldTileCount ++;
aStream << "empty tile";
}
}
if (!newValidRegion.Contains(aPaintRegion)) {
gfxCriticalError() << "Painting outside visible:"
<< " paint " << aPaintRegion.ToString().get()
<< " old valid " << oldValidRegion.ToString().get()
<< " new valid " << newValidRegion.ToString().get();
}
#ifdef DEBUG
nsIntRegion oldAndPainted(oldValidRegion);
oldAndPainted.Or(oldAndPainted, aPaintRegion);
if (!oldAndPainted.Contains(newValidRegion)) {
gfxCriticalError() << "Not fully painted:"
<< " paint " << aPaintRegion.ToString().get()
<< " old valid " << oldValidRegion.ToString().get()
<< " old painted " << oldAndPainted.ToString().get()
<< " new valid " << newValidRegion.ToString().get();
}
#endif
nsIntRegion regionToPaint(aPaintRegion);
#ifdef GFX_TILEDLAYER_RETAINING_LOG
{ // scope ss
std::stringstream ss;
ss << "TiledLayerBuffer " << this << " finished pass 1.5 of update\n";
for (size_t i = 0; i < oldRetainedTiles.Length(); i++) {
ss << "oldRetainedTiles[" << i << "] = ";
oldRetainedTiles[i].Dump(ss);
ss << "\n";
}
for (size_t i = 0; i < newRetainedTiles.Length(); i++) {
ss << "newRetainedTiles[" << i << "] = ";
newRetainedTiles[i].Dump(ss);
ss << "\n";
}
print_stderr(ss);
}
#endif
// Pass 2: Validate
// We know at this point that any tile in the new buffer that had valid content
// from the previous buffer is placed correctly in the new buffer.
// We know that any tile in the old buffer that isn't a place holder is
// of no use and can be recycled.
// We also know that any place holder tile in the new buffer must be
// allocated.
tileX = 0;
#ifdef GFX_TILEDLAYER_PREF_WARNINGS
printf_stderr("Update %i, %i, %i, %i\n", newBound.x, newBound.y, newBound.width, newBound.height);
#endif
for (int x = newBound.x; x < newBound.x + newBound.width; tileX++) {
// Compute tileRect(x,y,width,height) in layer space coordinate
// giving us the rect of the tile that hits the newBounds.
int tileStartX = RoundDownToTileEdge(x, scaledTileSize.width);
int width = scaledTileSize.width - GetTileStart(x, scaledTileSize.width);
if (x + width > newBound.XMost())
width = newBound.XMost() - x;
tileY = 0;
for (int y = newBound.y; y < newBound.y + newBound.height; tileY++) {
int tileStartY = RoundDownToTileEdge(y, scaledTileSize.height);
int height = scaledTileSize.height - GetTileStart(y, scaledTileSize.height);
if (y + height > newBound.YMost()) {
height = newBound.YMost() - y;
}
const gfx::IntRect tileRect(x, y, width, height);
nsIntRegion tileDrawRegion;
tileDrawRegion.And(tileRect, regionToPaint);
if (tileDrawRegion.IsEmpty()) {
// We have a tile but it doesn't hit the draw region
// because we can reuse all of the content from the
// previous buffer.
#ifdef DEBUG
int currTileX = floor_div(x - newBufferOrigin.x, scaledTileSize.width);
int currTileY = floor_div(y - newBufferOrigin.y, scaledTileSize.height);
int index = TileIndex(currTileX, currTileY);
// If allocating a tile failed we can run into this assertion.
// Rendering is going to be glitchy but we don't want to crash.
NS_ASSERTION(!newValidRegion.Intersects(tileRect) ||
!newRetainedTiles.
SafeElementAt(index, AsDerived().GetPlaceholderTile()).IsPlaceholderTile(),
"Unexpected placeholder tile");
#endif
y += height;
continue;
}
int tileX = floor_div(x - newBufferOrigin.x, scaledTileSize.width);
int tileY = floor_div(y - newBufferOrigin.y, scaledTileSize.height);
int index = TileIndex(tileX, tileY);
MOZ_ASSERT(index >= 0 &&
static_cast<unsigned>(index) < newRetainedTiles.Length(),
"index out of range");
Tile newTile = newRetainedTiles[index];
// Try to reuse a tile from the old retained tiles that had no partially
// valid content.
while (newTile.IsPlaceholderTile() && oldRetainedTiles.Length() > 0) {
AsDerived().SwapTiles(newTile, oldRetainedTiles[oldRetainedTiles.Length()-1]);
oldRetainedTiles.RemoveElementAt(oldRetainedTiles.Length()-1);
if (!newTile.IsPlaceholderTile()) {
oldTileCount--;
}
}
// We've done our best effort to recycle a tile but it can be null
// in which case it's up to the derived class's ValidateTile()
// implementation to allocate a new tile before drawing
nsIntPoint tileOrigin(tileStartX, tileStartY);
newTile = AsDerived().ValidateTile(newTile, nsIntPoint(tileStartX, tileStartY),
tileDrawRegion);
NS_ASSERTION(!newTile.IsPlaceholderTile(), "Unexpected placeholder tile - failed to allocate?");
#ifdef GFX_TILEDLAYER_PREF_WARNINGS
printf_stderr("Store Validate tile %i, %i -> %i\n", tileStartX, tileStartY, index);
#endif
newRetainedTiles[index] = newTile;
y += height;
}
x += width;
}
AsDerived().PostValidate(aPaintRegion);
for (unsigned int i = 0; i < newRetainedTiles.Length(); ++i) {
AsDerived().UnlockTile(newRetainedTiles[i]);
}
#ifdef GFX_TILEDLAYER_RETAINING_LOG
{ // scope ss
std::stringstream ss;
ss << "TiledLayerBuffer " << this << " finished pass 2 of update;"
<< " oldTileCount=" << oldTileCount << "\n";
for (size_t i = 0; i < oldRetainedTiles.Length(); i++) {
ss << "oldRetainedTiles[" << i << "] = ";
oldRetainedTiles[i].Dump(ss);
ss << "\n";
}
for (size_t i = 0; i < newRetainedTiles.Length(); i++) {
ss << "newRetainedTiles[" << i << "] = ";
newRetainedTiles[i].Dump(ss);
ss << "\n";
}
print_stderr(ss);
}
#endif
// At this point, oldTileCount should be zero
MOZ_ASSERT(oldTileCount == 0, "Failed to release old tiles");
mRetainedTiles = newRetainedTiles;
mValidRegion = newValidRegion;
mTiles.mFirst.x = floor_div(mValidRegion.GetBounds().x, scaledTileSize.width);
mTiles.mFirst.y = floor_div(mValidRegion.GetBounds().y, scaledTileSize.height);
mPaintedRegion.Or(mPaintedRegion, aPaintRegion);
}
} // layers

View File

@ -1100,6 +1100,71 @@ ClientTiledLayerBuffer::UnlockTile(TileClient aTile)
}
}
void ClientTiledLayerBuffer::Update(const nsIntRegion& newValidRegion,
const nsIntRegion& aPaintRegion)
{
const IntSize scaledTileSize = GetScaledTileSize();
const gfx::IntRect newBounds = newValidRegion.GetBounds();
const TilesPlacement oldTiles = mTiles;
const TilesPlacement newTiles(floor_div(newBounds.x, scaledTileSize.width),
floor_div(newBounds.y, scaledTileSize.height),
floor_div(GetTileStart(newBounds.x, scaledTileSize.width)
+ newBounds.width, scaledTileSize.width) + 1,
floor_div(GetTileStart(newBounds.y, scaledTileSize.height)
+ newBounds.height, scaledTileSize.height) + 1);
const size_t oldTileCount = mRetainedTiles.Length();
const size_t newTileCount = newTiles.mSize.width * newTiles.mSize.height;
nsTArray<TileClient> oldRetainedTiles;
mRetainedTiles.SwapElements(oldRetainedTiles);
mRetainedTiles.SetLength(newTileCount);
for (size_t oldIndex = 0; oldIndex < oldTileCount; oldIndex++) {
const TileIntPoint tilePosition = oldTiles.TilePosition(oldIndex);
const size_t newIndex = newTiles.TileIndex(tilePosition);
// First, get the already existing tiles to the right place in the new array.
// Leave placeholders (default constructor) where there was no tile.
if (newTiles.HasTile(tilePosition)) {
mRetainedTiles[newIndex] = oldRetainedTiles[oldIndex];
} else {
// release tiles that we are not going to reuse before allocating new ones
// to avoid allocating unnecessarily.
oldRetainedTiles[oldIndex].Release();
}
}
oldRetainedTiles.Clear();
for (size_t i = 0; i < newTileCount; ++i) {
const TileIntPoint tilePosition = newTiles.TilePosition(i);
IntPoint tileOffset = GetTileOffset(tilePosition);
nsIntRegion tileDrawRegion = IntRect(tileOffset, scaledTileSize);
tileDrawRegion.AndWith(aPaintRegion);
if (tileDrawRegion.IsEmpty()) {
continue;
}
TileClient tile = mRetainedTiles[i];
tile = ValidateTile(tile, GetTileOffset(tilePosition),
tileDrawRegion);
mRetainedTiles[i] = tile;
}
PostValidate(aPaintRegion);
for (size_t i = 0; i < mRetainedTiles.Length(); ++i) {
UnlockTile(mRetainedTiles[i]);
}
mTiles = newTiles;
mValidRegion = newValidRegion;
mPaintedRegion.OrWith(aPaintRegion);
}
TileClient
ClientTiledLayerBuffer::ValidateTile(TileClient aTile,
const nsIntPoint& aTileOrigin,

View File

@ -419,6 +419,8 @@ public:
LayerManager::DrawPaintedLayerCallback aCallback,
void* aCallbackData);
void Update(const nsIntRegion& aNewValidRegion, const nsIntRegion& aPaintRegion);
void ReadLock();
void Release();
@ -453,6 +455,19 @@ public:
mResolution = aResolution;
}
void ResetPaintedAndValidState() {
mPaintedRegion.SetEmpty();
mValidRegion.SetEmpty();
mTiles.mSize.width = 0;
mTiles.mSize.height = 0;
for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
if (!mRetainedTiles[i].IsPlaceholderTile()) {
mRetainedTiles[i].Release();
}
}
mRetainedTiles.Clear();
}
protected:
TileClient ValidateTile(TileClient aTile,
const nsIntPoint& aTileRect,
@ -462,10 +477,6 @@ protected:
void UnlockTile(TileClient aTile);
void ReleaseTile(TileClient aTile) { aTile.Release(); }
void SwapTiles(TileClient& aTileA, TileClient& aTileB) { std::swap(aTileA, aTileB); }
TileClient GetPlaceholderTile() const { return TileClient(); }
private:

View File

@ -39,7 +39,7 @@ namespace layers {
class Layer;
class Compositor;
class ThebesBufferData;
class TiledLayerComposer;
class TiledContentHost;
class CompositableParentManager;
class PCompositableParent;
struct EffectChain;
@ -132,7 +132,7 @@ public:
Layer* GetLayer() const { return mLayer; }
void SetLayer(Layer* aLayer) { mLayer = aLayer; }
virtual TiledLayerComposer* AsTiledLayerComposer() { return nullptr; }
virtual TiledContentHost* AsTiledContentHost() { return nullptr; }
typedef uint32_t AttachFlags;
static const AttachFlags NO_FLAGS = 0;

View File

@ -40,7 +40,6 @@ class Matrix4x4;
namespace layers {
class Compositor;
class ThebesBufferData;
class TiledLayerComposer;
struct EffectChain;
struct TexturedEffect;
@ -54,10 +53,6 @@ struct TexturedEffect;
class ContentHost : public CompositableHost
{
public:
// Subclasses should implement this method if they support being used as a
// tiling.
virtual TiledLayerComposer* AsTiledLayerComposer() { return nullptr; }
virtual bool UpdateThebes(const ThebesBufferData& aData,
const nsIntRegion& aUpdated,
const nsIntRegion& aOldValidRegionBack,

View File

@ -19,7 +19,7 @@
#include "LayerScope.h" // for LayerScope Tool
#include "protobuf/LayerScopePacket.pb.h" // for protobuf (LayerScope)
#include "PaintedLayerComposite.h" // for PaintedLayerComposite
#include "TiledLayerBuffer.h" // for TiledLayerComposer
#include "TiledContentHost.h"
#include "Units.h" // for ScreenIntRect
#include "UnitTransforms.h" // for ViewAs
#include "gfx2DGlue.h" // for ToMatrix4x4
@ -1016,10 +1016,10 @@ LayerManagerComposite::ComputeRenderIntegrityInternal(Layer* aLayer,
SubtractTransformedRegion(aScreenRegion, incompleteRegion, transformToScreen);
// See if there's any incomplete low-precision rendering
TiledLayerComposer* composer = nullptr;
TiledContentHost* composer = nullptr;
LayerComposite* shadow = aLayer->AsLayerComposite();
if (shadow) {
composer = shadow->GetTiledLayerComposer();
composer = shadow->GetCompositableHost()->AsTiledContentHost();
if (composer) {
incompleteRegion.Sub(incompleteRegion, composer->GetValidLowPrecisionRegion());
if (!incompleteRegion.IsEmpty()) {
@ -1338,7 +1338,8 @@ LayerManagerComposite::AsyncPanZoomEnabled() const
nsIntRegion
LayerComposite::GetFullyRenderedRegion() {
if (TiledLayerComposer* tiled = GetTiledLayerComposer()) {
if (TiledContentHost* tiled = GetCompositableHost() ? GetCompositableHost()->AsTiledContentHost()
: nullptr) {
nsIntRegion shadowVisibleRegion = GetShadowVisibleRegion();
// Discard the region which hasn't been drawn yet when doing
// progressive drawing. Note that if the shadow visible region

View File

@ -56,7 +56,6 @@ class ImageLayerComposite;
class LayerComposite;
class RefLayerComposite;
class PaintedLayerComposite;
class TiledLayerComposer;
class TextRenderer;
class CompositingRenderTarget;
struct FPSState;
@ -379,8 +378,6 @@ public:
virtual void CleanupResources() = 0;
virtual TiledLayerComposer* GetTiledLayerComposer() { return nullptr; }
virtual void DestroyFrontBuffer() { }
void AddBlendModeEffect(EffectChain& aEffectChain);

View File

@ -29,8 +29,6 @@
namespace mozilla {
namespace layers {
class TiledLayerComposer;
PaintedLayerComposite::PaintedLayerComposite(LayerManagerComposite *aManager)
: PaintedLayer(aManager, nullptr)
, LayerComposite(aManager)
@ -91,16 +89,6 @@ PaintedLayerComposite::SetLayerManager(LayerManagerComposite* aManager)
}
}
TiledLayerComposer*
PaintedLayerComposite::GetTiledLayerComposer()
{
if (!mBuffer) {
return nullptr;
}
MOZ_ASSERT(mBuffer->IsAttached());
return mBuffer->AsTiledLayerComposer();
}
LayerRenderState
PaintedLayerComposite::GetRenderState()
{

View File

@ -28,7 +28,6 @@ namespace layers {
class CompositableHost;
class ContentHost;
class TiledLayerComposer;
class PaintedLayerComposite : public PaintedLayer,
public LayerComposite
@ -52,8 +51,6 @@ public:
virtual void SetLayerManager(LayerManagerComposite* aManager) override;
virtual TiledLayerComposer* GetTiledLayerComposer() override;
virtual void RenderLayer(const gfx::IntRect& aClipRect) override;
virtual void CleanupResources() override;

View File

@ -498,9 +498,9 @@ TiledContentHost::RenderLayerBuffer(TiledLayerBufferComposite& aLayerBuffer,
float resolution = aLayerBuffer.GetResolution();
gfx::Size layerScale(1, 1);
// Make sure we don't render at low resolution where we have valid high
// resolution content, to avoid overdraw and artifacts with semi-transparent
// layers.
// We assume that the current frame resolution is the one used in our high
// precision layer buffer. Compensate for a changing frame resolution when
// rendering the low precision buffer.
if (aLayerBuffer.GetFrameResolution() != mTiledBuffer.GetFrameResolution()) {
const CSSToParentLayerScale2D& layerResolution = aLayerBuffer.GetFrameResolution();
const CSSToParentLayerScale2D& localResolution = mTiledBuffer.GetFrameResolution();
@ -509,9 +509,9 @@ TiledContentHost::RenderLayerBuffer(TiledLayerBufferComposite& aLayerBuffer,
aVisibleRegion.ScaleRoundOut(layerScale.width, layerScale.height);
}
// If we're drawing the low precision buffer, make sure the high precision
// buffer is masked out to avoid overdraw and rendering artifacts with
// non-opaque layers.
// Make sure we don't render at low resolution where we have valid high
// resolution content, to avoid overdraw and artifacts with semi-transparent
// layers.
nsIntRegion maskRegion;
if (resolution != mTiledBuffer.GetResolution()) {
maskRegion = mTiledBuffer.GetValidRegion();

View File

@ -167,8 +167,6 @@ public:
static void RecycleCallback(TextureHost* textureHost, void* aClosure);
protected:
void SwapTiles(TileHost& aTileA, TileHost& aTileB) { std::swap(aTileA, aTileB); }
CSSToParentLayerScale2D mFrameResolution;
};
@ -192,8 +190,7 @@ protected:
* buffer after compositing a new one. Rendering takes us to RenderTile which
* is similar to Composite for non-tiled ContentHosts.
*/
class TiledContentHost : public ContentHost,
public TiledLayerComposer
class TiledContentHost : public ContentHost
{
public:
explicit TiledContentHost(const TextureInfo& aTextureInfo);
@ -217,12 +214,12 @@ public:
return false;
}
const nsIntRegion& GetValidLowPrecisionRegion() const override
const nsIntRegion& GetValidLowPrecisionRegion() const
{
return mLowPrecisionTiledBuffer.GetValidRegion();
}
const nsIntRegion& GetValidRegion() const override
const nsIntRegion& GetValidRegion() const
{
return mTiledBuffer.GetValidRegion();
}
@ -235,8 +232,8 @@ public:
mLowPrecisionTiledBuffer.SetCompositor(aCompositor);
}
virtual bool UseTiledLayerBuffer(ISurfaceAllocator* aAllocator,
const SurfaceDescriptorTiles& aTiledDescriptor) override;
bool UseTiledLayerBuffer(ISurfaceAllocator* aAllocator,
const SurfaceDescriptorTiles& aTiledDescriptor);
void Composite(EffectChain& aEffectChain,
float aOpacity,
@ -247,7 +244,7 @@ public:
virtual CompositableType GetType() override { return CompositableType::CONTENT_TILED; }
virtual TiledLayerComposer* AsTiledLayerComposer() override { return this; }
virtual TiledContentHost* AsTiledContentHost() override { return this; }
virtual void Attach(Layer* aLayer,
Compositor* aCompositor,

View File

@ -11,7 +11,6 @@
#include "GLContext.h" // for GLContext
#include "Layers.h" // for Layer
#include "RenderTrace.h" // for RenderTraceInvalidateEnd, etc
#include "TiledLayerBuffer.h" // for TiledLayerComposer
#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
#include "mozilla/RefPtr.h" // for RefPtr
#include "mozilla/layers/CompositorTypes.h"
@ -23,6 +22,7 @@
#include "mozilla/layers/LayersTypes.h" // for MOZ_LAYERS_LOG
#include "mozilla/layers/TextureHost.h" // for TextureHost
#include "mozilla/layers/TextureHostOGL.h" // for TextureHostOGL
#include "mozilla/layers/TiledContentHost.h"
#include "mozilla/layers/PaintedLayerComposite.h"
#include "mozilla/mozalloc.h" // for operator delete
#include "mozilla/unused.h"
@ -112,13 +112,12 @@ CompositableParentManager::ReceiveCompositableUpdate(const CompositableOperation
case CompositableOperation::TOpUseTiledLayerBuffer: {
MOZ_LAYERS_LOG(("[ParentSide] Paint TiledLayerBuffer"));
const OpUseTiledLayerBuffer& op = aEdit.get_OpUseTiledLayerBuffer();
CompositableHost* compositable = AsCompositable(op);
TiledContentHost* compositable = AsCompositable(op)->AsTiledContentHost();
TiledLayerComposer* tileComposer = compositable->AsTiledLayerComposer();
NS_ASSERTION(tileComposer, "compositable is not a tile composer");
NS_ASSERTION(compositable, "The compositable is not tiled");
const SurfaceDescriptorTiles& tileDesc = op.tileLayerDescriptor();
bool success = tileComposer->UseTiledLayerBuffer(this, tileDesc);
bool success = compositable->UseTiledLayerBuffer(this, tileDesc);
if (!success) {
return false;
}

View File

@ -131,6 +131,7 @@ EXPORTS.mozilla.layers += [
'composite/LayerManagerComposite.h',
'composite/PaintedLayerComposite.h',
'composite/TextureHost.h',
'composite/TiledContentHost.h',
'Compositor.h',
'CompositorTypes.h',
'D3D11ShareHandleImage.h',

View File

@ -41,7 +41,6 @@
#include "ScopedGLHelpers.h"
#include "GLReadTexImageHelper.h"
#include "GLBlitTextureImageHelper.h"
#include "TiledLayerBuffer.h" // for TiledLayerComposer
#include "HeapCopyOfStackArray.h"
#if MOZ_WIDGET_ANDROID

View File

@ -10,81 +10,56 @@
namespace mozilla {
namespace layers {
struct TestTiledLayerTile {
int value;
explicit TestTiledLayerTile(int v = 0) {
value = v;
}
bool operator== (const TestTiledLayerTile& o) const {
return value == o.value;
}
bool operator!= (const TestTiledLayerTile& o) const {
return value != o.value;
}
bool IsPlaceholderTile() const {
return value == -1;
}
};
class TestTiledLayerBuffer : public TiledLayerBuffer<TestTiledLayerBuffer, TestTiledLayerTile>
{
friend class TiledLayerBuffer<TestTiledLayerBuffer, TestTiledLayerTile>;
public:
TestTiledLayerTile GetPlaceholderTile() const {
return TestTiledLayerTile(-1);
}
TestTiledLayerTile ValidateTile(TestTiledLayerTile aTile, const nsIntPoint& aTileOrigin, const nsIntRegion& aDirtyRect) {
return TestTiledLayerTile();
}
void ReleaseTile(TestTiledLayerTile aTile)
{
}
void SwapTiles(TestTiledLayerTile& aTileA, TestTiledLayerTile& aTileB)
{
TestTiledLayerTile oldTileA = aTileA;
aTileA = aTileB;
aTileB = oldTileA;
}
void TestUpdate(const nsIntRegion& aNewValidRegion, const nsIntRegion& aPaintRegion)
{
Update(aNewValidRegion, aPaintRegion);
}
void UnlockTile(TestTiledLayerTile aTile) {}
void PostValidate(const nsIntRegion& aPaintRegion) {}
};
TEST(TiledLayerBuffer, TileConstructor) {
gfxPlatform::GetPlatform()->ComputeTileSize();
TestTiledLayerBuffer buffer;
}
TEST(TiledLayerBuffer, TileStart) {
gfxPlatform::GetPlatform()->ComputeTileSize();
TestTiledLayerBuffer buffer;
ASSERT_EQ(buffer.RoundDownToTileEdge(10, 256), 0);
ASSERT_EQ(buffer.RoundDownToTileEdge(-10, 256), -256);
ASSERT_EQ(RoundDownToTileEdge(10, 256), 0);
ASSERT_EQ(RoundDownToTileEdge(-10, 256), -256);
}
TEST(TiledLayerBuffer, EmptyUpdate) {
gfxPlatform::GetPlatform()->ComputeTileSize();
TEST(TiledLayerBuffer, TilesPlacement) {
for (int firstY = -10; firstY < 10; ++firstY) {
for (int firstX = -10; firstX < 10; ++firstX) {
for (int height = 1; height < 10; ++height) {
for (int width = 1; width < 10; ++width) {
TestTiledLayerBuffer buffer;
const TilesPlacement p1 = TilesPlacement(firstX, firstY, width, height);
// Check that HasTile returns false with some positions that we know
// not to be in the rectangle of the TilesPlacement.
ASSERT_FALSE(p1.HasTile(TileIntPoint(firstX - 1, 0)));
ASSERT_FALSE(p1.HasTile(TileIntPoint(0, firstY - 1)));
ASSERT_FALSE(p1.HasTile(TileIntPoint(firstX + width + 1, 0)));
ASSERT_FALSE(p1.HasTile(TileIntPoint(0, firstY + height + 1)));
nsIntRegion validRegion(gfx::IntRect(0, 0, 10, 10));
buffer.TestUpdate(validRegion, validRegion);
// Verify that all positions within the rect that defines the
// TilesPlacement map to indices between 0 and width*height.
for (int y = firstY; y < (firstY+height); ++y) {
for (int x = firstX; x < (firstX+width); ++x) {
ASSERT_TRUE(p1.HasTile(TileIntPoint(x,y)));
ASSERT_TRUE(p1.TileIndex(TileIntPoint(x, y)) >= 0);
ASSERT_TRUE(p1.TileIndex(TileIntPoint(x, y)) < width * height);
}
}
ASSERT_EQ(buffer.GetValidRegion(), validRegion);
// XXX - This causes some versions of gcc to warn that it optimizes
// away the test, which gets caught in -WError in PGO builds.
// The lazy thing to do is to just comment this out since this specific
// test isn't critically important, but we should remove the warning instead.
// cf. bug 1179287
//
// Verify that indices map to positions that are within the rect that
// defines the TilesPlacement.
// for (int i = 0; i < width * height; ++i) {
// ASSERT_TRUE(p1.TilePosition(i).x >= firstX);
// ASSERT_TRUE(p1.TilePosition(i).x < firstX + width);
// ASSERT_TRUE(p1.TilePosition(i).y >= firstY);
// ASSERT_TRUE(p1.TilePosition(i).y < firstY + height);
// }
}
}
}
}
}
}

View File

@ -24,7 +24,8 @@ UNIFIED_SOURCES += [
'TestTextures.cpp',
# Test works but it doesn't assert anything
#'gfxTextRunPerfTest.cpp',
'TestTiledLayerBuffer.cpp',
# Bug 1179287 - PGO bustage on Linux
#'TestTiledLayerBuffer.cpp',
'TestVsync.cpp',
]

View File

@ -0,0 +1,8 @@
<!DOCTYPE html>
<style>
p {
border: 2px solid transparent;
border-image: linear-gradient(to right, orange, blue) 1 1;
}
</style>
<p>This paragraph must have an orange/blue gradient border.</p>

View File

@ -0,0 +1,9 @@
<!DOCTYPE html>
<style>
p {
border: 2px solid transparent;
border-image: linear-gradient(to right, orange, blue) 1 1;
border-image: linear-gradient(to right, garbage) 1 1;
}
</style>
<p>This paragraph must have an orange/blue gradient border.</p>

View File

@ -1927,4 +1927,5 @@ skip-if(B2G||Mulet) == 1150021-1.xul 1150021-1-ref.xul
== 1155828-1.html 1155828-1-ref.html
== 1156129-1.html 1156129-1-ref.html
== 1169331-1.html 1169331-1-ref.html
== 1179078-1.html 1179078-1-ref.html
fuzzy(1,74) == 1174332-1.html 1174332-1-ref.html

View File

@ -104,6 +104,7 @@ default-preferences pref(layout.css.variables.enabled,true)
== variable-reference-37.html variable-reference-37-ref.html
== variable-reference-38.html variable-reference-38-ref.html
== variable-reference-39.html support/color-green-ref.html
== variable-reference-40.html variable-reference-40-ref.html
== variable-supports-01.html support/color-green-ref.html
== variable-supports-02.html support/color-green-ref.html
== variable-supports-03.html support/color-green-ref.html

View File

@ -0,0 +1,14 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE html>
<title>CSS Reftest Reference</title>
<link rel="author" title="Cameron McCormack" href="mailto:cam@mcc.id.au">
<style>
p {
border: 2px solid transparent;
border-image: linear-gradient(to right, orange, blue) 1 1;
}
</style>
<p>This paragraph must have an orange/blue gradient border.</p>

View File

@ -0,0 +1,17 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE html>
<title>CSS Test: Test that a variable reference within a gradient value in a border-image shorthand parses correctly.</title>
<link rel="author" title="Cameron McCormack" href="mailto:cam@mcc.id.au">
<link rel="help" href="http://www.w3.org/TR/css-variables-1/#using-variables">
<link rel="match" href="variable-reference-40-ref.html">
<style>
p {
--orange: orange;
border: 2px solid transparent;
border-image: linear-gradient(to right, var(--orange), blue) 1 1;
}
</style>
<p>This paragraph must have an orange/blue gradient border.</p>

View File

@ -11347,10 +11347,14 @@ CSSParserImpl::ParseBorderImage()
nsCSSValue imageSourceValue;
while (!CheckEndProperty()) {
// <border-image-source>
if (!foundSource && ParseVariant(imageSourceValue, VARIANT_IMAGE, nullptr)) {
AppendValue(eCSSProperty_border_image_source, imageSourceValue);
foundSource = true;
continue;
if (!foundSource) {
nsAutoCSSParserInputStateRestorer stateRestorer(this);
if (ParseVariant(imageSourceValue, VARIANT_IMAGE, nullptr)) {
AppendValue(eCSSProperty_border_image_source, imageSourceValue);
foundSource = true;
stateRestorer.DoNotRestore();
continue;
}
}
// <border-image-slice>

View File

@ -286,7 +286,7 @@ nsBaseChannel::ClassifyURI()
if (mLoadFlags & LOAD_CLASSIFY_URI) {
nsRefPtr<nsChannelClassifier> classifier = new nsChannelClassifier();
if (classifier) {
classifier->Start(this, false);
classifier->Start(this);
} else {
Cancel(NS_ERROR_OUT_OF_MEMORY);
}

View File

@ -237,12 +237,9 @@ nsChannelClassifier::NotifyTrackingProtectionDisabled(nsIChannel *aChannel)
}
void
nsChannelClassifier::Start(nsIChannel *aChannel, bool aContinueBeginConnect)
nsChannelClassifier::Start(nsIChannel *aChannel)
{
mChannel = aChannel;
if (aContinueBeginConnect) {
mChannelInternal = do_QueryInterface(aChannel);
}
nsresult rv = StartInternal();
if (NS_FAILED(rv)) {
@ -530,13 +527,7 @@ nsChannelClassifier::OnClassifyComplete(nsresult aErrorCode)
mChannel->Resume();
}
// Even if we have cancelled the channel, we may need to call
// ContinueBeginConnect so that we abort appropriately.
if (mChannelInternal) {
mChannelInternal->ContinueBeginConnect();
}
mChannel = nullptr;
mChannelInternal = nullptr;
return NS_OK;
}

View File

@ -21,10 +21,8 @@ public:
NS_DECL_NSIURICLASSIFIERCALLBACK
// Calls nsIURIClassifier.Classify with the principal of the given channel,
// and cancels the channel on a bad verdict. If callContinueBeginConnect is true,
// and aChannel is an nsIHttpChannelInternal, nsChannelClassifier must call
// nsIHttpChannelInternal.ContinueBeginConnect once Start has returned.
void Start(nsIChannel *aChannel, bool aContinueBeginConnect);
// and cancels the channel on a bad verdict.
void Start(nsIChannel *aChannel);
// Whether or not tracking protection should be enabled on this channel.
nsresult ShouldEnableTrackingProtection(nsIChannel *aChannel, bool *result);
@ -34,7 +32,6 @@ private:
// True if the channel has been suspended.
bool mSuspendedChannel;
nsCOMPtr<nsIChannel> mChannel;
nsCOMPtr<nsIHttpChannelInternal> mChannelInternal;
~nsChannelClassifier() {}
// Caches good classifications for the channel principal.

View File

@ -1535,15 +1535,6 @@ HttpBaseChannel::RedirectTo(nsIURI *newURI)
// HttpBaseChannel::nsIHttpChannelInternal
//-----------------------------------------------------------------------------
NS_IMETHODIMP
HttpBaseChannel::ContinueBeginConnect()
{
MOZ_ASSERT(XRE_GetProcessType() != GeckoProcessType_Default,
"The parent overrides this");
MOZ_ASSERT(false, "This method must be overridden");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
HttpBaseChannel::GetTopWindowURI(nsIURI **aTopWindowURI)
{

View File

@ -197,7 +197,6 @@ public:
NS_IMETHOD GetCorsMode(uint32_t* aCorsMode) override;
NS_IMETHOD SetCorsMode(uint32_t aCorsMode) override;
NS_IMETHOD GetTopWindowURI(nsIURI **aTopWindowURI) override;
NS_IMETHOD ContinueBeginConnect() override;
NS_IMETHOD GetProxyURI(nsIURI **proxyURI) override;
inline void CleanRedirectCacheChainIfNecessary()

View File

@ -4782,6 +4782,11 @@ nsHttpChannel::GetSecurityInfo(nsISupports **securityInfo)
return NS_OK;
}
// If any of the functions that AsyncOpen calls returns immediately an error
// AsyncAbort(which calls onStart/onStopRequest) does not need to be call.
// To be sure that they are not call ReleaseListeners() is called.
// If AsyncOpen returns NS_OK, after that point AsyncAbort must be called on
// any error.
NS_IMETHODIMP
nsHttpChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *context)
{
@ -4855,8 +4860,11 @@ nsHttpChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *context)
return rv;
}
// On error BeginConnect() should call AsyncAbort() before exiting until
// ContineBeginConnect after that it should not call it.
// BeginConnect() will not call AsyncAbort() on an error and if AsyncAbort needs
// to be called the function calling BeginConnect will need to call AsyncAbort.
// If BeginConnect is called from AsyncOpen, AsyncnAbort doesn't need to be
// called. If it is called form another function (e.g. the function is called
// from OnProxyAvailable) that function should call AsyncOpen.
nsresult
nsHttpChannel::BeginConnect()
{
@ -4881,14 +4889,12 @@ nsHttpChannel::BeginConnect()
if (NS_SUCCEEDED(rv))
rv = mURI->GetAsciiSpec(mSpec);
if (NS_FAILED(rv)) {
AsyncAbort(rv);
return rv;
}
// Reject the URL if it doesn't specify a host
if (host.IsEmpty()) {
rv = NS_ERROR_MALFORMED_URI;
AsyncAbort(rv);
return rv;
}
LOG(("host=%s port=%d\n", host.get(), port));
@ -4964,7 +4970,6 @@ nsHttpChannel::BeginConnect()
if (NS_SUCCEEDED(rv))
rv = mAuthProvider->Init(this);
if (NS_FAILED(rv)) {
AsyncAbort(rv);
return rv;
}
@ -4977,11 +4982,7 @@ nsHttpChannel::BeginConnect()
// Check to see if we should redirect this channel elsewhere by
// nsIHttpChannel.redirectTo API request
if (mAPIRedirectToURI) {
rv = AsyncCall(&nsHttpChannel::HandleAsyncAPIRedirect);
if (NS_FAILED(rv)) {
AsyncAbort(rv);
}
return rv;
return AsyncCall(&nsHttpChannel::HandleAsyncAPIRedirect);
}
// Check to see if this principal exists on local blocklists.
nsRefPtr<nsChannelClassifier> channelClassifier = new nsChannelClassifier();
@ -5094,33 +5095,43 @@ nsHttpChannel::BeginConnect()
}
mCaps &= ~NS_HTTP_ALLOW_PIPELINING;
}
if (!(mLoadFlags & LOAD_CLASSIFY_URI)) {
// On error ContinueBeginConnect() will call AsyncAbort so do not do it
// here
return ContinueBeginConnect();
// We may have been cancelled already, either by on-modify-request
// listeners or load group observers; in that case, we should not send the
// request to the server
if (mCanceled) {
return mStatus;
}
if (!(mLoadFlags & LOAD_CLASSIFY_URI)) {
ContinueBeginConnect();
return NS_OK;
}
// mLocalBlocklist is true only if tracking protection is enabled and the
// URI is a tracking domain, it makes no guarantees about phishing or
// malware, so if LOAD_CLASSIFY_URI is true we must call
// nsChannelClassifier to catch phishing and malware URIs.
bool callContinueBeginConnect = true;
if (mCanceled || !mLocalBlocklist) {
rv = ContinueBeginConnect();
if (NS_FAILED(rv)) {
// On error ContinueBeginConnect() will call AsyncAbort so do not do
// it here
return rv;
}
callContinueBeginConnect = false;
if (!mLocalBlocklist) {
// Here we call ContinueBeginConnectWithResult and not
// ContinueBeginConnect so that in the case of an error we do not start
// channelClassifier.
rv = ContinueBeginConnectWithResult();
if (NS_FAILED(rv)) {
return rv;
}
callContinueBeginConnect = false;
}
// nsChannelClassifier calls ContinueBeginConnect if it has not already
// been called, after optionally cancelling the channel once we have a
// remote verdict. We call a concrete class instead of an nsI* that might
// be overridden.
if (!mCanceled) {
LOG(("nsHttpChannel::Starting nsChannelClassifier %p [this=%p]",
channelClassifier.get(), this));
channelClassifier->Start(this, callContinueBeginConnect);
LOG(("nsHttpChannel::Starting nsChannelClassifier %p [this=%p]",
channelClassifier.get(), this));
channelClassifier->Start(this);
if (callContinueBeginConnect) {
ContinueBeginConnect();
}
return NS_OK;
}
@ -5158,28 +5169,39 @@ nsHttpChannel::SetPriority(int32_t value)
return NS_OK;
}
//-----------------------------------------------------------------------------
// nsHttpChannel::nsIHttpChannelInternal
//-----------------------------------------------------------------------------
NS_IMETHODIMP
nsHttpChannel::ContinueBeginConnect()
nsresult
nsHttpChannel::ContinueBeginConnectWithResult()
{
LOG(("nsHttpChannel::ContinueBeginConnect [this=%p]", this));
LOG(("nsHttpChannel::ContinueBeginConnectWithResult [this=%p]", this));
NS_PRECONDITION(!mCallOnResume, "How did that happen?");
nsresult rv;
// We may have been cancelled already, either by on-modify-request
// listeners or load group observers or nsChannelClassifier; in that case,
// we should not send the request to the server
if (mCanceled) {
if (mSuspendCount) {
LOG(("Waiting until resume to do async connect [this=%p]\n", this));
mCallOnResume = &nsHttpChannel::ContinueBeginConnect;
rv = NS_OK;
} else if (mCanceled) {
// We may have been cancelled already, by nsChannelClassifier in that
// case, we should not send the request to the server
rv = mStatus;
} else {
rv = Connect();
}
LOG(("nsHttpChannel::ContinueBeginConnectWithResult result [this=%p rv=%x "
"mCanceled=%i]\n", this, rv, mCanceled));
return rv;
}
void
nsHttpChannel::ContinueBeginConnect()
{
nsresult rv = ContinueBeginConnectWithResult();
if (NS_FAILED(rv)) {
LOG(("Calling AsyncAbort [rv=%x mCanceled=%i]\n", rv, mCanceled));
CloseCacheEntry(true);
AsyncAbort(rv);
}
return rv;
}
//-----------------------------------------------------------------------------
@ -5232,14 +5254,13 @@ nsHttpChannel::OnProxyAvailable(nsICancelable *request, nsIChannel *channel,
LOG(("nsHttpChannel::OnProxyAvailable [this=%p] "
"Handler no longer active.\n", this));
rv = NS_ERROR_NOT_AVAILABLE;
AsyncAbort(rv);
}
else {
// On error BeginConnect() will call AsyncAbort.
rv = BeginConnect();
}
if (NS_FAILED(rv)) {
AsyncAbort(rv);
Cancel(rv);
}
return rv;

View File

@ -126,7 +126,6 @@ public:
NS_IMETHOD AsyncOpen(nsIStreamListener *listener, nsISupports *aContext) override;
// nsIHttpChannelInternal
NS_IMETHOD SetupFallbackChannel(const char *aFallbackKey) override;
NS_IMETHOD ContinueBeginConnect() override;
// nsISupportsPriority
NS_IMETHOD SetPriority(int32_t value) override;
// nsIClassOfService
@ -241,6 +240,8 @@ private:
bool RequestIsConditional();
nsresult BeginConnect();
nsresult ContinueBeginConnectWithResult();
void ContinueBeginConnect();
nsresult Connect();
void SpeculativeConnect();
nsresult SetupTransaction();

View File

@ -38,7 +38,7 @@ interface nsIHttpUpgradeListener : nsISupports
* using any feature exposed by this interface, be aware that this interface
* will change and you will be broken. You have been warned.
*/
[scriptable, uuid(26833ec7-4555-4f23-9281-3a12d4b76db1)]
[scriptable, uuid(c025c35a-dda3-4a1d-9e6c-e02d7149ac79)]
interface nsIHttpChannelInternal : nsISupports
{
@ -253,12 +253,6 @@ interface nsIHttpChannelInternal : nsISupports
*/
attribute ACString networkInterfaceId;
/**
* Used only by nsChannelClassifier to resume connecting or abort the
* channel after a remote classification verdict.
*/
void continueBeginConnect();
/**
* Read the proxy URI, which, if non-null, will be used to resolve
* proxies for this channel.

View File

@ -0,0 +1,108 @@
const CC = Components.Constructor;
Cu.import("resource://gre/modules/Services.jsm");
const ServerSocket = CC("@mozilla.org/network/server-socket;1",
"nsIServerSocket",
"init");
var obs = Cc["@mozilla.org/observer-service;1"]
.getService(Ci.nsIObserverService);
var ios = Cc["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
// A server that waits for a connect. If a channel is suspended it should not
// try to connect to the server until it is is resumed or not try at all if it
// is cancelled as in this test.
function TestServer() {
this.listener = ServerSocket(-1, true, -1);
this.port = this.listener.port;
this.listener.asyncListen(this);
}
TestServer.prototype = {
onSocketAccepted: function(socket, trans) {
do_check_true(false, "Socket should not have tried to connect!");
},
onStopListening: function(socket) {
},
stop: function() {
try { this.listener.close(); } catch(ignore) {}
}
}
var requestListenerObserver = {
QueryInterface: function queryinterface(iid) {
if (iid.equals(Ci.nsISupports) ||
iid.equals(Ci.nsIObserver))
return this;
throw Components.results.NS_ERROR_NO_INTERFACE;
},
observe: function(subject, topic, data) {
if (topic === "http-on-modify-request" &&
subject instanceof Ci.nsIHttpChannel) {
var chan = subject.QueryInterface(Ci.nsIHttpChannel);
chan.suspend();
var obs = Cc["@mozilla.org/observer-service;1"].getService();
obs = obs.QueryInterface(Ci.nsIObserverService);
obs.removeObserver(this, "http-on-modify-request");
// Timers are bad, but we need to wait to see that we are not trying to
// connect to the server. There are no other event since nothing should
// happen until we resume the channel.
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
timer.initWithCallback(() => {
chan.cancel(Cr.NS_BINDING_ABORTED);
chan.resume();
}, 1000, Ci.nsITimer.TYPE_ONE_SHOT);
}
}
};
var listener = {
onStartRequest: function test_onStartR(request, ctx) {
},
onDataAvailable: function test_ODA() {
do_throw("Should not get any data!");
},
onStopRequest: function test_onStopR(request, ctx, status) {
do_execute_soon(run_next_test);
}
};
// Add observer and start a channel. Observer is going to suspend the channel on
// "http-on-modify-request" even. If a channel is suspended so early it should
// not try to connect at all until it is resumed. In this case we are going to
// wait for some time and cancel the channel before resuming it.
add_test(function testNoConnectChannelCanceledEarly() {
serv = new TestServer();
obs.addObserver(requestListenerObserver, "http-on-modify-request", false);
var chan = ios.newChannel2("http://localhost:" + serv.port,
"",
null,
null, // aLoadingNode
Services.scriptSecurityManager.getSystemPrincipal(),
null, // aTriggeringPrincipal
Ci.nsILoadInfo.SEC_NORMAL,
Ci.nsIContentPolicy.TYPE_OTHER);
chan.asyncOpen(listener, chan);
do_register_cleanup(function(){ serv.stop(); });
});
function run_test() {
run_next_test();
}

View File

@ -319,3 +319,4 @@ skip-if = os == "android"
[test_multipart_streamconv_application_package.js]
[test_safeoutputstream_append.js]
[test_packaged_app_service.js]
[test_suspend_channel_before_connect.js]

View File

@ -1078,7 +1078,7 @@
if (cell.childElt == "twisty")
return;
if (cell.col) {
if (cell.col && event.button == 0) {
if (cell.col.cycler) {
view.cycleCell(cell.row, cell.col);
return;

View File

@ -365,8 +365,12 @@ uint32_t UniqueStacks::Stack::GetOrAddIndex() const
uint32_t UniqueStacks::FrameKey::Hash() const
{
uint32_t hash = 0;
if (!mLocation.empty()) {
if (!mLocation.IsEmpty()) {
#ifdef SPS_STANDALONE
hash = mozilla::HashString(mLocation.c_str());
#else
hash = mozilla::HashString(mLocation.get());
#endif
}
if (mLine.isSome()) {
hash = mozilla::AddToHash(hash, *mLine);
@ -539,7 +543,11 @@ void UniqueStacks::StreamFrame(const OnStackFrameKey& aFrame)
#else
{
#endif
#ifdef SPS_STANDALONE
mUniqueStrings.WriteElement(mFrameTableWriter, aFrame.mLocation.c_str());
#else
mUniqueStrings.WriteElement(mFrameTableWriter, aFrame.mLocation.get());
#endif
if (aFrame.mLine.isSome()) {
mFrameTableWriter.NullElement(); // implementation
mFrameTableWriter.NullElement(); // optimizations

View File

@ -148,7 +148,13 @@ class UniqueStacks
{
public:
struct FrameKey {
#ifdef SPS_STANDALONE
std::string mLocation;
#else
// This cannot be a std::string, as it is not memmove compatible, which
// is used by nsHashTable
nsCString mLocation;
#endif
mozilla::Maybe<unsigned> mLine;
mozilla::Maybe<unsigned> mCategory;
mozilla::Maybe<void*> mJITAddress;

View File

@ -49,7 +49,7 @@ pref("dom.mozTCPSocket.enabled", true);
pref("general.smoothScroll", true);
// WebPayment
pref("dom.mozPay.enabled", true);
pref("dom.mozPay.enabled", false);
// System messages
pref("dom.sysmsg.enabled", true);

View File

@ -19,9 +19,6 @@ function test() {
ok(aResponse.tabs[0].styleEditorActor, "styleEditorActor set");
ok(aResponse.tabs[0].inspectorActor, "inspectorActor set");
ok(aResponse.tabs[0].traceActor, "traceActor set");
ok(aResponse.chromeDebugger, "chromeDebugger set");
ok(aResponse.consoleActor, "consoleActor set");
ok(aResponse.profilerActor, "profilerActor set");
ok(aResponse.deviceActor, "deviceActor set");
client.close(() => {

View File

@ -12,10 +12,10 @@ function test() {
providerUri: "https://example.com:443/webapprtChrome/webapprt/test/chrome/mozpay-success.html?req=",
message: "Success."
});
tests.push({
providerUri: "https://example.com:443/webapprtChrome/webapprt/test/chrome/mozpay-failure.html?req=",
message: "Chocolate rejected."
});
// tests.push({
// providerUri: "https://example.com:443/webapprtChrome/webapprt/test/chrome/mozpay-failure.html?req=",
// message: "Chocolate rejected."
// });
let jwt = "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJhdWQiOiAibW9j" +
"a3BheXByb3ZpZGVyLnBocGZvZ2FwcC5jb20iLCAiaXNzIjogIkVudGVyI" +
@ -36,23 +36,25 @@ function test() {
requestMethod: "GET"
};
let providerWindow;
let winObserver = function(win, topic) {
if (topic == "domwindowopened") {
win.addEventListener("load", function onLoadWindow() {
win.removeEventListener("load", onLoadWindow, false);
if (win.document.getElementById("content").getAttribute("src") ==
(tests[curTest].providerUri + jwt)) {
ok(true, "Payment provider window shown.");
providerWindow = win;
}
}, false);
}
}
Services.ww.registerNotification(winObserver);
// Disabled because the mozPay API is disabled, so the provider window
// won't be shown.
//
// let providerWindow;
// let winObserver = function(win, topic) {
// if (topic == "domwindowopened") {
// win.addEventListener("load", function onLoadWindow() {
// win.removeEventListener("load", onLoadWindow, false);
//
// if (win.document.getElementById("content") &&
// win.document.getElementById("content").getAttribute("src") ==
// (tests[curTest].providerUri + jwt)) {
// ok(true, "Payment provider window shown.");
// providerWindow = win;
// }
// }, false);
// }
// }
// Services.ww.registerNotification(winObserver);
let mutObserver = null;
@ -61,12 +63,12 @@ function test() {
mutObserver = new MutationObserver(function(mutations) {
is(msg.textContent, tests[curTest].message, "Got: " + tests[curTest].message);
if (!providerWindow) {
ok(false, "Payment provider window shown.");
} else {
providerWindow.close();
providerWindow = null;
}
// if (!providerWindow) {
// ok(false, "Payment provider window shown.");
// } else {
// providerWindow.close();
// providerWindow = null;
// }
runNextTest();
});
@ -76,7 +78,7 @@ function test() {
loadWebapp("mozpay.webapp", undefined, onLoad);
function runNextTest() {
providerWindow = null;
// providerWindow = null;
if (mutObserver) {
mutObserver.disconnect();
}
@ -97,7 +99,7 @@ function test() {
}
registerCleanupFunction(function() {
Services.ww.unregisterNotification(winObserver);
// Services.ww.unregisterNotification(winObserver);
mutObserver.disconnect();
});
}

View File

@ -32,13 +32,21 @@
"gInR5cCI6ICJtb2NrL3BheW1lbnRzL2luYXBwL3YxIn0.QZxc62USCy4U" +
"IyKIC1TKelVhNklvk-Ou1l_daKntaFI";
var request = navigator.mozPay(jwt);
request.onsuccess = function onsuccess() {
document.getElementById("msg").textContent = "Success.";
};
request.onerror = function onerror() {
document.getElementById("msg").textContent = request.error.name;
};
// mozPay is currently disabled in the desktop runtime, so we check
// that the property is set to null on the navigator object.
window.addEventListener("load", function() {
document.getElementById("msg").textContent =
(navigator.mozPay === null) ? "Success." : "navigator.mozPay defined";
}, false);
// This is the old code for checking the behavior of the API when enabled:
// var request = navigator.mozPay(jwt);
// request.onsuccess = function onsuccess() {
// document.getElementById("msg").textContent = "Success.";
// };
// request.onerror = function onerror() {
// document.getElementById("msg").textContent = request.error.name;
// };
</script>
<p id="msg">Webapp waiting to be paid...</p>
</body>